From 79b8aacbb7b347bba9d14b1332666e7263a3a058 Mon Sep 17 00:00:00 2001 From: Jesse Morgan Date: Sun, 8 Oct 2017 17:29:48 -0700 Subject: Attempt to update chapter completion on every request. I'm still seeing occasional users with missing progress information in CCB. Currently, the progress is only updated when the user completes a chapter. With this change, the progress will be updated every time the training page is loaded. CCBProgressReporter does not actually update the user's profile unless the new level is greater than the existing level. This provision will prevent needlessly calling the CCB API. Additionally, this update will execute in a background thread to avoid impacting the page load. --- .../com/p4square/grow/ccb/CCBProgressReporter.java | 2 +- .../com/p4square/grow/frontend/GrowFrontend.java | 9 ++++ .../grow/frontend/TrainingPageResource.java | 61 +++++++++++++++------- src/main/java/com/p4square/grow/model/Chapter.java | 21 ++++++++ 4 files changed, 73 insertions(+), 20 deletions(-) diff --git a/src/main/java/com/p4square/grow/ccb/CCBProgressReporter.java b/src/main/java/com/p4square/grow/ccb/CCBProgressReporter.java index 6eb9c15..c352fc7 100644 --- a/src/main/java/com/p4square/grow/ccb/CCBProgressReporter.java +++ b/src/main/java/com/p4square/grow/ccb/CCBProgressReporter.java @@ -57,7 +57,7 @@ public class CCBProgressReporter implements ProgressReporter { if (currentLevel != null) { if (Score.numericScore(chapter) <= Score.numericScore(currentLevel.getSelection().getLabel())) { - LOG.info("Not updating level for " + user.getIdentifier() + LOG.debug("Not updating level for " + user.getIdentifier() + " because current level (" + currentLevel.getSelection().getLabel() + ") is greater than new level (" + chapter + ")"); return; diff --git a/src/main/java/com/p4square/grow/frontend/GrowFrontend.java b/src/main/java/com/p4square/grow/frontend/GrowFrontend.java index b5f62fb..ebaa8df 100644 --- a/src/main/java/com/p4square/grow/frontend/GrowFrontend.java +++ b/src/main/java/com/p4square/grow/frontend/GrowFrontend.java @@ -7,6 +7,8 @@ package com.p4square.grow.frontend; import java.io.File; import java.io.IOException; import java.lang.reflect.Constructor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import freemarker.template.Template; @@ -51,6 +53,8 @@ public class GrowFrontend extends FMFacade { private IntegrationDriver mIntegrationFactory; + private final ExecutorService mThreadPool; + public GrowFrontend() { this(new Config(), new MetricRegistry()); } @@ -58,6 +62,7 @@ public class GrowFrontend extends FMFacade { public GrowFrontend(Config config, MetricRegistry metricRegistry) { mConfig = config; mMetricRegistry = metricRegistry; + mThreadPool = Executors.newFixedThreadPool(mConfig.getInt("threadPoolSize", 10)); } public Config getConfig() { @@ -68,6 +73,10 @@ public class GrowFrontend extends FMFacade { return mMetricRegistry; } + public ExecutorService getThreadPool() { + return mThreadPool; + } + @Override public synchronized void start() throws Exception { Template errorTemplate = getTemplate("templates/error.ftl"); diff --git a/src/main/java/com/p4square/grow/frontend/TrainingPageResource.java b/src/main/java/com/p4square/grow/frontend/TrainingPageResource.java index a1e7789..108c7a7 100644 --- a/src/main/java/com/p4square/grow/frontend/TrainingPageResource.java +++ b/src/main/java/com/p4square/grow/frontend/TrainingPageResource.java @@ -4,24 +4,22 @@ package com.p4square.grow.frontend; -import java.util.ArrayList; +import java.io.IOException; import java.util.Collections; import java.util.Comparator; -import java.util.HashMap; +import java.util.Date; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.ExecutorService; import freemarker.template.Template; -import org.restlet.data.CookieSetting; -import org.restlet.data.Form; 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; @@ -36,6 +34,7 @@ import com.p4square.grow.model.VideoRecord; import com.p4square.grow.model.Playlist; import com.p4square.grow.provider.TrainingRecordProvider; import com.p4square.grow.provider.Provider; +import org.restlet.security.User; /** * TrainingPageResource handles rendering the training page. @@ -49,26 +48,25 @@ public class TrainingPageResource extends FreeMarkerPageResource { private static final Logger LOG = Logger.getLogger(TrainingPageResource.class); private static final String[] CHAPTERS = { "introduction", "seeker", "believer", "disciple", "teacher", "leader" }; - private static final Comparator> VIDEO_COMPARATOR = new Comparator>() { - @Override - public int compare(Map left, Map right) { - String leftNumberStr = (String) left.get("number"); - String rightNumberStr = (String) right.get("number"); - - if (leftNumberStr == null || rightNumberStr == null) { - return -1; - } - - double leftNumber = Double.valueOf(leftNumberStr); - double rightNumber = Double.valueOf(rightNumberStr); + private static final Comparator> VIDEO_COMPARATOR = (left, right) -> { + String leftNumberStr = (String) left.get("number"); + String rightNumberStr = (String) right.get("number"); - return Double.compare(leftNumber, rightNumber); + if (leftNumberStr == null || rightNumberStr == null) { + return -1; } + + double leftNumber = Double.valueOf(leftNumberStr); + double rightNumber = Double.valueOf(rightNumberStr); + + return Double.compare(leftNumber, rightNumber); }; private Config mConfig; private Template mTrainingTemplate; private JsonRequestClient mJsonClient; + private ExecutorService mThreadPool; + private ProgressReporter mProgressReporter; private Provider mTrainingRecordProvider; private FeedData mFeedData; @@ -90,12 +88,14 @@ public class TrainingPageResource extends FreeMarkerPageResource { } mJsonClient = new JsonRequestClient(getContext().getClientDispatcher()); - mTrainingRecordProvider = new TrainingRecordProvider(new JsonRequestProvider(getContext().getClientDispatcher(), TrainingRecord.class)) { + mTrainingRecordProvider = new TrainingRecordProvider(new JsonRequestProvider<>(getContext().getClientDispatcher(), TrainingRecord.class)) { @Override public String makeKey(String userid) { return getBackendEndpoint() + "/accounts/" + userid + "/training"; } }; + mThreadPool = growFrontend.getThreadPool(); + mProgressReporter = growFrontend.getThirdPartyIntegrationFactory().getProgressReporter(); mFeedData = new FeedData(getContext(), mConfig); @@ -125,6 +125,7 @@ public class TrainingPageResource extends FreeMarkerPageResource { // to skip ahead. boolean allowUserToSkip = mConfig.getBoolean("allowUserToSkip", false) || getQueryValue("magicskip") != null; String defaultChapter = null; + String highestCompletedChapter = null; boolean userTriedToSkip = false; int overallProgress = 0; @@ -152,6 +153,7 @@ public class TrainingPageResource extends FreeMarkerPageResource { allowedChapters.put(chapterId, allowed); if (completed) { + highestCompletedChapter = chapterId; overallProgress++; } } @@ -227,6 +229,27 @@ public class TrainingPageResource extends FreeMarkerPageResource { root.put("feeddata", mFeedData); } + // Updated the integration database with the last completed chapter, + // just in case this failed previously. + if (highestCompletedChapter != null) { + try { + final User user = getRequest().getClientInfo().getUser(); + // Get the date of the highest completed chapter. + final Date completionDate = playlist.getChaptersMap().get(highestCompletedChapter).getCompletionDate(); + final String completedChapter = highestCompletedChapter; + mThreadPool.execute(() -> { + try { + mProgressReporter.reportChapterComplete(user, completedChapter, completionDate); + } catch (IOException e) { + LOG.error("Failed to sync progress", e); + } + }); + } catch (Throwable e) { + // Don't let any failures here fail the page load. + LOG.error("Unexpected throwable", e); + } + } + return new TemplateRepresentation(mTrainingTemplate, root, MediaType.TEXT_HTML); } catch (Exception e) { diff --git a/src/main/java/com/p4square/grow/model/Chapter.java b/src/main/java/com/p4square/grow/model/Chapter.java index 3a08e4c..ac27de6 100644 --- a/src/main/java/com/p4square/grow/model/Chapter.java +++ b/src/main/java/com/p4square/grow/model/Chapter.java @@ -4,6 +4,7 @@ package com.p4square.grow.model; +import java.util.Date; import java.util.HashMap; import java.util.Map; @@ -97,6 +98,26 @@ public class Chapter implements Cloneable { return complete; } + /** + * @return the completion date for the chapter, or null if it has not been completed. + */ + @JsonIgnore + public Date getCompletionDate() { + Date latest = new Date(0); + for (VideoRecord video : mVideos.values()) { + if (video.getRequired() && !video.getComplete()) { + // Hey, this chapter isn't complete! + return null; + } + + Date completionDate = video.getCompletionDate(); + if (completionDate.after(latest)) { + latest = completionDate; + } + } + return latest; + } + /** * Deeply clone a chapter. * -- cgit v1.2.3