From e77c9b418fdfb935ff8e99f10f607a4bbd7e1c8c Mon Sep 17 00:00:00 2001 From: Jesse Morgan Date: Sun, 20 Oct 2013 23:14:51 -0700 Subject: 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. --- src/com/p4square/grow/backend/resources/Point.java | 52 -------- src/com/p4square/grow/backend/resources/Score.java | 49 -------- .../backend/resources/SurveyResultsResource.java | 139 +++++---------------- .../backend/resources/TrainingRecordResource.java | 2 + 4 files changed, 33 insertions(+), 209 deletions(-) delete mode 100644 src/com/p4square/grow/backend/resources/Point.java delete mode 100644 src/com/p4square/grow/backend/resources/Score.java (limited to 'src/com/p4square/grow/backend/resources') diff --git a/src/com/p4square/grow/backend/resources/Point.java b/src/com/p4square/grow/backend/resources/Point.java deleted file mode 100644 index e1b15a8..0000000 --- a/src/com/p4square/grow/backend/resources/Point.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.backend.resources; - -/** - * Simple double based point class. - * - * @author Jesse Morgan - */ -class Point { - public static Point valueOf(String str) { - final int comma = str.indexOf(','); - if (comma == -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; - - public Point(double x, double y) { - mX = x; - mY = y; - } - - public double distance(Point other) { - final double dx = mX - other.mX; - final double dy = mY - other.mY; - - return Math.sqrt(dx*dx + dy*dy); - } - - public double getX() { - return mX; - } - - public double getY() { - return mY; - } - - @Override - public String toString() { - return String.format("%.2f,%.2f", mX, mY); - } -} diff --git a/src/com/p4square/grow/backend/resources/Score.java b/src/com/p4square/grow/backend/resources/Score.java deleted file mode 100644 index 6f52c02..0000000 --- a/src/com/p4square/grow/backend/resources/Score.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.backend.resources; - -/** - * Simple structure containing a score's sum and count. - * - * @author Jesse Morgan - */ -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; - - @Override - public String toString() { - final double score = sum / count; - - 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/backend/resources/SurveyResultsResource.java b/src/com/p4square/grow/backend/resources/SurveyResultsResource.java index f0bb2aa..91d4d0f 100644 --- a/src/com/p4square/grow/backend/resources/SurveyResultsResource.java +++ b/src/com/p4square/grow/backend/resources/SurveyResultsResource.java @@ -14,16 +14,20 @@ import org.codehaus.jackson.map.ObjectMapper; import org.restlet.data.MediaType; import org.restlet.data.Status; -import org.restlet.resource.ServerResource; import org.restlet.representation.Representation; import org.restlet.representation.StringRepresentation; +import org.restlet.resource.ServerResource; import org.apache.log4j.Logger; -import com.p4square.grow.model.Answer; -import com.p4square.grow.model.Question; import com.p4square.grow.backend.GrowBackend; import com.p4square.grow.backend.db.CassandraDatabase; +import com.p4square.grow.model.Answer; +import com.p4square.grow.model.Question; +import com.p4square.grow.model.RecordedAnswer; +import com.p4square.grow.model.Score; +import com.p4square.grow.provider.Provider; + /** * Store the user's answers to the assessment and generate their score. @@ -31,15 +35,16 @@ import com.p4square.grow.backend.db.CassandraDatabase; * @author Jesse Morgan */ public class SurveyResultsResource extends ServerResource { - private final static Logger cLog = Logger.getLogger(SurveyResultsResource.class); + private static final Logger LOG = Logger.getLogger(SurveyResultsResource.class); - private final static ObjectMapper MAPPER = new ObjectMapper(); + private static final ObjectMapper MAPPER = new ObjectMapper(); static enum RequestType { ASSESSMENT, ANSWER } private CassandraDatabase mDb; + private Provider mQuestionProvider; private RequestType mRequestType; private String mUserId; @@ -51,6 +56,7 @@ public class SurveyResultsResource extends ServerResource { final GrowBackend backend = (GrowBackend) getApplication(); mDb = backend.getDatabase(); + mQuestionProvider = backend.getQuestionProvider(); mUserId = getAttribute("userId"); mQuestionId = getAttribute("questionId"); @@ -105,7 +111,7 @@ public class SurveyResultsResource extends ServerResource { success = true; } catch (Exception e) { - cLog.warn("Caught exception putting answer: " + e.getMessage(), e); + LOG.warn("Caught exception putting answer: " + e.getMessage(), e); } break; @@ -146,18 +152,30 @@ public class SurveyResultsResource extends ServerResource { continue; } - final String questionId = c.getName(); - final String answerId = c.getStringValue(); - if (!scoringDone) { - scoringDone = !scoreQuestion(score, questionId, answerId); + try { + Question question = mQuestionProvider.get(c.getName()); + RecordedAnswer userAnswer = MAPPER.readValue(c.getStringValue(), RecordedAnswer.class); + + if (question == null) { + LOG.warn("Answer for unknown question: " + c.getName()); + continue; + } + + LOG.error("Scoring questionId: " + c.getName()); + scoringDone = !question.scoreAnswer(score, userAnswer); + + } catch (Exception e) { + LOG.error("Failed to score question: {userid: \"" + mUserId + + "\", questionid:\"" + c.getName() + + "\", userAnswer:\"" + c.getStringValue() + "\"}", e); } totalAnswers++; } - sb.append(", \"score\":" + score.sum / score.count); - sb.append(", \"sum\":" + score.sum); - sb.append(", \"count\":" + score.count); + sb.append(", \"score\":" + score.getScore()); + sb.append(", \"sum\":" + score.getSum()); + sb.append(", \"count\":" + score.getCount()); sb.append(", \"totalAnswers\":" + totalAnswers); sb.append(", \"result\":\"" + score.toString() + "\""); } @@ -170,99 +188,4 @@ public class SurveyResultsResource extends ServerResource { return summary; } - - private boolean scoreQuestion(final Score score, final String questionId, - final String answerJson) { - - final String data = mDb.getKey("strings", "/questions/" + questionId); - - try { - final Map questionMap = MAPPER.readValue(data, Map.class); - final Map answerMap = MAPPER.readValue(answerJson, Map.class); - final Question question = new Question((Map) questionMap); - final String answerId = (String) answerMap.get("answerId"); - - switch (question.getType()) { - case TEXT: - case IMAGE: - final Answer answer = question.getAnswers().get(answerId); - if (answer == null) { - cLog.warn("Got unknown answer " + answerId - + " for question " + questionId); - } else { - if (!scoreAnswer(score, answer)) { - return false; // Quit scoring - } - } - break; - - case SLIDER: - score.sum += Double.valueOf(answerId) * 4 + 1; - score.count++; - break; - - case CIRCLE: - case QUAD: - scoreQuad(score, question, answerId); - break; - } - - } catch (Exception e) { - cLog.error("Exception parsing question id " + questionId, e); - } - - return true; - } - - private boolean scoreAnswer(final Score score, final Answer answer) { - switch (answer.getType()) { - case TRUMP: - score.sum = answer.getScoreFactor(); - score.count = 1; - return false; // Quit scoring. - - case AVERAGE: - score.sum += answer.getScoreFactor(); - score.count++; - break; - - case NONE: - break; - } - - return true; // Continue scoring - } - - private boolean scoreQuad(final Score score, final Question question, - final String answerId) { - - Point[] answers = new Point[question.getAnswers().size()]; - { - int i = 0; - for (String answer : question.getAnswers().keySet()) { - answers[i++] = Point.valueOf(answer); - } - } - - Point userAnswer = Point.valueOf(answerId); - - double minDistance = Double.MAX_VALUE; - int answerIndex = 0; - for (int i = 0; i < answers.length; i++) { - final double distance = userAnswer.distance(answers[i]); - if (distance < minDistance) { - minDistance = distance; - answerIndex = i; - } - } - - cLog.debug("Quad " + question.getId() + ": Got answer " - + answers[answerIndex].toString() + " for user point " + answerId); - - final Answer answer = question.getAnswers().get(answers[answerIndex].toString()); - score.sum += answer.getScoreFactor(); - score.count++; - - return true; // Continue scoring - } } diff --git a/src/com/p4square/grow/backend/resources/TrainingRecordResource.java b/src/com/p4square/grow/backend/resources/TrainingRecordResource.java index 009d0fe..6de9507 100644 --- a/src/com/p4square/grow/backend/resources/TrainingRecordResource.java +++ b/src/com/p4square/grow/backend/resources/TrainingRecordResource.java @@ -27,6 +27,8 @@ import org.apache.log4j.Logger; import com.p4square.grow.backend.GrowBackend; import com.p4square.grow.backend.db.CassandraDatabase; +import com.p4square.grow.model.Score; + /** * * @author Jesse Morgan -- cgit v1.2.3