summaryrefslogtreecommitdiff
path: root/src/com/p4square/grow/model
diff options
context:
space:
mode:
authorJesse Morgan <jesse@jesterpm.net>2013-10-20 23:14:51 -0700
committerJesse Morgan <jesse@jesterpm.net>2013-10-20 23:14:51 -0700
commite77c9b418fdfb935ff8e99f10f607a4bbd7e1c8c (patch)
tree9005f8c0e3b18f853fb8093b01f6aad64949bc13 /src/com/p4square/grow/model
parent5037f4797461649994068d97a8433b6cd793c523 (diff)
First stage of a major refactoring.
Question and Answer can now be serialized and deserialized to/from JSON. As such, I no longer have to pass awkward maps around. As part of this change I have introduced a Provider interface to abstract out loading and persisting these beans. The scoring logic has been completed factored out of SurveyResultsResource and into the various ScoringEngines. Tests have been added for Question, Answer, and the ScoringEngines. A bug has been fixed in computing the value for slider questions. The label identifiers in the circle questions have changed from all lower case to camel case. That is, topleft is now topLeft. Several issues have been corrected in the circle answers where the point values did not match the labels. Testing and code coverage support and reports have been added.
Diffstat (limited to 'src/com/p4square/grow/model')
-rw-r--r--src/com/p4square/grow/model/Answer.java135
-rw-r--r--src/com/p4square/grow/model/CircleQuestion.java89
-rw-r--r--src/com/p4square/grow/model/ImageQuestion.java24
-rw-r--r--src/com/p4square/grow/model/Point.java79
-rw-r--r--src/com/p4square/grow/model/QuadQuestion.java89
-rw-r--r--src/com/p4square/grow/model/QuadScoringEngine.java49
-rw-r--r--src/com/p4square/grow/model/Question.java134
-rw-r--r--src/com/p4square/grow/model/RecordedAnswer.java34
-rw-r--r--src/com/p4square/grow/model/Score.java70
-rw-r--r--src/com/p4square/grow/model/ScoringEngine.java26
-rw-r--r--src/com/p4square/grow/model/SimpleScoringEngine.java26
-rw-r--r--src/com/p4square/grow/model/SliderQuestion.java24
-rw-r--r--src/com/p4square/grow/model/SliderScoringEngine.java29
-rw-r--r--src/com/p4square/grow/model/TextQuestion.java24
14 files changed, 769 insertions, 63 deletions
diff --git a/src/com/p4square/grow/model/Answer.java b/src/com/p4square/grow/model/Answer.java
index 4c84060..57a1e5d 100644
--- a/src/com/p4square/grow/model/Answer.java
+++ b/src/com/p4square/grow/model/Answer.java
@@ -4,7 +4,7 @@
package com.p4square.grow.model;
-import java.util.Map;
+import org.apache.log4j.Logger;
/**
* This is the model of an assessment question's answer.
@@ -12,52 +12,131 @@ import java.util.Map;
* @author Jesse Morgan <jesse@jesterpm.net>
*/
public class Answer {
+ private static final Logger LOG = Logger.getLogger(Answer.class);
+
+ /**
+ * ScoreType determines how the answer will be scored.
+ *
+ */
public static enum ScoreType {
- NONE, AVERAGE, TRUMP;
- }
-
- private final String mAnswerId;
- private final String mAnswerText;
- private final ScoreType mType;
- private final float mScoreFactor;
- private final String mNextQuestionId;
-
- public Answer(final String id, final Map<String, Object> answer) {
- mAnswerId = id;
- mAnswerText = (String) answer.get("text");
- final String typeStr = (String) answer.get("type");
- if (typeStr == null) {
- mType = ScoreType.AVERAGE;
- } else {
- mType = ScoreType.valueOf(typeStr.toUpperCase());
- }
+ /**
+ * This question has no effect on the score.
+ */
+ NONE,
- if (mType != ScoreType.NONE) {
- mScoreFactor = Float.valueOf((String) answer.get("score"));
- } else {
- mScoreFactor = 0;
- }
+ /**
+ * The score of this question is part of the average.
+ */
+ AVERAGE,
- mNextQuestionId = (String) answer.get("nextQuestion");
+ /**
+ * The score of this question is the total score, no other questions
+ * matter after this point.
+ */
+ TRUMP;
+
+ @Override
+ public String toString() {
+ return name().toLowerCase();
+ }
}
- public String getId() {
- return mAnswerId;
+ private String mAnswerText;
+ private ScoreType mType;
+ private float mScoreFactor;
+ private String mNextQuestionId;
+
+ public Answer() {
+ mType = ScoreType.AVERAGE;
}
+ /**
+ * @return The text associated with the answer.
+ */
public String getText() {
return mAnswerText;
}
+ /**
+ * Set the text associated with the answer.
+ * @param text The new text.
+ */
+ public void setText(String text) {
+ mAnswerText = text;
+ }
+
+ /**
+ * @return the ScoreType for the Answer.
+ */
public ScoreType getType() {
return mType;
}
- public float getScoreFactor() {
+ /**
+ * Set the ScoreType for the answer.
+ * @param type The new ScoreType.
+ */
+ public void setType(ScoreType type) {
+ mType = type;
+ }
+
+ /**
+ * @return the delta of the score if this answer is selected.
+ */
+ public float getScore() {
+ if (mType == ScoreType.NONE) {
+ return 0;
+ }
+
return mScoreFactor;
}
+ /**
+ * Set the score delta for this answer.
+ * @param score The new delta.
+ */
+ public void setScore(float score) {
+ mScoreFactor = score;
+ }
+
+ /**
+ * @return the id of the next question if this answer is selected, or null
+ * if selecting this answer has no effect.
+ */
public String getNextQuestion() {
return mNextQuestionId;
}
+
+ /**
+ * Set the id of the next question when this answer is selected.
+ * @param id The next question id or null to proceed as usual.
+ */
+ public void setNextQuestion(String id) {
+ mNextQuestionId = id;
+ }
+
+ /**
+ * Adjust the running score for the selection of this answer.
+ * @param score The running score to adjust.
+ * @return true if scoring should continue, false if this answer trumps all.
+ */
+ public boolean score(final Score score) {
+ switch (getType()) {
+ case TRUMP:
+ score.sum = getScore();
+ score.count = 1;
+ return false; // Quit scoring.
+
+ case AVERAGE:
+ LOG.error("ScoreType.AVERAGE: { delta: \"" + getScore() + "\" }");
+ score.sum += getScore();
+ score.count++;
+ break;
+
+ case NONE:
+ break;
+ }
+
+ return true; // Continue scoring
+ }
}
diff --git a/src/com/p4square/grow/model/CircleQuestion.java b/src/com/p4square/grow/model/CircleQuestion.java
new file mode 100644
index 0000000..71acc14
--- /dev/null
+++ b/src/com/p4square/grow/model/CircleQuestion.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2013 Jesse Morgan
+ */
+
+package com.p4square.grow.model;
+
+/**
+ * Circle Question.
+ *
+ * @author Jesse Morgan <jesse@jesterpm.net>
+ */
+public class CircleQuestion extends Question {
+ private static final ScoringEngine ENGINE = new QuadScoringEngine();
+
+ private String mTopLeft;
+ private String mTopRight;
+ private String mBottomLeft;
+ private String mBottomRight;
+
+ /**
+ * @return the Top Left label.
+ */
+ public String getTopLeft() {
+ return mTopLeft;
+ }
+
+ /**
+ * Set the Top Left label.
+ * @param s The new top left label.
+ */
+ public void setTopLeft(String s) {
+ mTopLeft = s;
+ }
+
+ /**
+ * @return the Top Right label.
+ */
+ public String getTopRight() {
+ return mTopRight;
+ }
+
+ /**
+ * Set the Top Right label.
+ * @param s The new top left label.
+ */
+ public void setTopRight(String s) {
+ mTopRight = s;
+ }
+
+ /**
+ * @return the Bottom Left label.
+ */
+ public String getBottomLeft() {
+ return mBottomLeft;
+ }
+
+ /**
+ * Set the Bottom Left label.
+ * @param s The new top left label.
+ */
+ public void setBottomLeft(String s) {
+ mBottomLeft = s;
+ }
+
+ /**
+ * @return the Bottom Right label.
+ */
+ public String getBottomRight() {
+ return mBottomRight;
+ }
+
+ /**
+ * Set the Bottom Right label.
+ * @param s The new top left label.
+ */
+ public void setBottomRight(String s) {
+ mBottomRight = s;
+ }
+
+ @Override
+ public boolean scoreAnswer(Score score, RecordedAnswer answer) {
+ return ENGINE.scoreAnswer(score, this, answer);
+ }
+
+ @Override
+ public QuestionType getType() {
+ return QuestionType.CIRCLE;
+ }
+}
diff --git a/src/com/p4square/grow/model/ImageQuestion.java b/src/com/p4square/grow/model/ImageQuestion.java
new file mode 100644
index 0000000..d94c32c
--- /dev/null
+++ b/src/com/p4square/grow/model/ImageQuestion.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2013 Jesse Morgan
+ */
+
+package com.p4square.grow.model;
+
+/**
+ * Image Question.
+ *
+ * @author Jesse Morgan <jesse@jesterpm.net>
+ */
+public class ImageQuestion extends Question {
+ private static final ScoringEngine ENGINE = new SimpleScoringEngine();
+
+ @Override
+ public boolean scoreAnswer(Score score, RecordedAnswer answer) {
+ return ENGINE.scoreAnswer(score, this, answer);
+ }
+
+ @Override
+ public QuestionType getType() {
+ return QuestionType.IMAGE;
+ }
+}
diff --git a/src/com/p4square/grow/model/Point.java b/src/com/p4square/grow/model/Point.java
new file mode 100644
index 0000000..e9fc0ca
--- /dev/null
+++ b/src/com/p4square/grow/model/Point.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2013 Jesse Morgan
+ */
+
+package com.p4square.grow.model;
+
+/**
+ * Simple double based point class.
+ *
+ * @author Jesse Morgan <jesse@jesterpm.net>
+ */
+public class Point {
+ /**
+ * Parse a comma separated x,y pair into a point.
+ *
+ * @return The point represented by the string.
+ * @throws IllegalArgumentException if the input is malformed.
+ */
+ public static Point valueOf(String str) {
+ final int comma = str.indexOf(',');
+ if (comma == -1 || comma == 0 || comma == str.length() - 1) {
+ throw new IllegalArgumentException("Malformed point string");
+ }
+
+ final String sX = str.substring(0, comma);
+ final String sY = str.substring(comma + 1);
+
+ return new Point(Double.valueOf(sX), Double.valueOf(sY));
+ }
+
+ private final double mX;
+ private final double mY;
+
+ /**
+ * Create a new point with the given coordinates.
+ *
+ * @param x The x coordinate.
+ * @param y The y coordinate.
+ */
+ public Point(double x, double y) {
+ mX = x;
+ mY = y;
+ }
+
+ /**
+ * Compute the distance between this point and another.
+ *
+ * @param other The other point.
+ * @return The distance between this point and other.
+ */
+ public double distance(Point other) {
+ final double dx = mX - other.mX;
+ final double dy = mY - other.mY;
+
+ return Math.sqrt(dx*dx + dy*dy);
+ }
+
+ /**
+ * @return The x coordinate.
+ */
+ public double getX() {
+ return mX;
+ }
+
+ /**
+ * @return The y coordinate.
+ */
+ public double getY() {
+ return mY;
+ }
+
+ /**
+ * @return The point represented as a comma separated pair.
+ */
+ @Override
+ public String toString() {
+ return String.format("%.2f,%.2f", mX, mY);
+ }
+}
diff --git a/src/com/p4square/grow/model/QuadQuestion.java b/src/com/p4square/grow/model/QuadQuestion.java
new file mode 100644
index 0000000..a7b4179
--- /dev/null
+++ b/src/com/p4square/grow/model/QuadQuestion.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2013 Jesse Morgan
+ */
+
+package com.p4square.grow.model;
+
+/**
+ * Two-dimensional Question.
+ *
+ * @author Jesse Morgan <jesse@jesterpm.net>
+ */
+public class QuadQuestion extends Question {
+ private static final ScoringEngine ENGINE = new QuadScoringEngine();
+
+ private String mTop;
+ private String mRight;
+ private String mBottom;
+ private String mLeft;
+
+ /**
+ * @return the top label.
+ */
+ public String getTop() {
+ return mTop;
+ }
+
+ /**
+ * Set the top label.
+ * @param s The new top label.
+ */
+ public void setTop(String s) {
+ mTop = s;
+ }
+
+ /**
+ * @return the right label.
+ */
+ public String getRight() {
+ return mRight;
+ }
+
+ /**
+ * Set the right label.
+ * @param s The new right label.
+ */
+ public void setRight(String s) {
+ mRight = s;
+ }
+
+ /**
+ * @return the bottom label.
+ */
+ public String getBottom() {
+ return mBottom;
+ }
+
+ /**
+ * Set the bottom label.
+ * @param s The new bottom label.
+ */
+ public void setBottom(String s) {
+ mBottom = s;
+ }
+
+ /**
+ * @return the left label.
+ */
+ public String getLeft() {
+ return mLeft;
+ }
+
+ /**
+ * Set the left label.
+ * @param s The new left label.
+ */
+ public void setLeft(String s) {
+ mLeft = s;
+ }
+
+ @Override
+ public boolean scoreAnswer(Score score, RecordedAnswer answer) {
+ return ENGINE.scoreAnswer(score, this, answer);
+ }
+
+ @Override
+ public QuestionType getType() {
+ return QuestionType.QUAD;
+ }
+}
diff --git a/src/com/p4square/grow/model/QuadScoringEngine.java b/src/com/p4square/grow/model/QuadScoringEngine.java
new file mode 100644
index 0000000..33403b5
--- /dev/null
+++ b/src/com/p4square/grow/model/QuadScoringEngine.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2013 Jesse Morgan
+ */
+
+package com.p4square.grow.model;
+
+import com.p4square.grow.model.Point;
+
+/**
+ * QuadScoringEngine expects the user's answer to be a Point string. We find
+ * the closest answer Point to the user's answer and treat that as the answer.
+ *
+ * @author Jesse Morgan <jesse@jesterpm.net>
+ */
+public class QuadScoringEngine extends ScoringEngine {
+
+ @Override
+ public boolean scoreAnswer(Score score, Question question, RecordedAnswer userAnswer) {
+ // Find all of the answer points.
+ Point[] answers = new Point[question.getAnswers().size()];
+ {
+ int i = 0;
+ for (String answerStr : question.getAnswers().keySet()) {
+ answers[i++] = Point.valueOf(answerStr);
+ }
+ }
+
+ // Parse the user's answer.
+ Point userPoint = Point.valueOf(userAnswer.getAnswerId());
+
+ // Find the closest answer point to the user's answer.
+ double minDistance = Double.MAX_VALUE;
+ int answerIndex = 0;
+ for (int i = 0; i < answers.length; i++) {
+ final double distance = userPoint.distance(answers[i]);
+ if (distance < minDistance) {
+ minDistance = distance;
+ answerIndex = i;
+ }
+ }
+
+ LOG.debug("Quad " + question.getId() + ": Got answer "
+ + answers[answerIndex].toString() + " for user point " + userAnswer);
+
+ // Get the answer and update the score.
+ final Answer answer = question.getAnswers().get(answers[answerIndex].toString());
+ return answer.score(score);
+ }
+}
diff --git a/src/com/p4square/grow/model/Question.java b/src/com/p4square/grow/model/Question.java
index 387d723..37deffa 100644
--- a/src/com/p4square/grow/model/Question.java
+++ b/src/com/p4square/grow/model/Question.java
@@ -4,76 +4,125 @@
package com.p4square.grow.model;
-import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
+import org.codehaus.jackson.annotate.JsonSubTypes;
+import org.codehaus.jackson.annotate.JsonSubTypes.Type;
+import org.codehaus.jackson.annotate.JsonTypeInfo;
+
/**
* Model of an assessment question.
*
* @author Jesse Morgan <jesse@jesterpm.net>
*/
-public class Question {
- public static enum QuestionType {
- TEXT, IMAGE, SLIDER, QUAD, CIRCLE;
+@JsonTypeInfo(
+ use = JsonTypeInfo.Id.NAME,
+ include = JsonTypeInfo.As.PROPERTY,
+ property = "type")
+@JsonSubTypes({
+ @Type(value = TextQuestion.class, name = "text"),
+ @Type(value = ImageQuestion.class, name = "image"),
+ @Type(value = SliderQuestion.class, name = "slider"),
+ @Type(value = QuadQuestion.class, name = "quad"),
+ @Type(value = CircleQuestion.class, name = "circle"),
+})
+public abstract class Question {
+ /**
+ * QuestionType indicates the type of Question.
+ *
+ * @author Jesse Morgan <jesse@jesterpm.net>
+ */
+ public enum QuestionType {
+ TEXT,
+ IMAGE,
+ SLIDER,
+ QUAD,
+ CIRCLE;
+
+ @Override
+ public String toString() {
+ return name().toLowerCase();
+ }
}
- private final Map<String, Object> mMap;
- private final String mQuestionId;
- private final QuestionType mType;
- private final String mQuestionText;
+ private String mQuestionId;
+ private QuestionType mType;
+ private String mQuestionText;
private Map<String, Answer> mAnswers;
- private final String mPreviousQuestionId;
- private final String mNextQuestionId;
-
- public Question(final Map<String, Object> map) {
- mMap = map;
- mQuestionId = (String) map.get("id");
- mType = QuestionType.valueOf(((String) map.get("type")).toUpperCase());
-
- mQuestionText = (String) map.get("text");
-
- mPreviousQuestionId = (String) map.get("previousQuestion");
- mNextQuestionId = (String) map.get("nextQuestion");
+ private String mPreviousQuestionId;
+ private String mNextQuestionId;
+ public Question() {
mAnswers = new HashMap<String, Answer>();
- for (Map.Entry<String, Object> answer :
- ((Map<String, Object>) map.get("answers")).entrySet()) {
-
- final String id = answer.getKey();
- final Map<String, Object> answerMap = (Map<String, Object>) answer.getValue();
- final Answer answerObj = new Answer(id, answerMap);
- mAnswers.put(id, answerObj);
- }
}
+ /**
+ * @return the id String for this question.
+ */
public String getId() {
return mQuestionId;
}
- public QuestionType getType() {
- return mType;
+ /**
+ * Set the id String for this question.
+ * @param id New id
+ */
+ public void setId(String id) {
+ mQuestionId = id;
}
- public String getText() {
+ /**
+ * @return The Question text.
+ */
+ public String getQuestion() {
return mQuestionText;
}
+ /**
+ * Set the question text.
+ * @param value The new question text.
+ */
+ public void setQuestion(String value) {
+ mQuestionText = value;
+ }
+
+ /**
+ * @return The id String of the previous question or null if no previous question exists.
+ */
public String getPreviousQuestion() {
return mPreviousQuestionId;
}
+ /**
+ * Set the id string of the previous question.
+ * @param id Previous question id or null if there is no previous question.
+ */
+ public void setPreviousQuestion(String id) {
+ mPreviousQuestionId = id;
+ }
+
+ /**
+ * @return The id String of the next question or null if no next question exists.
+ */
public String getNextQuestion() {
return mNextQuestionId;
}
- public Map<String, Answer> getAnswers() {
- return Collections.unmodifiableMap(mAnswers);
+ /**
+ * Set the id string of the next question.
+ * @param id next question id or null if there is no next question.
+ */
+ public void setNextQuestion(String id) {
+ mNextQuestionId = id;
}
- public Map<String, Object> getMap() {
- return Collections.unmodifiableMap(mMap);
+ /**
+ * @return a map of Answer id Strings to Answer objects.
+ */
+ public Map<String, Answer> getAnswers() {
+ return mAnswers;
}
/**
@@ -98,4 +147,19 @@ public class Question {
return nextQuestion;
}
+
+ /**
+ * Update the score based on the answer to this question.
+ *
+ * @param score The running score to update.
+ * @param answer The answer give to this question.
+ * @return true if scoring should continue, false if this answer trumps everything else.
+ */
+ public abstract boolean scoreAnswer(Score score, RecordedAnswer answer);
+
+ /**
+ * @return the QuestionType of this question.
+ */
+ public abstract QuestionType getType();
+
}
diff --git a/src/com/p4square/grow/model/RecordedAnswer.java b/src/com/p4square/grow/model/RecordedAnswer.java
new file mode 100644
index 0000000..7d9905d
--- /dev/null
+++ b/src/com/p4square/grow/model/RecordedAnswer.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2013 Jesse Morgan
+ */
+
+package com.p4square.grow.model;
+
+/**
+ * Simple model for a user's assessment answer.
+ *
+ * @author Jesse Morgan <jesse@jesterpm.net>
+ */
+public class RecordedAnswer {
+ private String mAnswerId;
+
+ /**
+ * @return The user's answer.
+ */
+ public String getAnswerId() {
+ return mAnswerId;
+ }
+
+ /**
+ * Set the answer id field.
+ * @param id The new id.
+ */
+ public void setAnswerId(String id) {
+ mAnswerId = id;
+ }
+
+ @Override
+ public String toString() {
+ return mAnswerId;
+ }
+}
diff --git a/src/com/p4square/grow/model/Score.java b/src/com/p4square/grow/model/Score.java
new file mode 100644
index 0000000..b6deda2
--- /dev/null
+++ b/src/com/p4square/grow/model/Score.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2013 Jesse Morgan
+ */
+
+package com.p4square.grow.model;
+
+/**
+ * Simple structure containing a score's sum and count.
+ *
+ * @author Jesse Morgan <jesse@jesterpm.net>
+ */
+public class Score {
+ /**
+ * Return the integer value for the given Score String.
+ */
+ public static int numericScore(String score) {
+ if ("teacher".equals(score)) {
+ return 4;
+ } else if ("disciple".equals(score)) {
+ return 3;
+ } else if ("believer".equals(score)) {
+ return 2;
+ } else {
+ return 1;
+ }
+ }
+
+ double sum;
+ int count;
+
+ /**
+ * @return The sum of all the points.
+ */
+ public double getSum() {
+ return sum;
+ }
+
+ /**
+ * @return The number of questions included in the score.
+ */
+ public int getCount() {
+ return count;
+ }
+
+ /**
+ * @return The final score.
+ */
+ public double getScore() {
+ return sum / count;
+ }
+
+ @Override
+ public String toString() {
+ final double score = getScore();
+
+ if (score >= 4) {
+ return "teacher";
+
+ } else if (score >= 3) {
+ return "disciple";
+
+ } else if (score >= 2) {
+ return "believer";
+
+ } else {
+ return "seeker";
+ }
+ }
+
+}
diff --git a/src/com/p4square/grow/model/ScoringEngine.java b/src/com/p4square/grow/model/ScoringEngine.java
new file mode 100644
index 0000000..8ff18b3
--- /dev/null
+++ b/src/com/p4square/grow/model/ScoringEngine.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2013 Jesse Morgan
+ */
+
+package com.p4square.grow.model;
+
+import org.apache.log4j.Logger;
+
+/**
+ * ScoringEngine computes the score for a question and a given answer.
+ *
+ * @author Jesse Morgan <jesse@jesterpm.net>
+ */
+public abstract class ScoringEngine {
+ protected static final Logger LOG = Logger.getLogger(ScoringEngine.class);
+
+ /**
+ * Update the score based on the given question and answer.
+ *
+ * @param score The running score to update.
+ * @param question The question to compute the score for.
+ * @param answer The answer give to this question.
+ * @return true if scoring should continue, false if this answer trumps everything else.
+ */
+ public abstract boolean scoreAnswer(Score score, Question question, RecordedAnswer answer);
+}
diff --git a/src/com/p4square/grow/model/SimpleScoringEngine.java b/src/com/p4square/grow/model/SimpleScoringEngine.java
new file mode 100644
index 0000000..6ef2dbb
--- /dev/null
+++ b/src/com/p4square/grow/model/SimpleScoringEngine.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2013 Jesse Morgan
+ */
+
+package com.p4square.grow.model;
+
+/**
+ * SimpleScoringEngine expects the user's answer to a valid answer id and
+ * scores accordingly.
+ *
+ * If the answer id is not valid an Exception is thrown.
+ *
+ * @author Jesse Morgan <jesse@jesterpm.net>
+ */
+public class SimpleScoringEngine extends ScoringEngine {
+
+ @Override
+ public boolean scoreAnswer(Score score, Question question, RecordedAnswer userAnswer) {
+ final Answer answer = question.getAnswers().get(userAnswer.getAnswerId());
+ if (answer == null) {
+ throw new IllegalArgumentException("Not a valid answer.");
+ }
+
+ return answer.score(score);
+ }
+}
diff --git a/src/com/p4square/grow/model/SliderQuestion.java b/src/com/p4square/grow/model/SliderQuestion.java
new file mode 100644
index 0000000..f0861e3
--- /dev/null
+++ b/src/com/p4square/grow/model/SliderQuestion.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2013 Jesse Morgan
+ */
+
+package com.p4square.grow.model;
+
+/**
+ * Slider Question.
+ *
+ * @author Jesse Morgan <jesse@jesterpm.net>
+ */
+public class SliderQuestion extends Question {
+ private static final ScoringEngine ENGINE = new SliderScoringEngine();
+
+ @Override
+ public boolean scoreAnswer(Score score, RecordedAnswer answer) {
+ return ENGINE.scoreAnswer(score, this, answer);
+ }
+
+ @Override
+ public QuestionType getType() {
+ return QuestionType.SLIDER;
+ }
+}
diff --git a/src/com/p4square/grow/model/SliderScoringEngine.java b/src/com/p4square/grow/model/SliderScoringEngine.java
new file mode 100644
index 0000000..76811b3
--- /dev/null
+++ b/src/com/p4square/grow/model/SliderScoringEngine.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2013 Jesse Morgan
+ */
+
+package com.p4square.grow.model;
+
+/**
+ * SliderScoringEngine expects the user's answer to be a decimal value in the
+ * range [0, 1]. The value is scaled to the range [1, 4] and added to the
+ * score.
+ *
+ * @author Jesse Morgan <jesse@jesterpm.net>
+ */
+public class SliderScoringEngine extends ScoringEngine {
+
+ @Override
+ public boolean scoreAnswer(Score score, Question question, RecordedAnswer userAnswer) {
+ float delta = Float.valueOf(userAnswer.getAnswerId()) * 3 + 1;
+
+ if (delta < 0 || delta > 4) {
+ throw new IllegalArgumentException("Answer out of bounds.");
+ }
+
+ score.sum += delta;
+ score.count++;
+
+ return true;
+ }
+}
diff --git a/src/com/p4square/grow/model/TextQuestion.java b/src/com/p4square/grow/model/TextQuestion.java
new file mode 100644
index 0000000..88c2a34
--- /dev/null
+++ b/src/com/p4square/grow/model/TextQuestion.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2013 Jesse Morgan
+ */
+
+package com.p4square.grow.model;
+
+/**
+ * Text Question.
+ *
+ * @author Jesse Morgan <jesse@jesterpm.net>
+ */
+public class TextQuestion extends Question {
+ private static final ScoringEngine ENGINE = new SimpleScoringEngine();
+
+ @Override
+ public boolean scoreAnswer(Score score, RecordedAnswer answer) {
+ return ENGINE.scoreAnswer(score, this, answer);
+ }
+
+ @Override
+ public QuestionType getType() {
+ return QuestionType.TEXT;
+ }
+}