diff options
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; + } + } } |