SEBSERV-147: finished todos
This commit is contained in:
parent
69f8d6cd4a
commit
433aad87df
12 changed files with 157 additions and 39 deletions
12
pom.xml
12
pom.xml
|
@ -25,6 +25,7 @@
|
|||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
</properties>
|
||||
|
||||
|
||||
<!-- NOTE: There currently are two profiles, a default one to build on
|
||||
Java 11 (from eclipse and command-line) and one to build still on Java 8
|
||||
to support the Jenkins build on CI-Server that still no Java 11 installed -->
|
||||
|
@ -45,6 +46,17 @@
|
|||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<release>${java.version}</release>
|
||||
<encoding>UTF-8</encoding>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<encoding>UTF-8</encoding>
|
||||
<systemPropertyVariables>
|
||||
<file.encoding>UTF-8</file.encoding>
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
|
|
@ -12,6 +12,7 @@ import java.util.Base64;
|
|||
import java.util.Base64.Encoder;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.eclipse.rap.rwt.RWT;
|
||||
|
@ -203,25 +204,15 @@ public class MonitoringClientConnection implements TemplateComposer {
|
|||
getConnectionData,
|
||||
indicators);
|
||||
|
||||
this.serverPushService.runServerPush(
|
||||
new ServerPushContext(
|
||||
content,
|
||||
Utils.truePredicate(),
|
||||
MonitoringRunningExam.createServerPushUpdateErrorHandler(this.pageService, pageContext)),
|
||||
this.pollInterval,
|
||||
context1 -> clientConnectionDetails.updateData(),
|
||||
context -> clientConnectionDetails.updateGUI());
|
||||
|
||||
final PageService.PageActionBuilder actionBuilder = this.pageService
|
||||
.pageActionBuilder(
|
||||
pageContext
|
||||
.clearAttributes()
|
||||
.clearEntityKeys());
|
||||
|
||||
// NOTIFICATIONS
|
||||
final boolean hasNotification = BooleanUtils.isTrue(connectionData.pendingNotification());
|
||||
if (hasNotification) {
|
||||
// add notification table
|
||||
final boolean hasNotifications = BooleanUtils.isTrue(connectionData.pendingNotification());
|
||||
Supplier<EntityTable<ClientNotification>> _notificationTableSupplier = () -> null;
|
||||
if (hasNotifications) {
|
||||
final PageService.PageActionBuilder actionBuilder = this.pageService
|
||||
.pageActionBuilder(
|
||||
pageContext
|
||||
.clearAttributes()
|
||||
.clearEntityKeys());
|
||||
|
||||
widgetFactory.addFormSubContextHeader(
|
||||
content,
|
||||
|
@ -281,9 +272,28 @@ public class MonitoringClientConnection implements TemplateComposer {
|
|||
NOTIFICATION_LIST_NO_SELECTION_KEY)
|
||||
|
||||
.publishIf(() -> currentUser.get().hasRole(UserRole.EXAM_SUPPORTER), false);
|
||||
|
||||
_notificationTableSupplier = () -> notificationTable;
|
||||
}
|
||||
|
||||
final Supplier<EntityTable<ClientNotification>> notificationTableSupplier = _notificationTableSupplier;
|
||||
// server push update
|
||||
this.serverPushService.runServerPush(
|
||||
new ServerPushContext(
|
||||
content,
|
||||
Utils.truePredicate(),
|
||||
MonitoringRunningExam.createServerPushUpdateErrorHandler(this.pageService, pageContext)),
|
||||
this.pollInterval,
|
||||
context -> clientConnectionDetails.updateData(),
|
||||
context -> clientConnectionDetails.updateGUI(notificationTableSupplier, pageContext));
|
||||
|
||||
// CLIENT EVENTS
|
||||
final PageService.PageActionBuilder actionBuilder = this.pageService
|
||||
.pageActionBuilder(
|
||||
pageContext
|
||||
.clearAttributes()
|
||||
.clearEntityKeys());
|
||||
|
||||
widgetFactory.addFormSubContextHeader(
|
||||
content,
|
||||
EVENT_LIST_TITLE_KEY,
|
||||
|
|
|
@ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.gui.service.session;
|
|||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.eclipse.swt.graphics.Color;
|
||||
|
@ -23,8 +24,11 @@ import ch.ethz.seb.sebserver.gbl.model.Domain;
|
|||
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.ConnectionStatus;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientNotification;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.IndicatorValue;
|
||||
import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
|
||||
import ch.ethz.seb.sebserver.gui.form.Form;
|
||||
import ch.ethz.seb.sebserver.gui.form.FormBuilder;
|
||||
import ch.ethz.seb.sebserver.gui.form.FormHandle;
|
||||
|
@ -32,8 +36,11 @@ 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.event.ActionEvent;
|
||||
import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
|
||||
import ch.ethz.seb.sebserver.gui.service.session.IndicatorData.ThresholdColor;
|
||||
import ch.ethz.seb.sebserver.gui.table.EntityTable;
|
||||
|
||||
public class ClientConnectionDetails {
|
||||
|
||||
|
@ -50,6 +57,7 @@ public class ClientConnectionDetails {
|
|||
|
||||
private static final int NUMBER_OF_NONE_INDICATOR_ROWS = 3;
|
||||
|
||||
private final PageService pageService;
|
||||
private final ResourceService resourceService;
|
||||
private final Map<Long, IndicatorData> indicatorMapping;
|
||||
private final RestCall<ClientConnectionData>.RestCallBuilder restCallBuilder;
|
||||
|
@ -69,6 +77,7 @@ public class ClientConnectionDetails {
|
|||
|
||||
final Display display = pageContext.getRoot().getDisplay();
|
||||
|
||||
this.pageService = pageService;
|
||||
this.resourceService = pageService.getResourceService();
|
||||
this.restCallBuilder = restCallBuilder;
|
||||
this.colorData = new ColorData(display);
|
||||
|
@ -132,9 +141,13 @@ public class ClientConnectionDetails {
|
|||
.toBoolean(connectionData.missingPing);
|
||||
}
|
||||
this.connectionData = connectionData;
|
||||
|
||||
}
|
||||
|
||||
public void updateGUI() {
|
||||
public void updateGUI(
|
||||
final Supplier<EntityTable<ClientNotification>> notificationTableSupplier,
|
||||
final PageContext pageContext) {
|
||||
|
||||
if (this.connectionData == null) {
|
||||
return;
|
||||
}
|
||||
|
@ -192,6 +205,29 @@ public class ClientConnectionDetails {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
// update notifications
|
||||
final EntityTable<ClientNotification> notificationTable = notificationTableSupplier.get();
|
||||
if (notificationTable != null && this.connectionData.clientConnection.status == ConnectionStatus.CLOSED) {
|
||||
reloadPage(pageContext);
|
||||
} else {
|
||||
if (BooleanUtils.isTrue(this.connectionData.pendingNotification())) {
|
||||
if (notificationTable == null) {
|
||||
reloadPage(pageContext);
|
||||
} else {
|
||||
notificationTable.refreshPageSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void reloadPage(final PageContext pageContext) {
|
||||
final PageAction pageReloadAction = this.pageService.pageActionBuilder(pageContext)
|
||||
.newAction(ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION)
|
||||
.create();
|
||||
this.pageService.firePageEvent(
|
||||
new ActionEvent(pageReloadAction),
|
||||
pageContext);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -763,4 +763,19 @@ public class EntityTable<ROW> {
|
|||
}
|
||||
}
|
||||
|
||||
public void refreshPageSize() {
|
||||
if (this.pageSupplier.newBuilder()
|
||||
.withPaging(this.pageNumber, this.pageSize)
|
||||
.withSorting(this.sortColumn, this.sortOrder)
|
||||
.withQueryParams((this.filter != null) ? this.filter.getFilterParameter() : null)
|
||||
.withQueryParams(this.staticQueryParams)
|
||||
.apply(this.pageSupplierAdapter)
|
||||
.getPage()
|
||||
.map(page -> page.content.size())
|
||||
.map(size -> size != this.table.getItems().length)
|
||||
.getOr(false)) {
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -761,7 +761,7 @@ public class ExamDAOImpl implements ExamDAO {
|
|||
entry.getValue(),
|
||||
cached)
|
||||
.onError(error -> log.error(
|
||||
"Failed to get quizzes form LMS Setup: {}",
|
||||
"Failed to get quizzes from LMS Setup: {}",
|
||||
entry.getKey(), error))
|
||||
.getOr(Collections.emptyList())
|
||||
.stream())
|
||||
|
|
|
@ -65,7 +65,7 @@ final class MockupLmsAPITemplate implements LmsAPITemplate {
|
|||
"2020-01-01T09:00:00Z", null, "http://lms.mockup.com/api/"));
|
||||
this.mockups.add(new QuizData(
|
||||
"quiz2", institutionId, lmsSetupId, lmsType, "Demo Quiz 2 (MOCKUP)", "Demo Quiz Mockup",
|
||||
"2020-01-01T09:00:00Z", "2021-01-01T09:00:00Z", "http://lms.mockup.com/api/"));
|
||||
"2020-01-01T09:00:00Z", "2022-01-01T09:00:00Z", "http://lms.mockup.com/api/"));
|
||||
this.mockups.add(new QuizData(
|
||||
"quiz3", institutionId, lmsSetupId, lmsType, "Demo Quiz 3 (MOCKUP)", "Demo Quiz Mockup",
|
||||
"2018-07-30T09:00:00Z", "2018-08-01T00:00:00Z", "http://lms.mockup.com/api/"));
|
||||
|
@ -74,13 +74,13 @@ final class MockupLmsAPITemplate implements LmsAPITemplate {
|
|||
"2018-01-01T00:00:00Z", "2019-01-01T00:00:00Z", "http://lms.mockup.com/api/"));
|
||||
this.mockups.add(new QuizData(
|
||||
"quiz5", institutionId, lmsSetupId, lmsType, "Demo Quiz 5 (MOCKUP)", "Demo Quiz Mockup",
|
||||
"2018-01-01T09:00:00Z", "2021-01-01T09:00:00Z", "http://lms.mockup.com/api/"));
|
||||
"2018-01-01T09:00:00Z", "2022-01-01T09:00:00Z", "http://lms.mockup.com/api/"));
|
||||
this.mockups.add(new QuizData(
|
||||
"quiz6", institutionId, lmsSetupId, lmsType, "Demo Quiz 6 (MOCKUP)", "Demo Quiz Mockup",
|
||||
"2019-01-01T09:00:00Z", "2021-01-01T09:00:00Z", "http://lms.mockup.com/api/"));
|
||||
"2019-01-01T09:00:00Z", "2022-01-01T09:00:00Z", "http://lms.mockup.com/api/"));
|
||||
this.mockups.add(new QuizData(
|
||||
"quiz7", institutionId, lmsSetupId, lmsType, "Demo Quiz 7 (MOCKUP)", "Demo Quiz Mockup",
|
||||
"2018-01-01T09:00:00Z", "2021-01-01T09:00:00Z", "http://lms.mockup.com/api/"));
|
||||
"2018-01-01T09:00:00Z", "2022-01-01T09:00:00Z", "http://lms.mockup.com/api/"));
|
||||
|
||||
this.mockups.add(new QuizData(
|
||||
"quiz10", institutionId, lmsSetupId, lmsType, "Demo Quiz 10 (MOCKUP)",
|
||||
|
|
|
@ -217,7 +217,7 @@ public class MoodleCourseAccess extends CourseAccess {
|
|||
}
|
||||
} else if (this.moodleCourseDataLazyLoader.isLongRunningTask()) {
|
||||
// on long running tasks if we have a different fromCutTime as before
|
||||
// kick off the lazy loadung task imeditially with the new time filter
|
||||
// kick off the lazy loading task immediately with the new time filter
|
||||
if (fromCutTime > 0 && fromCutTime != this.moodleCourseDataLazyLoader.getFromCutTime()) {
|
||||
this.moodleCourseDataLazyLoader.setFromCutTime(fromCutTime);
|
||||
this.moodleCourseDataLazyLoader.loadAsync(restTemplate);
|
||||
|
|
|
@ -10,6 +10,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientNotification;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
|
||||
|
@ -27,6 +28,8 @@ public interface SEBClientNotificationService {
|
|||
|
||||
Result<List<ClientNotification>> getPendingNotifications(Long clientConnectionId);
|
||||
|
||||
void confirmPendingNotification(ClientEvent event, String connectionToken);
|
||||
|
||||
Result<ClientNotification> confirmPendingNotification(
|
||||
Long notificationId,
|
||||
Long examId,
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.springframework.context.annotation.Lazy;
|
|||
import org.springframework.stereotype.Service;
|
||||
|
||||
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.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientNotificationService;
|
||||
|
||||
|
@ -32,6 +33,17 @@ public class InternalClientConnectionDataFactory {
|
|||
}
|
||||
|
||||
public ClientConnectionDataInternal createClientConnectionData(final ClientConnection clientConnection) {
|
||||
|
||||
if (clientConnection.status == ConnectionStatus.CLOSED
|
||||
|| clientConnection.status == ConnectionStatus.DISABLED) {
|
||||
|
||||
// dispose notification indication for closed or disabled connection
|
||||
return new ClientConnectionDataInternal(
|
||||
clientConnection,
|
||||
() -> false,
|
||||
this.clientIndicatorFactory.createFor(clientConnection));
|
||||
}
|
||||
|
||||
return new ClientConnectionDataInternal(
|
||||
clientConnection,
|
||||
() -> this.sebClientNotificationService
|
||||
|
|
|
@ -28,7 +28,6 @@ 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.ClientConnectionData;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.EventType;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
|
@ -40,8 +39,8 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.session.EventHandlingStrate
|
|||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamSessionService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.PingHandlingStrategy;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientConnectionService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientNotificationService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientInstructionService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientNotificationService;
|
||||
import ch.ethz.seb.sebserver.webservice.weblayer.api.APIConstraintViolationException;
|
||||
|
||||
@Lazy
|
||||
|
@ -524,14 +523,22 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
|
|||
event,
|
||||
activeClientConnection.getConnectionId()));
|
||||
|
||||
if (event.eventType == EventType.NOTIFICATION || event.eventType == EventType.NOTIFICATION_CONFIRMED) {
|
||||
// notify notification service
|
||||
this.sebClientNotificationService.notifyNewNotification(activeClientConnection.getConnectionId());
|
||||
} else {
|
||||
// update indicators
|
||||
activeClientConnection.getIndicatorMapping(event.eventType)
|
||||
.forEach(indicator -> indicator.notifyValueChange(event));
|
||||
switch (event.eventType) {
|
||||
case NOTIFICATION: {
|
||||
this.sebClientNotificationService.notifyNewNotification(activeClientConnection.getConnectionId());
|
||||
break;
|
||||
}
|
||||
case NOTIFICATION_CONFIRMED: {
|
||||
this.sebClientNotificationService.confirmPendingNotification(event, connectionToken);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// update indicators
|
||||
activeClientConnection.getIndicatorMapping(event.eventType)
|
||||
.forEach(indicator -> indicator.notifyValueChange(event));
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
log.warn("No active ClientConnection found for connectionToken: {}", connectionToken);
|
||||
}
|
||||
|
|
|
@ -15,9 +15,12 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction.InstructionType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientNotification;
|
||||
|
@ -32,6 +35,8 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientNotificati
|
|||
@WebServiceProfile
|
||||
public class SEBClientNotificationServiceImpl implements SEBClientNotificationService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SEBClientNotificationServiceImpl.class);
|
||||
|
||||
private static final String CONFIRM_INSTRUCTION_ATTR_ID = "id";
|
||||
private static final String CONFIRM_INSTRUCTION_ATTR_TYPE = "type";
|
||||
|
||||
|
@ -69,6 +74,24 @@ public class SEBClientNotificationServiceImpl implements SEBClientNotificationSe
|
|||
return this.clientEventDAO.getPendingNotifications(clientConnectionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void confirmPendingNotification(final ClientEvent event, final String connectionToken) {
|
||||
try {
|
||||
final Long notificationId = (long) event.getValue();
|
||||
|
||||
this.clientEventDAO.getPendingNotification(notificationId)
|
||||
.flatMap(notification -> this.clientEventDAO.confirmPendingNotification(
|
||||
notificationId,
|
||||
notification.connectionId))
|
||||
.map(this::removeFromCache);
|
||||
|
||||
} catch (final Exception e) {
|
||||
log.error(
|
||||
"Failed to confirm pending notification from SEB Client side. Connection token: {} confirm event: {}",
|
||||
connectionToken, event, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<ClientNotification> confirmPendingNotification(
|
||||
final Long notificationId,
|
||||
|
|
|
@ -1554,13 +1554,13 @@ sebserver.monitoring.exam.connection.eventlist.text=Text
|
|||
sebserver.monitoring.exam.connection.eventlist.text.tooltip=The text of the log event<br/><br/>{0}
|
||||
|
||||
sebserver.monitoring.exam.connection.event.type.UNKNOWN=Unknown
|
||||
sebserver.monitoring.exam.connection.event.type.DEBUG_LOG=Debug
|
||||
sebserver.monitoring.exam.connection.event.type.INFO_LOG=Info
|
||||
sebserver.monitoring.exam.connection.event.type.WARN_LOG=Warn
|
||||
sebserver.monitoring.exam.connection.event.type.ERROR_LOG=Error
|
||||
sebserver.monitoring.exam.connection.event.type.DEBUG_LOG=Debug Log
|
||||
sebserver.monitoring.exam.connection.event.type.INFO_LOG=Info Log
|
||||
sebserver.monitoring.exam.connection.event.type.WARN_LOG=Warn Log
|
||||
sebserver.monitoring.exam.connection.event.type.ERROR_LOG=Error Log
|
||||
sebserver.monitoring.exam.connection.event.type.LAST_PING=Last Ping
|
||||
sebserver.monitoring.exam.connection.event.type.NOTIFICATION=Notification (pending)
|
||||
sebserver.monitoring.exam.connection.event.type.NOTIFICATION_CONFIRM=Notification (confirmed)
|
||||
sebserver.monitoring.exam.connection.event.type.NOTIFICATION_CONFIRMED=Notification (confirmed)
|
||||
|
||||
sebserver.monitoring.exam.connection.notification.type.UNKNOWN=Unknown
|
||||
sebserver.monitoring.exam.connection.notification.type.LOCK_SCREEN=Lock Screen
|
||||
|
|
Loading…
Reference in a new issue