summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/com/p4square/grow/backend/resources/Playlist.java134
-rw-r--r--src/com/p4square/grow/backend/resources/Score.java15
-rw-r--r--src/com/p4square/grow/backend/resources/SurveyResultsResource.java20
-rw-r--r--src/com/p4square/grow/backend/resources/TrainingRecordResource.java95
-rw-r--r--src/com/p4square/grow/backend/resources/VideoRecord.java56
-rw-r--r--src/com/p4square/grow/frontend/TrainingPageResource.java54
6 files changed, 344 insertions, 30 deletions
diff --git a/src/com/p4square/grow/backend/resources/Playlist.java b/src/com/p4square/grow/backend/resources/Playlist.java
new file mode 100644
index 0000000..f3d2f08
--- /dev/null
+++ b/src/com/p4square/grow/backend/resources/Playlist.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2013 Jesse Morgan
+ */
+
+package com.p4square.grow.backend.resources;
+
+import java.io.IOException;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.type.TypeReference;
+
+import com.p4square.grow.backend.db.CassandraDatabase;
+
+/**
+ *
+ * @author Jesse Morgan <jesse@jesterpm.net>
+ */
+class Playlist {
+ private static final ObjectMapper MAPPER = new ObjectMapper();
+
+ /**
+ * Load a Playlist from the database.
+ */
+ public static Playlist load(CassandraDatabase db, String userId) throws IOException {
+ String playlistString = db.getKey("training", userId, "playlist");
+
+ if (playlistString == null) {
+ return null;
+ }
+
+ Map<String, Map<String, VideoRecord>> playlist =
+ MAPPER.readValue(playlistString, new TypeReference<Map<String, Map<String, VideoRecord>>>() { });
+
+ return new Playlist(playlist);
+
+ }
+
+ /**
+ * Persist the Playlist for the given user.
+ * @return The String serialization of the playlist.
+ */
+ public static String save(CassandraDatabase db, String userId, Playlist playlist) throws IOException {
+ String playlistString = MAPPER.writeValueAsString(playlist.mPlaylist);
+ db.putKey("training", userId, "playlist", playlistString);
+ return playlistString;
+ }
+
+
+ private Map<String, Map<String, VideoRecord>> mPlaylist;
+
+ /**
+ * Construct an empty playlist.
+ */
+ public Playlist() {
+ mPlaylist = new HashMap<String, Map<String, VideoRecord>>();
+ }
+
+ /**
+ * Constructor for database initialization.
+ */
+ private Playlist(Map<String, Map<String, VideoRecord>> playlist) {
+ mPlaylist = playlist;
+ }
+
+ public VideoRecord find(String videoId) {
+ for (Map<String, VideoRecord> chapter : mPlaylist.values()) {
+ VideoRecord r = chapter.get(videoId);
+
+ if (r != null) {
+ return r;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Add a video to the playlist.
+ */
+ public VideoRecord add(String chapter, String videoId) {
+ Map<String, VideoRecord> chapterMap = mPlaylist.get(chapter);
+
+ if (chapterMap == null) {
+ chapterMap = new HashMap<String, VideoRecord>();
+ mPlaylist.put(chapter, chapterMap);
+ }
+
+ VideoRecord r = new VideoRecord();
+ chapterMap.put(videoId, r);
+ return r;
+ }
+
+ /**
+ * @return The last chapter to be completed.
+ */
+ public Map<String, Boolean> getChapterStatuses() {
+ Map<String, Boolean> completed = new HashMap<String, Boolean>();
+
+ for (String chapter : mPlaylist.keySet()) {
+ completed.put(chapter, isChapterComplete(chapter));
+ }
+
+ return completed;
+ }
+
+ public boolean isChapterComplete(String chapterId) {
+ boolean complete = true;
+
+ Map<String, VideoRecord> chapter = mPlaylist.get(chapterId);
+ if (chapter != null) {
+ for (VideoRecord r : chapter.values()) {
+ if (r.getRequired() && !r.getComplete()) {
+ return false;
+ }
+ }
+ }
+
+ return complete;
+ }
+
+ @Override
+ public String toString() {
+ try {
+ return MAPPER.writeValueAsString(mPlaylist);
+
+ } catch (IOException e) {
+ return super.toString();
+ }
+ }
+
+}
diff --git a/src/com/p4square/grow/backend/resources/Score.java b/src/com/p4square/grow/backend/resources/Score.java
index c7e5ecc..6f52c02 100644
--- a/src/com/p4square/grow/backend/resources/Score.java
+++ b/src/com/p4square/grow/backend/resources/Score.java
@@ -10,6 +10,21 @@ package com.p4square.grow.backend.resources;
* @author Jesse Morgan <jesse@jesterpm.net>
*/
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;
diff --git a/src/com/p4square/grow/backend/resources/SurveyResultsResource.java b/src/com/p4square/grow/backend/resources/SurveyResultsResource.java
index 5e4a8bb..e93e253 100644
--- a/src/com/p4square/grow/backend/resources/SurveyResultsResource.java
+++ b/src/com/p4square/grow/backend/resources/SurveyResultsResource.java
@@ -31,7 +31,7 @@ import com.p4square.grow.backend.db.CassandraDatabase;
public class SurveyResultsResource extends ServerResource {
private final static Logger cLog = Logger.getLogger(SurveyResultsResource.class);
- private final static ObjectMapper cMapper = new ObjectMapper();
+ private final static ObjectMapper MAPPER = new ObjectMapper();
static enum RequestType {
ASSESSMENT, ANSWER
@@ -72,7 +72,10 @@ public class SurveyResultsResource extends ServerResource {
break;
case ASSESSMENT:
- result = buildAssessment();
+ result = mDb.getKey("assessments", mUserId, "summary");
+ if (result == null) {
+ result = buildAssessment();
+ }
break;
}
@@ -135,7 +138,7 @@ public class SurveyResultsResource extends ServerResource {
if (!row.isEmpty()) {
Score score = new Score();
for (Column<String> c : row) {
- if (c.getName().equals("lastAnswered")) {
+ if (c.getName().equals("lastAnswered") || c.getName().equals("summary")) {
continue;
}
@@ -153,7 +156,12 @@ public class SurveyResultsResource extends ServerResource {
}
sb.append(" }");
- return sb.toString();
+ String summary = sb.toString();
+
+ // Persist summary
+ mDb.putKey("assessments", mUserId, "summary", summary);
+
+ return summary;
}
private boolean scoreQuestion(final Score score, final String questionId,
@@ -162,8 +170,8 @@ public class SurveyResultsResource extends ServerResource {
final String data = mDb.getKey("strings", "/questions/" + questionId);
try {
- final Map<?,?> questionMap = cMapper.readValue(data, Map.class);
- final Map<?,?> answerMap = cMapper.readValue(answerJson, Map.class);
+ final Map<?,?> questionMap = MAPPER.readValue(data, Map.class);
+ final Map<?,?> answerMap = MAPPER.readValue(answerJson, Map.class);
final Question question = new Question((Map<String, Object>) questionMap);
final String answerId = (String) answerMap.get("answerId");
diff --git a/src/com/p4square/grow/backend/resources/TrainingRecordResource.java b/src/com/p4square/grow/backend/resources/TrainingRecordResource.java
index 93f4fbc..8447c16 100644
--- a/src/com/p4square/grow/backend/resources/TrainingRecordResource.java
+++ b/src/com/p4square/grow/backend/resources/TrainingRecordResource.java
@@ -4,12 +4,18 @@
package com.p4square.grow.backend.resources;
+import java.io.IOException;
+
+import java.util.LinkedList;
+import java.util.List;
import java.util.Map;
import java.util.HashMap;
import com.netflix.astyanax.model.Column;
import com.netflix.astyanax.model.ColumnList;
+import org.codehaus.jackson.map.ObjectMapper;
+
import org.restlet.data.MediaType;
import org.restlet.data.Status;
import org.restlet.resource.ServerResource;
@@ -22,16 +28,19 @@ import com.p4square.grow.backend.GrowBackend;
import com.p4square.grow.backend.db.CassandraDatabase;
/**
- *
+ *
* @author Jesse Morgan <jesse@jesterpm.net>
*/
public class TrainingRecordResource extends ServerResource {
- private final static Logger cLog = Logger.getLogger(TrainingRecordResource.class);
+ private static final String[] CHAPTERS = { "seeker", "believer", "disciple", "teacher" };
+
+ private static final Logger LOG = Logger.getLogger(TrainingRecordResource.class);
+ private static final ObjectMapper MAPPER = new ObjectMapper();
static enum RequestType {
SUMMARY, VIDEO
}
-
+
private GrowBackend mBackend;
private CassandraDatabase mDb;
@@ -76,7 +85,7 @@ public class TrainingRecordResource extends ServerResource {
setStatus(Status.CLIENT_ERROR_NOT_FOUND);
return null;
}
-
+
return new StringRepresentation(result);
}
@@ -92,10 +101,20 @@ public class TrainingRecordResource extends ServerResource {
try {
mDb.putKey("training", mUserId, mVideoId, entity.getText());
mDb.putKey("training", mUserId, "lastVideo", mVideoId);
+
+ Playlist playlist = Playlist.load(mDb, mUserId);
+ if (playlist != null) {
+ VideoRecord r = playlist.find(mVideoId);
+ if (r != null && !r.getComplete()) {
+ r.complete();
+ Playlist.save(mDb, mUserId, playlist);
+ }
+ }
+
success = true;
} catch (Exception e) {
- cLog.warn("Caught exception updating training record: " + e.getMessage(), e);
+ LOG.warn("Caught exception updating training record: " + e.getMessage(), e);
}
break;
@@ -119,19 +138,20 @@ public class TrainingRecordResource extends ServerResource {
private String buildSummary() {
StringBuilder sb = new StringBuilder("{ ");
- // Last question answered
+ // Last watch video
final String lastVideo = mDb.getKey("training", mUserId, "lastVideo");
if (lastVideo != null) {
sb.append("\"lastVideo\": \"" + lastVideo + "\", ");
}
- // List of videos watched
+ // Get the user's video history
sb.append("\"videos\": { ");
ColumnList<String> row = mDb.getRow("training", mUserId);
if (!row.isEmpty()) {
boolean first = true;
for (Column<String> c : row) {
- if ("lastVideo".equals(c.getName())) {
+ if ("lastVideo".equals(c.getName()) ||
+ "playlist".equals(c.getName())) {
continue;
}
@@ -142,14 +162,69 @@ public class TrainingRecordResource extends ServerResource {
sb.append(", \"" + c.getName() + "\": ");
}
- sb.append(c.getStringValue());
+ sb.append(c.getStringValue());
}
}
sb.append(" }");
+ // Get the user's playlist
+ try {
+ Playlist playlist = Playlist.load(mDb, mUserId);
+ if (playlist == null) {
+ playlist = createInitialPlaylist();
+ }
+
+ sb.append(", \"playlist\": ");
+ sb.append(playlist.toString());
+
+ // Last Completed Section
+ Map<String, Boolean> chapters = playlist.getChapterStatuses();
+ String chaptersString = MAPPER.writeValueAsString(chapters);
+ sb.append(", \"chapters\":");
+ sb.append(chaptersString);
+
+
+ } catch (IOException e) {
+ LOG.warn("IOException loading playlist for user " + mUserId, e);
+ }
+
sb.append(" }");
return sb.toString();
}
-
+
+ /**
+ * Create the user's initial playlist.
+ *
+ * @return Returns the String representation of the initial playlist.
+ */
+ private Playlist createInitialPlaylist() throws IOException {
+ Playlist playlist = new Playlist();
+
+ // Get assessment score
+ String summaryString = mDb.getKey("assessments", mUserId, "summary");
+ if (summaryString == null) {
+ return null;
+ }
+ Map<?,?> summary = MAPPER.readValue(summaryString, Map.class);
+ double score = (Double) summary.get("score");
+
+ // Get videos for each section and build playlist
+ for (String chapter : CHAPTERS) {
+ // Chapter required if the floor of the score is <= the chapter's numeric value.
+ boolean required = score < Score.numericScore(chapter) + 1;
+
+ ColumnList<String> row = mDb.getRow("strings", "/training/" + chapter);
+ if (!row.isEmpty()) {
+ for (Column<String> c : row) {
+ VideoRecord r = playlist.add(chapter, c.getName());
+ r.setRequired(required);
+ }
+ }
+ }
+
+ Playlist.save(mDb, mUserId, playlist);
+
+ return playlist;
+ }
}
diff --git a/src/com/p4square/grow/backend/resources/VideoRecord.java b/src/com/p4square/grow/backend/resources/VideoRecord.java
new file mode 100644
index 0000000..2ba28c3
--- /dev/null
+++ b/src/com/p4square/grow/backend/resources/VideoRecord.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2013 Jesse Morgan
+ */
+
+package com.p4square.grow.backend.resources;
+
+import java.util.Date;
+
+/**
+ * Simple bean containing video completion data.
+ *
+ * @author Jesse Morgan <jesse@jesterpm.net>
+ */
+class VideoRecord {
+ private boolean mComplete;
+ private boolean mRequired;
+ private Date mCompletionDate;
+
+ public VideoRecord() {
+ mComplete = false;
+ mRequired = true;
+ mCompletionDate = null;
+ }
+
+ public boolean getComplete() {
+ return mComplete;
+ }
+
+ public void setComplete(boolean complete) {
+ mComplete = complete;
+ }
+
+ public boolean getRequired() {
+ return mRequired;
+ }
+
+ public void setRequired(boolean complete) {
+ mRequired = complete;
+ }
+
+ public Date getCompletionDate() {
+ return mCompletionDate;
+ }
+
+ public void setCompletionDate(Date date) {
+ mCompletionDate = date;
+ }
+
+ /**
+ * Convenience method to mark a video complete.
+ */
+ public void complete() {
+ mComplete = true;
+ mCompletionDate = new Date();
+ }
+}
diff --git a/src/com/p4square/grow/frontend/TrainingPageResource.java b/src/com/p4square/grow/frontend/TrainingPageResource.java
index ad7ea8d..f5265f6 100644
--- a/src/com/p4square/grow/frontend/TrainingPageResource.java
+++ b/src/com/p4square/grow/frontend/TrainingPageResource.java
@@ -16,6 +16,7 @@ import org.restlet.data.MediaType;
import org.restlet.data.Status;
import org.restlet.ext.freemarker.TemplateRepresentation;
import org.restlet.representation.Representation;
+import org.restlet.representation.StringRepresentation;
import org.restlet.resource.ServerResource;
import org.apache.log4j.Logger;
@@ -70,10 +71,34 @@ public class TrainingPageResource extends FreeMarkerPageResource {
@Override
protected Representation get() {
try {
- // Get the current chapter.
+ // Get the training summary
+ Map<String, Object> trainingRecord = null;
+ Map<String, Object> completedVideos = new HashMap<String, Object>();
+ Map<String, Boolean> chapters = null;
+ {
+ JsonResponse response = backendGet("/accounts/" + mUserId + "/training");
+ if (response.getStatus().isSuccess()) {
+ trainingRecord = response.getMap();
+ completedVideos = (Map<String, Object>) trainingRecord.get("videos");
+ chapters = (Map<String, Boolean>) trainingRecord.get("chapters");
+ }
+ }
+
+ // Get the current chapter (the lowest, incomplete chapter)
if (mChapter == null) {
- // TODO: Get user's current question
- mChapter = "seeker";
+ int min = Integer.MAX_VALUE;
+ for (Map.Entry<String, Boolean> chapter : chapters.entrySet()) {
+ int index = chapterIndex(chapter.getKey());
+ if (!chapter.getValue() && index < min) {
+ min = index;
+ mChapter = chapter.getKey();
+ }
+ }
+
+ String nextPage = mConfig.getString("dynamicRoot", "");
+ nextPage += "/account/training/" + mChapter;
+ getResponse().redirectSeeOther(nextPage);
+ return new StringRepresentation("Redirecting to " + nextPage);
}
// Get videos for the chapter.
@@ -87,17 +112,6 @@ public class TrainingPageResource extends FreeMarkerPageResource {
videos = (List<Map<String, Object>>) response.getMap().get("videos");
}
- // Get list of completed videos
- Map<String, Object> trainingRecord = null;
- Map<String, Object> completedVideos = new HashMap<String, Object>();
- {
- JsonResponse response = backendGet("/accounts/" + mUserId + "/training");
- if (response.getStatus().isSuccess()) {
- trainingRecord = response.getMap();
- completedVideos = (Map<String, Object>) trainingRecord.get("videos");
- }
- }
-
// Mark the completed videos as completed
int chapterProgress = 0;
for (Map<String, Object> video : videos) {
@@ -158,4 +172,16 @@ public class TrainingPageResource extends FreeMarkerPageResource {
return response;
}
+
+ int chapterIndex(String chapter) {
+ if ("teacher".equals(chapter)) {
+ return 4;
+ } else if ("disciple".equals(chapter)) {
+ return 3;
+ } else if ("believer".equals(chapter)) {
+ return 2;
+ } else {
+ return 1;
+ }
+ }
}