Commit c3391ee9 authored by Daniel Gerhardt's avatar Daniel Gerhardt

Add migrations for answers of grid image contents

Answers for `GridImageContents` are stored as `ChoiceAnswer`s.
parent a125d026
......@@ -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);
}
......
......@@ -335,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);
}
......@@ -346,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);
......@@ -453,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);
......
......@@ -307,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());
......@@ -319,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");
}
}
......@@ -331,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());
}
......@@ -406,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");
}
......@@ -423,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()) {
......@@ -487,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++) {
......@@ -496,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 {
return TypeFactory.defaultInstance().constructType(ChoiceAnswer.class);
case TEXT:
return TypeFactory.defaultInstance().constructType(TextAnswer.class);
case GRID:
return TypeFactory.defaultInstance().constructType(ChoiceAnswer.class);
default:
throw new IllegalArgumentException("Unsupported type ID.");
}
......
......@@ -45,6 +45,7 @@ import de.thm.arsnova.model.Answer;
import de.thm.arsnova.model.AnswerStatistics;
import de.thm.arsnova.model.ChoiceQuestionContent;
import de.thm.arsnova.model.Content;
import de.thm.arsnova.model.GridImageContent;
import de.thm.arsnova.model.Room;
import de.thm.arsnova.model.TextAnswer;
import de.thm.arsnova.persistence.AnswerRepository;
......@@ -158,15 +159,26 @@ public class AnswerServiceImpl extends DefaultEntityServiceImpl<Answer> implemen
@Override
@PreAuthorize("isAuthenticated()")
public AnswerStatistics getStatistics(final String contentId, final int round) {
final ChoiceQuestionContent content = (ChoiceQuestionContent) contentService.get(contentId);
final Content content = contentService.get(contentId);
if (content == null) {
throw new NotFoundException();
}
final int optionCount;
if (content instanceof ChoiceQuestionContent) {
optionCount = ((ChoiceQuestionContent) content).getOptions().size();
} else if (content instanceof GridImageContent) {
final GridImageContent.Grid grid = ((GridImageContent) content).getGrid();
optionCount = grid.getColumns() * grid.getRows();
} else {
throw new IllegalStateException(
"Content expected to be an instance of ChoiceQuestionContent or GridImageContent");
}
final AnswerStatistics stats = answerRepository.findByContentIdRound(
content.getId(), round, content.getOptions().size());
content.getId(), round, optionCount);
/* Fill list with zeros to prevent IndexOutOfBoundsExceptions */
final List<Integer> independentCounts = stats.getRoundStatistics().get(round - 1).getIndependentCounts();
while (independentCounts.size() < content.getOptions().size()) {
while (independentCounts.size() < optionCount) {
independentCounts.add(0);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment