diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/IndicatorValue.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/IndicatorValue.java index ef817f5e..3569e254 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/IndicatorValue.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/session/IndicatorValue.java @@ -32,7 +32,7 @@ public interface IndicatorValue extends IndicatorValueHolder { return Constants.EMPTY_NOTE; } if (type.integerValue) { - return String.valueOf((int) indicatorValue.getValue()); + return String.valueOf((long) indicatorValue.getValue()); } else { return String.valueOf(indicatorValue.getValue()); } diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java index 851baf74..36186c37 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/action/ActionDefinition.java @@ -835,11 +835,21 @@ public enum ActionDefinition { FINISHED_EXAM_VIEW_LIST( new LocTextKey("sebserver.finished.action.list"), PageStateDefinitionImpl.FINISHED_EXAM_LIST), - VIEW_EXAM_FROM_FINISHED_LIST( + VIEW_FINISHED_EXAM_FROM_LIST( new LocTextKey("sebserver.finished.exam.action.list.view"), ImageIcon.SHOW, PageStateDefinitionImpl.FINISHED_EXAM, ActionCategory.FINISHED_EXAM_LIST), + VIEW_FINISHED_EXAM_CLIENT_CONNECTION( + new LocTextKey("sebserver.finished.exam.connection.action.view"), + ImageIcon.SHOW, + PageStateDefinitionImpl.FINISHED_CLIENT_CONNECTION, + ActionCategory.CLIENT_EVENT_LIST), + FINISHED_EXAM_BACK_TO_OVERVIEW( + new LocTextKey("sebserver.finished.exam.action.detail.view"), + ImageIcon.SHOW, + PageStateDefinitionImpl.FINISHED_EXAM, + ActionCategory.FORM), LOGS_USER_ACTIVITY_LIST( new LocTextKey("sebserver.logs.activity.userlogs"), diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/PageStateDefinitionImpl.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/PageStateDefinitionImpl.java index 8365bfa2..2a485fd2 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/PageStateDefinitionImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/activity/PageStateDefinitionImpl.java @@ -34,6 +34,7 @@ import ch.ethz.seb.sebserver.gui.content.exam.LmsSetupForm; import ch.ethz.seb.sebserver.gui.content.exam.LmsSetupList; import ch.ethz.seb.sebserver.gui.content.exam.QuizLookupList; import ch.ethz.seb.sebserver.gui.content.monitoring.FinishedExam; +import ch.ethz.seb.sebserver.gui.content.monitoring.FinishedExamClientConnection; import ch.ethz.seb.sebserver.gui.content.monitoring.FinishedExamList; import ch.ethz.seb.sebserver.gui.content.monitoring.MonitoringClientConnection; import ch.ethz.seb.sebserver.gui.content.monitoring.MonitoringRunningExam; @@ -100,7 +101,7 @@ public enum PageStateDefinitionImpl implements PageStateDefinition { FINISHED_EXAM_LIST(Type.LIST_VIEW, FinishedExamList.class, ActivityDefinition.FINISHED_EXAMS), FINISHED_EXAM(Type.FORM_VIEW, FinishedExam.class, ActivityDefinition.FINISHED_EXAMS), - FINISHED_CLIENT_CONNECTION(Type.FORM_VIEW, MonitoringClientConnection.class, ActivityDefinition.FINISHED_EXAMS), + FINISHED_CLIENT_CONNECTION(Type.FORM_VIEW, FinishedExamClientConnection.class, ActivityDefinition.FINISHED_EXAMS), USER_ACTIVITY_LOGS(Type.LIST_VIEW, UserActivityLogs.class, ActivityDefinition.USER_ACTIVITY_LOGS), SEB_CLIENT_LOGS(Type.LIST_VIEW, SEBClientEvents.class, ActivityDefinition.SEB_CLIENT_LOGS) diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamForm.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamForm.java index ae492339..78928680 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamForm.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamForm.java @@ -244,10 +244,9 @@ public class ExamForm implements TemplateComposer { final boolean modifyGrant = userGrantCheck.m(); final boolean writeGrant = userGrantCheck.w(); final ExamStatus examStatus = exam.getStatus(); - final boolean editable = modifyGrant && (examStatus == ExamStatus.UP_COMING || - examStatus == ExamStatus.RUNNING); + final boolean editable = modifyGrant && + (examStatus == ExamStatus.UP_COMING || examStatus == ExamStatus.RUNNING); -// TODO this is not performat try to improve by doing one check with the CheckExamConsistency above final boolean sebRestrictionAvailable = testSEBRestrictionAPI(exam); final boolean isRestricted = readonly && sebRestrictionAvailable && this.restService .getBuilder(CheckSEBRestriction.class) @@ -408,6 +407,11 @@ public class ExamForm implements TemplateComposer { .withEntityKey(entityKey) .publishIf(() -> modifyGrant && readonly && editable) + .newAction(ActionDefinition.EXAM_DELETE) + .withEntityKey(entityKey) + .withExec(this.examDeletePopup.deleteWizardFunction(pageContext)) + .publishIf(() -> writeGrant && readonly) + .newAction(ActionDefinition.EXAM_SAVE) .withExec(action -> (importFromQuizData) ? importExam(action, formHandle, sebRestrictionAvailable && exam.status == ExamStatus.RUNNING) @@ -451,20 +455,15 @@ public class ExamForm implements TemplateComposer { .newAction(ActionDefinition.EXAM_PROCTORING_ON) .withEntityKey(entityKey) - .withExec(this.examProctoringSettings.settingsFunction(this.pageService, modifyGrant)) + .withExec(this.examProctoringSettings.settingsFunction(this.pageService, modifyGrant && editable)) .noEventPropagation() - .publishIf(() -> editable && proctoringEnabled && readonly) + .publishIf(() -> proctoringEnabled && readonly) .newAction(ActionDefinition.EXAM_PROCTORING_OFF) .withEntityKey(entityKey) - .withExec(this.examProctoringSettings.settingsFunction(this.pageService, modifyGrant)) + .withExec(this.examProctoringSettings.settingsFunction(this.pageService, modifyGrant && editable)) .noEventPropagation() - .publishIf(() -> editable && !proctoringEnabled && readonly) - - .newAction(ActionDefinition.EXAM_DELETE) - .withEntityKey(entityKey) - .withExec(this.examDeletePopup.deleteWizardFunction(pageContext)) - .publishIf(() -> writeGrant && readonly); + .publishIf(() -> !proctoringEnabled && readonly); // additional data in read-only view if (readonly && !importFromQuizData) { 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 e1b40515..4dae0d35 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 @@ -9,6 +9,7 @@ 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; @@ -17,26 +18,34 @@ 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; +import ch.ethz.seb.sebserver.gbl.model.exam.Exam; 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; import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; -import ch.ethz.seb.sebserver.gui.service.ResourceService; import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; import ch.ethz.seb.sebserver.gui.service.page.PageContext; import ch.ethz.seb.sebserver.gui.service.page.PageService; import ch.ethz.seb.sebserver.gui.service.page.PageService.PageActionBuilder; import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; +import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction; import ch.ethz.seb.sebserver.gui.service.push.ServerPushService; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExam; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicators; import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetFinishedExamClientConnectionPage; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; import ch.ethz.seb.sebserver.gui.table.ColumnDefinition; import ch.ethz.seb.sebserver.gui.table.ColumnDefinition.TableFilterAttribute; +import ch.ethz.seb.sebserver.gui.table.EntityTable; import ch.ethz.seb.sebserver.gui.table.TableBuilder; import ch.ethz.seb.sebserver.gui.table.TableFilter.CriteriaType; @@ -45,6 +54,8 @@ import ch.ethz.seb.sebserver.gui.table.TableFilter.CriteriaType; @GuiProfile public class FinishedExam implements TemplateComposer { + private static final LocTextKey EMPTY_SELECTION_TEXT_KEY = + new LocTextKey("sebserver.finished.exam.connection.emptySelection"); private static final LocTextKey TITLE_TEXT_KEY = new LocTextKey("sebserver.finished.exam.connections.title"); private static final LocTextKey EMPTY_LIST_TEXT_KEY = @@ -64,7 +75,6 @@ public class FinishedExam implements TemplateComposer { private final PageService pageService; private final RestService restService; - private final ResourceService resourceService; private final int pageSize; public FinishedExam( @@ -74,7 +84,6 @@ public class FinishedExam implements TemplateComposer { this.pageService = pageService; this.restService = pageService.getRestService(); - this.resourceService = pageService.getResourceService(); this.pageSize = pageSize; this.statusFilter = new TableFilterAttribute( @@ -86,19 +95,29 @@ public class FinishedExam implements TemplateComposer { @Override public void compose(final PageContext pageContext) { final EntityKey examKey = pageContext.getEntityKey(); + final CurrentUser currentUser = this.pageService.getResourceService().getCurrentUser(); + final UserInfo user = currentUser.get(); - final RestService restService = this.pageService.getRestService(); + final RestService restService = this.pageService + .getRestService(); final PageActionBuilder actionBuilder = this.pageService .pageActionBuilder(pageContext.clearEntityKeys()); - - final Collection indicators = restService.getBuilder(GetIndicators.class) + final Collection indicators = restService + .getBuilder(GetIndicators.class) .withQueryParam(Indicator.FILTER_ATTR_EXAM_ID, examKey.modelId) .call() .getOrThrow(); + final Exam exam = this.restService.getBuilder(GetExam.class) + .withURIVariable(API.PARAM_MODEL_ID, examKey.modelId) + .call() + .getOrThrow(); + final boolean supporting = user.hasRole(UserRole.EXAM_SUPPORTER) && + exam.supporter.contains(user.uuid); + final BooleanSupplier isExamSupporter = () -> supporting || user.hasRole(UserRole.EXAM_ADMIN); final Composite content = this.pageService.getWidgetFactory().defaultPageLayout( pageContext.getParent(), - TITLE_TEXT_KEY); + new LocTextKey(TITLE_TEXT_KEY.name, exam.getName())); final TableBuilder tableBuilder = this.pageService.entityTableBuilder(restService.getRestCall(GetFinishedExamClientConnectionPage.class)) @@ -126,21 +145,30 @@ public class FinishedExam implements TemplateComposer { .withFilter(this.statusFilter)) .withDefaultAction(t -> actionBuilder - .newAction(ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION) + .newAction(ActionDefinition.VIEW_FINISHED_EXAM_CLIENT_CONNECTION) .withParentEntityKey(examKey) .create()); indicators.stream().forEach(indicator -> { - tableBuilder.withColumn(new ColumnDefinition<>( - indicator.name, - new LocTextKey(indicator.name), - indicatorValueFunction(indicator))); + if (indicator.type != IndicatorType.LAST_PING) { + tableBuilder.withColumn(new ColumnDefinition<>( + indicator.name, + new LocTextKey(indicator.name), + indicatorValueFunction(indicator))); + } }); - tableBuilder.compose(pageContext.copyOf(content)); + final EntityTable table = tableBuilder.compose(pageContext.copyOf(content)); + + actionBuilder + + .newAction(ActionDefinition.VIEW_FINISHED_EXAM_CLIENT_CONNECTION) + .withParentEntityKey(examKey) + .withSelect(table::getSelection, PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY) + .publishIf(isExamSupporter, false); } - private Function indicatorValueFunction(final Indicator indicator) { + public Function indicatorValueFunction(final Indicator indicator) { return clientConnectionData -> { return clientConnectionData.indicatorValues .stream() diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/FinishedExamClientConnection.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/FinishedExamClientConnection.java new file mode 100644 index 00000000..e5a1936e --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/FinishedExamClientConnection.java @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.gui.content.monitoring; + +import java.util.Collection; +import java.util.function.BooleanSupplier; + +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.api.EntityType; +import ch.ethz.seb.sebserver.gbl.model.Domain; +import ch.ethz.seb.sebserver.gbl.model.EntityKey; +import ch.ethz.seb.sebserver.gbl.model.exam.Exam; +import ch.ethz.seb.sebserver.gbl.model.exam.Indicator; +import ch.ethz.seb.sebserver.gbl.model.exam.QuizData; +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.ClientEvent; +import ch.ethz.seb.sebserver.gbl.model.session.ExtendedClientEvent; +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; +import ch.ethz.seb.sebserver.gbl.util.Utils; +import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition; +import ch.ethz.seb.sebserver.gui.form.FormBuilder; +import ch.ethz.seb.sebserver.gui.service.ResourceService; +import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport; +import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey; +import ch.ethz.seb.sebserver.gui.service.page.PageContext; +import ch.ethz.seb.sebserver.gui.service.page.PageService; +import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExam; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicators; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.logs.GetExtendedClientEventPage; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetFinishedExamClientConnection; +import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser; +import ch.ethz.seb.sebserver.gui.table.ColumnDefinition; +import ch.ethz.seb.sebserver.gui.table.ColumnDefinition.TableFilterAttribute; +import ch.ethz.seb.sebserver.gui.table.TableFilter.CriteriaType; +import ch.ethz.seb.sebserver.gui.widget.WidgetFactory; + +@Lazy +@Component +@GuiProfile +public class FinishedExamClientConnection implements TemplateComposer { + + private static final LocTextKey PAGE_TITLE_KEY = + new LocTextKey("sebserver.finished.exam.connection.title"); + + private final static LocTextKey EXAM_NAME_TEXT_KEY = + new LocTextKey("sebserver.finished.connection.form.exam"); + private final static LocTextKey CONNECTION_ID_TEXT_KEY = + new LocTextKey("sebserver.finished.connection.form.id"); + private final static LocTextKey CONNECTION_INFO_TEXT_KEY = + new LocTextKey("sebserver.finished.connection.form.info"); + private final static LocTextKey CONNECTION_STATUS_TEXT_KEY = + new LocTextKey("sebserver.finished.connection.form.status"); + + private static final LocTextKey EVENT_LIST_TITLE_KEY = + new LocTextKey("sebserver.finished.exam.connection.eventlist.title"); + private static final LocTextKey EVENT_LIST_TITLE_TOOLTIP_KEY = + new LocTextKey("sebserver.finished.exam.connection.eventlist.title.tooltip"); + private static final LocTextKey EMPTY_LIST_TEXT_KEY = + new LocTextKey("sebserver.finished.exam.connection.eventlist.empty"); + private static final LocTextKey LIST_COLUMN_TYPE_KEY = + new LocTextKey("sebserver.finished.exam.connection.eventlist.type"); + + private static final LocTextKey LIST_COLUMN_CLIENT_TIME_KEY = + new LocTextKey("sebserver.finished.exam.connection.eventlist.clienttime"); + private static final LocTextKey LIST_COLUMN_SERVER_TIME_KEY = + new LocTextKey("sebserver.finished.exam.connection.eventlist.servertime"); + private static final LocTextKey LIST_COLUMN_VALUE_KEY = + new LocTextKey("sebserver.finished.exam.connection.eventlist.value"); + private static final LocTextKey LIST_COLUMN_TEXT_KEY = + new LocTextKey("sebserver.finished.exam.connection.eventlist.text"); + + private final PageService pageService; + private final ResourceService resourceService; + private final I18nSupport i18nSupport; + private final SEBClientEventDetailsPopup sebClientLogDetailsPopup; + private final int pageSize; + + private final TableFilterAttribute typeFilter; + private final TableFilterAttribute textFilter = + new TableFilterAttribute(CriteriaType.TEXT, ClientEvent.FILTER_ATTR_TEXT); + + protected FinishedExamClientConnection( + final PageService pageService, + final SEBClientEventDetailsPopup sebClientLogDetailsPopup, + @Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) { + + this.pageService = pageService; + this.resourceService = pageService.getResourceService(); + this.i18nSupport = this.resourceService.getI18nSupport(); + this.sebClientLogDetailsPopup = sebClientLogDetailsPopup; + this.pageSize = pageSize; + + this.typeFilter = new TableFilterAttribute( + CriteriaType.SINGLE_SELECTION, + Domain.CLIENT_EVENT.ATTR_TYPE, + this.resourceService::clientEventTypeResources); + } + + @Override + public void compose(final PageContext pageContext) { + final RestService restService = this.resourceService.getRestService(); + final WidgetFactory widgetFactory = this.pageService.getWidgetFactory(); + final CurrentUser currentUser = this.resourceService.getCurrentUser(); + final EntityKey parentEntityKey = pageContext.getParentEntityKey(); + final EntityKey entityKey = pageContext.getEntityKey(); + + // content page layout with title + final Composite content = widgetFactory.defaultPageLayout( + pageContext.getParent(), + PAGE_TITLE_KEY); + final Exam exam = restService + .getBuilder(GetExam.class) + .withURIVariable(API.PARAM_MODEL_ID, parentEntityKey.modelId) + .call() + .onError(error -> pageContext.notifyLoadError(EntityType.EXAM, error)) + .getOrThrow(); + final UserInfo user = currentUser.get(); + final boolean supporting = user.hasRole(UserRole.EXAM_SUPPORTER) && + exam.supporter.contains(user.uuid); + final BooleanSupplier isExamSupporter = () -> supporting || user.hasRole(UserRole.EXAM_ADMIN); + final Collection indicators = restService + .getBuilder(GetIndicators.class) + .withQueryParam(Indicator.FILTER_ATTR_EXAM_ID, parentEntityKey.modelId) + .call() + .getOrThrow(); + final ClientConnectionData connectionData = restService + .getBuilder(GetFinishedExamClientConnection.class) + .withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId) + .call() + .getOrThrow(); + + final FormBuilder formBuilder = this.pageService.formBuilder(pageContext.copyOf(content)) + .readonly(true) + .addField(FormBuilder.text( + QuizData.QUIZ_ATTR_NAME, + EXAM_NAME_TEXT_KEY, + exam.getName())) + .addField(FormBuilder.text( + Domain.CLIENT_CONNECTION.ATTR_EXAM_USER_SESSION_ID, + CONNECTION_ID_TEXT_KEY, + connectionData.clientConnection.userSessionId)) + .addField(FormBuilder.text( + ClientConnection.ATTR_INFO, + CONNECTION_INFO_TEXT_KEY, + connectionData.clientConnection.info)) + .withDefaultSpanInput(3) + .addField(FormBuilder.text( + Domain.CLIENT_CONNECTION.ATTR_STATUS, + CONNECTION_STATUS_TEXT_KEY, + this.resourceService.localizedClientConnectionStatusName( + connectionData.clientConnection.status)) + .asColorBox()) + .addEmptyCell(); + + indicators.forEach(indicator -> formBuilder.addField(FormBuilder.text( + indicator.name, + new LocTextKey(indicator.name), + connectionData.indicatorValues + .stream() + .filter(indicatorValue -> indicatorValue.getIndicatorId().equals(indicator.id)) + .findFirst() + .map(iv -> IndicatorValue.getDisplayValue(iv, indicator.type)) + .orElse(Constants.EMPTY_NOTE)) + .asColorBox() + .withDefaultLabel(indicator.name)) + .addEmptyCell()); + + formBuilder.build(); + + // CLIENT EVENTS + final PageService.PageActionBuilder actionBuilder = this.pageService + .pageActionBuilder( + pageContext + .clearAttributes() + .clearEntityKeys()); + + widgetFactory.addFormSubContextHeader( + content, + EVENT_LIST_TITLE_KEY, + EVENT_LIST_TITLE_TOOLTIP_KEY); + + // client event table for this connection + this.pageService + .entityTableBuilder( + "seb-client-" + connectionData.getModelId(), + restService.getRestCall(GetExtendedClientEventPage.class)) + .withEmptyMessage(EMPTY_LIST_TEXT_KEY) + .withPaging(this.pageSize) + .withRestCallAdapter(restCallBuilder -> restCallBuilder.withQueryParam( + ClientEvent.FILTER_ATTR_CONNECTION_ID, + entityKey.modelId)) + + .withColumn(new ColumnDefinition( + Domain.CLIENT_EVENT.ATTR_TYPE, + LIST_COLUMN_TYPE_KEY, + this.resourceService::getEventTypeName) + .withFilter(this.typeFilter) + .sortable() + .widthProportion(2)) + + .withColumn(new ColumnDefinition( + Domain.CLIENT_EVENT.ATTR_TEXT, + LIST_COLUMN_TEXT_KEY, + ClientEvent::getText) + .withFilter(this.textFilter) + .sortable() + .withCellTooltip() + .widthProportion(4)) + + .withColumn(new ColumnDefinition( + Domain.CLIENT_EVENT.ATTR_NUMERIC_VALUE, + LIST_COLUMN_VALUE_KEY, + ClientEvent::getValue) + .widthProportion(1)) + + .withColumn(new ColumnDefinition( + Domain.CLIENT_EVENT.ATTR_CLIENT_TIME, + new LocTextKey(LIST_COLUMN_CLIENT_TIME_KEY.name, + this.i18nSupport.getUsersTimeZoneTitleSuffix()), + this::getClientTime) + .sortable() + .widthProportion(1)) + + .withColumn(new ColumnDefinition( + Domain.CLIENT_EVENT.ATTR_SERVER_TIME, + new LocTextKey(LIST_COLUMN_SERVER_TIME_KEY.name, + this.i18nSupport.getUsersTimeZoneTitleSuffix()), + this::getServerTime) + .sortable() + .widthProportion(1)) + + .withDefaultAction(t -> actionBuilder + .newAction(ActionDefinition.LOGS_SEB_CLIENT_SHOW_DETAILS) + .withExec(action -> this.sebClientLogDetailsPopup.showDetails(action, + t.getSingleSelectedROWData())) + .noEventPropagation() + .create()) + + .compose(pageContext.copyOf(content)); + + actionBuilder + .newAction(ActionDefinition.FINISHED_EXAM_BACK_TO_OVERVIEW) + .withEntityKey(parentEntityKey) + .publishIf(isExamSupporter); + } + + private String getClientTime(final ClientEvent event) { + if (event == null || event.getClientTime() == null) { + return Constants.EMPTY_NOTE; + } + + return this.i18nSupport + .formatDisplayTime(Utils.toDateTimeUTC(event.getClientTime())); + } + + private String getServerTime(final ClientEvent event) { + if (event == null || event.getServerTime() == null) { + return Constants.EMPTY_NOTE; + } + + return this.i18nSupport + .formatDisplayTime(Utils.toDateTimeUTC(event.getServerTime())); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/FinishedExamList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/FinishedExamList.java index 19fa548d..ff21ef96 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/FinishedExamList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/FinishedExamList.java @@ -129,18 +129,18 @@ public class FinishedExamList implements TemplateComposer { .sortable()) .withDefaultAction(actionBuilder - .newAction(ActionDefinition.VIEW_EXAM_FROM_FINISHED_LIST) + .newAction(ActionDefinition.VIEW_FINISHED_EXAM_FROM_LIST) .create()) .withSelectionListener(this.pageService.getSelectionPublisher( pageContext, - ActionDefinition.VIEW_EXAM_FROM_FINISHED_LIST)) + ActionDefinition.VIEW_FINISHED_EXAM_FROM_LIST)) .compose(pageContext.copyOf(content)); actionBuilder - .newAction(ActionDefinition.VIEW_EXAM_FROM_FINISHED_LIST) + .newAction(ActionDefinition.VIEW_FINISHED_EXAM_FROM_LIST) .withSelect(table::getSelection, PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY) .publishIf(() -> currentUser.get().hasRole(UserRole.EXAM_SUPPORTER), false); diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringClientConnection.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringClientConnection.java index 5a18104a..aceef164 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringClientConnection.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringClientConnection.java @@ -90,6 +90,11 @@ public class MonitoringClientConnection implements TemplateComposer { private static final LocTextKey NOTIFICATION_LIST_COLUMN_TYPE_KEY = new LocTextKey("sebserver.monitoring.exam.connection.notificationlist.type"); + private static final LocTextKey CONFIRM_QUIT = + new LocTextKey("sebserver.monitoring.exam.connection.action.instruction.quit.confirm"); + private static final LocTextKey CONFIRM_OPEN_SINGLE_ROOM = + new LocTextKey("sebserver.monitoring.exam.connection.action.singleroom.confirm"); + private static final LocTextKey EVENT_LIST_TITLE_KEY = new LocTextKey("sebserver.monitoring.exam.connection.eventlist.title"); private static final LocTextKey EVENT_LIST_TITLE_TOOLTIP_KEY = @@ -107,10 +112,6 @@ public class MonitoringClientConnection implements TemplateComposer { new LocTextKey("sebserver.monitoring.exam.connection.eventlist.value"); private static final LocTextKey LIST_COLUMN_TEXT_KEY = new LocTextKey("sebserver.monitoring.exam.connection.eventlist.text"); - private static final LocTextKey CONFIRM_QUIT = - new LocTextKey("sebserver.monitoring.exam.connection.action.instruction.quit.confirm"); - private static final LocTextKey CONFIRM_OPEN_SINGLE_ROOM = - new LocTextKey("sebserver.monitoring.exam.connection.action.singleroom.confirm"); private final ServerPushService serverPushService; private final PageService pageService; diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringRunningExam.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringRunningExam.java index 21800970..0a953fd5 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringRunningExam.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/monitoring/MonitoringRunningExam.java @@ -70,8 +70,6 @@ import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService @GuiProfile public class MonitoringRunningExam implements TemplateComposer { - //private static final Logger log = LoggerFactory.getLogger(MonitoringRunningExam.class); - private static final LocTextKey EMPTY_SELECTION_TEXT_KEY = new LocTextKey("sebserver.monitoring.exam.connection.emptySelection"); private static final LocTextKey EMPTY_ACTIVE_SELECTION_TEXT_KEY = diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/AbstractLogLevelCountIndicator.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/AbstractLogLevelCountIndicator.java index 55461a1f..769cacf1 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/AbstractLogLevelCountIndicator.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/AbstractLogLevelCountIndicator.java @@ -55,8 +55,8 @@ public abstract class AbstractLogLevelCountIndicator extends AbstractLogIndicato @Override public double computeValueAt(final long timestamp) { - if (log.isDebugEnabled()) { - log.debug("computeValueAt: {}", timestamp); + if (log.isTraceEnabled()) { + log.trace("computeValueAt: {}", timestamp); } try { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/AbstractLogNumberIndicator.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/AbstractLogNumberIndicator.java index 994c307f..2ad92b56 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/AbstractLogNumberIndicator.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/indicator/AbstractLogNumberIndicator.java @@ -73,8 +73,8 @@ public abstract class AbstractLogNumberIndicator extends AbstractLogIndicator { @Override public double computeValueAt(final long timestamp) { - if (log.isDebugEnabled()) { - log.debug("computeValueAt: {}", timestamp); + if (log.isTraceEnabled()) { + log.trace("computeValueAt: {}", timestamp); } try { diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index c5d5dd54..a7225916 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -1831,7 +1831,6 @@ sebserver.monitoring.exam.connection.notificationlist.pleaseSelect=At first plea sebserver.monitoring.exam.connection.notificationlist.title=Pending Notification sebserver.monitoring.exam.connection.notificationlist.title.tooltip=All pending notifications sent by the SEB Client - sebserver.monitoring.exam.connection.eventlist.title=Events sebserver.monitoring.exam.connection.eventlist.title.tooltip=All events and logs sent by the SEB Client sebserver.monitoring.exam.connection.eventlist.empty=No event found @@ -1888,12 +1887,38 @@ sebserver.finished.exam.list.column.endTime=End Time {0} sebserver.finished.exam.list.column.endTime.tooltip=The end date and time of the exam

{0} sebserver.finished.exam.action.list.view=View Finished Exam -sebserver.finished.exam.connections.title=Search Connections +sebserver.finished.exam.connections.title=Finished Exam ({0}) sebserver.finished.exam.connections.action=Search sebserver.finished.exam.connections.empty=No Client Connections available sebserver.finished.exam.connections.name=Session or User Name sebserver.finished.exam.connections.info=Connection Info sebserver.finished.exam.connections.status=Status +sebserver.finished.exam.connection.emptySelection=At first please select a Connection from the list + +sebserver.finished.exam.connection.title=SEB Client Connection +sebserver.finished.connection.form.id=User Name or Session +sebserver.finished.connection.form.id.tooltip=The user session identifier or username sent by the SEB client after LMS login +sebserver.finished.connection.form.info=Connection Info +sebserver.finished.connection.form.info.tooltip=Format: IP Address,SEB Version, OSName +sebserver.finished.connection.form.status=Status +sebserver.finished.connection.form.status.tooltip=The current connection status +sebserver.finished.connection.form.exam=Exam +sebserver.finished.connection.form.exam.tooltip=The exam name + +sebserver.finished.exam.connection.eventlist.title=Events +sebserver.finished.exam.connection.eventlist.title.tooltip=All events and logs sent by the SEB Client +sebserver.finished.exam.connection.eventlist.empty=No event found +sebserver.finished.exam.connection.eventlist.type=Event Type +sebserver.finished.exam.connection.eventlist.type.tooltip=The type of the log event

Use the filter above to set a specific event type
{0} +sebserver.finished.exam.connection.eventlist.clienttime=Client Time {0} +sebserver.finished.exam.connection.eventlist.clienttime.tooltip=The time the SEB client has sent within the log event

{0} +sebserver.finished.exam.connection.eventlist.servertime=Server Time {0} +sebserver.finished.exam.connection.eventlist.servertime.tooltip=The exact time (UTC) the SEB Server has received the log event

{0} +sebserver.finished.exam.connection.eventlist.value=Value +sebserver.finished.exam.connection.eventlist.value.tooltip=The value of the log event

{0} +sebserver.finished.exam.connection.eventlist.text=Text +sebserver.finished.exam.connection.eventlist.text.tooltip=The text of the log event

{0} +sebserver.finished.exam.action.detail.view=Back To Exam ################################ # Logs