Commit bc5c682b authored by Daniel Gerhardt's avatar Daniel Gerhardt

Merge branch 'gridimage-content-format' into 'master'

Gridimage content format

See merge request arsnova/arsnova-backend!180
parents 7f7af445 c3391ee9
Pipeline #31968 failed with stages
in 0 seconds
......@@ -50,6 +50,7 @@ import de.thm.arsnova.controller.PaginationController;
import de.thm.arsnova.model.ChoiceAnswer;
import de.thm.arsnova.model.ChoiceQuestionContent;
import de.thm.arsnova.model.ContentGroup;
import de.thm.arsnova.model.GridImageContent;
import de.thm.arsnova.model.TextAnswer;
import de.thm.arsnova.model.migration.FromV2Migrator;
import de.thm.arsnova.model.migration.ToV2Migrator;
......@@ -469,7 +470,7 @@ public class ContentController extends PaginationController {
if (content.getFormat().equals(de.thm.arsnova.model.Content.Format.TEXT)) {
return toV2Migrator.migrate((TextAnswer) answer);
} else {
return toV2Migrator.migrate((ChoiceAnswer) answer, (ChoiceQuestionContent) content);
return toV2Migrator.migrate((ChoiceAnswer) answer, content);
}
}
......@@ -496,9 +497,9 @@ public class ContentController extends PaginationController {
@RequestParam(value = "all", required = false, defaultValue = "false") final Boolean allAnswers,
final HttpServletResponse response) {
final de.thm.arsnova.model.Content content = contentService.get(contentId);
if (content instanceof ChoiceQuestionContent) {
if (content instanceof ChoiceQuestionContent || content instanceof GridImageContent) {
return toV2Migrator.migrate(answerService.getAllStatistics(contentId),
(ChoiceQuestionContent) content, content.getState().getRound());
content, content.getState().getRound());
} else {
final List<de.thm.arsnova.model.TextAnswer> answers;
if (allAnswers) {
......@@ -539,7 +540,7 @@ public class ContentController extends PaginationController {
if (answerV3 instanceof TextAnswer) {
return toV2Migrator.migrate((TextAnswer) answerService.create(answerV3));
} else {
return toV2Migrator.migrate((ChoiceAnswer) answerService.create(answerV3), (ChoiceQuestionContent) content);
return toV2Migrator.migrate((ChoiceAnswer) answerService.create(answerV3), content);
}
}
......@@ -557,7 +558,7 @@ public class ContentController extends PaginationController {
if (answerV3 instanceof TextAnswer) {
return toV2Migrator.migrate((TextAnswer) answerService.update(answerV3));
} else {
return toV2Migrator.migrate((ChoiceAnswer) answerService.update(answerV3), (ChoiceQuestionContent) content);
return toV2Migrator.migrate((ChoiceAnswer) answerService.update(answerV3), content);
}
}
......@@ -679,7 +680,7 @@ public class ContentController extends PaginationController {
.map(a -> {
if (a instanceof ChoiceAnswer) {
return toV2Migrator.migrate(
(ChoiceAnswer) a, (ChoiceQuestionContent) contentService.get(a.getContentId()));
(ChoiceAnswer) a, contentService.get(a.getContentId()));
} else {
return toV2Migrator.migrate((TextAnswer) a);
}
......
package de.thm.arsnova.model;
import com.fasterxml.jackson.annotation.JsonView;
import java.util.ArrayList;
import java.util.List;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.Positive;
import de.thm.arsnova.model.serialization.View;
public class GridImageContent extends Content {
public static class Grid {
@Positive
private int columns;
@Positive
private int rows;
@Min(-1)
@Max(1)
private double normalizedX;
@Min(-1)
@Max(1)
private double normalizedY;
@Positive
@Max(1)
private double normalizedFieldSize;
private boolean visible;
@JsonView({View.Persistence.class, View.Public.class})
public int getColumns() {
return columns;
}
@JsonView({View.Persistence.class, View.Public.class})
public void setColumns(final int columns) {
this.columns = columns;
}
@JsonView({View.Persistence.class, View.Public.class})
public int getRows() {
return rows;
}
@JsonView({View.Persistence.class, View.Public.class})
public void setRows(final int rows) {
this.rows = rows;
}
@JsonView({View.Persistence.class, View.Public.class})
public double getNormalizedX() {
return normalizedX;
}
@JsonView({View.Persistence.class, View.Public.class})
public void setNormalizedX(final double normalizedX) {
this.normalizedX = normalizedX;
}
@JsonView({View.Persistence.class, View.Public.class})
public double getNormalizedY() {
return normalizedY;
}
@JsonView({View.Persistence.class, View.Public.class})
public void setNormalizedY(final double normalizedY) {
this.normalizedY = normalizedY;
}
@JsonView({View.Persistence.class, View.Public.class})
public double getNormalizedFieldSize() {
return normalizedFieldSize;
}
@JsonView({View.Persistence.class, View.Public.class})
public void setNormalizedFieldSize(final double normalizedFieldSize) {
this.normalizedFieldSize = normalizedFieldSize;
}
@JsonView({View.Persistence.class, View.Public.class})
public boolean isVisible() {
return visible;
}
@JsonView({View.Persistence.class, View.Public.class})
public void setVisible(final boolean visible) {
this.visible = visible;
}
}
public static class Image {
private String url;
@Min(-1)
@Max(1)
private double normalizedX;
@Min(-1)
@Max(1)
private double normalizedY;
@Positive
private double scaleFactor;
@Min(0)
@Max(360)
private int rotation;
@JsonView({View.Persistence.class, View.Public.class})
public String getUrl() {
return url;
}
@JsonView({View.Persistence.class, View.Public.class})
public void setUrl(final String url) {
this.url = url;
}
@JsonView({View.Persistence.class, View.Public.class})
public double getNormalizedX() {
return normalizedX;
}
@JsonView({View.Persistence.class, View.Public.class})
public void setNormalizedX(final double normalizedX) {
this.normalizedX = normalizedX;
}
@JsonView({View.Persistence.class, View.Public.class})
public double getNormalizedY() {
return normalizedY;
}
@JsonView({View.Persistence.class, View.Public.class})
public void setNormalizedY(final double normalizedY) {
this.normalizedY = normalizedY;
}
@JsonView({View.Persistence.class, View.Public.class})
public double getScaleFactor() {
return scaleFactor;
}
@JsonView({View.Persistence.class, View.Public.class})
public void setScaleFactor(final double scaleFactor) {
this.scaleFactor = scaleFactor;
}
@JsonView({View.Persistence.class, View.Public.class})
public int getRotation() {
return rotation;
}
@JsonView({View.Persistence.class, View.Public.class})
public void setRotation(final int rotation) {
this.rotation = rotation;
}
}
private Grid grid;
private Image image;
private List<Integer> correctOptionIndexes = new ArrayList<>();
@JsonView({View.Persistence.class, View.Public.class})
public Grid getGrid() {
if (grid == null) {
grid = new Grid();
}
return grid;
}
@JsonView({View.Persistence.class, View.Public.class})
public void setGrid(final Grid grid) {
this.grid = grid;
}
@JsonView({View.Persistence.class, View.Public.class})
public Image getImage() {
if (image == null) {
image = new Image();
}
return image;
}
@JsonView({View.Persistence.class, View.Public.class})
public void setImage(final Image image) {
this.image = image;
}
/* TODO: A new JsonView is needed here. */
@JsonView(View.Persistence.class)
public List<Integer> getCorrectOptionIndexes() {
return correctOptionIndexes;
}
@JsonView({View.Persistence.class, View.Public.class})
public void setCorrectOptionIndexes(final List<Integer> correctOptionIndexes) {
this.correctOptionIndexes = correctOptionIndexes;
}
}
......@@ -31,6 +31,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
import de.thm.arsnova.model.ChoiceAnswer;
import de.thm.arsnova.model.ChoiceQuestionContent;
import de.thm.arsnova.model.GridImageContent;
import de.thm.arsnova.model.TextAnswer;
import de.thm.arsnova.model.UserProfile;
import de.thm.arsnova.model.migration.v2.Answer;
......@@ -50,6 +51,7 @@ import de.thm.arsnova.model.migration.v2.RoomFeature;
* @author Daniel Gerhardt
*/
public class FromV2Migrator {
static final String V2 = "v2";
static final String V2_TYPE_ABCD = "abcd";
static final String V2_TYPE_SC = "sc";
static final String V2_TYPE_MC = "mc";
......@@ -60,6 +62,14 @@ public class FromV2Migrator {
static final String V2_TYPE_SLIDE = "slide";
static final String V2_TYPE_FLASHCARD = "flashcard";
static final String V2_TYPE_GRID = "grid";
static final String V2_GRID_DEFAULT_TYPE = "image";
static final String V2_GRID_TYPE = "gridType";
static final String V2_GRID_IMAGE_ABSOLUTE_X = "gridImageAbsoluteX";
static final String V2_GRID_IMAGE_ABSOLUTE_Y = "gridImageAbsoluteY";
static final String V2_GRID_MODERATION_DOT_LIMIT = "gridModerationDotLimit";
static final int V2_GRID_CONTAINER_SIZE = 400;
static final int V2_GRID_FIELD_COUNT = 16;
static final double V2_GRID_SCALE_FACTOR = 1.05;
private static final Map<String, de.thm.arsnova.model.Content.Format> formatMapping;
private boolean ignoreRevision = false;
......@@ -247,6 +257,54 @@ public class FromV2Migrator {
to.setAdditionalTextTitle("Back");
}
break;
case V2_TYPE_GRID:
final GridImageContent gridImageContent = new GridImageContent();
to = gridImageContent;
to.setFormat(de.thm.arsnova.model.Content.Format.GRID);
final GridImageContent.Grid grid = gridImageContent.getGrid();
grid.setColumns(from.getGridSizeX());
grid.setRows(from.getGridSizeY());
grid.setNormalizedX(1.0 * from.getGridOffsetX() / V2_GRID_CONTAINER_SIZE);
grid.setNormalizedY(1.0 * from.getGridOffsetY() / V2_GRID_CONTAINER_SIZE);
/* v3 normalized field size = v2 scale factor ^ v2 zoom level / v2 grid size */
grid.setNormalizedFieldSize(Math.pow(Double.valueOf(from.getGridScaleFactor()), from.getGridZoomLvl())
/ from.getGridSize());
grid.setVisible(!from.getGridIsHidden());
final GridImageContent.Image image = gridImageContent.getImage();
image.setUrl(from.getImage());
image.setRotation(from.getImgRotation() * 90 % 360);
image.setScaleFactor(Math.pow(Double.valueOf(from.getScaleFactor()), from.getZoomLvl()));
gridImageContent.setCorrectOptionIndexes(from.getPossibleAnswers().stream()
.filter(o -> o.isCorrect())
.map(o -> {
try {
final String[] coords = (o.getText() != null ? o.getText() : "").split(";");
return coords.length == 2
? Integer.valueOf(coords[0]) + Integer.valueOf(coords[1]) * from.getGridSizeX()
: -1;
} catch (final NumberFormatException e) {
return -1;
}
})
.filter(i -> i >= 0 && i < grid.getColumns() * grid.getRows())
.collect(Collectors.toList()));
extensions = new HashMap<>();
to.setExtensions(extensions);
v2 = new HashMap<>();
extensions.put(V2, v2);
v2.put(V2_GRID_TYPE, from.getGridType());
/* It is not possible to migrate legacy image offsets to normalized values. */
if (from.getOffsetX() != 0) {
v2.put(V2_GRID_IMAGE_ABSOLUTE_X, from.getOffsetX());
}
if (from.getOffsetY() != 0) {
v2.put(V2_GRID_IMAGE_ABSOLUTE_Y, from.getOffsetY());
}
if (from.getNumberOfDots() != 0) {
v2.put(V2_GRID_MODERATION_DOT_LIMIT, from.getNumberOfDots());
}
break;
default:
throw new IllegalArgumentException("Unsupported content format.");
......@@ -277,9 +335,8 @@ public class FromV2Migrator {
public de.thm.arsnova.model.Answer migrate(final Answer from, final de.thm.arsnova.model.Content content) {
final de.thm.arsnova.model.Answer answer;
if (content instanceof ChoiceQuestionContent) {
final ChoiceQuestionContent choiceQuestionContent = (ChoiceQuestionContent) content;
answer = migrate(from, choiceQuestionContent.getOptions(), choiceQuestionContent.isMultiple());
if (content instanceof ChoiceQuestionContent || content instanceof GridImageContent) {
answer = migrateChoice(from, content);
} else {
answer = migrate(from);
}
......@@ -288,46 +345,6 @@ public class FromV2Migrator {
return answer;
}
public ChoiceAnswer migrate(
final Answer from, final List<ChoiceQuestionContent.AnswerOption> options, final boolean multiple) {
final ChoiceAnswer to = new ChoiceAnswer();
copyCommonProperties(from, to);
to.setContentId(from.getQuestionId());
to.setRoomId(from.getSessionId());
to.setRound(from.getPiRound());
final List<Integer> selectedChoiceIndexes = new ArrayList<>();
to.setSelectedChoiceIndexes(selectedChoiceIndexes);
if (!from.isAbstention()) {
if (multiple) {
final List<Boolean> flags = Arrays.stream(from.getAnswerText().split(","))
.map("1"::equals).collect(Collectors.toList());
if (flags.size() != options.size()) {
throw new IndexOutOfBoundsException(
"Number of answer's choice flags does not match number of content's answer options");
}
int i = 0;
for (final boolean flag : flags) {
if (flag) {
selectedChoiceIndexes.add(i);
}
i++;
}
} else {
int i = 0;
for (final ChoiceQuestionContent.AnswerOption option : options) {
if (option.getLabel().equals(from.getAnswerText())) {
selectedChoiceIndexes.add(i);
break;
}
i++;
}
}
}
return to;
}
public TextAnswer migrate(final Answer from) {
final TextAnswer to = new TextAnswer();
copyCommonProperties(from, to);
......@@ -395,6 +412,74 @@ public class FromV2Migrator {
return to;
}
private ChoiceAnswer migrateChoice(final Answer from, final de.thm.arsnova.model.Content content) {
final ChoiceAnswer to = new ChoiceAnswer();
copyCommonProperties(from, to);
to.setContentId(from.getQuestionId());
to.setRoomId(from.getSessionId());
to.setRound(from.getPiRound());
if (from.isAbstention()) {
return to;
}
if (content instanceof ChoiceQuestionContent) {
final List<Integer> selectedChoiceIndexes = new ArrayList<>();
to.setSelectedChoiceIndexes(selectedChoiceIndexes);
final ChoiceQuestionContent choiceQuestionContent = (ChoiceQuestionContent) content;
if (choiceQuestionContent.isMultiple()) {
final List<Boolean> flags = Arrays.stream(from.getAnswerText().split(","))
.map("1"::equals).collect(Collectors.toList());
if (flags.size() != choiceQuestionContent.getOptions().size()) {
throw new IndexOutOfBoundsException(
"Number of answer's choice flags does not match number of content's answer options");
}
int i = 0;
for (final boolean flag : flags) {
if (flag) {
selectedChoiceIndexes.add(i);
}
i++;
}
} else {
int i = 0;
for (final ChoiceQuestionContent.AnswerOption option : choiceQuestionContent.getOptions()) {
if (option.getLabel().equals(from.getAnswerText())) {
selectedChoiceIndexes.add(i);
break;
}
i++;
}
}
} else if (content instanceof GridImageContent) {
final GridImageContent gridImageContent = (GridImageContent) content;
to.setSelectedChoiceIndexes(migrateChoice(from.getAnswerText(), gridImageContent.getGrid()));
} else {
throw new IllegalArgumentException(
"Content expected to be an instance of ChoiceQuestionContent or GridImageContent");
}
return to;
}
private List<Integer> migrateChoice(final String choice, final GridImageContent.Grid grid) {
return Arrays.stream(choice.split(","))
.map(c -> {
try {
final String[] coords = c.split(";");
return coords.length == 2
? Integer.valueOf(coords[0])
+ Integer.valueOf(coords[1]) * grid.getColumns()
: -1;
} catch (final NumberFormatException e) {
return -1;
}
})
.filter(i -> i >= 0
&& i < grid.getColumns() * grid.getRows())
.collect(Collectors.toList());
}
private void updateProfileFromLoginId(final UserProfile profile, final String loginId) {
if (loginId.length() == 15 && loginId.startsWith("Guest")) {
profile.setAuthProvider(UserProfile.AuthProvider.ARSNOVA_GUEST);
......
......@@ -18,6 +18,15 @@
package de.thm.arsnova.model.migration;
import static de.thm.arsnova.model.migration.FromV2Migrator.V2;
import static de.thm.arsnova.model.migration.FromV2Migrator.V2_GRID_CONTAINER_SIZE;
import static de.thm.arsnova.model.migration.FromV2Migrator.V2_GRID_DEFAULT_TYPE;
import static de.thm.arsnova.model.migration.FromV2Migrator.V2_GRID_FIELD_COUNT;
import static de.thm.arsnova.model.migration.FromV2Migrator.V2_GRID_IMAGE_ABSOLUTE_X;
import static de.thm.arsnova.model.migration.FromV2Migrator.V2_GRID_IMAGE_ABSOLUTE_Y;
import static de.thm.arsnova.model.migration.FromV2Migrator.V2_GRID_MODERATION_DOT_LIMIT;
import static de.thm.arsnova.model.migration.FromV2Migrator.V2_GRID_SCALE_FACTOR;
import static de.thm.arsnova.model.migration.FromV2Migrator.V2_GRID_TYPE;
import static de.thm.arsnova.model.migration.FromV2Migrator.V2_TYPE_ABCD;
import static de.thm.arsnova.model.migration.FromV2Migrator.V2_TYPE_FLASHCARD;
import static de.thm.arsnova.model.migration.FromV2Migrator.V2_TYPE_FREETEXT;
......@@ -38,6 +47,7 @@ import java.util.stream.Collectors;
import de.thm.arsnova.model.AnswerStatistics;
import de.thm.arsnova.model.ChoiceQuestionContent;
import de.thm.arsnova.model.GridImageContent;
import de.thm.arsnova.model.RoomStatistics;
import de.thm.arsnova.model.UserProfile;
import de.thm.arsnova.model.migration.v2.Answer;
......@@ -233,6 +243,51 @@ public class ToV2Migrator {
to.setQuestionType(V2_TYPE_FREETEXT);
break;
}
break;
case GRID:
final GridImageContent fromGridImageContent = (GridImageContent) from;
final GridImageContent.Grid grid = fromGridImageContent.getGrid();
final GridImageContent.Image image = fromGridImageContent.getImage();
to.setQuestionType(V2_TYPE_GRID);
to.setGridSizeX(grid.getColumns());
to.setGridSizeY(grid.getRows());
to.setGridOffsetX((int) (Math.round(grid.getNormalizedX() * V2_GRID_CONTAINER_SIZE)));
to.setGridOffsetY((int) (Math.round(grid.getNormalizedY() * V2_GRID_CONTAINER_SIZE)));
/* v3 normalized field size = v2 scale factor ^ v2 zoom level / v2 grid size */
to.setGridSize(V2_GRID_FIELD_COUNT);
to.setGridScaleFactor(String.valueOf(V2_GRID_SCALE_FACTOR));
to.setGridZoomLvl((int) Math.round(
Math.log(grid.getNormalizedFieldSize() * V2_GRID_FIELD_COUNT)
/ Math.log(V2_GRID_SCALE_FACTOR)));
to.setGridIsHidden(!grid.isVisible());
to.setImage(image.getUrl());
to.setImgRotation(image.getRotation() / 90 % 4);
to.setScaleFactor(String.valueOf(V2_GRID_SCALE_FACTOR));
to.setZoomLvl((int) Math.round(
Math.log(image.getScaleFactor()) / Math.log(V2_GRID_SCALE_FACTOR)));
to.setPossibleAnswers(
fromGridImageContent.getCorrectOptionIndexes().stream()
.map(i -> {
final int x = i % fromGridImageContent.getGrid().getColumns();
final int y = i / fromGridImageContent.getGrid().getColumns();
final AnswerOption answerOption = new AnswerOption();
answerOption.setText(x + ";" + y);
answerOption.setCorrect(true);
return answerOption;
})
.collect(Collectors.toList()));
if (fromGridImageContent.getExtensions() != null) {
final Map<String, Object> v2 = fromGridImageContent.getExtensions()
.getOrDefault(V2, Collections.emptyMap());
to.setGridType((String) v2.getOrDefault(V2_GRID_TYPE, V2_GRID_DEFAULT_TYPE));
to.setOffsetX((int) v2.getOrDefault(V2_GRID_IMAGE_ABSOLUTE_X, 0));
to.setOffsetY((int) v2.getOrDefault(V2_GRID_IMAGE_ABSOLUTE_Y, 0));
to.setNumberOfDots((int) v2.getOrDefault(V2_GRID_MODERATION_DOT_LIMIT, 0));
} else {
to.setGridType(V2_GRID_DEFAULT_TYPE);
}
break;
default:
throw new IllegalArgumentException("Unsupported content format.");
......@@ -252,7 +307,7 @@ public class ToV2Migrator {
}
public Answer migrate(final de.thm.arsnova.model.ChoiceAnswer from,
final de.thm.arsnova.model.ChoiceQuestionContent content, final Optional<UserProfile> creator) {
final de.thm.arsnova.model.Content content, final Optional<UserProfile> creator) {
final Answer to = new Answer();
copyCommonProperties(from, to);
to.setQuestionId(from.getContentId());
......@@ -264,11 +319,21 @@ public class ToV2Migrator {
if (from.getSelectedChoiceIndexes().isEmpty()) {
to.setAbstention(true);
} else {
if (content.isMultiple()) {
to.setAnswerText(migrateChoice(from.getSelectedChoiceIndexes(), content.getOptions()));
if (content instanceof ChoiceQuestionContent) {
final ChoiceQuestionContent choiceQuestionContent = (ChoiceQuestionContent) content;
if (choiceQuestionContent.isMultiple()) {
to.setAnswerText(migrateChoice(from.getSelectedChoiceIndexes(),
choiceQuestionContent.getOptions()));
} else {
final int index = from.getSelectedChoiceIndexes().get(0);
to.setAnswerText(choiceQuestionContent.getOptions().get(index).getLabel());
}
} else if (content instanceof GridImageContent) {
final GridImageContent gridImageContent = (GridImageContent) content;
to.setAnswerText(migrateChoice(from.getSelectedChoiceIndexes(), gridImageContent.getGrid()));
} else {
final int index = from.getSelectedChoiceIndexes().get(0);
to.setAnswerText(content.getOptions().get(index).getLabel());
throw new IllegalArgumentException(
"Content expected to be an instance of ChoiceQuestionContent or GridImageContent");
}
}
......@@ -276,7 +341,7 @@ public class ToV2Migrator {
}
public Answer migrate(final de.thm.arsnova.model.ChoiceAnswer from,
final de.thm.arsnova.model.ChoiceQuestionContent content) {
final de.thm.arsnova.model.Content content) {
return migrate(from, content, Optional.empty());
}
......@@ -351,7 +416,7 @@ public class ToV2Migrator {
}
public List<Answer> migrate(final AnswerStatistics from,
final de.thm.arsnova.model.ChoiceQuestionContent content, final int round) {
final de.thm.arsnova.model.Content content, final int round) {
if (round < 1 || round > content.getState().getRound()) {
throw new IllegalArgumentException("Invalid value for round");
}
......@@ -368,22 +433,34 @@ public class ToV2Migrator {
}
final Map<String, Integer> choices;
if (content.isMultiple()) {
/* Map selected choice indexes -> answer count */
if (content instanceof ChoiceQuestionContent) {
final ChoiceQuestionContent choiceQuestionContent = (ChoiceQuestionContent) content;
if (choiceQuestionContent.isMultiple()) {
/* Map selected choice indexes -> answer count */
choices = stats.getCombinatedCounts().stream().collect(Collectors.toMap(
c -> migrateChoice(c.getSelectedChoiceIndexes(), choiceQuestionContent.getOptions()),
c -> c.getCount(),
(u, v) -> {
throw new IllegalStateException(String.format("Duplicate key %s", u));
},
LinkedHashMap::new));
} else {
choices = new LinkedHashMap<>();
int i = 0;
for (final ChoiceQuestionContent.AnswerOption option : choiceQuestionContent.getOptions()) {
choices.put(option.getLabel(), stats.getIndependentCounts().get(i));
i++;
}
}
} else {
final GridImageContent gridImageContent = (GridImageContent) content;
choices = stats.getCombinatedCounts().stream().collect(Collectors.toMap(
c -> migrateChoice(c.getSelectedChoiceIndexes(), content.getOptions()),
c -> migrateChoice(c.getSelectedChoiceIndexes(), gridImageContent.getGrid()),
c -> c.getCount(),
(u, v) -> {
throw new IllegalStateException(String.format("Duplicate key %s", u));
},
LinkedHashMap::new));
} else {
choices = new LinkedHashMap<>();
int i = 0;
for (final ChoiceQuestionContent.AnswerOption option : content.getOptions()) {
choices.put(option.getLabel(), stats.getIndependentCounts().get(i));
i++;
}
}
for (final Map.Entry<String, Integer> choice : choices.entrySet()) {
......@@ -432,7 +509,7 @@ public class ToV2Migrator {
return to;
}
public String migrateChoice(final List<Integer> selectedChoiceIndexes,
private String migrateChoice(final List<Integer> selectedChoiceIndexes,
final List<ChoiceQuestionContent.AnswerOption> options) {
final List<String> answers = new ArrayList<>();
for (int i = 0; i < options.size(); i++) {
......@@ -441,4 +518,14 @@ public class ToV2Migrator {
return answers.stream().collect(Collectors.joining(","));
}
private String migrateChoice(final List<Integer> selectedChoiceIndexes, final GridImageContent.Grid grid) {
return selectedChoiceIndexes.stream()
.map(i -> {
final int x = i % grid.getColumns();
final int y = i / grid.getColumns();
return x + ";" + y;
})
.collect(Collectors.joining(","));
}
}
......@@ -59,6 +59,8 @@ public class FormatAnswerTypeIdResolver extends TypeIdResolverBase {