fixed various issues
This commit is contained in:
parent
873391394a
commit
4c002b4ac2
19 changed files with 270 additions and 35 deletions
|
@ -46,7 +46,7 @@ public final class LmsSetup implements GrantEntity, Activatable {
|
||||||
public enum LmsType {
|
public enum LmsType {
|
||||||
MOCKUP(Features.COURSE_API),
|
MOCKUP(Features.COURSE_API),
|
||||||
OPEN_EDX(Features.COURSE_API, Features.SEB_RESTRICTION),
|
OPEN_EDX(Features.COURSE_API, Features.SEB_RESTRICTION),
|
||||||
MOODLE(Features.COURSE_API, Features.SEB_RESTRICTION);
|
MOODLE(Features.COURSE_API /* , Features.SEB_RESTRICTION */);
|
||||||
|
|
||||||
public final EnumSet<Features> features;
|
public final EnumSet<Features> features;
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ public final class LmsSetupTestResult {
|
||||||
public static final String ATTR_MISSING_ATTRIBUTE = "missingLMSSetupAttribute";
|
public static final String ATTR_MISSING_ATTRIBUTE = "missingLMSSetupAttribute";
|
||||||
|
|
||||||
public enum ErrorType {
|
public enum ErrorType {
|
||||||
|
FEATURE_NOT_AVAILABLE,
|
||||||
MISSING_ATTRIBUTE,
|
MISSING_ATTRIBUTE,
|
||||||
TOKEN_REQUEST,
|
TOKEN_REQUEST,
|
||||||
QUIZ_ACCESS_API_REQUEST,
|
QUIZ_ACCESS_API_REQUEST,
|
||||||
|
@ -118,6 +119,10 @@ public final class LmsSetupTestResult {
|
||||||
return new LmsSetupTestResult(new Error(ErrorType.QUIZ_RESTRICTION_API_REQUEST, message));
|
return new LmsSetupTestResult(new Error(ErrorType.QUIZ_RESTRICTION_API_REQUEST, message));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static LmsSetupTestResult ofQuizRestrictionNotAvailable() {
|
||||||
|
return new LmsSetupTestResult(new Error(ErrorType.FEATURE_NOT_AVAILABLE, "Restriction Feature Not Available"));
|
||||||
|
}
|
||||||
|
|
||||||
public final static class Error {
|
public final static class Error {
|
||||||
|
|
||||||
@JsonProperty(ATTR_ERROR_TYPE)
|
@JsonProperty(ATTR_ERROR_TYPE)
|
||||||
|
|
|
@ -55,6 +55,7 @@ public final class ClientConnection implements GrantEntity {
|
||||||
public static final String FILTER_ATTR_EXAM_ID = Domain.CLIENT_CONNECTION.ATTR_EXAM_ID;
|
public static final String FILTER_ATTR_EXAM_ID = Domain.CLIENT_CONNECTION.ATTR_EXAM_ID;
|
||||||
public static final String FILTER_ATTR_STATUS = Domain.CLIENT_CONNECTION.ATTR_STATUS;
|
public static final String FILTER_ATTR_STATUS = Domain.CLIENT_CONNECTION.ATTR_STATUS;
|
||||||
public static final String FILTER_ATTR_SESSION_ID = Domain.CLIENT_CONNECTION.ATTR_EXAM_USER_SESSION_ID;
|
public static final String FILTER_ATTR_SESSION_ID = Domain.CLIENT_CONNECTION.ATTR_EXAM_USER_SESSION_ID;
|
||||||
|
public static final String FILTER_ATTR_IP_STRING = Domain.CLIENT_CONNECTION.ATTR_CLIENT_ADDRESS;
|
||||||
|
|
||||||
@JsonProperty(Domain.CLIENT_CONNECTION.ATTR_ID)
|
@JsonProperty(Domain.CLIENT_CONNECTION.ATTR_ID)
|
||||||
public final Long id;
|
public final Long id;
|
||||||
|
|
|
@ -474,7 +474,8 @@ public class ExamForm implements TemplateComposer {
|
||||||
}
|
}
|
||||||
|
|
||||||
final LmsSetupTestResult lmsSetupTestResult = result.get();
|
final LmsSetupTestResult lmsSetupTestResult = result.get();
|
||||||
return !lmsSetupTestResult.hasError(ErrorType.QUIZ_RESTRICTION_API_REQUEST);
|
return !lmsSetupTestResult.hasError(ErrorType.QUIZ_RESTRICTION_API_REQUEST)
|
||||||
|
&& !lmsSetupTestResult.hasError(ErrorType.FEATURE_NOT_AVAILABLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showConsistencyChecks(final Collection<APIMessage> result, final Composite parent) {
|
private void showConsistencyChecks(final Collection<APIMessage> result, final Composite parent) {
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 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;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
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.session.ClientConnection;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||||
|
import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
|
||||||
|
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.impl.ModalInputDialog;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetClientConnectionPage;
|
||||||
|
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.TableFilter.CriteriaType;
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Component
|
||||||
|
@GuiProfile
|
||||||
|
public class MonitoringExamSearchPopup {
|
||||||
|
|
||||||
|
private static final LocTextKey TITLE_TEXT_KEY =
|
||||||
|
new LocTextKey("sebserver.monitoring.search.title");
|
||||||
|
private static final LocTextKey EMPTY_LIST_TEXT_KEY =
|
||||||
|
new LocTextKey("sebserver.monitoring.search.list.empty");
|
||||||
|
private static final LocTextKey TABLE_COLUMN_NAME =
|
||||||
|
new LocTextKey("sebserver.monitoring.search.list.name");
|
||||||
|
|
||||||
|
private final PageService pageService;
|
||||||
|
|
||||||
|
private final TableFilterAttribute nameFilter =
|
||||||
|
new TableFilterAttribute(CriteriaType.TEXT, ClientConnection.FILTER_ATTR_SESSION_ID);
|
||||||
|
|
||||||
|
protected MonitoringExamSearchPopup(final PageService pageService) {
|
||||||
|
this.pageService = pageService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void show(final PageContext pageContext) {
|
||||||
|
final ModalInputDialog<Void> dialog = new ModalInputDialog<>(
|
||||||
|
pageContext.getParent().getShell(),
|
||||||
|
this.pageService.getWidgetFactory());
|
||||||
|
dialog.setLargeDialogWidth();
|
||||||
|
dialog.open(
|
||||||
|
TITLE_TEXT_KEY,
|
||||||
|
pageContext,
|
||||||
|
pc -> this.compose(pc, dialog));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void compose(final PageContext pageContext, final ModalInputDialog<Void> dialog) {
|
||||||
|
final EntityKey examKey = pageContext.getEntityKey();
|
||||||
|
|
||||||
|
final RestService restService = this.pageService.getRestService();
|
||||||
|
final PageActionBuilder actionBuilder = this.pageService
|
||||||
|
.pageActionBuilder(pageContext.clearEntityKeys());
|
||||||
|
|
||||||
|
final EntityTable<ClientConnection> table =
|
||||||
|
this.pageService.entityTableBuilder(restService.getRestCall(GetClientConnectionPage.class))
|
||||||
|
.withEmptyMessage(EMPTY_LIST_TEXT_KEY)
|
||||||
|
.withPaging(10)
|
||||||
|
.withStaticFilter(ClientConnection.FILTER_ATTR_EXAM_ID, examKey.modelId)
|
||||||
|
|
||||||
|
.withColumn(new ColumnDefinition<>(
|
||||||
|
Domain.CLIENT_CONNECTION.ATTR_EXAM_USER_SESSION_ID,
|
||||||
|
TABLE_COLUMN_NAME,
|
||||||
|
ClientConnection::getUserSessionId)
|
||||||
|
.withFilter(this.nameFilter))
|
||||||
|
.withDefaultAction(t -> actionBuilder
|
||||||
|
.newAction(ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION)
|
||||||
|
.withParentEntityKey(examKey)
|
||||||
|
.withExec(action -> showClientConnection(action, dialog, t))
|
||||||
|
.create())
|
||||||
|
|
||||||
|
.compose(pageContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PageAction showClientConnection(
|
||||||
|
final PageAction pageAction,
|
||||||
|
final ModalInputDialog<Void> dialog,
|
||||||
|
final EntityTable<ClientConnection> table) {
|
||||||
|
|
||||||
|
final ClientConnection singleSelectedROWData = table.getSingleSelectedROWData();
|
||||||
|
dialog.close();
|
||||||
|
return pageAction
|
||||||
|
.withEntityKey(new EntityKey(
|
||||||
|
singleSelectedROWData.id,
|
||||||
|
EntityType.CLIENT_CONNECTION))
|
||||||
|
.withAttribute(
|
||||||
|
Domain.CLIENT_CONNECTION.ATTR_CONNECTION_TOKEN,
|
||||||
|
singleSelectedROWData.getConnectionToken());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -125,16 +125,18 @@ public class MonitoringRunningExam implements TemplateComposer {
|
||||||
private final ResourceService resourceService;
|
private final ResourceService resourceService;
|
||||||
private final InstructionProcessor instructionProcessor;
|
private final InstructionProcessor instructionProcessor;
|
||||||
private final GuiServiceInfo guiServiceInfo;
|
private final GuiServiceInfo guiServiceInfo;
|
||||||
|
private final MonitoringExamSearchPopup monitoringExamSearchPopup;
|
||||||
|
private final ProctorRoomConnectionsPopup proctorRoomConnectionsPopup;
|
||||||
private final long pollInterval;
|
private final long pollInterval;
|
||||||
private final long proctoringRoomUpdateInterval;
|
private final long proctoringRoomUpdateInterval;
|
||||||
private final String remoteProctoringEndpoint;
|
private final String remoteProctoringEndpoint;
|
||||||
private final ProctorRoomConnectionsPopup proctorRoomConnectionsPopup;
|
|
||||||
|
|
||||||
protected MonitoringRunningExam(
|
protected MonitoringRunningExam(
|
||||||
final ServerPushService serverPushService,
|
final ServerPushService serverPushService,
|
||||||
final PageService pageService,
|
final PageService pageService,
|
||||||
final InstructionProcessor instructionProcessor,
|
final InstructionProcessor instructionProcessor,
|
||||||
final GuiServiceInfo guiServiceInfo,
|
final GuiServiceInfo guiServiceInfo,
|
||||||
|
final MonitoringExamSearchPopup monitoringExamSearchPopup,
|
||||||
final ProctorRoomConnectionsPopup proctorRoomConnectionsPopup,
|
final ProctorRoomConnectionsPopup proctorRoomConnectionsPopup,
|
||||||
@Value("${sebserver.gui.webservice.poll-interval:1000}") final long pollInterval,
|
@Value("${sebserver.gui.webservice.poll-interval:1000}") final long pollInterval,
|
||||||
@Value("${sebserver.gui.remote.proctoring.entrypoint:/remote-proctoring}") final String remoteProctoringEndpoint,
|
@Value("${sebserver.gui.remote.proctoring.entrypoint:/remote-proctoring}") final String remoteProctoringEndpoint,
|
||||||
|
@ -146,6 +148,7 @@ public class MonitoringRunningExam implements TemplateComposer {
|
||||||
this.instructionProcessor = instructionProcessor;
|
this.instructionProcessor = instructionProcessor;
|
||||||
this.guiServiceInfo = guiServiceInfo;
|
this.guiServiceInfo = guiServiceInfo;
|
||||||
this.pollInterval = pollInterval;
|
this.pollInterval = pollInterval;
|
||||||
|
this.monitoringExamSearchPopup = monitoringExamSearchPopup;
|
||||||
this.remoteProctoringEndpoint = remoteProctoringEndpoint;
|
this.remoteProctoringEndpoint = remoteProctoringEndpoint;
|
||||||
this.proctorRoomConnectionsPopup = proctorRoomConnectionsPopup;
|
this.proctorRoomConnectionsPopup = proctorRoomConnectionsPopup;
|
||||||
this.proctoringRoomUpdateInterval = proctoringRoomUpdateInterval;
|
this.proctoringRoomUpdateInterval = proctoringRoomUpdateInterval;
|
||||||
|
@ -243,6 +246,12 @@ public class MonitoringRunningExam implements TemplateComposer {
|
||||||
.noEventPropagation()
|
.noEventPropagation()
|
||||||
.publishIf(privilege)
|
.publishIf(privilege)
|
||||||
|
|
||||||
|
.newAction(ActionDefinition.MONITORING_EXAM_SEARCH_CONNECTIONS)
|
||||||
|
.withEntityKey(entityKey)
|
||||||
|
.withExec(this::openSearchPopup)
|
||||||
|
.noEventPropagation()
|
||||||
|
.publishIf(privilege)
|
||||||
|
|
||||||
.newAction(ActionDefinition.MONITOR_EXAM_QUIT_SELECTED)
|
.newAction(ActionDefinition.MONITOR_EXAM_QUIT_SELECTED)
|
||||||
.withEntityKey(entityKey)
|
.withEntityKey(entityKey)
|
||||||
.withConfirm(() -> CONFIRM_QUIT_SELECTED)
|
.withConfirm(() -> CONFIRM_QUIT_SELECTED)
|
||||||
|
@ -391,6 +400,11 @@ public class MonitoringRunningExam implements TemplateComposer {
|
||||||
return townhall != null && townhall.id != null;
|
return townhall != null && townhall.id != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private PageAction openSearchPopup(final PageAction action) {
|
||||||
|
this.monitoringExamSearchPopup.show(action.pageContext());
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
|
||||||
private PageAction toggleTownhallRoom(final PageAction action) {
|
private PageAction toggleTownhallRoom(final PageAction action) {
|
||||||
if (isTownhallRoomActive(action.getEntityKey().modelId)) {
|
if (isTownhallRoomActive(action.getEntityKey().modelId)) {
|
||||||
closeTownhallRoom(action);
|
closeTownhallRoom(action);
|
||||||
|
|
|
@ -699,6 +699,17 @@ public enum ActionDefinition {
|
||||||
PageStateDefinitionImpl.MONITORING_RUNNING_EXAM,
|
PageStateDefinitionImpl.MONITORING_RUNNING_EXAM,
|
||||||
ActionCategory.FILTER),
|
ActionCategory.FILTER),
|
||||||
|
|
||||||
|
MONITORING_EXAM_SEARCH_CONNECTIONS(
|
||||||
|
new LocTextKey("sebserver.monitoring.search.action"),
|
||||||
|
ImageIcon.SEARCH,
|
||||||
|
PageStateDefinitionImpl.MONITORING_RUNNING_EXAM,
|
||||||
|
ActionCategory.FORM),
|
||||||
|
MONITORING_EXAM_SEARCH_VIEW_CONNECTION(
|
||||||
|
new LocTextKey("sebserver.monitoring.search.action.view"),
|
||||||
|
ImageIcon.SEARCH,
|
||||||
|
PageStateDefinitionImpl.MONITORING_RUNNING_EXAM,
|
||||||
|
ActionCategory.CLIENT_EVENT_LIST),
|
||||||
|
|
||||||
MONITOR_EXAM_NEW_PROCTOR_ROOM(
|
MONITOR_EXAM_NEW_PROCTOR_ROOM(
|
||||||
new LocTextKey("sebserver.monitoring.exam.action.newroom"),
|
new LocTextKey("sebserver.monitoring.exam.action.newroom"),
|
||||||
ImageIcon.VISIBILITY,
|
ImageIcon.VISIBILITY,
|
||||||
|
|
|
@ -170,16 +170,16 @@ public class ModalInputDialog<T> extends Dialog {
|
||||||
final Consumer<PageContext> contentComposer) {
|
final Consumer<PageContext> contentComposer) {
|
||||||
|
|
||||||
// Create the info dialog window
|
// Create the info dialog window
|
||||||
final Shell shell = new Shell(getParent(), getStyle());
|
this.shell = new Shell(getParent(), getStyle());
|
||||||
shell.setText(getText());
|
this.shell.setText(getText());
|
||||||
shell.setData(RWT.CUSTOM_VARIANT, CustomVariant.MESSAGE.key);
|
this.shell.setData(RWT.CUSTOM_VARIANT, CustomVariant.MESSAGE.key);
|
||||||
shell.setText(this.widgetFactory.getI18nSupport().getText(title));
|
this.shell.setText(this.widgetFactory.getI18nSupport().getText(title));
|
||||||
shell.setLayout(new GridLayout());
|
this.shell.setLayout(new GridLayout());
|
||||||
final GridData gridData2 = new GridData(SWT.FILL, SWT.TOP, true, true);
|
final GridData gridData2 = new GridData(SWT.FILL, SWT.TOP, true, true);
|
||||||
|
|
||||||
shell.setLayoutData(gridData2);
|
this.shell.setLayoutData(gridData2);
|
||||||
|
|
||||||
final Composite main = new Composite(shell, SWT.NONE);
|
final Composite main = new Composite(this.shell, SWT.NONE);
|
||||||
main.setLayout(new GridLayout());
|
main.setLayout(new GridLayout());
|
||||||
final GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, true);
|
final GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, true);
|
||||||
gridData.widthHint = this.dialogWidth;
|
gridData.widthHint = this.dialogWidth;
|
||||||
|
@ -188,13 +188,19 @@ public class ModalInputDialog<T> extends Dialog {
|
||||||
contentComposer.accept(pageContext.copyOf(main));
|
contentComposer.accept(pageContext.copyOf(main));
|
||||||
gridData.heightHint = calcDialogHeight(main);
|
gridData.heightHint = calcDialogHeight(main);
|
||||||
|
|
||||||
final Button close = this.widgetFactory.buttonLocalized(shell, CLOSE_TEXT_KEY);
|
final Button close = this.widgetFactory.buttonLocalized(this.shell, CLOSE_TEXT_KEY);
|
||||||
final GridData data = new GridData(GridData.HORIZONTAL_ALIGN_CENTER);
|
final GridData data = new GridData(GridData.HORIZONTAL_ALIGN_CENTER);
|
||||||
data.widthHint = this.buttonWidth;
|
data.widthHint = this.buttonWidth;
|
||||||
close.setLayoutData(data);
|
close.setLayoutData(data);
|
||||||
close.addListener(SWT.Selection, event -> shell.close());
|
close.addListener(SWT.Selection, event -> this.shell.close());
|
||||||
|
|
||||||
finishUp(shell);
|
finishUp(this.shell);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
if (this.shell != null) {
|
||||||
|
this.shell.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void finishUp(final Shell shell) {
|
private void finishUp(final Shell shell) {
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 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.service.remote.webservice.api.session;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.Page;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Component
|
||||||
|
@GuiProfile
|
||||||
|
public class GetClientConnectionPage extends RestCall<Page<ClientConnection>> {
|
||||||
|
|
||||||
|
public GetClientConnectionPage() {
|
||||||
|
super(new TypeKey<>(
|
||||||
|
CallType.GET_PAGE,
|
||||||
|
EntityType.CLIENT_CONNECTION,
|
||||||
|
new TypeReference<Page<ClientConnection>>() {
|
||||||
|
}),
|
||||||
|
HttpMethod.GET,
|
||||||
|
MediaType.APPLICATION_FORM_URLENCODED,
|
||||||
|
API.SEB_CLIENT_CONNECTION_ENDPOINT);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -205,6 +205,14 @@ public class FilterMap extends POSTMapper {
|
||||||
return getString(ClientConnection.FILTER_ATTR_STATUS);
|
return getString(ClientConnection.FILTER_ATTR_STATUS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getClientConnectionUserId() {
|
||||||
|
return getSQLWildcard(ClientConnection.FILTER_ATTR_SESSION_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getClientConnectionIPAddress() {
|
||||||
|
return getSQLWildcard(ClientConnection.FILTER_ATTR_IP_STRING);
|
||||||
|
}
|
||||||
|
|
||||||
public Long getClientEventConnectionId() {
|
public Long getClientEventConnectionId() {
|
||||||
return getLong(ClientEvent.FILTER_ATTR_CONNECTION_ID);
|
return getLong(ClientEvent.FILTER_ATTR_CONNECTION_ID);
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,6 +99,12 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO {
|
||||||
.and(
|
.and(
|
||||||
ClientConnectionRecordDynamicSqlSupport.status,
|
ClientConnectionRecordDynamicSqlSupport.status,
|
||||||
isEqualToWhenPresent(filterMap.getClientConnectionStatus()))
|
isEqualToWhenPresent(filterMap.getClientConnectionStatus()))
|
||||||
|
.and(
|
||||||
|
ClientConnectionRecordDynamicSqlSupport.examUserSessionId,
|
||||||
|
isLikeWhenPresent(filterMap.getClientConnectionUserId()))
|
||||||
|
.and(
|
||||||
|
ClientConnectionRecordDynamicSqlSupport.clientAddress,
|
||||||
|
isLikeWhenPresent(filterMap.getClientConnectionIPAddress()))
|
||||||
.build()
|
.build()
|
||||||
.execute()
|
.execute()
|
||||||
.stream()
|
.stream()
|
||||||
|
|
|
@ -118,7 +118,11 @@ public class LmsAPIServiceImpl implements LmsAPIService {
|
||||||
return testCourseAccessAPI;
|
return testCourseAccessAPI;
|
||||||
}
|
}
|
||||||
|
|
||||||
return template.testCourseRestrictionAPI();
|
if (template.lmsSetup().getLmsType().features.contains(LmsSetup.Features.SEB_RESTRICTION)) {
|
||||||
|
return template.testCourseRestrictionAPI();
|
||||||
|
} else {
|
||||||
|
return LmsSetupTestResult.ofQuizRestrictionNotAvailable();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -149,7 +149,7 @@ public class MoodleCourseAccess extends CourseAccess {
|
||||||
if (restTemplateRequest.hasError()) {
|
if (restTemplateRequest.hasError()) {
|
||||||
final String message = "Failed to gain access token from Moodle Rest API:\n tried token endpoints: " +
|
final String message = "Failed to gain access token from Moodle Rest API:\n tried token endpoints: " +
|
||||||
this.moodleRestTemplateFactory.knownTokenAccessPaths;
|
this.moodleRestTemplateFactory.knownTokenAccessPaths;
|
||||||
log.error(message, restTemplateRequest.getError().getMessage());
|
log.error(message + " cause: ", restTemplateRequest.getError());
|
||||||
return LmsSetupTestResult.ofTokenRequestError(message);
|
return LmsSetupTestResult.ofTokenRequestError(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,13 +254,15 @@ public class MoodleCourseAccess extends CourseAccess {
|
||||||
CourseQuizData.class);
|
CourseQuizData.class);
|
||||||
|
|
||||||
final Map<String, CourseData> finalCourseDataRef = courseData;
|
final Map<String, CourseData> finalCourseDataRef = courseData;
|
||||||
courseQuizData.quizzes
|
if (courseQuizData.quizzes != null) {
|
||||||
.forEach(quiz -> {
|
courseQuizData.quizzes
|
||||||
final CourseData course = finalCourseDataRef.get(quiz.course);
|
.forEach(quiz -> {
|
||||||
if (course != null) {
|
final CourseData course = finalCourseDataRef.get(quiz.course);
|
||||||
course.quizzes.add(quiz);
|
if (course != null) {
|
||||||
}
|
course.quizzes.add(quiz);
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return courseData.values()
|
return courseData.values()
|
||||||
.stream()
|
.stream()
|
||||||
|
@ -273,12 +275,17 @@ public class MoodleCourseAccess extends CourseAccess {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Predicate<CourseData> getCourseFilter(final long from) {
|
private Predicate<CourseData> getCourseFilter(final long from) {
|
||||||
final long now = DateTime.now(DateTimeZone.UTC).getMillis();
|
final long now = DateTime.now(DateTimeZone.UTC).getMillis() / 1000;
|
||||||
return course -> course.time_created == null
|
return course -> {
|
||||||
|| course.time_created.longValue() > from
|
if (course.end_date != null && course.end_date > 0 && course.end_date < now) {
|
||||||
|| (course.end_date == null
|
return false;
|
||||||
|| (course.end_date <= 0
|
}
|
||||||
|| course.end_date > now));
|
if (course.time_created != null && course.time_created.longValue() < from) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private Collection<CourseData> getCoursesPage(
|
private Collection<CourseData> getCoursesPage(
|
||||||
|
@ -287,7 +294,7 @@ public class MoodleCourseAccess extends CourseAccess {
|
||||||
final int size) throws JsonParseException, JsonMappingException, IOException {
|
final int size) throws JsonParseException, JsonMappingException, IOException {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final long aYearAgo = DateTime.now(DateTimeZone.UTC).minusYears(1).getMillis();
|
final long aYearAgo = DateTime.now(DateTimeZone.UTC).minusYears(1).getMillis() / 1000;
|
||||||
// get course ids per page
|
// get course ids per page
|
||||||
final LinkedMultiValueMap<String, String> attributes = new LinkedMultiValueMap<>();
|
final LinkedMultiValueMap<String, String> attributes = new LinkedMultiValueMap<>();
|
||||||
attributes.add(MOODLE_COURSE_API_SEARCH_CRITERIA_NAME, "search");
|
attributes.add(MOODLE_COURSE_API_SEARCH_CRITERIA_NAME, "search");
|
||||||
|
|
|
@ -64,7 +64,6 @@ public class MoodleCourseRestriction {
|
||||||
private static final String MOODLE_DEFAULT_COURSE_RESTRICTION_WS_FUNCTION_CREATE = "seb_restriction_create";
|
private static final String MOODLE_DEFAULT_COURSE_RESTRICTION_WS_FUNCTION_CREATE = "seb_restriction_create";
|
||||||
private static final String MOODLE_DEFAULT_COURSE_RESTRICTION_WS_FUNCTION_UPDATE = "seb_restriction_update";
|
private static final String MOODLE_DEFAULT_COURSE_RESTRICTION_WS_FUNCTION_UPDATE = "seb_restriction_update";
|
||||||
private static final String MOODLE_DEFAULT_COURSE_RESTRICTION_WS_FUNCTION_DELETE = "seb_restriction_delete";
|
private static final String MOODLE_DEFAULT_COURSE_RESTRICTION_WS_FUNCTION_DELETE = "seb_restriction_delete";
|
||||||
//private static final String MOODLE_DEFAULT_COURSE_RESTRICTION_COURSE_ID = "courseId";
|
|
||||||
private static final String MOODLE_DEFAULT_COURSE_RESTRICTION_SHORT_NAME = "shortname";
|
private static final String MOODLE_DEFAULT_COURSE_RESTRICTION_SHORT_NAME = "shortname";
|
||||||
private static final String MOODLE_DEFAULT_COURSE_RESTRICTION_ID_NUMBER = "idnumber";
|
private static final String MOODLE_DEFAULT_COURSE_RESTRICTION_ID_NUMBER = "idnumber";
|
||||||
private static final String MOODLE_DEFAULT_COURSE_RESTRICTION_QUIZ_ID = "quizId";
|
private static final String MOODLE_DEFAULT_COURSE_RESTRICTION_QUIZ_ID = "quizId";
|
||||||
|
|
|
@ -29,6 +29,7 @@ import ch.ethz.seb.sebserver.gbl.model.user.ExamineeAccountDetails;
|
||||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.NoSEBRestrictionException;
|
||||||
|
|
||||||
public class MoodleLmsAPITemplate implements LmsAPITemplate {
|
public class MoodleLmsAPITemplate implements LmsAPITemplate {
|
||||||
|
|
||||||
|
@ -60,7 +61,8 @@ public class MoodleLmsAPITemplate implements LmsAPITemplate {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LmsSetupTestResult testCourseRestrictionAPI() {
|
public LmsSetupTestResult testCourseRestrictionAPI() {
|
||||||
return this.moodleCourseRestriction.initAPIAccess();
|
throw new NoSEBRestrictionException();
|
||||||
|
//return this.moodleCourseRestriction.initAPIAccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -20,6 +20,8 @@ import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.http.HttpEntity;
|
import org.springframework.http.HttpEntity;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
|
@ -50,6 +52,8 @@ import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||||
|
|
||||||
class MoodleRestTemplateFactory {
|
class MoodleRestTemplateFactory {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(MoodleRestTemplateFactory.class);
|
||||||
|
|
||||||
final JSONMapper jsonMapper;
|
final JSONMapper jsonMapper;
|
||||||
final LmsSetup lmsSetup;
|
final LmsSetup lmsSetup;
|
||||||
final ClientCredentials credentials;
|
final ClientCredentials credentials;
|
||||||
|
@ -113,6 +117,12 @@ class MoodleRestTemplateFactory {
|
||||||
return this.knownTokenAccessPaths
|
return this.knownTokenAccessPaths
|
||||||
.stream()
|
.stream()
|
||||||
.map(this::createRestTemplate)
|
.map(this::createRestTemplate)
|
||||||
|
.map(result -> {
|
||||||
|
if (result.hasError()) {
|
||||||
|
log.error("Failed to get access token: ", result.getError());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
})
|
||||||
.filter(Result::hasValue)
|
.filter(Result::hasValue)
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElse(Result.ofRuntimeError(
|
.orElse(Result.ofRuntimeError(
|
||||||
|
|
|
@ -61,7 +61,7 @@ public abstract class AbstractLogLevelCountIndicator extends AbstractLogIndicato
|
||||||
return errors.doubleValue();
|
return errors.doubleValue();
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
log.error("Failed to get indicator count from persistent storage: ", e);
|
log.error("Failed to get indicator count from persistent storage: ", e);
|
||||||
return this.currentValue;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.indicator;
|
||||||
|
|
||||||
import static org.mybatis.dynamic.sql.SqlBuilder.*;
|
import static org.mybatis.dynamic.sql.SqlBuilder.*;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.mybatis.dynamic.sql.SqlBuilder;
|
import org.mybatis.dynamic.sql.SqlBuilder;
|
||||||
|
@ -64,12 +65,17 @@ public abstract class AbstractLogNumberIndicator extends AbstractLogIndicator {
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
if (execute == null || execute.isEmpty()) {
|
if (execute == null || execute.isEmpty()) {
|
||||||
return this.currentValue;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return execute.get(execute.size() - 1).getNumericValue().doubleValue();
|
final BigDecimal numericValue = execute.get(execute.size() - 1).getNumericValue();
|
||||||
|
if (numericValue != null) {
|
||||||
|
return numericValue.doubleValue();
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
log.error("Failed to get indicator number from persistent storage: ", e);
|
log.error("Failed to get indicator number from persistent storage: {}", e.getMessage());
|
||||||
return this.currentValue;
|
return this.currentValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1498,6 +1498,12 @@ sebserver.monitoring.connection.form.status.tooltip=The current connection statu
|
||||||
sebserver.monitoring.connection.form.exam=Exam
|
sebserver.monitoring.connection.form.exam=Exam
|
||||||
sebserver.monitoring.connection.form.exam.tooltip=The exam name
|
sebserver.monitoring.connection.form.exam.tooltip=The exam name
|
||||||
|
|
||||||
|
sebserver.monitoring.search.title=Search Connections
|
||||||
|
sebserver.monitoring.search.action=Search
|
||||||
|
sebserver.monitoring.search.list.empty=No Client Connections available
|
||||||
|
sebserver.monitoring.search.list.name=Session or User Name
|
||||||
|
|
||||||
|
|
||||||
sebserver.monitoring.exam.connection.emptySelection=At first please select a Connection from the list
|
sebserver.monitoring.exam.connection.emptySelection=At first please select a Connection from the list
|
||||||
sebserver.monitoring.exam.connection.emptySelection.active=At first please select an active Connection from the list
|
sebserver.monitoring.exam.connection.emptySelection.active=At first please select an active Connection from the list
|
||||||
sebserver.monitoring.exam.connection.title=SEB Client Connection
|
sebserver.monitoring.exam.connection.title=SEB Client Connection
|
||||||
|
|
Loading…
Add table
Reference in a new issue