/* * This file is part of ARSnova Backend. * Copyright (C) 2012-2017 The ARSnova Team * * ARSnova Backend is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * ARSnova Backend is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package de.thm.arsnova.entities; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; import java.util.Date; import java.util.List; /** * A question the teacher is asking. */ @ApiModel(value = "lecturerquestion", description = "the question entity") public class Question implements Serializable { private String type; private String questionType; private String questionVariant; private String subject; private String text; private boolean active; private String releasedFor; private List<PossibleAnswer> possibleAnswers; private boolean noCorrect; // TODO: We currently need both sessionId and sessionKeyword, but sessionKeyword will not be persisted. private String sessionId; // This property is needed because the client does not have the session ID, only the keyword. private String sessionKeyword; private long timestamp; private int number; private int duration; private int piRound; private long piRoundEndTime = 0; private long piRoundStartTime = 0; private boolean piRoundFinished = false; private boolean piRoundActive = false; private boolean votingDisabled; private boolean showStatistic; // sic private boolean showAnswer; private boolean abstention; private boolean ignoreCaseSensitive; private boolean ignoreWhitespaces; private boolean ignorePunctuation; private boolean fixedAnswer; private boolean strictMode; private int rating; private String correctAnswer; private String _id; private String _rev; private String image; private String fcImage; private int gridSize; private int offsetX; private int offsetY; private int zoomLvl; private int gridOffsetX; private int gridOffsetY; private int gridZoomLvl; private int gridSizeX; private int gridSizeY; private boolean gridIsHidden; private int imgRotation; private boolean toggleFieldsLeft; private int numClickableFields; private int thresholdCorrectAnswers; private boolean cvIsColored; private String gridLineColor; private int numberOfDots; private String gridType; private String scaleFactor; private String gridScaleFactor; private boolean imageQuestion; private boolean textAnswerEnabled; private String hint; private String solution; @ApiModelProperty(required = true, value = "the type") public final String getType() { return type; } public final void setType(final String type) { this.type = type; } @ApiModelProperty(required = true, value = "the question type") public final String getQuestionType() { return questionType; } public final void setQuestionType(final String questionType) { this.questionType = questionType; } @ApiModelProperty(required = true, value = "either lecture or preparation") public final String getQuestionVariant() { return questionVariant; } public final void setQuestionVariant(final String questionVariant) { this.questionVariant = questionVariant; } @ApiModelProperty(required = true, value = "used to display subject") public final String getSubject() { return subject; } public final void setSubject(final String subject) { this.subject = subject; } @ApiModelProperty(required = true, value = "the text") public final String getText() { return text; } public final void setText(final String text) { this.text = text; } @ApiModelProperty(required = true, value = "true for active question") public final boolean isActive() { return active; } public final void setActive(final boolean active) { this.active = active; } @ApiModelProperty(required = true, value = "deprecated - previously used to limitate the audience") public final String getReleasedFor() { return releasedFor; } public final void setReleasedFor(final String releasedFor) { this.releasedFor = releasedFor; } @ApiModelProperty(required = true, value = "list of possible answers") public final List<PossibleAnswer> getPossibleAnswers() { return possibleAnswers; } public final void setPossibleAnswers(final List<PossibleAnswer> possibleAnswers) { this.possibleAnswers = possibleAnswers; } @ApiModelProperty(required = true, value = "if true, no answer is marked correct") public final boolean isNoCorrect() { return noCorrect; } public final void setNoCorrect(final boolean noCorrect) { this.noCorrect = noCorrect; } @ApiModelProperty(required = true, value = "couchDB ID of the session, the question is assigned to") public final String getSessionId() { return sessionId; } public final void setSessionId(final String sessionId) { this.sessionId = sessionId; } @ApiModelProperty(required = true, value = "couchDB ID of the session, the question is assigned to") public final String getSession() { return sessionId; } public final void setSession(final String session) { sessionId = session; } @ApiModelProperty(required = true, value = "the session keyword, the question is assigned to") public final String getSessionKeyword() { return sessionKeyword; } public final void setSessionKeyword(final String keyword) { sessionKeyword = keyword; } @ApiModelProperty(required = true, value = "creation date timestamp") public final long getTimestamp() { return timestamp; } public final void setTimestamp(final long timestamp) { this.timestamp = timestamp; } @ApiModelProperty(required = true, value = "used to display number") public final int getNumber() { return number; } public final void setNumber(final int number) { this.number = number; } @ApiModelProperty(required = true, value = "used to display duration") public final int getDuration() { return duration; } @ApiModelProperty(required = true, value = "true for image question") public final boolean isImageQuestion() { return imageQuestion; } public void setImageQuestion(boolean imageQuestion) { this.imageQuestion = imageQuestion; } public final void setDuration(final int duration) { this.duration = duration; } @ApiModelProperty(required = true, value = "the peer instruction round no.") public int getPiRound() { return piRound; } public void setPiRound(final int piRound) { this.piRound = piRound; } @ApiModelProperty(required = true, value = "the peer instruction round end timestamp") public long getPiRoundEndTime() { return piRoundEndTime; } public void setPiRoundEndTime(long piRoundEndTime) { this.piRoundEndTime = piRoundEndTime; } @ApiModelProperty(required = true, value = "the peer instruction round start timestamp") public long getPiRoundStartTime() { return piRoundStartTime; } public void setPiRoundStartTime(long piRoundStartTime) { this.piRoundStartTime = piRoundStartTime; } @ApiModelProperty(required = true, value = "true for active peer instruction round") public boolean isPiRoundActive() { return piRoundActive; } public void setPiRoundActive(boolean piRoundActive) { this.piRoundActive = piRoundActive; } @ApiModelProperty(required = true, value = "true for finished peer instruction round") public boolean isPiRoundFinished() { return piRoundFinished; } public void setPiRoundFinished(boolean piRoundFinished) { this.piRoundFinished = piRoundFinished; } @ApiModelProperty(required = true, value = "used to display showStatistic") public boolean isShowStatistic() { return showStatistic; } public void setShowStatistic(final boolean showStatistic) { this.showStatistic = showStatistic; } @ApiModelProperty(required = true, value = "used to display cvIsColored") public boolean getCvIsColored() { return cvIsColored; } public void setCvIsColored(boolean cvIsColored) { this.cvIsColored = cvIsColored; } @ApiModelProperty(required = true, value = "used to display showAnswer") public boolean isShowAnswer() { return showAnswer; } public void setShowAnswer(final boolean showAnswer) { this.showAnswer = showAnswer; } @ApiModelProperty(required = true, value = "used to display abstention") public boolean isAbstention() { return abstention; } public void setAbstention(final boolean abstention) { this.abstention = abstention; } public boolean isIgnoreCaseSensitive() { return ignoreCaseSensitive; } public void setIgnoreCaseSensitive(final boolean ignoreCaseSensitive) { this.ignoreCaseSensitive = ignoreCaseSensitive; } public boolean isIgnoreWhitespaces() { return ignoreWhitespaces; } public void setIgnoreWhitespaces(final boolean ignoreWhitespaces) { this.ignoreWhitespaces = ignoreWhitespaces; } public boolean isIgnorePunctuation() { return ignorePunctuation; } public void setIgnorePunctuation(final boolean ignorePunctuation) { this.ignorePunctuation = ignorePunctuation; } public boolean isFixedAnswer() { return this.fixedAnswer; } public void setFixedAnswer(final boolean fixedAnswer) { this.fixedAnswer = fixedAnswer; } public boolean isStrictMode() { return this.strictMode; } public void setStrictMode(final boolean strictMode) { this.strictMode = strictMode; } public final int getRating() { return this.rating; } public final void setRating(final int rating) { this.rating = rating; } public final String getCorrectAnswer() { return correctAnswer; } public final void setCorrectAnswer(final String correctAnswer) { this.correctAnswer = correctAnswer; } @ApiModelProperty(required = true, value = "the couchDB ID") public final String get_id() { return _id; } public final void set_id(final String _id) { this._id = _id; } public final String get_rev() { return _rev; } public final void set_rev(final String _rev) { this._rev = _rev; } @ApiModelProperty(required = true, value = "the image") public String getImage() { return image; } public void setImage(final String image) { this.image = image; } @ApiModelProperty(required = true, value = "the fcImage") public String getFcImage() { return fcImage; } public void setFcImage(final String fcImage) { this.fcImage = fcImage; } @ApiModelProperty(required = true, value = "the grid size") public int getGridSize() { return gridSize; } public void setGridSize(final int gridSize) { this.gridSize = gridSize; } @ApiModelProperty(required = true, value = "the image X offset") public int getOffsetX() { return offsetX; } public void setOffsetX(final int offsetX) { this.offsetX = offsetX; } @ApiModelProperty(required = true, value = "the image Y offset") public int getOffsetY() { return offsetY; } public void setOffsetY(final int offsetY) { this.offsetY = offsetY; } @ApiModelProperty(required = true, value = "the image zoom level") public int getZoomLvl() { return zoomLvl; } public void setZoomLvl(final int zoomLvl) { this.zoomLvl = zoomLvl; } @ApiModelProperty(required = true, value = "the grid X offset") public int getGridOffsetX() { return gridOffsetX; } public void setGridOffsetX(int gridOffsetX) { this.gridOffsetX = gridOffsetX; } @ApiModelProperty(required = true, value = "the grid Y offset") public int getGridOffsetY() { return gridOffsetY; } public void setGridOffsetY(int gridOffsetY) { this.gridOffsetY = gridOffsetY; } @ApiModelProperty(required = true, value = "the grid zoom lvl") public int getGridZoomLvl() { return gridZoomLvl; } public void setGridZoomLvl(int gridZoomLvl) { this.gridZoomLvl = gridZoomLvl; } @ApiModelProperty(required = true, value = "the grid X size") public int getGridSizeX() { return gridSizeX; } public void setGridSizeX(int gridSizeX) { this.gridSizeX = gridSizeX; } @ApiModelProperty(required = true, value = "the grid Y size") public int getGridSizeY() { return gridSizeY; } public void setGridSizeY(int gridSizeY) { this.gridSizeY = gridSizeY; } @ApiModelProperty(required = true, value = "true for hidden grid") public boolean getGridIsHidden() { return gridIsHidden; } public void setGridIsHidden(boolean gridIsHidden) { this.gridIsHidden = gridIsHidden; } @ApiModelProperty(required = true, value = "the image rotation") public int getImgRotation() { return imgRotation; } public void setImgRotation(int imgRotation) { this.imgRotation = imgRotation; } @ApiModelProperty(required = true, value = "the toggled left fields") public boolean getToggleFieldsLeft() { return toggleFieldsLeft; } public void setToggleFieldsLeft(boolean toggleFieldsLeft) { this.toggleFieldsLeft = toggleFieldsLeft; } @ApiModelProperty(required = true, value = "the number of clickable fields") public int getNumClickableFields() { return numClickableFields; } public void setNumClickableFields(int numClickableFields) { this.numClickableFields = numClickableFields; } @ApiModelProperty(required = true, value = "the threshold of correct answers") public int getThresholdCorrectAnswers() { return thresholdCorrectAnswers; } public void setThresholdCorrectAnswers(int thresholdCorrectAnswers) { this.thresholdCorrectAnswers = thresholdCorrectAnswers; } @ApiModelProperty(required = true, value = "the grid line color") public String getGridLineColor() { return gridLineColor; } public void setGridLineColor(String gridLineColor) { this.gridLineColor = gridLineColor; } @ApiModelProperty(required = true, value = "the number of dots") public int getNumberOfDots() { return numberOfDots; } public void setNumberOfDots(int numberOfDots) { this.numberOfDots = numberOfDots; } @ApiModelProperty(required = true, value = "the grid type") public String getGridType() { return gridType; } public void setGridType(String gridType) { this.gridType = gridType; } public void setScaleFactor(String scaleFactor) { this.scaleFactor = scaleFactor; } @ApiModelProperty(required = true, value = "the image scale factor") public String getScaleFactor() { return this.scaleFactor; } public void setGridScaleFactor(String scaleFactor) { this.gridScaleFactor = scaleFactor; } @ApiModelProperty(required = true, value = "the grid scale factor") public String getGridScaleFactor() { return this.gridScaleFactor; } @ApiModelProperty(required = true, value = "true for a question that can be answered via text") public boolean isTextAnswerEnabled() { return this.textAnswerEnabled; } public void setTextAnswerEnabled(boolean textAnswerEnabled) { this.textAnswerEnabled = textAnswerEnabled; } @ApiModelProperty(required = true, value = "true for disabled voting") public boolean isVotingDisabled() { return votingDisabled; } public void setVotingDisabled(boolean votingDisabled) { this.votingDisabled = votingDisabled; } public String getHint() { return hint; } public void setHint(String hint) { this.hint = hint; } public String getSolution() { return solution; } public void setSolution(String solution) { this.solution = solution; } @Override public final String toString() { return "Question type '" + type + "': " + subject + ";\n" + text + possibleAnswers; } @Override public int hashCode() { // auto generated! final int prime = 31; int result = 1; result = prime * result + ((_id == null) ? 0 : _id.hashCode()); return result; } @Override public boolean equals(Object obj) { // auto generated! if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } Question other = (Question) obj; if (_id == null) { if (other._id != null) { return false; } } else if (!_id.equals(other._id)) { return false; } return true; } public int calculateValue(Answer answer) { if (answer.isAbstention()) { return 0; } else if ("mc".equals(this.questionType)) { return calculateMultipleChoiceValue(answer); } else if ("grid".equals(this.questionType)) { return calculateGridValue(answer); } else { return calculateRegularValue(answer); } } public String checkCaseSensitive(String answerText) { if (this.isIgnoreCaseSensitive()) { this.setCorrectAnswer(this.getCorrectAnswer().toLowerCase()); return answerText.toLowerCase(); } return answerText; } public String checkWhitespaces(String answerText) { if (this.isIgnoreWhitespaces()) { this.setCorrectAnswer(this.getCorrectAnswer().replaceAll("[\\s]", "")); return answerText.replaceAll("[\\s]", ""); } return answerText; } public String checkPunctuation(String answerText) { if (this.isIgnorePunctuation()) { this.setCorrectAnswer(this.getCorrectAnswer().replaceAll("\\p{Punct}", "")); return answerText.replaceAll("\\p{Punct}", ""); } return answerText; } public void checkTextStrictOptions(Answer answer) { answer.setAnswerTextRaw(this.checkCaseSensitive(answer.getAnswerTextRaw())); answer.setAnswerTextRaw(this.checkPunctuation(answer.getAnswerTextRaw())); answer.setAnswerTextRaw(this.checkWhitespaces(answer.getAnswerTextRaw())); } public int evaluateCorrectAnswerFixedText(String answerTextRaw) { if (answerTextRaw != null) { if (answerTextRaw.equals(this.getCorrectAnswer())) { return this.getRating(); } } return 0; } public boolean isSuccessfulFreeTextAnswer(String answerTextRaw) { return answerTextRaw != null && answerTextRaw.equals(this.getCorrectAnswer()); } public void updateRoundStartVariables(Date start, Date end) { if (this.getPiRound() == 1 && this.isPiRoundFinished()) { this.setPiRound(2); } this.setActive(true); this.setShowAnswer(false); this.setPiRoundActive(true); this.setShowStatistic(false); this.setVotingDisabled(false); this.setPiRoundFinished(false); this.setPiRoundStartTime(start.getTime()); this.setPiRoundEndTime(end.getTime()); } public void updateRoundManagementState() { final long time = new Date().getTime(); if (time > this.getPiRoundEndTime() && this.isPiRoundActive()) { this.setPiRoundEndTime(0); this.setPiRoundStartTime(0); this.setPiRoundActive(false); this.setPiRoundFinished(true); } } public void resetRoundManagementState() { this.setPiRoundEndTime(0); this.setPiRoundStartTime(0); this.setVotingDisabled(true); this.setPiRoundActive(false); this.setPiRoundFinished(false); this.setShowStatistic(false); this.setShowAnswer(false); } public void resetQuestionState() { this.setPiRoundEndTime(0); this.setPiRoundStartTime(0); this.setPiRoundActive(false); this.setPiRoundFinished(false); this.setVotingDisabled(false); if ("freetext".equals(this.getQuestionType())) { this.setPiRound(0); } else { this.setPiRound(1); } } private int calculateRegularValue(Answer answer) { String answerText = answer.getAnswerText(); for (PossibleAnswer p : this.possibleAnswers) { if (answerText.equals(p.getText())) { return p.getValue(); } } return 0; } private int calculateGridValue(Answer answer) { int value = 0; String[] answers = answer.getAnswerText().split(","); for (String a : answers) { for (PossibleAnswer p : this.possibleAnswers) { if (a.equals(p.getText())) { value += p.getValue(); } } } return value; } private int calculateMultipleChoiceValue(Answer answer) { int value = 0; String[] answers = answer.getAnswerText().split(","); for (int i = 0; i < this.possibleAnswers.size() && i < answers.length; i++) { if ("1".equals(answers[i])) { PossibleAnswer p = this.possibleAnswers.get(i); value += p.getValue(); } } return value; } }