SEBSERV-240 implementation

This commit is contained in:
anhefti 2022-03-23 08:06:25 +01:00
parent 51e27ff4e5
commit 6d56e71dbe
14 changed files with 459 additions and 7 deletions

View file

@ -193,6 +193,7 @@ public final class API {
public static final String EXAM_MONITORING_NOTIFICATION_ENDPOINT = "/notification";
public static final String EXAM_MONITORING_DISABLE_CONNECTION_ENDPOINT = "/disable-connection";
public static final String EXAM_MONITORING_STATE_FILTER = "hidden-states";
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 + "}";

View file

@ -35,7 +35,9 @@ public enum ActionCategory {
LOGS_SEB_CLIENT_LIST(new LocTextKey("sebserver.userlogs.list.actions"), 1),
VARIA(new LocTextKey("sebserver.overall.action.category.varia"), 0),
FILTER(new LocTextKey("sebserver.exam.monitoring.action.category.filter"), 50),
PROCTORING(new LocTextKey("sebserver.exam.overall.action.category.proctoring"), 60);
PROCTORING(new LocTextKey("sebserver.exam.overall.action.category.proctoring"), 60),
FINISHED_EXAM_LIST(new LocTextKey("sebserver.finished.exam.list.actions"), 1);
public final LocTextKey title;
public final int slotPosition;

View file

@ -832,6 +832,15 @@ public enum ActionDefinition {
PageStateDefinitionImpl.MONITORING_RUNNING_EXAM,
ActionCategory.PROCTORING),
FINISHED_EXAM_VIEW_LIST(
new LocTextKey("sebserver.finished.action.list"),
PageStateDefinitionImpl.FINISHED_EXAM_LIST),
VIEW_EXAM_FROM_FINISHED_LIST(
new LocTextKey("sebserver.finished.exam.action.list.view"),
ImageIcon.SHOW,
PageStateDefinitionImpl.FINISHED_EXAM,
ActionCategory.FINISHED_EXAM_LIST),
LOGS_USER_ACTIVITY_LIST(
new LocTextKey("sebserver.logs.activity.userlogs"),
PageStateDefinitionImpl.USER_ACTIVITY_LOGS),

View file

@ -339,14 +339,25 @@ public class ActivitiesPane implements TemplateComposer {
// Monitoring exams
if (isSupporter) {
final TreeItem clientConfig = this.widgetFactory.treeItemLocalized(
final TreeItem monitoringExams = this.widgetFactory.treeItemLocalized(
monitoring,
ActivityDefinition.MONITORING_EXAMS.displayName);
injectActivitySelection(
clientConfig,
monitoringExams,
actionBuilder
.newAction(ActionDefinition.RUNNING_EXAM_VIEW_LIST)
.create());
final TreeItem clientConfig = this.widgetFactory.treeItemLocalized(
monitoring,
ActivityDefinition.FINISHED_EXAMS.displayName);
injectActivitySelection(
clientConfig,
actionBuilder
.newAction(ActionDefinition.FINISHED_EXAM_VIEW_LIST)
.create());
}
// SEB Client Logs

View file

@ -28,6 +28,7 @@ public enum ActivityDefinition implements Activity {
SEB_CERTIFICATE_MANAGEMENT(new LocTextKey("sebserver.certificate.action.list")),
MONITORING(new LocTextKey("sebserver.overall.activity.title.monitoring")),
MONITORING_EXAMS(new LocTextKey("sebserver.monitoring.action.list")),
FINISHED_EXAMS(new LocTextKey("sebserver.finished.action.list")),
SEB_CLIENT_LOGS(new LocTextKey("sebserver.logs.activity.seblogs"));
public final LocTextKey displayName;

View file

@ -33,6 +33,8 @@ import ch.ethz.seb.sebserver.gui.content.exam.IndicatorTemplateForm;
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.FinishedExamList;
import ch.ethz.seb.sebserver.gui.content.monitoring.MonitoringClientConnection;
import ch.ethz.seb.sebserver.gui.content.monitoring.MonitoringRunningExam;
import ch.ethz.seb.sebserver.gui.content.monitoring.MonitoringRunningExamList;
@ -96,6 +98,10 @@ public enum PageStateDefinitionImpl implements PageStateDefinition {
MONITORING_RUNNING_EXAM(Type.FORM_VIEW, MonitoringRunningExam.class, ActivityDefinition.MONITORING_EXAMS),
MONITORING_CLIENT_CONNECTION(Type.FORM_VIEW, MonitoringClientConnection.class, ActivityDefinition.MONITORING_EXAMS),
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),
USER_ACTIVITY_LOGS(Type.LIST_VIEW, UserActivityLogs.class, ActivityDefinition.USER_ACTIVITY_LOGS),
SEB_CLIENT_LOGS(Type.LIST_VIEW, SEBClientEvents.class, ActivityDefinition.SEB_CLIENT_LOGS)

View file

@ -0,0 +1,127 @@
/*
* 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 org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator;
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.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.push.ServerPushService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicators;
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.TableBuilder;
import ch.ethz.seb.sebserver.gui.table.TableFilter.CriteriaType;
@Lazy
@Component
@GuiProfile
public class FinishedExam implements TemplateComposer {
private static final LocTextKey TITLE_TEXT_KEY =
new LocTextKey("sebserver.finished.exam.connections.title");
private static final LocTextKey EMPTY_LIST_TEXT_KEY =
new LocTextKey("sebserver.finished.exam.connections.empty");
private static final LocTextKey TABLE_COLUMN_NAME =
new LocTextKey("sebserver.finished.exam.connections.name");
private static final LocTextKey TABLE_COLUMN_INFO =
new LocTextKey("sebserver.finished.exam.connections.info");
private static final LocTextKey TABLE_COLUMN_STATUS =
new LocTextKey("sebserver.finished.exam.connections.status");
private final TableFilterAttribute nameFilter =
new TableFilterAttribute(CriteriaType.TEXT, ClientConnection.FILTER_ATTR_SESSION_ID);
private final TableFilterAttribute infoFilter =
new TableFilterAttribute(CriteriaType.TEXT, ClientConnection.ATTR_INFO);
private final TableFilterAttribute statusFilter;
private final PageService pageService;
private final RestService restService;
private final ResourceService resourceService;
private final int pageSize;
public FinishedExam(
final ServerPushService serverPushService,
final PageService pageService,
@Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) {
this.pageService = pageService;
this.restService = pageService.getRestService();
this.resourceService = pageService.getResourceService();
this.pageSize = pageSize;
this.statusFilter = new TableFilterAttribute(
CriteriaType.SINGLE_SELECTION,
ClientConnection.FILTER_ATTR_STATUS,
pageService.getResourceService()::localizedClientConnectionStatusResources);
}
@Override
public void compose(final PageContext pageContext) {
final EntityKey examKey = pageContext.getEntityKey();
final RestService restService = this.pageService.getRestService();
final PageActionBuilder actionBuilder = this.pageService
.pageActionBuilder(pageContext.clearEntityKeys());
final Collection<Indicator> indicators = restService.getBuilder(GetIndicators.class)
.withQueryParam(Indicator.FILTER_ATTR_EXAM_ID, examKey.modelId)
.call()
.getOrThrow();
final TableBuilder<ClientConnection> tableBuilder =
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))
.withColumn(new ColumnDefinition<>(
ClientConnection.ATTR_INFO,
TABLE_COLUMN_INFO,
ClientConnection::getInfo)
.withFilter(this.infoFilter))
.withColumn(new ColumnDefinition<ClientConnection>(
Domain.CLIENT_CONNECTION.ATTR_STATUS,
TABLE_COLUMN_STATUS,
row -> this.pageService.getResourceService()
.localizedClientConnectionStatusName(row.getStatus()))
.withFilter(this.statusFilter))
.withDefaultAction(t -> actionBuilder
.newAction(ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION)
.withParentEntityKey(examKey)
.create());
tableBuilder.compose(pageContext);
}
}

View file

@ -0,0 +1,149 @@
/*
* 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 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.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
import ch.ethz.seb.sebserver.gbl.model.exam.QuizData;
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.content.exam.ExamList;
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.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.remote.webservice.api.RestService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetFinishedExamPage;
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.TableFilter.CriteriaType;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@Lazy
@Component
@GuiProfile
public class FinishedExamList implements TemplateComposer {
private static final LocTextKey PAGE_TITLE_KEY =
new LocTextKey("sebserver.finished.exam.list.title");
private final static LocTextKey EMPTY_SELECTION_TEXT_KEY =
new LocTextKey("sebserver.finished.exam.info.pleaseSelect");
private final static LocTextKey COLUMN_TITLE_NAME_KEY =
new LocTextKey("sebserver.finished.exam.list.column.name");
private final static LocTextKey COLUMN_TITLE_TYPE_KEY =
new LocTextKey("sebserver.finished.exam.list.column.type");
private final static LocTextKey EMPTY_LIST_TEXT_KEY =
new LocTextKey("sebserver.finished.exam.list.empty");
private final TableFilterAttribute nameFilter =
new TableFilterAttribute(CriteriaType.TEXT, QuizData.FILTER_ATTR_NAME);
private final TableFilterAttribute typeFilter;
private final PageService pageService;
private final ResourceService resourceService;
private final int pageSize;
protected FinishedExamList(
final PageService pageService,
@Value("${sebserver.gui.list.page.size:20}") final Integer pageSize) {
this.pageService = pageService;
this.resourceService = pageService.getResourceService();
this.pageSize = pageSize;
this.typeFilter = new TableFilterAttribute(
CriteriaType.SINGLE_SELECTION,
Exam.FILTER_ATTR_TYPE,
this.resourceService::examTypeResources);
}
@Override
public void compose(final PageContext pageContext) {
final WidgetFactory widgetFactory = this.pageService.getWidgetFactory();
final CurrentUser currentUser = this.resourceService.getCurrentUser();
final RestService restService = this.resourceService.getRestService();
final I18nSupport i18nSupport = this.resourceService.getI18nSupport();
// content page layout with title
final Composite content = widgetFactory.defaultPageLayout(
pageContext.getParent(),
PAGE_TITLE_KEY);
final PageActionBuilder actionBuilder = this.pageService
.pageActionBuilder(pageContext.clearEntityKeys());
// table
final EntityTable<Exam> table =
this.pageService.entityTableBuilder(restService.getRestCall(GetFinishedExamPage.class))
.withEmptyMessage(EMPTY_LIST_TEXT_KEY)
.withPaging(this.pageSize)
.withRowDecorator(ExamList.decorateOnExamConsistency(this.pageService))
.withDefaultSort(QuizData.QUIZ_ATTR_NAME)
.withColumn(new ColumnDefinition<>(
QuizData.QUIZ_ATTR_NAME,
COLUMN_TITLE_NAME_KEY,
Exam::getName)
.withFilter(this.nameFilter)
.sortable())
.withColumn(new ColumnDefinition<Exam>(
Domain.EXAM.ATTR_TYPE,
COLUMN_TITLE_TYPE_KEY,
this.resourceService::localizedExamTypeName)
.withFilter(this.typeFilter)
.sortable())
.withColumn(new ColumnDefinition<>(
QuizData.QUIZ_ATTR_START_TIME,
new LocTextKey(
"sebserver.finished.exam.list.column.startTime",
i18nSupport.getUsersTimeZoneTitleSuffix()),
Exam::getStartTime)
.sortable())
.withColumn(new ColumnDefinition<>(
QuizData.QUIZ_ATTR_END_TIME,
new LocTextKey(
"sebserver.finished.exam.list.column.endTime",
i18nSupport.getUsersTimeZoneTitleSuffix()),
Exam::getEndTime)
.sortable())
.withDefaultAction(actionBuilder
.newAction(ActionDefinition.VIEW_EXAM_FROM_FINISHED_LIST)
.create())
.withSelectionListener(this.pageService.getSelectionPublisher(
pageContext,
ActionDefinition.VIEW_EXAM_FROM_FINISHED_LIST))
.compose(pageContext.copyOf(content));
actionBuilder
.newAction(ActionDefinition.VIEW_EXAM_FROM_FINISHED_LIST)
.withSelect(table::getSelection, PageAction::applySingleSelectionAsEntityKey, EMPTY_SELECTION_TEXT_KEY)
.publishIf(() -> currentUser.get().hasRole(UserRole.EXAM_SUPPORTER), false);
}
}

View file

@ -98,7 +98,7 @@ public class MonitoringRunningExam implements TemplateComposer {
private final boolean distributedSetup;
private final long pollInterval;
protected MonitoringRunningExam(
public MonitoringRunningExam(
final ServerPushService serverPushService,
final PageService pageService,
final AsyncRunner asyncRunner,
@ -122,10 +122,9 @@ public class MonitoringRunningExam implements TemplateComposer {
@Override
public void compose(final PageContext pageContext) {
final RestService restService = this.resourceService.getRestService();
final EntityKey entityKey = pageContext.getEntityKey();
final CurrentUser currentUser = this.resourceService.getCurrentUser();
final Exam exam = restService.getBuilder(GetExam.class)
final Exam exam = this.restService.getBuilder(GetExam.class)
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
.call()
.getOrThrow();
@ -134,7 +133,7 @@ public class MonitoringRunningExam implements TemplateComposer {
exam.supporter.contains(user.uuid);
final BooleanSupplier isExamSupporter = () -> supporting || user.hasRole(UserRole.EXAM_ADMIN);
final Collection<Indicator> indicators = restService.getBuilder(GetIndicators.class)
final Collection<Indicator> indicators = this.restService.getBuilder(GetIndicators.class)
.withQueryParam(Indicator.FILTER_ATTR_EXAM_ID, entityKey.modelId)
.call()
.getOrThrow();

View file

@ -0,0 +1,41 @@
/*
* 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.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.exam.Exam;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class GetFinishedExamPage extends RestCall<Page<Exam>> {
public GetFinishedExamPage() {
super(new TypeKey<>(
CallType.GET_PAGE,
EntityType.EXAM,
new TypeReference<Page<Exam>>() {
}),
HttpMethod.GET,
MediaType.APPLICATION_FORM_URLENCODED,
API.EXAM_MONITORING_ENDPOINT + API.EXAM_MONITORING_FINISHED_ENDPOINT);
}
}

View file

@ -134,6 +134,15 @@ public interface ExamSessionService {
FilterMap filterMap,
Predicate<Exam> predicate);
/** Gets all finished Exams for a particular FilterMap.
*
* @param filterMap the FilterMap containing the filter attributes
* @param predicate additional filter predicate
* @return Result referencing the list of all currently finished Exams or to an error if happened. */
Result<Collection<Exam>> getFilteredFinishedExams(
FilterMap filterMap,
Predicate<Exam> predicate);
/** Streams the default SEB Exam Configuration to a ClientConnection with given connectionToken.
*
* @param institutionId the Institution identifier

View file

@ -254,6 +254,15 @@ public class ExamSessionServiceImpl implements ExamSessionService {
.collect(Collectors.toList()));
}
@Override
public Result<Collection<Exam>> getFilteredFinishedExams(
final FilterMap filterMap,
final Predicate<Exam> predicate) {
filterMap.putIfAbsent(Exam.FILTER_ATTR_STATUS, ExamStatus.FINISHED.name());
return this.examDAO.allMatching(filterMap, predicate);
}
@Override
public void streamDefaultExamConfig(
final Long institutionId,

View file

@ -174,6 +174,67 @@ public class ExamMonitoringController {
ExamAdministrationController.pageSort(sort));
}
/** Get a page of all currently finished exams
*
* GET /{api}/{entity-type-endpoint-name}
*
* GET /admin-api/v1/monitoring
* GET /admin-api/v1/monitoring?page_number=2&page_size=10&sort=-name
* GET /admin-api/v1/monitoring?name=seb&active=true
*
* @param institutionId The institution identifier of the request.
* Default is the institution identifier of the institution of the current user
* @param pageNumber the number of the page that is requested
* @param pageSize the size of the page that is requested
* @param sort the sort parameter to sort the list of entities before paging
* the sort parameter is the name of the entity-model attribute to sort with a leading '-' sign for
* descending sort order
* @param allRequestParams a MultiValueMap of all request parameter that is used for filtering
* @return Page of domain-model-entities of specified type */
@RequestMapping(
path = API.EXAM_MONITORING_FINISHED_ENDPOINT,
method = RequestMethod.GET,
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
public Page<Exam> getFinishedExamsPage(
@RequestParam(
name = API.PARAM_INSTITUTION_ID,
required = true,
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
@RequestParam(name = Page.ATTR_PAGE_NUMBER, required = false) final Integer pageNumber,
@RequestParam(name = Page.ATTR_PAGE_SIZE, required = false) final Integer pageSize,
@RequestParam(name = Page.ATTR_SORT, required = false) final String sort,
@RequestParam final MultiValueMap<String, String> allRequestParams,
final HttpServletRequest request) {
this.authorization.checkRole(
institutionId,
EntityType.EXAM,
UserRole.EXAM_SUPPORTER,
UserRole.EXAM_ADMIN);
final FilterMap filterMap = new FilterMap(allRequestParams, request.getQueryString());
// if current user has no read access for specified entity type within other institution
// then the current users institutionId is put as a SQL filter criteria attribute to extends query performance
if (!this.authorization.hasGrant(PrivilegeType.READ, EntityType.EXAM)) {
filterMap.putIfAbsent(API.PARAM_INSTITUTION_ID, String.valueOf(institutionId));
}
final Collection<Exam> exams = this.examSessionService
.getFilteredFinishedExams(
filterMap,
exam -> this.hasRunningExamPrivilege(exam, institutionId))
.getOrThrow();
return this.paginationService.buildPageFromList(
pageNumber,
pageSize,
sort,
exams,
ExamAdministrationController.pageSort(sort));
}
@RequestMapping(
path = API.PARENT_MODEL_ID_VAR_PATH_SEGMENT,
method = RequestMethod.GET,

View file

@ -1868,6 +1868,33 @@ sebserver.monitoring.exam.connection.status.CLOSED=Closed
sebserver.monitoring.exam.connection.status.ABORTED=Aborted
sebserver.monitoring.exam.connection.status.DISABLED=Canceled
################################
# Finished Exams
################################
sebserver.finished.action.list=Finished Exams
sebserver.finished.exam.list.title=Finished Exams
sebserver.finished.exam.list.actions=
sebserver.finished.exam.info.pleaseSelect=At first please select an Exam from the list
sebserver.finished.exam.list.empty=There are currently no finished exams
sebserver.finished.exam.list.column.name=Name
sebserver.finished.exam.list.column.name.tooltip=The name of the exam<br/><br/>Use the filter above to narrow down to a specific exam name<br/>{0}
sebserver.finished.exam.list.column.type=Type
sebserver.finished.exam.list.column.type.tooltip=The type of the exam<br/><br/>Use the filter above to set a specific exam type<br/>{0}
sebserver.finished.exam.list.column.startTime=Start Time {0}
sebserver.finished.exam.list.column.startTime.tooltip=The start date and time of the exam<br/><br/>{0}
sebserver.finished.exam.list.column.endTime=End Time {0}
sebserver.finished.exam.list.column.endTime.tooltip=The end date and time of the exam<br/><br/>{0}
sebserver.finished.exam.action.list.view=View Finished Exam
sebserver.finished.exam.connections.title=Search Connections
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
################################
# Logs
################################