SEBSERV-45 seb configuration table implementation

This commit is contained in:
anhefti 2019-05-07 16:34:02 +02:00
parent 0a648d8c84
commit 5032e39352
18 changed files with 619 additions and 151 deletions

View file

@ -41,10 +41,6 @@ public final class API {
public static final String PRIVILEGES_ENDPOINT = INFO_ENDPOINT + PRIVILEGES_PATH_SEGMENT;
public static final String INSTITUTION_ENDPOINT = "/institution";
// public static final String SEB_CONFIG_EXPORT_PATH_SEGMENT = "/sebconfig";
// public static final String SEB_CONFIG_EXPORT_ENDPOINT = INSTITUTION_ENDPOINT
// + INSTITUTION_VAR_PATH_SEGMENT
// + SEB_CONFIG_EXPORT_PATH_SEGMENT;
public static final String LMS_SETUP_ENDPOINT = "/lms_setup";
public static final String LMS_SETUP_TEST_PATH_SEGMENT = "/test";

View file

@ -24,8 +24,7 @@ import ch.ethz.seb.sebserver.gbl.model.GrantEntity;
@JsonIgnoreProperties(ignoreUnknown = true)
public final class ConfigurationTableValue implements GrantEntity {
public static final String ATTR_COLUMNS = "columnAttributeIds";
public static final String ATTR_VALUES = "values";
public static final String ATTR_TABLE_VALUES = "tableValues";
@NotNull
@JsonProperty(CONFIGURATION_VALUE.ATTR_INSTITUTION_ID)
@ -39,24 +38,19 @@ public final class ConfigurationTableValue implements GrantEntity {
@JsonProperty(CONFIGURATION_VALUE.ATTR_CONFIGURATION_ATTRIBUTE_ID)
public final Long attributeId;
@JsonProperty(ATTR_COLUMNS)
public final List<Long> columnAttributeIds;
@JsonProperty(ATTR_VALUES)
public final List<String> values;
@JsonProperty(ATTR_TABLE_VALUES)
public final List<TableValue> values;
@JsonCreator
public ConfigurationTableValue(
@JsonProperty(CONFIGURATION_VALUE.ATTR_INSTITUTION_ID) final Long institutionId,
@JsonProperty(CONFIGURATION_VALUE.ATTR_CONFIGURATION_ID) final Long configurationId,
@JsonProperty(CONFIGURATION_VALUE.ATTR_CONFIGURATION_ATTRIBUTE_ID) final Long attributeId,
@JsonProperty(ATTR_COLUMNS) final List<Long> columns,
@JsonProperty(ATTR_VALUES) final List<String> values) {
@JsonProperty(ATTR_TABLE_VALUES) final List<TableValue> values) {
this.institutionId = institutionId;
this.configurationId = configurationId;
this.attributeId = attributeId;
this.columnAttributeIds = Collections.unmodifiableList(columns);
this.values = Collections.unmodifiableList(values);
}
@ -88,11 +82,7 @@ public final class ConfigurationTableValue implements GrantEntity {
return this.attributeId;
}
public List<Long> getColumnAttributeIds() {
return this.columnAttributeIds;
}
public List<String> getValues() {
public List<TableValue> getValues() {
return this.values;
}
@ -100,8 +90,31 @@ public final class ConfigurationTableValue implements GrantEntity {
public String toString() {
return "ConfigurationTableValue [institutionId=" + this.institutionId + ", configurationId="
+ this.configurationId
+ ", attributeId=" + this.attributeId + ", columnAttributeIds=" + this.columnAttributeIds + ", values="
+ this.values
+ "]";
+ ", attributeId=" + this.attributeId + ", values=" + this.values + "]";
}
@JsonIgnoreProperties(ignoreUnknown = true)
public static final class TableValue {
@JsonProperty(CONFIGURATION_VALUE.ATTR_CONFIGURATION_ATTRIBUTE_ID)
public final Long attributeId;
@JsonProperty(CONFIGURATION_VALUE.ATTR_LIST_INDEX)
public final Integer listIndex;
@JsonProperty(CONFIGURATION_VALUE.ATTR_VALUE)
public final String value;
public TableValue(
@JsonProperty(CONFIGURATION_VALUE.ATTR_CONFIGURATION_ATTRIBUTE_ID) final Long attributeId,
@JsonProperty(CONFIGURATION_VALUE.ATTR_LIST_INDEX) final Integer listIndex,
@JsonProperty(CONFIGURATION_VALUE.ATTR_VALUE) final String value) {
this.attributeId = attributeId;
this.listIndex = listIndex;
this.value = value;
}
public static TableValue of(final ConfigurationValue value) {
return new TableValue(value.attributeId, value.listIndex, value.value);
}
}
}

View file

@ -28,7 +28,7 @@ public abstract class AbstractInputField<T extends Control> implements InputFiel
protected String initValue = "";
protected int listIndex = 0;
AbstractInputField(
protected AbstractInputField(
final ConfigurationAttribute attribute,
final Orientation orientation,
final T control,
@ -93,11 +93,15 @@ public abstract class AbstractInputField<T extends Control> implements InputFiel
.map(v -> {
this.initValue = v.value;
this.listIndex = (v.listIndex != null) ? v.listIndex : 0;
setDefaultValue();
setValueToControl(this.initValue);
return this.initValue;
});
}
protected abstract void setDefaultValue();
protected void setDefaultValue() {
setValueToControl(this.attribute.defaultValue);
}
protected abstract void setValueToControl(String value);
}

View file

@ -81,7 +81,7 @@ public class CheckBoxBuilder implements InputFieldBuilder {
}
@Override
protected void setDefaultValue() {
protected void setValueToControl(final String value) {
this.control.setSelection(Boolean.valueOf(this.initValue));
}
}

View file

@ -51,6 +51,7 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.Ge
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.GetOrientations;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.GetViewList;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.SaveExamConfigTableValue;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.seb.examconfig.SaveExamConfigValue;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@ -269,8 +270,9 @@ public class ExamConfigurationServiceImpl implements ExamConfigurationService {
@Override
public void tableChanged(final ConfigurationTableValue tableValue) {
// TODO Auto-generated method stub
this.restService.getBuilder(SaveExamConfigTableValue.class)
.withBody(tableValue)
.call();
}
private String verifyErrorMessage(final Throwable error) {

View file

@ -70,7 +70,7 @@ public class LabelBuilder implements InputFieldBuilder {
}
@Override
protected void setDefaultValue() {
protected void setValueToControl(final String value) {
// Does Nothing, Label has no default value
}

View file

@ -146,11 +146,11 @@ public class PassworFieldBuilder implements InputFieldBuilder {
}
@Override
protected void setDefaultValue() {
protected void setValueToControl(final String value) {
// TODO clarify setting some "fake" input when a password is set (like in config tool)
if (this.initValue != null) {
this.control.setText(this.initValue);
this.confirm.setText(this.initValue);
this.control.setText(value);
this.confirm.setText(value);
}
}

View file

@ -97,10 +97,9 @@ public class TextFieldBuilder implements InputFieldBuilder {
}
@Override
protected void setDefaultValue() {
this.control.setText(this.initValue);
protected void setValueToControl(final String value) {
this.control.setText(value);
}
}
}

View file

@ -10,10 +10,12 @@ package ch.ethz.seb.sebserver.gui.service.examconfig.impl;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Configuration;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.View;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputField;
@ -84,6 +86,11 @@ public final class ViewContext {
return this.rows;
}
public List<ConfigurationAttribute> getChildAttributes(final ConfigurationAttribute attribute) {
// TODO Auto-generated method stub
return null;
}
public ValueChangeListener getValueChangeListener() {
return this.valueChangeListener;
}

View file

@ -19,6 +19,8 @@ import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
@ -31,6 +33,8 @@ import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
public class ViewGridBuilder {
private static final Logger log = LoggerFactory.getLogger(ViewGridBuilder.class);
private final ExamConfigurationService examConfigurationService;
private final Composite parent;
private final ViewContext viewContext;
@ -85,34 +89,29 @@ public class ViewGridBuilder {
attribute,
orientation);
final CellFieldBuilderAdapter fieldBuilderAdapter = fieldBuilderAdapter(
this.grid[ypos][xpos] = fieldBuilderAdapter(
inputFieldBuilder,
attribute);
switch (orientation.title) {
case RIGHT: {
this.grid[ypos][xpos] = fieldBuilderAdapter;
this.grid[ypos][xpos + 1] = labelBuilder(attribute, orientation);
if (attribute.type == AttributeType.PASSWORD_FIELD) {
this.grid[ypos + 1][xpos + 1] = passwordConfirmLabel(attribute, orientation);
try {
switch (orientation.title) {
case RIGHT: {
this.grid[ypos][xpos + 1] = labelBuilder(attribute, orientation);
break;
}
break;
}
case LEFT: {
this.grid[ypos][xpos] = labelBuilder(attribute, orientation);
if (attribute.type == AttributeType.PASSWORD_FIELD) {
this.grid[ypos + 1][xpos] = passwordConfirmLabel(attribute, orientation);
case LEFT: {
this.grid[ypos][xpos - 1] = labelBuilder(attribute, orientation);
break;
}
case TOP: {
this.grid[ypos - 1][xpos] = labelBuilder(attribute, orientation);
break;
}
default: {
}
this.grid[ypos][xpos + 1] = fieldBuilderAdapter;
break;
}
case TOP: {
this.grid[ypos][xpos] = labelBuilder(attribute, orientation);
this.grid[ypos + 1][xpos] = fieldBuilderAdapter;
}
default: {
this.grid[ypos][xpos] = fieldBuilderAdapter;
}
} catch (final ArrayIndexOutOfBoundsException e) {
log.error("Failed to set title as configured in: {} for attribute: {}", orientation, attribute, e);
}
return this;
@ -136,6 +135,7 @@ public class ViewGridBuilder {
private static interface CellFieldBuilderAdapter {
void createCell(ViewGridBuilder builder);
}
private CellFieldBuilderAdapter dummyBuilderAdapter() {
@ -143,6 +143,12 @@ public class ViewGridBuilder {
@Override
public void createCell(final ViewGridBuilder builder) {
}
@Override
public String toString() {
return "[DUMMY]";
}
};
}
@ -161,26 +167,10 @@ public class ViewGridBuilder {
ViewGridBuilder.this.viewContext.registerInputField(inputField);
}
};
}
private CellFieldBuilderAdapter passwordConfirmLabel(
final ConfigurationAttribute attribute,
final Orientation orientation) {
return new CellFieldBuilderAdapter() {
@Override
public void createCell(final ViewGridBuilder builder) {
final WidgetFactory widgetFactory = builder.examConfigurationService.getWidgetFactory();
final Label label = widgetFactory.labelLocalized(
ViewGridBuilder.this.parent,
new LocTextKey(
ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + attribute.name + ".confirm"),
"Confirm Password");
final GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, false);
label.setAlignment(SWT.LEFT);
gridData.verticalIndent = 10;
label.setLayoutData(gridData);
public String toString() {
return "[FIELD]";
}
};
}
@ -189,6 +179,10 @@ public class ViewGridBuilder {
final ConfigurationAttribute attribute,
final Orientation orientation) {
if (attribute.type == AttributeType.PASSWORD_FIELD) {
return passwordConfirmLabel(attribute, orientation);
}
return new CellFieldBuilderAdapter() {
@Override
public void createCell(final ViewGridBuilder builder) {
@ -208,7 +202,7 @@ public class ViewGridBuilder {
break;
}
case TOP: {
label.setAlignment(SWT.BOTTOM);
label.setAlignment(SWT.LEFT);
break;
}
@ -221,6 +215,27 @@ public class ViewGridBuilder {
};
}
private CellFieldBuilderAdapter passwordConfirmLabel(
final ConfigurationAttribute attribute,
final Orientation orientation) {
return new CellFieldBuilderAdapter() {
@Override
public void createCell(final ViewGridBuilder builder) {
final WidgetFactory widgetFactory = builder.examConfigurationService.getWidgetFactory();
final Label label = widgetFactory.labelLocalized(
ViewGridBuilder.this.parent,
new LocTextKey(
ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + attribute.name + ".confirm"),
"Confirm Password");
final GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, false);
label.setAlignment(SWT.LEFT);
gridData.verticalIndent = 10;
label.setLayoutData(gridData);
}
};
}
private static class GroupCellFieldBuilderAdapter implements CellFieldBuilderAdapter {
final ViewGridBuilder builder;

View file

@ -0,0 +1,365 @@
/*
* Copyright (c) 2019 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.examconfig.impl.table;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValue;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValue.TableValue;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.Orientation;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.examconfig.ExamConfigurationService;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputField;
import ch.ethz.seb.sebserver.gui.service.examconfig.InputFieldBuilder;
import ch.ethz.seb.sebserver.gui.service.examconfig.impl.AbstractInputField;
import ch.ethz.seb.sebserver.gui.service.examconfig.impl.ViewContext;
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.impl.ModalInputDialog;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory.ImageIcon;
@Lazy
@Component
@GuiProfile
public class TableFieldBuilder implements InputFieldBuilder {
private static final Logger log = LoggerFactory.getLogger(TableFieldBuilder.class);
private static final String ROW_VALUE_KEY = "RowValues";
private final WidgetFactory widgetFactory;
public TableFieldBuilder(final WidgetFactory widgetFactory) {
this.widgetFactory = widgetFactory;
}
@Override
public boolean builderFor(
final ConfigurationAttribute attribute,
final Orientation orientation) {
if (attribute == null) {
return false;
}
return AttributeType.TABLE == attribute.type;
}
@Override
public InputField createInputField(
final Composite parent,
final ConfigurationAttribute attribute,
final ViewContext viewContext) {
final I18nSupport i18nSupport = viewContext.getI18nSupport();
final Orientation orientation = viewContext.attributeMapping
.getOrientation(attribute.id);
final List<ConfigurationAttribute> childAttributes =
viewContext.attributeMapping.childAttributeMapping.get(attribute.id);
final List<ConfigurationAttribute> columnAttributes = childAttributes
.stream()
.filter(attr -> viewContext.attributeMapping.getOrientation(attr.id).xPosition > 0)
.sorted((attr1, attr2) -> viewContext.attributeMapping.getOrientation(attr1.id).xPosition.compareTo(
viewContext.attributeMapping.getOrientation(attr2.id).xPosition))
.collect(Collectors.toList());
final Table table = new Table(parent, SWT.NONE | SWT.H_SCROLL);
table.setLayout(new GridLayout());
final GridData gridData = new GridData(
SWT.FILL, SWT.FILL,
true, false,
(orientation != null) ? orientation.width() : 1,
(orientation != null) ? orientation.height() : 1);
gridData.heightHint = orientation.height * 40;
table.setLayoutData(gridData);
table.setHeaderVisible(true);
table.addListener(SWT.Resize, this::adaptColumnWidth);
for (final ConfigurationAttribute columnAttribute : columnAttributes) {
final TableColumn column = new TableColumn(table, SWT.NONE);
final String text = i18nSupport.getText(
ExamConfigurationService.ATTRIBUTE_LABEL_LOC_TEXT_PREFIX + columnAttribute.name,
columnAttribute.name);
column.setText(text);
column.setWidth(100);
column.setResizable(false);
}
final TableInputField tableField = new TableInputField(
this.widgetFactory,
attribute,
orientation,
table,
childAttributes,
columnAttributes,
viewContext);
TableColumn column = new TableColumn(table, SWT.NONE);
column.setImage(ImageIcon.ADD_BOX.getImage(parent.getDisplay()));
column.setWidth(20);
column.setResizable(false);
column.setMoveable(false);
column.addListener(SWT.Selection, event -> {
tableField.addRow();
});
column = new TableColumn(table, SWT.NONE);
column.setImage(ImageIcon.REMOVE_BOX.getImage(parent.getDisplay()));
column.setWidth(20);
column.setResizable(false);
column.setMoveable(false);
column.addListener(SWT.Selection, event -> {
final int selectionIndex = table.getSelectionIndex();
if (selectionIndex >= 0) {
tableField.deleteRow(selectionIndex);
}
});
table.addListener(SWT.MouseDoubleClick, event -> {
final int selectionIndex = table.getSelectionIndex();
if (selectionIndex >= 0) {
tableField.openForm(selectionIndex);
}
});
return tableField;
}
private void adaptColumnWidth(final Event event) {
try {
final Table table = (Table) event.widget;
final int currentTableWidth = table.getClientArea().width - 50;
final TableColumn[] columns = table.getColumns();
final int columnWidth = currentTableWidth / (columns.length - 2);
for (int i = 0; i < columns.length - 2; i++) {
columns[i].setWidth(columnWidth);
}
} catch (final Exception e) {
log.warn("Failed to adaptColumnWidth: ", e);
}
}
static final class TableInputField extends AbstractInputField<Table> {
private final List<ConfigurationAttribute> childAttributes;
private final List<ConfigurationAttribute> columnAttributes;
private final ViewContext viewContext;
private final WidgetFactory widgetFactory;
private List<Map<Long, TableValue>> values;
TableInputField(
final WidgetFactory widgetFactory,
final ConfigurationAttribute attribute,
final Orientation orientation,
final Table control,
final List<ConfigurationAttribute> childAttributes,
final List<ConfigurationAttribute> columnAttributes,
final ViewContext viewContext) {
super(attribute, orientation, control, null);
this.childAttributes = childAttributes;
this.columnAttributes = columnAttributes;
this.viewContext = viewContext;
this.widgetFactory = widgetFactory;
}
@Override
public void initValue(final Collection<ConfigurationValue> values) {
// get all child values as TableValues
final List<TableValue> tableValues = values.stream()
.filter(this::isChildValue)
.map(TableValue::of)
.collect(Collectors.toList());
final Map<Integer, Map<Long, TableValue>> _initValue = new HashMap<>();
for (final TableValue tableValue : tableValues) {
final Map<Long, TableValue> rowValues = _initValue.computeIfAbsent(
tableValue.listIndex,
key -> new HashMap<>());
rowValues.put(tableValue.attributeId, tableValue);
}
final List<Integer> rows = new ArrayList<>(_initValue.keySet());
rows.sort((i1, i2) -> i1.compareTo(i2));
this.values = new ArrayList<>();
rows
.stream()
.forEach(i -> {
final Map<Long, TableValue> rowValues = _initValue.get(i);
this.values.add(rowValues);
addTableRow(rowValues);
});
}
private boolean isChildValue(final ConfigurationValue value) {
return this.attribute.id.equals(
this.viewContext.attributeMapping.attributeIdMapping
.get(value.attributeId).parentId);
}
private void deleteRow(final int selectionIndex) {
this.control.remove(selectionIndex);
this.values.remove(selectionIndex);
// send new values to web-service
this.viewContext.getValueChangeListener()
.tableChanged(extractTableValue());
}
private void addRow() {
final int index = this.values.size();
// create new values form default values
final Map<Long, TableValue> rowValues = this.childAttributes
.stream()
.map(attr -> new TableValue(attr.id, index, attr.defaultValue))
.collect(Collectors.toMap(
tv -> tv.attributeId,
Function.identity()));
this.values.add(rowValues);
addTableRow(rowValues);
this.control.layout();
}
private void addTableRow(final Map<Long, TableValue> rowValues) {
final TableItem tableItem = new TableItem(this.control, SWT.NONE);
applyTableRowValues(this.values.size() - 1);
// TODO delete icon is not working on row as expected
// final TableEditor editor = new TableEditor(this.control);
// editor.horizontalAlignment = SWT.CENTER;
// editor.grabHorizontal = true;
// editor.minimumWidth = 20;
// final Image image = ImageIcon.REMOVE_BOX.getImage(this.control.getDisplay());
// final Label imageLabel = new Label(this.control, SWT.NONE);
// imageLabel.setAlignment(SWT.CENTER);
// imageLabel.setImage(image);
// imageLabel.addListener(SWT.MouseDown, event -> System.out.println("*************** removeRow"));
// editor.setEditor(imageLabel, tableItem, this.columnAttributes.size());
// tableItem.setData("EDITOR", editor);
//
// editor.layout();
// this.control.layout(true, true);
}
private void applyRowValues(
final int rowIndex,
final Map<Long, TableValue> rowValues) {
// set the new values
this.values.set(rowIndex, rowValues);
// update table row
applyTableRowValues(rowIndex);
}
private void applyTableRowValues(final int index) {
final TableItem item = this.control.getItem(index);
final Map<Long, TableValue> rowValues = this.values.get(index);
int cellIndex = 0;
for (final ConfigurationAttribute attr : this.columnAttributes) {
final String value = rowValues.containsKey(attr.id)
? rowValues.get(attr.id).value
: null;
item.setText(cellIndex, value);
cellIndex++;
}
item.setData(ROW_VALUE_KEY, item);
// send new values to web-service
this.viewContext.getValueChangeListener()
.tableChanged(extractTableValue());
}
private void openForm(final int selectionIndex) {
final Map<Long, TableValue> rowValues = this.values.get(selectionIndex);
final ModalInputDialog<Map<Long, TableValue>> dialog = new ModalInputDialog<>(
this.control.getShell(),
this.widgetFactory);
final TableRowFormBuilder builder = new TableRowFormBuilder(rowValues);
dialog.open(
new LocTextKey("Title"),
v -> System.out.println("Values Applied"),
builder);
}
private ConfigurationTableValue extractTableValue() {
final List<TableValue> collect = this.values
.stream()
.flatMap(map -> map.values().stream())
.collect(Collectors.toList());
return new ConfigurationTableValue(
this.viewContext.getInstitutionId(),
this.viewContext.getConfigurationId(),
this.attribute.id,
collect);
}
@Override
protected void setDefaultValue() {
// NOTE this just empty the list for now
// TODO do we need default values for lists?
this.control.setSelection(-1);
if (this.control.getItemCount() > 0) {
for (final TableItem item : this.control.getItems()) {
item.dispose();
}
}
final List<TableValue> values = new ArrayList<>();
this.viewContext.getValueChangeListener().tableChanged(
new ConfigurationTableValue(
this.viewContext.getInstitutionId(),
this.viewContext.getConfigurationId(),
this.attribute.id,
values));
}
@Override
protected void setValueToControl(final String value) {
throw new UnsupportedOperationException();
}
}
}

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 2019 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.examconfig.impl.table;
import java.util.Map;
import java.util.function.Supplier;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValue.TableValue;
import ch.ethz.seb.sebserver.gui.service.page.ModalInputDialogComposer;
public class TableRowFormBuilder implements ModalInputDialogComposer<Map<Long, TableValue>> {
private final Map<Long, TableValue> rowValues;
public TableRowFormBuilder(final Map<Long, TableValue> rowValues) {
this.rowValues = rowValues;
}
@Override
public Supplier<Map<Long, TableValue>> compose(final Composite parent) {
final Label test = new Label(parent, SWT.NONE);
test.setText("TEST");
return () -> null;
}
}

View file

@ -10,9 +10,11 @@ package ch.ethz.seb.sebserver.gui.service.page;
import java.util.function.Supplier;
import org.eclipse.swt.widgets.Composite;
@FunctionalInterface
public interface ModalInputDialogComposer<T> {
Supplier<T> compose(PageContext pageContext);
Supplier<T> compose(Composite parent);
}

View file

@ -50,7 +50,6 @@ public class ModalInputDialog<T> extends Dialog {
public void open(
final LocTextKey title,
final PageContext pageContext,
final Consumer<T> callback,
final ModalInputDialogComposer<T> contentComposer) {
@ -68,8 +67,7 @@ public class ModalInputDialog<T> extends Dialog {
gridData.horizontalSpan = 2;
main.setLayoutData(gridData);
final PageContext internalPageContext = pageContext.copyOf(main);
final Supplier<T> valueSuppier = contentComposer.compose(internalPageContext);
final Supplier<T> valueSuppier = contentComposer.compose(main);
final Button ok = this.widgetFactory.buttonLocalized(shell, OK_TEXT_KEY);
GridData data = new GridData(GridData.HORIZONTAL_ALIGN_END);

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2019 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.seb.examconfig;
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.sebconfig.ConfigurationTableValue;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class SaveExamConfigTableValue extends RestCall<ConfigurationTableValue> {
protected SaveExamConfigTableValue() {
super(new TypeKey<>(
CallType.SAVE,
EntityType.CONFIGURATION_VALUE,
new TypeReference<ConfigurationTableValue>() {
}),
HttpMethod.PUT,
MediaType.APPLICATION_JSON_UTF8,
API.CONFIGURATION_VALUE_ENDPOINT + API.CONFIGURATION_TABLE_VALUE_PATH_SEGMENT);
}
}

View file

@ -35,7 +35,7 @@ import ch.ethz.seb.sebserver.gbl.util.Result;
* </code> */
public interface BulkActionService {
/** Use this to collect all EntityKey's of all dependent entities for a given BulkAction.
/** Use this to collect all EntityKey's of dependent entities for a given BulkAction.
*
* @param action the BulkAction defining the source entity keys and acts also as the
* dependency collector */

View file

@ -45,10 +45,10 @@ public interface BulkActionSupportDAO<T extends Entity> {
/** This processed a given BulkAction for all entities of the concrete type of this BulkActionSupportDAO
* that are defined by this given BulkAction.
*
*
* This returns a Collection of EntityKey results of each Entity that has been processed.
* If there was an error for a particular Entity, the Result will have an error reference.
*
*
* @param bulkAction the BulkAction containing the source entity and all dependencies
* @return a Collection of EntityKey results of each Entity that has been processed. */
@Transactional
@ -92,7 +92,15 @@ public interface BulkActionSupportDAO<T extends Entity> {
.collect(Collectors.toList());
}
@Transactional(readOnly = true)
/** Get dependency keys of all source entities of a given BulkAction
* This method simply goes through all source EntityKeys of the given BulkAction
* and applies the selection functions for each, collecting the resulting dependency EntityKeys
* into one Set of all dependency keys for all source keys
*
*
* @param bulkAction The BulkAction that defines the source keys
* @param selectionFunction a selection functions that gives all dependency keys for a given source key
* @return */
default Set<EntityKey> getDependencies(
final BulkAction bulkAction,
final Function<EntityKey, Result<Collection<EntityKey>>> selectionFunction) {

View file

@ -13,10 +13,10 @@ import static org.mybatis.dynamic.sql.SqlBuilder.isIn;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@ -30,6 +30,7 @@ import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeValueType;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValue;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValue.TableValue;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Result;
@ -213,61 +214,49 @@ public class ConfigurationValueDAOImpl implements ConfigurationValueDAO {
.build()
.execute();
final List<Long> columnAttributeIds = columnAttributes.stream()
.map(a -> a.getId())
.collect(Collectors.toList());
final Map<Long, ConfigurationAttributeRecord> attributeMapping = columnAttributes
.stream()
.collect(Collectors.toMap(attr -> attr.getId(), Function.identity()));
// get all values of the table and group them by attribute and sorted by list/row index
final List<ConfigurationValueRecord> valueRecords =
this.configurationValueRecordMapper.selectByExample()
.where(
ConfigurationValueRecordDynamicSqlSupport.institutionId,
isEqualTo(institutionId))
.and(
ConfigurationValueRecordDynamicSqlSupport.configurationId,
isEqualTo(configurationId))
.and(
ConfigurationValueRecordDynamicSqlSupport.configurationAttributeId,
SqlBuilder.isIn(columnAttributeIds))
.build()
.execute();
int rows = 0;
final List<String> values = new ArrayList<>();
final Map<Long, List<ConfigurationValueRecord>> valueMapping = new LinkedHashMap<>();
for (final ConfigurationValueRecord valueRecord : valueRecords) {
final List<ConfigurationValueRecord> list = valueMapping.putIfAbsent(
valueRecord.getId(),
new ArrayList<>());
list.add(valueRecord);
list.sort((r1, r2) -> r1.getListIndex().compareTo(r2.getListIndex()));
rows = list.size();
}
for (int row = 0; row < rows; row++) {
for (final ConfigurationAttributeRecord aRecord : columnAttributes) {
final List<ConfigurationValueRecord> list = valueMapping.get(aRecord.getId());
if (list != null) {
final ConfigurationValueRecord valueRecord = list.get(row);
if (valueRecord != null) {
values.add((isBigValue(aRecord)) ? valueRecord.getText() : valueRecord.getValue());
continue;
}
}
values.add(null);
}
}
final List<TableValue> values = this.configurationValueRecordMapper.selectByExample()
.where(
ConfigurationValueRecordDynamicSqlSupport.institutionId,
isEqualTo(institutionId))
.and(
ConfigurationValueRecordDynamicSqlSupport.configurationId,
isEqualTo(configurationId))
.and(
ConfigurationValueRecordDynamicSqlSupport.configurationAttributeId,
SqlBuilder.isIn(new ArrayList<>(attributeMapping.keySet())))
.build()
.execute()
.stream()
.map(value -> getTableValue(value, attributeMapping))
.collect(Collectors.toList());
return new ConfigurationTableValue(
institutionId,
configurationId,
attributeId,
new ArrayList<>(valueMapping.keySet()),
values);
});
}
private TableValue getTableValue(
final ConfigurationValueRecord value,
final Map<Long, ConfigurationAttributeRecord> attributeMapping) {
final Long configurationAttributeId = value.getConfigurationAttributeId();
final ConfigurationAttributeRecord configurationAttributeRecord = attributeMapping
.get(configurationAttributeId);
final boolean bigValue = isBigValue(configurationAttributeRecord);
return new TableValue(
value.getConfigurationAttributeId(),
value.getListIndex(),
bigValue ? value.getText() : value.getValue());
}
@Override
@Transactional
public Result<ConfigurationTableValue> saveTableValue(final ConfigurationTableValue value) {
@ -276,18 +265,20 @@ public class ConfigurationValueDAOImpl implements ConfigurationValueDAO {
.flatMap(val -> attributeRecordById(val.attributeId))
.map(attributeRecord -> {
final List<ConfigurationAttributeRecord> columnAttributes =
this.configurationAttributeRecordMapper.selectByExample()
.where(
ConfigurationAttributeRecordDynamicSqlSupport.parentId,
isEqualTo(attributeRecord.getId()))
.build()
.execute();
final Map<Long, ConfigurationAttributeRecord> attributeMap = this.configurationAttributeRecordMapper
.selectByExample()
.where(
ConfigurationAttributeRecordDynamicSqlSupport.parentId,
isEqualTo(attributeRecord.getId()))
.build()
.execute()
.stream()
.collect(Collectors.toMap(rec -> rec.getId(), Function.identity()));
final List<Long> columnAttributeIds = columnAttributes.stream()
final List<Long> columnAttributeIds = attributeMap.values()
.stream()
.map(a -> a.getId())
.collect(Collectors.toList());
final int columns = columnAttributeIds.size();
// first delete all old values of this table
this.configurationValueRecordMapper.deleteByExample()
@ -301,27 +292,19 @@ public class ConfigurationValueDAOImpl implements ConfigurationValueDAO {
.execute();
// then add the new values
int columnIndex = 0;
int rowIndex = 0;
for (final String val : value.values) {
final ConfigurationAttributeRecord columnAttr = columnAttributes.get(columnIndex);
for (final TableValue tableValue : value.values) {
final ConfigurationAttributeRecord columnAttr = attributeMap.get(tableValue.attributeId);
final boolean bigValue = isBigValue(columnAttr);
final ConfigurationValueRecord valueRecord = new ConfigurationValueRecord(
null,
value.institutionId,
value.configurationId,
columnAttr.getId(),
rowIndex,
(bigValue) ? null : val,
(bigValue) ? val : null);
tableValue.listIndex,
(bigValue) ? null : tableValue.value,
(bigValue) ? tableValue.value : null);
this.configurationValueRecordMapper.insert(valueRecord);
columnIndex++;
if (columnIndex >= columns) {
columnIndex = 0;
rowIndex++;
}
}
return value;