SEBSERV-218 implemented

This commit is contained in:
anhefti 2022-06-16 10:56:42 +02:00
parent a979d4c13b
commit 3b4c168c43
7 changed files with 159 additions and 12 deletions

View file

@ -8,6 +8,10 @@
package ch.ethz.seb.sebserver.gui.content.configs;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.swt.widgets.Composite;
import org.springframework.context.annotation.Lazy;
@ -19,13 +23,16 @@ 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.EntityProcessingReport;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Configuration;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode.ConfigurationStatus;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode.ConfigurationType;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.TemplateAttribute;
import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gbl.util.Result;
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.form.FormHandle;
@ -40,6 +47,8 @@ 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.seb.examconfig.DeleteExamConfiguration;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetConfigurationValues;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetConfigurations;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetExamConfigNode;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetTemplateAttributePage;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.NewExamConfig;
@ -75,6 +84,8 @@ public class ConfigTemplateForm implements TemplateComposer {
new LocTextKey("sebserver.configtemplate.attrs.list.view");
private static final LocTextKey ATTRIBUTES_LIST_GROUP_TEXT_KEY =
new LocTextKey("sebserver.configtemplate.attrs.list.group");
private static final LocTextKey ATTRIBUTES_LIST_VALUE_TEXT_KEY =
new LocTextKey("sebserver.configtemplate.attrs.list.value");
private static final LocTextKey ATTRIBUTES_LIST_TYPE_TEXT_KEY =
new LocTextKey("sebserver.configtemplate.attrs.list.type");
private static final LocTextKey EMPTY_ATTRIBUTE_SELECTION_TEXT_KEY =
@ -205,6 +216,20 @@ public class ConfigTemplateForm implements TemplateComposer {
TemplateAttribute.FILTER_ATTR_TYPE,
this.resourceService::getAttributeTypeResources);
// TODO move this to an supplier that also can be updated
// the follow-up configuration
final Configuration configuration = this.restService
.getBuilder(GetConfigurations.class)
.withQueryParam(Configuration.FILTER_ATTR_CONFIGURATION_NODE_ID, examConfig.getModelId())
.withQueryParam(Configuration.FILTER_ATTR_FOLLOWUP, Constants.TRUE_STRING)
.call()
.map(Utils::toSingleton)
.onError(error -> pageContext.notifyLoadError(EntityType.CONFIGURATION, error))
.getOrThrow();
final AttributeValueSupplier attributeValueSupplier = new AttributeValueSupplier(
this.pageService,
configuration.getModelId());
final EntityTable<TemplateAttribute> attrTable =
this.pageService.entityTableBuilder(
Domain.CONFIGURATION_NODE.TYPE_NAME + "_Template",
@ -213,6 +238,7 @@ public class ConfigTemplateForm implements TemplateComposer {
API.PARAM_PARENT_MODEL_ID,
entityKey.modelId))
.withPaging(15)
.withColumn(new ColumnDefinition<>(
Domain.CONFIGURATION_ATTRIBUTE.ATTR_NAME,
ATTRIBUTES_LIST_NAME_TEXT_KEY,
@ -220,6 +246,7 @@ public class ConfigTemplateForm implements TemplateComposer {
.withFilter(this.nameFilter)
.sortable()
.widthProportion(3))
.withColumn(new ColumnDefinition<TemplateAttribute>(
Domain.CONFIGURATION_ATTRIBUTE.ATTR_TYPE,
ATTRIBUTES_LIST_TYPE_TEXT_KEY,
@ -227,6 +254,7 @@ public class ConfigTemplateForm implements TemplateComposer {
.withFilter(typeFilter)
.sortable()
.widthProportion(1))
.withColumn(new ColumnDefinition<>(
Domain.ORIENTATION.ATTR_VIEW_ID,
ATTRIBUTES_LIST_VIEW_TEXT_KEY,
@ -234,6 +262,7 @@ public class ConfigTemplateForm implements TemplateComposer {
.withFilter(viewFilter)
.sortable()
.widthProportion(1))
.withColumn(new ColumnDefinition<>(
Domain.ORIENTATION.ATTR_GROUP_ID,
ATTRIBUTES_LIST_GROUP_TEXT_KEY,
@ -241,6 +270,13 @@ public class ConfigTemplateForm implements TemplateComposer {
.withFilter(this.groupFilter)
.sortable()
.widthProportion(1))
.withColumn(new ColumnDefinition<TemplateAttribute>(
Domain.CONFIGURATION_VALUE.ATTR_VALUE,
ATTRIBUTES_LIST_VALUE_TEXT_KEY,
attr -> attributeValueSupplier.getAttributeValue(attr.getConfigAttribute().id))
.widthProportion(1))
.withDefaultActionIf(
() -> modifyGrant,
() -> pageActionBuilder
@ -271,7 +307,7 @@ public class ConfigTemplateForm implements TemplateComposer {
.withParentEntityKey(entityKey)
.withSelect(
attrTable::getMultiSelection,
action -> this.resetToDefaults(action, attrTable),
action -> this.resetToDefaults(attributeValueSupplier, action, attrTable),
EMPTY_ATTRIBUTE_SELECTION_TEXT_KEY)
.noEventPropagation()
.publishIf(() -> modifyGrant, false)
@ -378,12 +414,14 @@ public class ConfigTemplateForm implements TemplateComposer {
}
private PageAction resetToDefaults(
final AttributeValueSupplier attributeValueSupplier,
final PageAction action,
final EntityTable<TemplateAttribute> attrTable) {
final PageAction resetToDefaults = this.examConfigurationService.resetToDefaults(action);
// reload the list
attrTable.applyFilter();
attributeValueSupplier.update();
attrTable.updateCurrentPage();
return resetToDefaults;
}
@ -392,8 +430,8 @@ public class ConfigTemplateForm implements TemplateComposer {
final EntityTable<TemplateAttribute> attrTable) {
final PageAction removeFormView = this.examConfigurationService.removeFromView(action);
// reload the list
attrTable.applyFilter();
// reload the page
attrTable.updateCurrentPage();
return removeFormView;
}
@ -402,9 +440,54 @@ public class ConfigTemplateForm implements TemplateComposer {
final EntityTable<TemplateAttribute> attrTable) {
final PageAction attachView = this.examConfigurationService.attachToDefaultView(action);
// reload the list
attrTable.applyFilter();
// reload the page
attrTable.updateCurrentPage();
return attachView;
}
private final class AttributeValueSupplier {
private final PageService pageService;
private final String configurationId;
private final Map<Long, String> attrValueMapping = new HashMap<>();
public AttributeValueSupplier(
final PageService pageService,
final String configurationId) {
this.pageService = pageService;
this.configurationId = configurationId;
update();
}
public String getAttributeValue(final Long attributeId) {
if (this.attrValueMapping.containsKey(attributeId)) {
return this.attrValueMapping.get(attributeId);
} else {
return Constants.EMPTY_NOTE;
}
}
private void update() {
this.attrValueMapping.clear();
this.pageService.getRestService()
.getBuilder(GetConfigurationValues.class)
.withQueryParam(
ConfigurationValue.FILTER_ATTR_CONFIGURATION_ID,
this.configurationId)
.call()
.getOrElse(Collections::emptyList)
.stream()
.forEach(val -> {
if (this.attrValueMapping.containsKey(val.attributeId)) {
this.attrValueMapping.put(val.attributeId,
this.attrValueMapping.get(val.attributeId) + "," + val.value);
} else {
this.attrValueMapping.put(val.attributeId, val.value);
}
});
}
}
}

View file

@ -72,6 +72,11 @@ public class ExamConfigurationServiceImpl implements ExamConfigurationService {
private static final Logger log = LoggerFactory.getLogger(ExamConfigurationServiceImpl.class);
public static final LocTextKey ACTION_ERROR_TITLE =
new LocTextKey("sebserver.configtemplate.action.error.title");
public static final LocTextKey ACTION_ERROR_MSG =
new LocTextKey("sebserver.configtemplate.action.error.noview.message");
private final RestService restService;
private final JSONMapper jsonMapper;
private final WidgetFactory widgetFactory;
@ -254,12 +259,14 @@ public class ExamConfigurationServiceImpl implements ExamConfigurationService {
final Set<EntityKey> selection = action.getMultiSelection();
if (selection != null && !selection.isEmpty()) {
selection.forEach(entityKey -> callTemplateAction(
action,
ResetTemplateValues.class,
parentEntityKey.modelId,
entityKey.modelId));
} else {
final EntityKey entityKey = action.getEntityKey();
callTemplateAction(
action,
ResetTemplateValues.class,
parentEntityKey.modelId,
entityKey.modelId);
@ -274,12 +281,14 @@ public class ExamConfigurationServiceImpl implements ExamConfigurationService {
final Set<EntityKey> selection = action.getMultiSelection();
if (selection != null && !selection.isEmpty()) {
selection.forEach(entityKey -> callTemplateAction(
action,
RemoveOrientation.class,
parentEntityKey.modelId,
entityKey.modelId));
} else {
final EntityKey entityKey = action.getEntityKey();
callTemplateAction(
action,
RemoveOrientation.class,
parentEntityKey.modelId,
entityKey.modelId);
@ -294,21 +303,23 @@ public class ExamConfigurationServiceImpl implements ExamConfigurationService {
final Set<EntityKey> selection = action.getMultiSelection();
if (selection != null && !selection.isEmpty()) {
selection.forEach(entityKey -> callTemplateAction(
action,
AttachDefaultOrientation.class,
parentEntityKey.modelId,
entityKey.modelId));
} else {
final EntityKey entityKey = action.getEntityKey();
callTemplateAction(
action,
AttachDefaultOrientation.class,
parentEntityKey.modelId,
entityKey.modelId);
}
return action;
}
private void callTemplateAction(
final PageAction action,
final Class<? extends RestCall<TemplateAttribute>> actionType,
final String templateId,
final String attributeId) {
@ -317,7 +328,11 @@ public class ExamConfigurationServiceImpl implements ExamConfigurationService {
.withURIVariable(API.PARAM_PARENT_MODEL_ID, templateId)
.withURIVariable(API.PARAM_MODEL_ID, attributeId)
.call()
.getOrThrow();
.onError(error -> {
action.pageContext().publishPageMessage(
ACTION_ERROR_TITLE,
ACTION_ERROR_MSG);
});
}
private static final class ValueChangeListenerImpl implements ValueChangeListener {

View file

@ -300,6 +300,10 @@ public class EntityTable<ROW extends ModelIdAware> {
applyFilter();
}
public void updateCurrentPage() {
this.selectPage(this.pageNumber);
}
public void applyFilter() {
try {

View file

@ -0,0 +1,27 @@
/*
* 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.webservice.servicelayer.dao;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
@ResponseStatus(HttpStatus.NOT_FOUND)
public class NoResourceFoundException extends RuntimeException {
private static final long serialVersionUID = 4347712679241097195L;
public final EntityType entityType;
public NoResourceFoundException(final EntityType entityType, final String message) {
super("Resource " + entityType + " not found: " + message);
this.entityType = entityType;
}
}

View file

@ -25,6 +25,7 @@ import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.PageSortOrder;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType;
@ -39,6 +40,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationAttributeD
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationValueDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.NoResourceFoundException;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.OrientationDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ViewDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ExamConfigTemplateService;
@ -172,7 +174,13 @@ public class ExamConfigTemplateServiceImpl implements ExamConfigTemplateService
return Result.tryCatch(() -> {
final Orientation orientation = getOrientation(templateId, attributeId);
this.orientationDAO.delete(new HashSet<>(Arrays.asList(orientation.getEntityKey())))
if (orientation == null) {
throw new NoResourceFoundException(EntityType.ORIENTATION,
"No default view found for attribute: " + attributeId);
}
this.orientationDAO
.delete(new HashSet<>(Arrays.asList(orientation.getEntityKey())))
.getOrThrow();
final TemplateAttribute attribute = getAttribute(institutionId, templateId, attributeId)
@ -198,8 +206,14 @@ public class ExamConfigTemplateServiceImpl implements ExamConfigTemplateService
final Orientation orientation = getOrientation(templateId, attributeId);
final Orientation devOrientation = getOrientation(ConfigurationNode.DEFAULT_TEMPLATE_ID, attributeId);
if (devOrientation == null) {
throw new NoResourceFoundException(EntityType.ORIENTATION,
"No default view found for attribute: " + attributeId);
}
if (orientation != null) {
this.orientationDAO.delete(new HashSet<>(Arrays.asList(orientation.getEntityKey())))
this.orientationDAO
.delete(new HashSet<>(Arrays.asList(orientation.getEntityKey())))
.getOrThrow();
}
@ -246,7 +260,7 @@ public class ExamConfigTemplateServiceImpl implements ExamConfigTemplateService
return this.orientationDAO.allMatching(filterMap)
.get(error -> {
log.warn("Unexpecrted error while get Orientation: ", error);
log.warn("Unexpected error while get Orientation: ", error);
return Collections.emptyList();
})
.stream()

View file

@ -455,7 +455,6 @@ public class ZoomProctoringService implements ExamProctoringService {
e);
}
});
}
@Override

View file

@ -1667,7 +1667,11 @@ sebserver.configtemplate.attrs.list.group=Group
sebserver.configtemplate.attrs.list.group.tooltip=The group on the view/tab where the exam configuration attribute belongs to<br/><br/>{0}
sebserver.configtemplate.attrs.list.type=Type
sebserver.configtemplate.attrs.list.type.tooltip=The type of the exam configuration attribute<br/><br/>{0}
sebserver.configtemplate.attrs.list.value=Value
sebserver.configtemplate.attrs.list.value.tooltip=The settings value that is set as default value for this template
sebserver.configtemplate.action.error.title=Action Failed
sebserver.configtemplate.action.error.noview.message=This setting has no default view<br/>and cannot be attached/detached from a view
sebserver.configtemplate.attr.list.actions=
sebserver.configtemplate.attr.list.actions.modify=Edit Attribute
sebserver.configtemplate.attr.list.actions.setdefault=Set Default Values
@ -1675,6 +1679,7 @@ sebserver.configtemplate.attr.list.actions.removeview=Remove From View
sebserver.configtemplate.attr.list.actions.attach-default-view=Attach To View
sebserver.configtemplate.attr.info.pleaseSelect=At first please select an Attribute from the list
sebserver.configtemplate.attr.form.title=Configuration Template Attribute
sebserver.configtemplate.attr.form.title.subtitle=
sebserver.configtemplate.attr.form.name=Name