diff options
author | Jesse Morgan <jesse@jesterpm.net> | 2016-04-09 14:22:20 -0700 |
---|---|---|
committer | Jesse Morgan <jesse@jesterpm.net> | 2016-04-09 15:48:01 -0700 |
commit | 3102d8bce3426d9cf41aeaf201c360d342677770 (patch) | |
tree | 38c4f1e8828f9af9c4b77a173bee0d312b321698 /src/com/p4square/grow | |
parent | bbf907e51dfcf157bdee24dead1d531122aa25db (diff) |
Switching from Ivy+Ant to Maven.
Diffstat (limited to 'src/com/p4square/grow')
94 files changed, 0 insertions, 9602 deletions
diff --git a/src/com/p4square/grow/GrowProcessComponent.java b/src/com/p4square/grow/GrowProcessComponent.java deleted file mode 100644 index f63538c..0000000 --- a/src/com/p4square/grow/GrowProcessComponent.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow; - -import java.io.File; -import java.io.IOException; -import java.util.concurrent.TimeUnit; - -import com.codahale.metrics.ConsoleReporter; -import com.codahale.metrics.MetricRegistry; - -import org.apache.log4j.Logger; - -import org.restlet.Application; -import org.restlet.Component; -import org.restlet.Restlet; -import org.restlet.data.ChallengeScheme; -import org.restlet.data.Protocol; -import org.restlet.resource.Directory; -import org.restlet.security.ChallengeAuthenticator; - -import com.p4square.grow.backend.BackendVerifier; -import com.p4square.grow.backend.GrowBackend; -import com.p4square.grow.config.Config; -import com.p4square.grow.frontend.GrowFrontend; -import com.p4square.restlet.metrics.MetricsApplication; - -/** - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class GrowProcessComponent extends Component { - private static Logger LOG = Logger.getLogger(GrowProcessComponent.class); - - private static final String BACKEND_REALM = "Grow Backend"; - - private final Config mConfig; - private final MetricRegistry mMetricRegistry; - - /** - * Create a new Grow Process website component combining a frontend and backend. - */ - public GrowProcessComponent() throws Exception { - this(new Config()); - } - - public GrowProcessComponent(Config config) throws Exception { - // Clients - getClients().add(Protocol.FILE); - getClients().add(Protocol.HTTP); - getClients().add(Protocol.HTTPS); - - // Prepare mConfig - mConfig = config; - mConfig.updateConfig(this.getClass().getResourceAsStream("/grow.properties")); - - // Prepare Metrics - mMetricRegistry = new MetricRegistry(); - - // Frontend - GrowFrontend frontend = new GrowFrontend(mConfig, mMetricRegistry); - getDefaultHost().attach(frontend); - - // Backend - GrowBackend backend = new GrowBackend(mConfig, mMetricRegistry); - getInternalRouter().attach("/backend", backend); - - // Authenticated access to the backend - BackendVerifier verifier = new BackendVerifier(backend.getUserRecordProvider()); - ChallengeAuthenticator auth = new ChallengeAuthenticator(getContext().createChildContext(), - false, ChallengeScheme.HTTP_BASIC, BACKEND_REALM, verifier); - auth.setNext(backend); - getDefaultHost().attach("/backend", auth); - - // Authenticated access to metrics - ChallengeAuthenticator metricAuth = new ChallengeAuthenticator( - getContext().createChildContext(), false, - ChallengeScheme.HTTP_BASIC, BACKEND_REALM, verifier); - metricAuth.setNext(new MetricsApplication(mMetricRegistry)); - getDefaultHost().attach("/metrics", metricAuth); - } - - - @Override - public void start() throws Exception { - String configDomain = getContext().getParameters().getFirstValue("com.p4square.grow.configDomain"); - if (configDomain != null) { - mConfig.setDomain(configDomain); - } - - String configFilename = getContext().getParameters().getFirstValue("com.p4square.grow.configFile"); - if (configFilename != null) { - mConfig.updateConfig(configFilename); - } - - super.start(); - } - - /** - * Stand-alone main for testing. - */ - public static void main(String[] args) throws Exception { - // Load an optional config file from the first argument. - Config config = new Config(); - config.setDomain("dev"); - if (args.length >= 1) { - config.updateConfig(args[0]); - } - - // Override domain - if (args.length == 2) { - config.setDomain(args[1]); - } - - // Start the HTTP Server - final GrowProcessComponent component = new GrowProcessComponent(config); - component.getServers().add(Protocol.HTTP, 8085); - - // Static content - try { - component.getDefaultHost().attach("/images/", new FileServingApp("./build/root/images/")); - component.getDefaultHost().attach("/scripts", new FileServingApp("./build/root/scripts")); - component.getDefaultHost().attach("/style.css", new FileServingApp("./build/root/style.css")); - component.getDefaultHost().attach("/favicon.ico", new FileServingApp("./build/root/favicon.ico")); - component.getDefaultHost().attach("/notfound.html", new FileServingApp("./build/root/notfound.html")); - component.getDefaultHost().attach("/error.html", new FileServingApp("./build/root/error.html")); - } catch (IOException e) { - LOG.error("Could not create directory for static resources: " - + e.getMessage(), e); - } - - // Setup shutdown hook - Runtime.getRuntime().addShutdownHook(new Thread() { - public void run() { - try { - component.stop(); - } catch (Exception e) { - LOG.error("Exception during cleanup", e); - } - } - }); - - LOG.info("Starting server..."); - - try { - component.start(); - } catch (Exception e) { - LOG.fatal("Could not start: " + e.getMessage(), e); - } - } - - private static class FileServingApp extends Application { - private final String mPath; - - public FileServingApp(String path) throws IOException { - mPath = new File(path).getAbsolutePath(); - } - - @Override - public Restlet createInboundRoot() { - return new Directory(getContext(), "file://" + mPath); - } - } -} diff --git a/src/com/p4square/grow/backend/BackendVerifier.java b/src/com/p4square/grow/backend/BackendVerifier.java deleted file mode 100644 index 83160a9..0000000 --- a/src/com/p4square/grow/backend/BackendVerifier.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.backend; - -import java.io.IOException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -import org.apache.commons.codec.binary.Hex; - -import org.restlet.security.SecretVerifier; - -import com.p4square.grow.model.UserRecord; -import com.p4square.grow.provider.Provider; - -/** - * Verify the given credentials against the users with backend access. - */ -public class BackendVerifier extends SecretVerifier { - - private final Provider<String, UserRecord> mUserProvider; - - public BackendVerifier(Provider<String, UserRecord> userProvider) { - mUserProvider = userProvider; - } - - @Override - public int verify(String identifier, char[] secret) { - if (identifier == null) { - throw new IllegalArgumentException("Null identifier"); - } - - if (secret == null) { - throw new IllegalArgumentException("Null secret"); - } - - // Does the user exist? - UserRecord user; - try { - user = mUserProvider.get(identifier); - if (user == null) { - return RESULT_UNKNOWN; - } - - } catch (IOException e) { - return RESULT_UNKNOWN; - } - - // Does the user have a backend password? - String storedHash = user.getBackendPasswordHash(); - if (storedHash == null) { - // This user doesn't have access - return RESULT_INVALID; - } - - // Validate the password. - try { - String hashedInput = hashPassword(secret); - if (hashedInput.equals(storedHash)) { - return RESULT_VALID; - } - - } catch (NoSuchAlgorithmException e) { - return RESULT_UNSUPPORTED; - } - - // If all else fails, fail. - return RESULT_INVALID; - } - - /** - * Hash the given secret. - */ - public static String hashPassword(char[] secret) throws NoSuchAlgorithmException { - MessageDigest md = MessageDigest.getInstance("SHA-1"); - - // Convert the char[] to byte[] - // FIXME This approach is incorrectly truncating multibyte - // characters. - byte[] b = new byte[secret.length]; - for (int i = 0; i < secret.length; i++) { - b[i] = (byte) secret[i]; - } - - md.update(b); - - byte[] hash = md.digest(); - return new String(Hex.encodeHex(hash)); - } -} diff --git a/src/com/p4square/grow/backend/CassandraGrowData.java b/src/com/p4square/grow/backend/CassandraGrowData.java deleted file mode 100644 index 22a7716..0000000 --- a/src/com/p4square/grow/backend/CassandraGrowData.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.backend; - -import java.io.IOException; - -import com.p4square.grow.config.Config; - -import com.p4square.grow.backend.db.CassandraDatabase; -import com.p4square.grow.backend.db.CassandraKey; -import com.p4square.grow.backend.db.CassandraProviderImpl; -import com.p4square.grow.backend.db.CassandraCollectionProvider; -import com.p4square.grow.backend.db.CassandraTrainingRecordProvider; - -import com.p4square.grow.model.Message; -import com.p4square.grow.model.MessageThread; -import com.p4square.grow.model.Playlist; -import com.p4square.grow.model.Question; -import com.p4square.grow.model.TrainingRecord; -import com.p4square.grow.model.UserRecord; - -import com.p4square.grow.provider.CollectionProvider; -import com.p4square.grow.provider.DelegateCollectionProvider; -import com.p4square.grow.provider.DelegateProvider; -import com.p4square.grow.provider.Provider; - -/** - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -class CassandraGrowData implements GrowData { - private static final String DEFAULT_COLUMN = "value"; - - private final Config mConfig; - private final CassandraDatabase mDatabase; - - private final Provider<String, UserRecord> mUserRecordProvider; - - private final Provider<String, Question> mQuestionProvider; - private final CassandraTrainingRecordProvider mTrainingRecordProvider; - private final CollectionProvider<String, String, String> mVideoProvider; - - private final CollectionProvider<String, String, MessageThread> mFeedThreadProvider; - private final CollectionProvider<String, String, Message> mFeedMessageProvider; - - private final Provider<String, String> mStringProvider; - - private final CollectionProvider<String, String, String> mAnswerProvider; - - public CassandraGrowData(final Config config) { - mConfig = config; - mDatabase = new CassandraDatabase(); - - mUserRecordProvider = new DelegateProvider<String, CassandraKey, UserRecord>( - new CassandraProviderImpl<UserRecord>(mDatabase, UserRecord.class)) { - @Override - public CassandraKey makeKey(String userid) { - return new CassandraKey("accounts", userid, DEFAULT_COLUMN); - } - }; - - mQuestionProvider = new DelegateProvider<String, CassandraKey, Question>( - new CassandraProviderImpl<Question>(mDatabase, Question.class)) { - @Override - public CassandraKey makeKey(String questionId) { - return new CassandraKey("strings", "/questions/" + questionId, DEFAULT_COLUMN); - } - }; - - mFeedThreadProvider = new CassandraCollectionProvider<MessageThread>(mDatabase, - "feedthreads", MessageThread.class); - mFeedMessageProvider = new CassandraCollectionProvider<Message>(mDatabase, - "feedmessages", Message.class); - - mTrainingRecordProvider = new CassandraTrainingRecordProvider(mDatabase); - - mVideoProvider = new DelegateCollectionProvider<String, String, String, String, String>( - new CassandraCollectionProvider<String>(mDatabase, "strings", String.class)) { - @Override - public String makeCollectionKey(String key) { - return "/training/" + key; - } - - @Override - public String makeKey(String key) { - return key; - } - - @Override - public String unmakeKey(String key) { - return key; - } - }; - - mStringProvider = new DelegateProvider<String, CassandraKey, String>( - new CassandraProviderImpl<String>(mDatabase, String.class)) { - @Override - public CassandraKey makeKey(String id) { - return new CassandraKey("strings", id, DEFAULT_COLUMN); - } - }; - - mAnswerProvider = new CassandraCollectionProvider<String>( - mDatabase, "assessments", String.class); - } - - @Override - public void start() throws Exception { - mDatabase.setClusterName(mConfig.getString("clusterName", "Dev Cluster")); - mDatabase.setKeyspaceName(mConfig.getString("keyspace", "GROW")); - mDatabase.init(); - } - - @Override - public void stop() throws Exception { - mDatabase.close(); - } - - /** - * @return the current database. - */ - public CassandraDatabase getDatabase() { - return mDatabase; - } - - @Override - public Provider<String, UserRecord> getUserRecordProvider() { - return mUserRecordProvider; - } - - @Override - public Provider<String, Question> getQuestionProvider() { - return mQuestionProvider; - } - - @Override - public Provider<String, TrainingRecord> getTrainingRecordProvider() { - return mTrainingRecordProvider; - } - - @Override - public CollectionProvider<String, String, String> getVideoProvider() { - return mVideoProvider; - } - - @Override - public Playlist getDefaultPlaylist() throws IOException { - return mTrainingRecordProvider.getDefaultPlaylist(); - } - - @Override - public CollectionProvider<String, String, MessageThread> getThreadProvider() { - return mFeedThreadProvider; - } - - @Override - public CollectionProvider<String, String, Message> getMessageProvider() { - return mFeedMessageProvider; - } - - @Override - public Provider<String, String> getStringProvider() { - return mStringProvider; - } - - @Override - public CollectionProvider<String, String, String> getAnswerProvider() { - return mAnswerProvider; - } -} diff --git a/src/com/p4square/grow/backend/DynamoGrowData.java b/src/com/p4square/grow/backend/DynamoGrowData.java deleted file mode 100644 index 3b38eac..0000000 --- a/src/com/p4square/grow/backend/DynamoGrowData.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.backend; - -import java.io.IOException; - -import com.amazonaws.auth.AWSCredentials; - -import com.p4square.grow.backend.dynamo.DynamoDatabase; -import com.p4square.grow.backend.dynamo.DynamoKey; -import com.p4square.grow.backend.dynamo.DynamoProviderImpl; -import com.p4square.grow.backend.dynamo.DynamoCollectionProviderImpl; - -import com.p4square.grow.config.Config; - -import com.p4square.grow.model.Message; -import com.p4square.grow.model.MessageThread; -import com.p4square.grow.model.Playlist; -import com.p4square.grow.model.Question; -import com.p4square.grow.model.TrainingRecord; -import com.p4square.grow.model.UserRecord; - -import com.p4square.grow.provider.CollectionProvider; -import com.p4square.grow.provider.DelegateCollectionProvider; -import com.p4square.grow.provider.DelegateProvider; -import com.p4square.grow.provider.Provider; -import com.p4square.grow.provider.JsonEncodedProvider; - -/** - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -class DynamoGrowData implements GrowData { - private static final String DEFAULT_COLUMN = "value"; - private static final String DEFAULT_PLAYLIST_KEY = "/training/defaultplaylist"; - - private final Config mConfig; - private final DynamoDatabase mDatabase; - - private final Provider<String, UserRecord> mUserRecordProvider; - - private final Provider<String, Question> mQuestionProvider; - private final Provider<String, TrainingRecord> mTrainingRecordProvider; - private final CollectionProvider<String, String, String> mVideoProvider; - - private final CollectionProvider<String, String, MessageThread> mFeedThreadProvider; - private final CollectionProvider<String, String, Message> mFeedMessageProvider; - - private final Provider<String, String> mStringProvider; - - private final CollectionProvider<String, String, String> mAnswerProvider; - - public DynamoGrowData(final Config config) { - mConfig = config; - - mDatabase = new DynamoDatabase(config); - - mUserRecordProvider = new DelegateProvider<String, DynamoKey, UserRecord>( - new DynamoProviderImpl<UserRecord>(mDatabase, UserRecord.class)) { - @Override - public DynamoKey makeKey(String userid) { - return DynamoKey.newAttributeKey("accounts", userid, DEFAULT_COLUMN); - } - }; - - mQuestionProvider = new DelegateProvider<String, DynamoKey, Question>( - new DynamoProviderImpl<Question>(mDatabase, Question.class)) { - @Override - public DynamoKey makeKey(String questionId) { - return DynamoKey.newAttributeKey("strings", - "/questions/" + questionId, - DEFAULT_COLUMN); - } - }; - - mFeedThreadProvider = new DynamoCollectionProviderImpl<MessageThread>( - mDatabase, "feedthreads", MessageThread.class); - mFeedMessageProvider = new DynamoCollectionProviderImpl<Message>( - mDatabase, "feedmessages", Message.class); - - mTrainingRecordProvider = new DelegateProvider<String, DynamoKey, TrainingRecord>( - new DynamoProviderImpl<TrainingRecord>(mDatabase, TrainingRecord.class)) { - @Override - public DynamoKey makeKey(String userId) { - return DynamoKey.newAttributeKey("training", - userId, - DEFAULT_COLUMN); - } - }; - - mVideoProvider = new DelegateCollectionProvider<String, String, String, String, String>( - new DynamoCollectionProviderImpl<String>(mDatabase, "strings", String.class)) { - @Override - public String makeCollectionKey(String key) { - return "/training/" + key; - } - - @Override - public String makeKey(String key) { - return key; - } - - @Override - public String unmakeKey(String key) { - return key; - } - }; - - mStringProvider = new DelegateProvider<String, DynamoKey, String>( - new DynamoProviderImpl<String>(mDatabase, String.class)) { - @Override - public DynamoKey makeKey(String id) { - return DynamoKey.newAttributeKey("strings", id, DEFAULT_COLUMN); - } - }; - - mAnswerProvider = new DynamoCollectionProviderImpl<String>( - mDatabase, "assessments", String.class); - } - - @Override - public void start() throws Exception { - } - - @Override - public void stop() throws Exception { - } - - @Override - public Provider<String, UserRecord> getUserRecordProvider() { - return mUserRecordProvider; - } - - @Override - public Provider<String, Question> getQuestionProvider() { - return mQuestionProvider; - } - - @Override - public Provider<String, TrainingRecord> getTrainingRecordProvider() { - return mTrainingRecordProvider; - } - - @Override - public CollectionProvider<String, String, String> getVideoProvider() { - return mVideoProvider; - } - - @Override - public Playlist getDefaultPlaylist() throws IOException { - String blob = mStringProvider.get(DEFAULT_PLAYLIST_KEY); - if (blob == null) { - return null; - } - - return JsonEncodedProvider.MAPPER.readValue(blob, Playlist.class); - } - - @Override - public CollectionProvider<String, String, MessageThread> getThreadProvider() { - return mFeedThreadProvider; - } - - @Override - public CollectionProvider<String, String, Message> getMessageProvider() { - return mFeedMessageProvider; - } - - @Override - public Provider<String, String> getStringProvider() { - return mStringProvider; - } - - @Override - public CollectionProvider<String, String, String> getAnswerProvider() { - return mAnswerProvider; - } -} diff --git a/src/com/p4square/grow/backend/GrowBackend.java b/src/com/p4square/grow/backend/GrowBackend.java deleted file mode 100644 index 4091138..0000000 --- a/src/com/p4square/grow/backend/GrowBackend.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright 2012 Jesse Morgan - */ - -package com.p4square.grow.backend; - -import java.io.IOException; - -import com.codahale.metrics.MetricRegistry; - -import org.apache.log4j.Logger; - -import org.restlet.Application; -import org.restlet.Component; -import org.restlet.Restlet; -import org.restlet.data.Protocol; -import org.restlet.data.Reference; -import org.restlet.resource.Directory; -import org.restlet.routing.Router; - -import com.p4square.grow.config.Config; - -import com.p4square.grow.model.Message; -import com.p4square.grow.model.MessageThread; -import com.p4square.grow.model.Playlist; -import com.p4square.grow.model.Question; -import com.p4square.grow.model.TrainingRecord; -import com.p4square.grow.model.UserRecord; - -import com.p4square.grow.provider.CollectionProvider; -import com.p4square.grow.provider.Provider; -import com.p4square.grow.provider.ProvidesQuestions; -import com.p4square.grow.provider.ProvidesTrainingRecords; -import com.p4square.grow.provider.ProvidesUserRecords; - -import com.p4square.grow.backend.resources.AccountResource; -import com.p4square.grow.backend.resources.BannerResource; -import com.p4square.grow.backend.resources.SurveyResource; -import com.p4square.grow.backend.resources.SurveyResultsResource; -import com.p4square.grow.backend.resources.TrainingRecordResource; -import com.p4square.grow.backend.resources.TrainingResource; - -import com.p4square.grow.backend.feed.FeedDataProvider; -import com.p4square.grow.backend.feed.ThreadResource; -import com.p4square.grow.backend.feed.TopicResource; - -import com.p4square.restlet.metrics.MetricRouter; - -/** - * Main class for the backend application. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class GrowBackend extends Application implements GrowData { - - private final static Logger LOG = Logger.getLogger(GrowBackend.class); - - private final MetricRegistry mMetricRegistry; - - private final Config mConfig; - private final GrowData mGrowData; - - public GrowBackend() { - this(new Config(), new MetricRegistry()); - } - - public GrowBackend(Config config, MetricRegistry metricRegistry) { - mConfig = config; - - mMetricRegistry = metricRegistry; - - mGrowData = new DynamoGrowData(config); - } - - public MetricRegistry getMetrics() { - return mMetricRegistry; - } - - @Override - public Restlet createInboundRoot() { - Router router = new MetricRouter(getContext(), mMetricRegistry); - - // Account API - router.attach("/accounts/{userId}", AccountResource.class); - - // Survey API - router.attach("/assessment/question/{questionId}", SurveyResource.class); - - router.attach("/accounts/{userId}/assessment", SurveyResultsResource.class); - router.attach("/accounts/{userId}/assessment/answers/{questionId}", - SurveyResultsResource.class); - - // Training API - router.attach("/training/{level}", TrainingResource.class); - router.attach("/training/{level}/videos/{videoId}", TrainingResource.class); - - router.attach("/accounts/{userId}/training", TrainingRecordResource.class); - router.attach("/accounts/{userId}/training/videos/{videoId}", - TrainingRecordResource.class); - - // Misc. - router.attach("/banner", BannerResource.class); - - // Feed - router.attach("/feed/{topic}", TopicResource.class); - router.attach("/feed/{topic}/{thread}", ThreadResource.class); - //router.attach("/feed/{topic/{thread}/{message}", MessageResource.class); - - router.attachDefault(new Directory(getContext(), new Reference(getClass().getResource("apiinfo.html")))); - - return router; - } - - /** - * Open the database. - */ - @Override - public void start() throws Exception { - super.start(); - - mGrowData.start(); - } - - /** - * Close the database. - */ - @Override - public void stop() throws Exception { - LOG.info("Shutting down..."); - mGrowData.stop(); - - super.stop(); - } - - @Override - public Provider<String, UserRecord> getUserRecordProvider() { - return mGrowData.getUserRecordProvider(); - } - - @Override - public Provider<String, Question> getQuestionProvider() { - return mGrowData.getQuestionProvider(); - } - - @Override - public CollectionProvider<String, String, String> getVideoProvider() { - return mGrowData.getVideoProvider(); - } - - @Override - public Provider<String, TrainingRecord> getTrainingRecordProvider() { - return mGrowData.getTrainingRecordProvider(); - } - - /** - * @return the Default Playlist. - */ - public Playlist getDefaultPlaylist() throws IOException { - return mGrowData.getDefaultPlaylist(); - } - - @Override - public CollectionProvider<String, String, MessageThread> getThreadProvider() { - return mGrowData.getThreadProvider(); - } - - @Override - public CollectionProvider<String, String, Message> getMessageProvider() { - return mGrowData.getMessageProvider(); - } - - @Override - public Provider<String, String> getStringProvider() { - return mGrowData.getStringProvider(); - } - - @Override - public CollectionProvider<String, String, String> getAnswerProvider() { - return mGrowData.getAnswerProvider(); - } - - /** - * Stand-alone main for testing. - */ - public static void main(String[] args) throws Exception { - // Start the HTTP Server - final Component component = new Component(); - component.getServers().add(Protocol.HTTP, 9095); - component.getClients().add(Protocol.HTTP); - component.getDefaultHost().attach(new GrowBackend()); - - // Setup shutdown hook - Runtime.getRuntime().addShutdownHook(new Thread() { - public void run() { - try { - component.stop(); - } catch (Exception e) { - LOG.error("Exception during cleanup", e); - } - } - }); - - LOG.info("Starting server..."); - - try { - component.start(); - } catch (Exception e) { - LOG.fatal("Could not start: " + e.getMessage(), e); - } - } -} diff --git a/src/com/p4square/grow/backend/GrowData.java b/src/com/p4square/grow/backend/GrowData.java deleted file mode 100644 index 293bb88..0000000 --- a/src/com/p4square/grow/backend/GrowData.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.backend; - -import com.p4square.grow.backend.feed.FeedDataProvider; -import com.p4square.grow.model.Playlist; -import com.p4square.grow.provider.ProvidesAssessments; -import com.p4square.grow.provider.ProvidesQuestions; -import com.p4square.grow.provider.ProvidesStrings; -import com.p4square.grow.provider.ProvidesTrainingRecords; -import com.p4square.grow.provider.ProvidesUserRecords; -import com.p4square.grow.provider.ProvidesVideos; - -/** - * Aggregate of the data provider interfaces. - * - * Used by GrowBackend to swap out implementations of the providers. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -interface GrowData extends ProvidesQuestions, ProvidesTrainingRecords, ProvidesVideos, - FeedDataProvider, ProvidesUserRecords, ProvidesStrings, - ProvidesAssessments { - - /** - * Start the data provider. - */ - void start() throws Exception; - - /** - * Stop the data provider. - */ - void stop() throws Exception; -} diff --git a/src/com/p4square/grow/backend/apiinfo.html b/src/com/p4square/grow/backend/apiinfo.html deleted file mode 100644 index a3637c9..0000000 --- a/src/com/p4square/grow/backend/apiinfo.html +++ /dev/null @@ -1,41 +0,0 @@ -<html> -<head> -<title>API Info</title> -</head> -<body> -<dl> -<dt>/backend/accounts/{userId}</dt> -<dd>GET information about <em>userId</em> or PUT new information.</dd> - -<dt>/backend/assessment/question/{questionId}</dt> -<dd>GET information about <em>questionId</em>. Special <em>questionId</em>s: first identifies first question. count returns total number of questions.</dd> - -<dt>/backend/accounts/{userId}/assessment</dt> -<dd>GET the assessment summary for <em>userId</em> or DELETE <em>userId</em>'s assessment.</dd> - -<dt>/backend/accounts/{userId}/assessment/answers/{questionId}</dt> -<dd>GET <em>userId</em>'s answer to <em>questionId</em>, PUT a new answer, or DELETE an answer.</dd> - -<dt>/backend/training/{level}</dt> -<dd>GET all video information for <em>level</em>.</dd> - -<dt>/backend/training/{level}/videos/{videoId}</dt> -<dd>GET video information for <em>videoId</em> in <em>level</em>.</dd> - -<dt>/backend/accounts/{userId}/training</dt> -<dd>GET training record summary for <em>userId</em>.</dd> - -<dt>/backend/accounts/{userId}/training/videos/{videoId}</dt> -<dd>GET training record for <em>userId</em> and <em>videoId</em> or PUT a new record.</dd> - -<dt>/backend/banner</dt> -<dd>GET the info banner or PUT new banner info.</dd> - -<dt>/backend/feed/{topic}</dt> -<dd>Get all threads for forum <em>topic</em>.</dd> - -<dt>/backend/feed/{topic}/{thread}</dt> -<dd>Get all responses to question <em>thread</em> on forum <em>topic</em>.</dd> -</dl> -</body> -</html> diff --git a/src/com/p4square/grow/backend/db/CassandraCollectionProvider.java b/src/com/p4square/grow/backend/db/CassandraCollectionProvider.java deleted file mode 100644 index bfcb48d..0000000 --- a/src/com/p4square/grow/backend/db/CassandraCollectionProvider.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.backend.db; - -import java.io.IOException; - -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; - -import com.netflix.astyanax.model.Column; -import com.netflix.astyanax.model.ColumnList; - -import com.p4square.grow.provider.CollectionProvider; -import com.p4square.grow.provider.JsonEncodedProvider; - -/** - * CollectionProvider implementation backed by a Cassandra ColumnFamily. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class CassandraCollectionProvider<V> implements CollectionProvider<String, String, V> { - private final CassandraDatabase mDb; - private final String mCF; - private final Class<V> mClazz; - - public CassandraCollectionProvider(CassandraDatabase db, String columnFamily, Class<V> clazz) { - mDb = db; - mCF = columnFamily; - mClazz = clazz; - } - - @Override - public V get(String collection, String key) throws IOException { - String blob = mDb.getKey(mCF, collection, key); - return decode(blob); - } - - @Override - public Map<String, V> query(String collection) throws IOException { - return query(collection, -1); - } - - @Override - public Map<String, V> query(String collection, int limit) throws IOException { - Map<String, V> result = new LinkedHashMap<>(); - - ColumnList<String> row = mDb.getRow(mCF, collection); - if (!row.isEmpty()) { - int count = 0; - for (Column<String> c : row) { - if (limit >= 0 && ++count > limit) { - break; // Limit reached. - } - - String key = c.getName(); - String blob = c.getStringValue(); - V obj = decode(blob); - - result.put(key, obj); - } - } - - return Collections.unmodifiableMap(result); - } - - @Override - public void put(String collection, String key, V obj) throws IOException { - String blob = encode(obj); - mDb.putKey(mCF, collection, key, blob); - } - - /** - * Encode the object as JSON. - * - * @param obj The object to encode. - * @return The JSON encoding of obj. - * @throws IOException if the object cannot be encoded. - */ - protected String encode(V obj) throws IOException { - if (mClazz == String.class) { - return (String) obj; - } else { - return JsonEncodedProvider.MAPPER.writeValueAsString(obj); - } - } - - /** - * Decode the JSON string as an object. - * - * @param blob The JSON data to decode. - * @return The decoded object or null if blob is null. - * @throws IOException If an object cannot be decoded. - */ - protected V decode(String blob) throws IOException { - if (blob == null) { - return null; - } - - if (mClazz == String.class) { - return (V) blob; - } - - V obj = JsonEncodedProvider.MAPPER.readValue(blob, mClazz); - return obj; - } -} diff --git a/src/com/p4square/grow/backend/db/CassandraDatabase.java b/src/com/p4square/grow/backend/db/CassandraDatabase.java deleted file mode 100644 index b8cb6df..0000000 --- a/src/com/p4square/grow/backend/db/CassandraDatabase.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.backend.db; - -import com.netflix.astyanax.AstyanaxContext; -import com.netflix.astyanax.connectionpool.exceptions.ConnectionException; -import com.netflix.astyanax.connectionpool.impl.ConnectionPoolConfigurationImpl; -import com.netflix.astyanax.connectionpool.impl.CountingConnectionPoolMonitor; -import com.netflix.astyanax.connectionpool.NodeDiscoveryType; -import com.netflix.astyanax.connectionpool.OperationResult; -import com.netflix.astyanax.impl.AstyanaxConfigurationImpl; -import com.netflix.astyanax.Keyspace; -import com.netflix.astyanax.ColumnMutation; -import com.netflix.astyanax.model.Column; -import com.netflix.astyanax.model.ColumnFamily; -import com.netflix.astyanax.model.ColumnList; -import com.netflix.astyanax.ColumnListMutation; -import com.netflix.astyanax.MutationBatch; -import com.netflix.astyanax.serializers.StringSerializer; -import com.netflix.astyanax.thrift.ThriftFamilyFactory; - -import org.apache.log4j.Logger; - -/** - * Cassandra Database Abstraction for the Backend. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class CassandraDatabase { - private static Logger cLog = Logger.getLogger(CassandraDatabase.class); - - // Configuration fields. - private String mClusterName; - private String mKeyspaceName; - private String mSeedEndpoint = "127.0.0.1:9160"; - private int mPort = 9160; - - private AstyanaxContext<Keyspace> mContext; - private Keyspace mKeyspace; - - /** - * Connect to Cassandra. - * - * Cluster and Keyspace must be set before calling init(). - */ - public void init() { - mContext = new AstyanaxContext.Builder() - .forCluster(mClusterName) - .forKeyspace(mKeyspaceName) - .withAstyanaxConfiguration(new AstyanaxConfigurationImpl() - .setDiscoveryType(NodeDiscoveryType.RING_DESCRIBE) - ) - .withConnectionPoolConfiguration(new ConnectionPoolConfigurationImpl("MyConnectionPool") - .setPort(mPort) - .setMaxConnsPerHost(1) - .setSeeds(mSeedEndpoint) - ) - .withConnectionPoolMonitor(new CountingConnectionPoolMonitor()) - .buildKeyspace(ThriftFamilyFactory.getInstance()); - - mContext.start(); - mKeyspace = mContext.getClient(); - } - - /** - * Close the database connection. - */ - public void close() { - mContext.shutdown(); - } - - /** - * Set the cluster name to connect to. - */ - public void setClusterName(final String cluster) { - mClusterName = cluster; - } - - /** - * Set the name of the keyspace to open. - */ - public void setKeyspaceName(final String keyspace) { - mKeyspaceName = keyspace; - } - - /** - * Change the seed endpoint. - * The default is 127.0.0.1:9160. - */ - public void setSeedEndpoint(final String endpoint) { - mSeedEndpoint = endpoint; - } - - /** - * Change the port to connect to. - * The default is 9160. - */ - public void setPort(final int port) { - mPort = port; - } - - /** - * @return The entire row associated with this key. - */ - public ColumnList<String> getRow(final String cfName, final String key) { - try { - ColumnFamily<String, String> cf = new ColumnFamily(cfName, - StringSerializer.get(), - StringSerializer.get()); - - OperationResult<ColumnList<String>> result = - mKeyspace.prepareQuery(cf) - .getKey(key) - .execute(); - - return result.getResult(); - - } catch (ConnectionException e) { - cLog.error("getRow failed due to Connection Exception", e); - throw new RuntimeException(e); - } - } - - /** - * @return The value associated with the given key. - */ - public String getKey(final String cfName, final String key) { - return getKey(cfName, key, "value"); - } - - /** - * @return The value associated with the given key, column pair. - */ - public String getKey(final String cfName, final String key, final String column) { - final ColumnList<String> row = getRow(cfName, key); - - if (row != null) { - final Column rowColumn = row.getColumnByName(column); - if (rowColumn != null) { - return rowColumn.getStringValue(); - } - } - - return null; - } - - /** - * Assign value to key. - */ - public void putKey(final String cfName, final String key, final String value) { - putKey(cfName, key, "value", value); - } - - /** - * Assign value to the key, column pair. - */ - public void putKey(final String cfName, final String key, - final String column, final String value) { - - ColumnFamily<String, String> cf = new ColumnFamily(cfName, - StringSerializer.get(), - StringSerializer.get()); - - MutationBatch m = mKeyspace.prepareMutationBatch(); - m.withRow(cf, key).putColumn(column, value); - - try { - m.execute(); - } catch (ConnectionException e) { - cLog.error("putKey failed due to Connection Exception", e); - throw new RuntimeException(e); - } - } - - /** - * Remove a key, column pair. - */ - public void deleteKey(final String cfName, final String key, final String column) { - ColumnFamily<String, String> cf = new ColumnFamily(cfName, - StringSerializer.get(), - StringSerializer.get()); - - try { - ColumnMutation m = mKeyspace.prepareColumnMutation(cf, key, column); - m.deleteColumn().execute(); - } catch (ConnectionException e) { - cLog.error("deleteKey failed due to Connection Exception", e); - throw new RuntimeException(e); - } - } - - /** - * Remove a row - */ - public void deleteRow(final String cfName, final String key) { - ColumnFamily<String, String> cf = new ColumnFamily(cfName, - StringSerializer.get(), - StringSerializer.get()); - - try { - MutationBatch batch = mKeyspace.prepareMutationBatch(); - ColumnListMutation<String> cfm = batch.withRow(cf, key).delete(); - batch.execute(); - - } catch (ConnectionException e) { - cLog.error("deleteRow failed due to Connection Exception", e); - throw new RuntimeException(e); - } - } -} diff --git a/src/com/p4square/grow/backend/db/CassandraKey.java b/src/com/p4square/grow/backend/db/CassandraKey.java deleted file mode 100644 index 853fe96..0000000 --- a/src/com/p4square/grow/backend/db/CassandraKey.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.backend.db; - -/** - * CassandraKey represents a Cassandra key / column pair. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class CassandraKey { - private final String mColumnFamily; - private final String mId; - private final String mColumn; - - public CassandraKey(String columnFamily, String id, String column) { - mColumnFamily = columnFamily; - mId = id; - mColumn = column; - } - - public String getColumnFamily() { - return mColumnFamily; - } - - public String getId() { - return mId; - } - - public String getColumn() { - return mColumn; - } -} diff --git a/src/com/p4square/grow/backend/db/CassandraProviderImpl.java b/src/com/p4square/grow/backend/db/CassandraProviderImpl.java deleted file mode 100644 index da5a9f2..0000000 --- a/src/com/p4square/grow/backend/db/CassandraProviderImpl.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.backend.db; - -import java.io.IOException; - -import com.p4square.grow.provider.Provider; -import com.p4square.grow.provider.JsonEncodedProvider; - -/** - * Provider implementation backed by a Cassandra ColumnFamily. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class CassandraProviderImpl<V> extends JsonEncodedProvider<V> implements Provider<CassandraKey, V> { - private final CassandraDatabase mDb; - - public CassandraProviderImpl(CassandraDatabase db, Class<V> clazz) { - super(clazz); - - mDb = db; - } - - @Override - public V get(CassandraKey key) throws IOException { - String blob = mDb.getKey(key.getColumnFamily(), key.getId(), key.getColumn()); - return decode(blob); - } - - @Override - public void put(CassandraKey key, V obj) throws IOException { - String blob = encode(obj); - mDb.putKey(key.getColumnFamily(), key.getId(), key.getColumn(), blob); - } -} diff --git a/src/com/p4square/grow/backend/db/CassandraTrainingRecordProvider.java b/src/com/p4square/grow/backend/db/CassandraTrainingRecordProvider.java deleted file mode 100644 index 4face52..0000000 --- a/src/com/p4square/grow/backend/db/CassandraTrainingRecordProvider.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.backend.db; - -import java.io.IOException; - -import com.p4square.grow.model.Playlist; -import com.p4square.grow.model.TrainingRecord; - -import com.p4square.grow.provider.JsonEncodedProvider; -import com.p4square.grow.provider.Provider; - -/** - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class CassandraTrainingRecordProvider implements Provider<String, TrainingRecord> { - private static final CassandraKey DEFAULT_PLAYLIST_KEY = new CassandraKey("strings", "defaultPlaylist", "value"); - - private static final String COLUMN_FAMILY = "training"; - private static final String PLAYLIST_KEY = "playlist"; - private static final String LAST_VIDEO_KEY = "lastVideo"; - - private final CassandraDatabase mDb; - private final Provider<CassandraKey, Playlist> mPlaylistProvider; - - public CassandraTrainingRecordProvider(CassandraDatabase db) { - mDb = db; - mPlaylistProvider = new CassandraProviderImpl<>(db, Playlist.class); - } - - @Override - public TrainingRecord get(String userid) throws IOException { - Playlist playlist = mPlaylistProvider.get(new CassandraKey(COLUMN_FAMILY, userid, PLAYLIST_KEY)); - - if (playlist == null) { - // We consider no playlist to mean no record whatsoever. - return null; - } - - TrainingRecord r = new TrainingRecord(); - r.setPlaylist(playlist); - r.setLastVideo(mDb.getKey(COLUMN_FAMILY, userid, LAST_VIDEO_KEY)); - - return r; - } - - @Override - public void put(String userid, TrainingRecord record) throws IOException { - String lastVideo = record.getLastVideo(); - Playlist playlist = record.getPlaylist(); - - mDb.putKey(COLUMN_FAMILY, userid, LAST_VIDEO_KEY, lastVideo); - mPlaylistProvider.put(new CassandraKey(COLUMN_FAMILY, userid, PLAYLIST_KEY), playlist); - } - - /** - * @return the default playlist stored in the database. - */ - public Playlist getDefaultPlaylist() throws IOException { - Playlist playlist = mPlaylistProvider.get(DEFAULT_PLAYLIST_KEY); - - if (playlist == null) { - playlist = new Playlist(); - } - - return playlist; - } -} diff --git a/src/com/p4square/grow/backend/dynamo/DbTool.java b/src/com/p4square/grow/backend/dynamo/DbTool.java deleted file mode 100644 index 374fa83..0000000 --- a/src/com/p4square/grow/backend/dynamo/DbTool.java +++ /dev/null @@ -1,481 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.backend.dynamo; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.io.File; -import java.io.FilenameFilter; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; - -import com.p4square.grow.backend.dynamo.DynamoDatabase; -import com.p4square.grow.backend.dynamo.DynamoKey; -import com.p4square.grow.config.Config; -import com.p4square.grow.model.UserRecord; -import com.p4square.grow.provider.Provider; - -/** - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class DbTool { - private static final FilenameFilter JSON_FILTER = new JsonFilter(); - - private static Config mConfig; - private static DynamoDatabase mDatabase; - - public static void usage() { - System.out.println("java com.p4square.grow.backend.dynamo.DbTool <command>...\n"); - System.out.println("Commands:"); - System.out.println("\t--domain <domain> Set config domain"); - System.out.println("\t--dev Set config domain to dev"); - System.out.println("\t--config <file> Merge in config file"); - System.out.println("\t--list List all tables"); - System.out.println("\t--create <table> <reads> <writes> Create a table"); - System.out.println("\t--update <table> <reads> <writes> Update table throughput"); - System.out.println("\t--drop <table> Delete a table"); - System.out.println("\t--get <table> <key> <attribute> Get a value"); - System.out.println("\t--put <table> <key> <attribute> <value> Put a value"); - System.out.println("\t--delete <table> <key> <attribute> Delete a value"); - System.out.println("\t--scan <table> List all rows"); - System.out.println("\t--scanf <table> <attribute> List all rows"); - System.out.println(); - System.out.println("Bootstrap Commands:"); - System.out.println("\t--bootstrap <data> Create all tables and import all data"); - System.out.println("\t--loadStrings <data> Load all videos and questions"); - System.out.println("\t--destroy Drop all tables"); - System.out.println("\t--addadmin <user> <pass> Add a backend account"); - System.out.println("\t--import <table> <file> Backfill a table"); - } - - public static void main(String... args) { - if (args.length == 0) { - usage(); - System.exit(1); - } - - mConfig = new Config(); - - try { - mConfig.updateConfig(DbTool.class.getResourceAsStream("/grow.properties")); - - int offset = 0; - while (offset < args.length) { - if ("--domain".equals(args[offset])) { - mConfig.setDomain(args[offset + 1]); - mDatabase = null; - offset += 2; - - } else if ("--dev".equals(args[offset])) { - mConfig.setDomain("dev"); - mDatabase = null; - offset += 1; - - } else if ("--config".equals(args[offset])) { - mConfig.updateConfig(args[offset + 1]); - mDatabase = null; - offset += 2; - - } else if ("--list".equals(args[offset])) { - //offset = list(args, ++offset); - - } else if ("--create".equals(args[offset])) { - offset = create(args, ++offset); - - } else if ("--update".equals(args[offset])) { - offset = update(args, ++offset); - - } else if ("--drop".equals(args[offset])) { - offset = drop(args, ++offset); - - } else if ("--get".equals(args[offset])) { - offset = get(args, ++offset); - - } else if ("--put".equals(args[offset])) { - offset = put(args, ++offset); - - } else if ("--delete".equals(args[offset])) { - offset = delete(args, ++offset); - - } else if ("--scan".equals(args[offset])) { - offset = scan(args, ++offset); - - } else if ("--scanf".equals(args[offset])) { - offset = scanf(args, ++offset); - - /* Bootstrap Commands */ - } else if ("--bootstrap".equals(args[offset])) { - if ("dev".equals(mConfig.getDomain())) { - offset = bootstrapDevTables(args, ++offset); - } else { - offset = bootstrapTables(args, ++offset); - } - offset = loadStrings(args, offset); - - } else if ("--loadStrings".equals(args[offset])) { - offset = loadStrings(args, ++offset); - - } else if ("--destroy".equals(args[offset])) { - offset = destroy(args, ++offset); - - } else if ("--addadmin".equals(args[offset])) { - offset = addAdmin(args, ++offset); - - } else if ("--import".equals(args[offset])) { - offset = importTable(args, ++offset); - - } else { - throw new IllegalArgumentException("Unknown command " + args[offset]); - } - } - } catch (Exception e) { - e.printStackTrace(); - System.exit(2); - } - } - - private static DynamoDatabase getDatabase() { - if (mDatabase == null) { - mDatabase = new DynamoDatabase(mConfig); - } - - return mDatabase; - } - - private static int create(String[] args, int offset) { - String name = args[offset++]; - long reads = Long.parseLong(args[offset++]); - long writes = Long.parseLong(args[offset++]); - - DynamoDatabase db = getDatabase(); - - db.createTable(name, reads, writes); - - return offset; - } - - private static int update(String[] args, int offset) { - String name = args[offset++]; - long reads = Long.parseLong(args[offset++]); - long writes = Long.parseLong(args[offset++]); - - DynamoDatabase db = getDatabase(); - - db.updateTable(name, reads, writes); - - return offset; - } - - private static int drop(String[] args, int offset) { - String name = args[offset++]; - - DynamoDatabase db = getDatabase(); - - db.deleteTable(name); - - return offset; - } - - private static int get(String[] args, int offset) { - String table = args[offset++]; - String key = args[offset++]; - String attribute = args[offset++]; - - DynamoDatabase db = getDatabase(); - - String value = db.getAttribute(DynamoKey.newAttributeKey(table, key, attribute)); - - if (value == null) { - value = "<null>"; - } - - System.out.printf("%s %s:%s\n%s\n\n", table, key, attribute, value); - - return offset; - } - - private static int put(String[] args, int offset) { - String table = args[offset++]; - String key = args[offset++]; - String attribute = args[offset++]; - String value = args[offset++]; - - DynamoDatabase db = getDatabase(); - - db.putAttribute(DynamoKey.newAttributeKey(table, key, attribute), value); - - return offset; - } - - private static int delete(String[] args, int offset) { - String table = args[offset++]; - String key = args[offset++]; - String attribute = args[offset++]; - - DynamoDatabase db = getDatabase(); - - db.deleteAttribute(DynamoKey.newAttributeKey(table, key, attribute)); - - System.out.printf("Deleted %s %s:%s\n\n", table, key, attribute); - - return offset; - } - - private static int scan(String[] args, int offset) { - String table = args[offset++]; - - DynamoKey key = DynamoKey.newKey(table, null); - - doScan(key); - - return offset; - } - - private static int scanf(String[] args, int offset) { - String table = args[offset++]; - String attribute = args[offset++]; - - DynamoKey key = DynamoKey.newAttributeKey(table, null, attribute); - - doScan(key); - - return offset; - } - - private static void doScan(DynamoKey key) { - DynamoDatabase db = getDatabase(); - - String attributeFilter = key.getAttribute(); - - while (key != null) { - Map<DynamoKey, Map<String, String>> result = db.getAll(key); - - key = null; // If there are no results, exit - - for (Map.Entry<DynamoKey, Map<String, String>> entry : result.entrySet()) { - key = entry.getKey(); // Save the last key - - for (Map.Entry<String, String> attribute : entry.getValue().entrySet()) { - if (attributeFilter == null || attributeFilter.equals(attribute.getKey())) { - String keyString = key.getHashKey(); - if (key.getRangeKey() != null) { - keyString += "(" + key.getRangeKey() + ")"; - } - System.out.printf("%s %s:%s\n%s\n\n", - key.getTable(), keyString, attribute.getKey(), - attribute.getValue()); - } - } - } - } - } - - - private static int bootstrapTables(String[] args, int offset) { - DynamoDatabase db = getDatabase(); - - db.createTable("strings", 5, 1); - db.createTable("accounts", 5, 1); - db.createTable("assessments", 5, 5); - db.createTable("training", 5, 5); - db.createTable("feedthreads", 5, 1); - db.createTable("feedmessages", 5, 1); - - return offset; - } - - private static int bootstrapDevTables(String[] args, int offset) { - DynamoDatabase db = getDatabase(); - - db.createTable("strings", 1, 1); - db.createTable("accounts", 1, 1); - db.createTable("assessments", 1, 1); - db.createTable("training", 1, 1); - db.createTable("feedthreads", 1, 1); - db.createTable("feedmessages", 1, 1); - - return offset; - } - - private static int loadStrings(String[] args, int offset) throws IOException { - String data = args[offset++]; - File baseDir = new File(data); - - DynamoDatabase db = getDatabase(); - - insertQuestions(baseDir); - insertVideos(baseDir); - insertDefaultPlaylist(baseDir); - - return offset; - } - - private static int destroy(String[] args, int offset) { - DynamoDatabase db = getDatabase(); - - final String[] tables = { "strings", - "accounts", - "assessments", - "training", - "feedthreads", - "feedmessages" - }; - - for (String table : tables) { - try { - db.deleteTable(table); - } catch (Exception e) { - System.err.println("Deleting " + table + ": " + e.getMessage()); - } - } - - return offset; - } - - private static int addAdmin(String[] args, int offset) throws IOException { - String user = args[offset++]; - String pass = args[offset++]; - - DynamoDatabase db = getDatabase(); - - UserRecord record = new UserRecord(); - record.setId(user); - record.setBackendPassword(pass); - - Provider<DynamoKey, UserRecord> provider = new DynamoProviderImpl(db, UserRecord.class); - provider.put(DynamoKey.newAttributeKey("accounts", user, "value"), record); - - return offset; - } - - private static int importTable(String[] args, int offset) throws IOException { - String table = args[offset++]; - String filename = args[offset++]; - - DynamoDatabase db = getDatabase(); - - List<String> lines = Files.readAllLines(new File(filename).toPath(), - StandardCharsets.UTF_8); - - int count = 0; - - String key = null; - Map<String, String> attributes = new HashMap<>(); - for (String line : lines) { - if (line.length() == 0) { - if (attributes.size() > 0) { - db.putKey(DynamoKey.newKey(table, key), attributes); - count++; - - if (count % 50 == 0) { - System.out.printf("Imported %d records into %s...\n", count, table); - } - } - key = null; - attributes = new HashMap<>(); - continue; - } - - if (key == null) { - key = line; - continue; - } - - int space = line.indexOf(' '); - String attribute = line.substring(0, space); - String value = line.substring(space + 1); - - attributes.put(attribute, value); - } - - // Finish up the remaining attributes. - if (key != null && attributes.size() > 0) { - db.putKey(DynamoKey.newKey(table, key), attributes); - count++; - } - - System.out.printf("Imported %d records into %s.\n", count, table); - - return offset; - } - - private static void insertQuestions(File baseDir) throws IOException { - DynamoDatabase db = getDatabase(); - File questions = new File(baseDir, "questions"); - - File[] files = questions.listFiles(JSON_FILTER); - Arrays.sort(files); - - for (File file : files) { - String filename = file.getName(); - String questionId = filename.substring(0, filename.lastIndexOf('.')); - - byte[] encoded = Files.readAllBytes(file.toPath()); - String value = new String(encoded, StandardCharsets.UTF_8); - db.putAttribute(DynamoKey.newAttributeKey("strings", - "/questions/" + questionId, "value"), value); - System.out.println("Inserted /questions/" + questionId); - } - - String filename = files[0].getName(); - String first = filename.substring(0, filename.lastIndexOf('.')); - int count = files.length; - String summary = "{\"first\": \"" + first + "\", \"count\": " + count + "}"; - db.putAttribute(DynamoKey.newAttributeKey("strings", "/questions", "value"), summary); - System.out.println("Inserted /questions"); - } - - private static void insertVideos(File baseDir) throws IOException { - DynamoDatabase db = getDatabase(); - File videos = new File(baseDir, "videos"); - - for (File topic : videos.listFiles()) { - if (!topic.isDirectory()) { - continue; - } - - String topicName = topic.getName(); - - Map<String, String> attributes = new HashMap<>(); - File[] files = topic.listFiles(JSON_FILTER); - for (File file : files) { - String filename = file.getName(); - String videoId = filename.substring(0, filename.lastIndexOf('.')); - - byte[] encoded = Files.readAllBytes(file.toPath()); - String value = new String(encoded, StandardCharsets.UTF_8); - - attributes.put(videoId, value); - System.out.println("Found /training/" + topicName + ":" + videoId); - } - - db.putKey(DynamoKey.newKey("strings", - "/training/" + topicName), attributes); - System.out.println("Inserted /training/" + topicName); - } - } - - private static void insertDefaultPlaylist(File baseDir) throws IOException { - DynamoDatabase db = getDatabase(); - File file = new File(baseDir, "videos/playlist.json"); - - byte[] encoded = Files.readAllBytes(file.toPath()); - String value = new String(encoded, StandardCharsets.UTF_8); - db.putAttribute(DynamoKey.newAttributeKey("strings", - "/training/defaultplaylist", "value"), value); - System.out.println("Inserted /training/defaultplaylist"); - } - - private static class JsonFilter implements FilenameFilter { - @Override - public boolean accept(File dir, String name) { - return name.endsWith(".json"); - } - } -} diff --git a/src/com/p4square/grow/backend/dynamo/DynamoCollectionProviderImpl.java b/src/com/p4square/grow/backend/dynamo/DynamoCollectionProviderImpl.java deleted file mode 100644 index b53e9f7..0000000 --- a/src/com/p4square/grow/backend/dynamo/DynamoCollectionProviderImpl.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.backend.dynamo; - -import java.io.IOException; - -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; - -import com.p4square.grow.provider.CollectionProvider; -import com.p4square.grow.provider.JsonEncodedProvider; - -/** - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class DynamoCollectionProviderImpl<V> implements CollectionProvider<String, String, V> { - private final DynamoDatabase mDb; - private final String mTable; - private final Class<V> mClazz; - - public DynamoCollectionProviderImpl(DynamoDatabase db, String table, Class<V> clazz) { - mDb = db; - mTable = table; - mClazz = clazz; - } - - @Override - public V get(String collection, String key) throws IOException { - String blob = mDb.getAttribute(DynamoKey.newAttributeKey(mTable, collection, key)); - return decode(blob); - } - - @Override - public Map<String, V> query(String collection) throws IOException { - return query(collection, -1); - } - - @Override - public Map<String, V> query(String collection, int limit) throws IOException { - Map<String, V> result = new LinkedHashMap<>(); - - Map<String, String> row = mDb.getKey(DynamoKey.newKey(mTable, collection)); - if (row.size() > 0) { - int count = 0; - for (Map.Entry<String, String> c : row.entrySet()) { - if (limit >= 0 && ++count > limit) { - break; // Limit reached. - } - - String key = c.getKey(); - String blob = c.getValue(); - V obj = decode(blob); - - result.put(key, obj); - } - } - - return Collections.unmodifiableMap(result); - } - - @Override - public void put(String collection, String key, V obj) throws IOException { - if (obj == null) { - mDb.deleteAttribute(DynamoKey.newAttributeKey(mTable, collection, key)); - } else { - String blob = encode(obj); - mDb.putAttribute(DynamoKey.newAttributeKey(mTable, collection, key), blob); - } - } - - /** - * Encode the object as JSON. - * - * @param obj The object to encode. - * @return The JSON encoding of obj. - * @throws IOException if the object cannot be encoded. - */ - protected String encode(V obj) throws IOException { - if (mClazz == String.class) { - return (String) obj; - } else { - return JsonEncodedProvider.MAPPER.writeValueAsString(obj); - } - } - - /** - * Decode the JSON string as an object. - * - * @param blob The JSON data to decode. - * @return The decoded object or null if blob is null. - * @throws IOException If an object cannot be decoded. - */ - protected V decode(String blob) throws IOException { - if (blob == null) { - return null; - } - - if (mClazz == String.class) { - return (V) blob; - } - - V obj = JsonEncodedProvider.MAPPER.readValue(blob, mClazz); - return obj; - } -} diff --git a/src/com/p4square/grow/backend/dynamo/DynamoDatabase.java b/src/com/p4square/grow/backend/dynamo/DynamoDatabase.java deleted file mode 100644 index 68a165d..0000000 --- a/src/com/p4square/grow/backend/dynamo/DynamoDatabase.java +++ /dev/null @@ -1,307 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.backend.dynamo; - -import java.util.Arrays; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; - -import com.amazonaws.auth.AWSCredentials; -import com.amazonaws.auth.DefaultAWSCredentialsProviderChain; -import com.amazonaws.regions.Region; -import com.amazonaws.regions.Regions; -import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient; -import com.amazonaws.services.dynamodbv2.model.AttributeAction; -import com.amazonaws.services.dynamodbv2.model.AttributeDefinition; -import com.amazonaws.services.dynamodbv2.model.AttributeValue; -import com.amazonaws.services.dynamodbv2.model.AttributeValueUpdate; -import com.amazonaws.services.dynamodbv2.model.CreateTableRequest; -import com.amazonaws.services.dynamodbv2.model.CreateTableResult; -import com.amazonaws.services.dynamodbv2.model.DeleteItemRequest; -import com.amazonaws.services.dynamodbv2.model.DeleteItemResult; -import com.amazonaws.services.dynamodbv2.model.DeleteTableRequest; -import com.amazonaws.services.dynamodbv2.model.DeleteTableResult; -import com.amazonaws.services.dynamodbv2.model.GetItemRequest; -import com.amazonaws.services.dynamodbv2.model.GetItemResult; -import com.amazonaws.services.dynamodbv2.model.KeySchemaElement; -import com.amazonaws.services.dynamodbv2.model.KeyType; -import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput; -import com.amazonaws.services.dynamodbv2.model.PutItemRequest; -import com.amazonaws.services.dynamodbv2.model.PutItemResult; -import com.amazonaws.services.dynamodbv2.model.ScanRequest; -import com.amazonaws.services.dynamodbv2.model.ScanResult; -import com.amazonaws.services.dynamodbv2.model.UpdateItemRequest; -import com.amazonaws.services.dynamodbv2.model.UpdateItemResult; -import com.amazonaws.services.dynamodbv2.model.UpdateTableRequest; -import com.amazonaws.services.dynamodbv2.model.UpdateTableResult; - -import com.p4square.grow.config.Config; - -/** - * A wrapper around the Dynamo API. - */ -public class DynamoDatabase { - private final AmazonDynamoDBClient mClient; - private final String mTablePrefix; - - public DynamoDatabase(final Config config) { - AWSCredentials creds; - - String awsAccessKey = config.getString("awsAccessKey"); - if (awsAccessKey != null) { - creds = new AWSCredentials() { - @Override - public String getAWSAccessKeyId() { - return config.getString("awsAccessKey"); - } - @Override - public String getAWSSecretKey() { - return config.getString("awsSecretKey"); - } - }; - } else { - creds = new DefaultAWSCredentialsProviderChain().getCredentials(); - } - - mClient = new AmazonDynamoDBClient(creds); - - String endpoint = config.getString("dynamoEndpoint"); - if (endpoint != null) { - mClient.setEndpoint(endpoint); - } - - String region = config.getString("awsRegion"); - if (region != null) { - mClient.setRegion(Region.getRegion(Regions.fromName(region))); - } - - mTablePrefix = config.getString("dynamoTablePrefix", ""); - } - - public void createTable(String name, long reads, long writes) { - ArrayList<AttributeDefinition> attributeDefinitions = new ArrayList<>(); - attributeDefinitions.add(new AttributeDefinition() - .withAttributeName("id") - .withAttributeType("S")); - - ArrayList<KeySchemaElement> ks = new ArrayList<>(); - ks.add(new KeySchemaElement().withAttributeName("id").withKeyType(KeyType.HASH)); - - ProvisionedThroughput provisionedThroughput = new ProvisionedThroughput() - .withReadCapacityUnits(reads) - .withWriteCapacityUnits(writes); - - CreateTableRequest request = new CreateTableRequest() - .withTableName(mTablePrefix + name) - .withAttributeDefinitions(attributeDefinitions) - .withKeySchema(ks) - .withProvisionedThroughput(provisionedThroughput); - - CreateTableResult result = mClient.createTable(request); - } - - public void updateTable(String name, long reads, long writes) { - ProvisionedThroughput provisionedThroughput = new ProvisionedThroughput() - .withReadCapacityUnits(reads) - .withWriteCapacityUnits(writes); - - UpdateTableRequest request = new UpdateTableRequest() - .withTableName(mTablePrefix + name) - .withProvisionedThroughput(provisionedThroughput); - - UpdateTableResult result = mClient.updateTable(request); - } - - public void deleteTable(String name) { - DeleteTableRequest deleteTableRequest = new DeleteTableRequest() - .withTableName(mTablePrefix + name); - - DeleteTableResult result = mClient.deleteTable(deleteTableRequest); - } - - /** - * Get all rows from a table. - * - * The key parameter must specify a table. If hash/range key is specified, - * the scan will begin after that key. - * - * @param key Previous key to start with. - * @return An ordered map of all results. - */ - public Map<DynamoKey, Map<String, String>> getAll(final DynamoKey key) { - ScanRequest scanRequest = new ScanRequest().withTableName(mTablePrefix + key.getTable()); - - if (key.getHashKey() != null) { - scanRequest.setExclusiveStartKey(generateKey(key)); - } - - ScanResult scanResult = mClient.scan(scanRequest); - - Map<DynamoKey, Map<String, String>> result = new LinkedHashMap<>(); - for (Map<String, AttributeValue> map : scanResult.getItems()) { - String id = null; - String range = null; - Map<String, String> row = new LinkedHashMap<>(); - for (Map.Entry<String, AttributeValue> entry : map.entrySet()) { - if ("id".equals(entry.getKey())) { - id = entry.getValue().getS(); - } else if ("range".equals(entry.getKey())) { - range = entry.getValue().getS(); - } else { - row.put(entry.getKey(), entry.getValue().getS()); - } - } - result.put(DynamoKey.newRangeKey(key.getTable(), id, range), row); - } - - return result; - } - - public Map<String, String> getKey(final DynamoKey key) { - GetItemRequest getItemRequest = new GetItemRequest() - .withTableName(mTablePrefix + key.getTable()) - .withKey(generateKey(key)); - - GetItemResult getItemResult = mClient.getItem(getItemRequest); - Map<String, AttributeValue> map = getItemResult.getItem(); - - Map<String, String> result = new LinkedHashMap<>(); - if (map != null) { - for (Map.Entry<String, AttributeValue> entry : map.entrySet()) { - if (!"id".equals(entry.getKey())) { - result.put(entry.getKey(), entry.getValue().getS()); - } - } - } - - return result; - } - - public String getAttribute(final DynamoKey key) { - checkAttributeKey(key); - - GetItemRequest getItemRequest = new GetItemRequest() - .withTableName(mTablePrefix + key.getTable()) - .withKey(generateKey(key)) - .withAttributesToGet(key.getAttribute()); - - GetItemResult result = mClient.getItem(getItemRequest); - Map<String, AttributeValue> map = result.getItem(); - - if (map == null) { - return null; - } - - AttributeValue value = map.get(key.getAttribute()); - if (value != null) { - return value.getS(); - - } else { - return null; - } - } - - /** - * Set all attributes for the given key. - * - * @param key The key. - * @param values Map of attributes to values. - */ - public void putKey(final DynamoKey key, final Map<String, String> values) { - Map<String, AttributeValue> item = new HashMap<>(); - for (Map.Entry<String, String> entry : values.entrySet()) { - item.put(entry.getKey(), new AttributeValue().withS(entry.getValue())); - } - - // Set the Key - item.putAll(generateKey(key)); - - PutItemRequest putItemRequest = new PutItemRequest() - .withTableName(mTablePrefix + key.getTable()) - .withItem(item); - - PutItemResult result = mClient.putItem(putItemRequest); - } - - /** - * Set the particular attributes of the given key. - * - * @param key The key. - * @param value The new value. - */ - public void putAttribute(final DynamoKey key, final String value) { - checkAttributeKey(key); - - Map<String, AttributeValueUpdate> updateItem = new HashMap<>(); - updateItem.put(key.getAttribute(), - new AttributeValueUpdate() - .withAction(AttributeAction.PUT) - .withValue(new AttributeValue().withS(value))); - - UpdateItemRequest updateItemRequest = new UpdateItemRequest() - .withTableName(mTablePrefix + key.getTable()) - .withKey(generateKey(key)) - .withAttributeUpdates(updateItem); - // TODO: Check conditions. - - UpdateItemResult result = mClient.updateItem(updateItemRequest); - } - - /** - * Delete the given key. - * - * @param key The key. - */ - public void deleteKey(final DynamoKey key) { - DeleteItemRequest deleteItemRequest = new DeleteItemRequest() - .withTableName(mTablePrefix + key.getTable()) - .withKey(generateKey(key)); - - DeleteItemResult result = mClient.deleteItem(deleteItemRequest); - } - - /** - * Delete an attribute from the given key. - * - * @param key The key. - */ - public void deleteAttribute(final DynamoKey key) { - checkAttributeKey(key); - - Map<String, AttributeValueUpdate> updateItem = new HashMap<>(); - updateItem.put(key.getAttribute(), - new AttributeValueUpdate().withAction(AttributeAction.DELETE)); - - UpdateItemRequest updateItemRequest = new UpdateItemRequest() - .withTableName(mTablePrefix + key.getTable()) - .withKey(generateKey(key)) - .withAttributeUpdates(updateItem); - - UpdateItemResult result = mClient.updateItem(updateItemRequest); - } - - /** - * Generate a DynamoDB Key Map from the DynamoKey. - */ - private Map<String, AttributeValue> generateKey(final DynamoKey key) { - HashMap<String, AttributeValue> keyMap = new HashMap<>(); - keyMap.put("id", new AttributeValue().withS(key.getHashKey())); - - String range = key.getRangeKey(); - if (range != null) { - keyMap.put("range", new AttributeValue().withS(range)); - } - - return keyMap; - } - - private void checkAttributeKey(DynamoKey key) { - if (null == key.getAttribute()) { - throw new IllegalArgumentException("Attribute must be non-null"); - } - } -} diff --git a/src/com/p4square/grow/backend/dynamo/DynamoKey.java b/src/com/p4square/grow/backend/dynamo/DynamoKey.java deleted file mode 100644 index 5cdbacd..0000000 --- a/src/com/p4square/grow/backend/dynamo/DynamoKey.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.backend.dynamo; - -/** - * DynamoKey represents a table, hash key, and range key tupl. - */ -public class DynamoKey { - private final String mTable; - private final String mHashKey; - private final String mRangeKey; - private final String mAttribute; - - public static DynamoKey newKey(final String table, final String hashKey) { - return new DynamoKey(table, hashKey, null, null); - } - - public static DynamoKey newRangeKey(final String table, final String hashKey, - final String rangeKey) { - - return new DynamoKey(table, hashKey, rangeKey, null); - } - - public static DynamoKey newAttributeKey(final String table, final String hashKey, - final String attribute) { - - return new DynamoKey(table, hashKey, null, attribute); - } - - public DynamoKey(final String table, final String hashKey, final String rangeKey, - final String attribute) { - - mTable = table; - mHashKey = hashKey; - mRangeKey = rangeKey; - mAttribute = attribute; - } - - public String getTable() { - return mTable; - } - - public String getHashKey() { - return mHashKey; - } - - public String getRangeKey() { - return mRangeKey; - } - - public String getAttribute() { - return mAttribute; - } -} diff --git a/src/com/p4square/grow/backend/dynamo/DynamoProviderImpl.java b/src/com/p4square/grow/backend/dynamo/DynamoProviderImpl.java deleted file mode 100644 index 93a535f..0000000 --- a/src/com/p4square/grow/backend/dynamo/DynamoProviderImpl.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.backend.dynamo; - -import java.io.IOException; - -import com.p4square.grow.provider.Provider; -import com.p4square.grow.provider.JsonEncodedProvider; - -/** - * Provider implementation backed by a DynamoDB Table. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class DynamoProviderImpl<V> extends JsonEncodedProvider<V> implements Provider<DynamoKey, V> { - private final DynamoDatabase mDb; - - public DynamoProviderImpl(DynamoDatabase db, Class<V> clazz) { - super(clazz); - - mDb = db; - } - - @Override - public V get(DynamoKey key) throws IOException { - String blob = mDb.getAttribute(key); - return decode(blob); - } - - @Override - public void put(DynamoKey key, V obj) throws IOException { - String blob = encode(obj); - mDb.putAttribute(key, blob); - } -} diff --git a/src/com/p4square/grow/backend/feed/FeedDataProvider.java b/src/com/p4square/grow/backend/feed/FeedDataProvider.java deleted file mode 100644 index 6f090c0..0000000 --- a/src/com/p4square/grow/backend/feed/FeedDataProvider.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.backend.feed; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; - -import com.p4square.grow.model.MessageThread; -import com.p4square.grow.model.Message; -import com.p4square.grow.provider.CollectionProvider; - -/** - * Implementing this interface indicates you can provide a data source for the Feed. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public interface FeedDataProvider { - public static final Collection<String> TOPICS = Collections.unmodifiableCollection( - Arrays.asList(new String[] { "seeker", "believer", "disciple", "teacher", "leader" })); - - /** - * @return a CollectionProvider of Threads. - */ - CollectionProvider<String, String, MessageThread> getThreadProvider(); - - /** - * @return a CollectionProvider of Messages. - */ - CollectionProvider<String, String, Message> getMessageProvider(); -} diff --git a/src/com/p4square/grow/backend/feed/ThreadResource.java b/src/com/p4square/grow/backend/feed/ThreadResource.java deleted file mode 100644 index e8f46c2..0000000 --- a/src/com/p4square/grow/backend/feed/ThreadResource.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.backend.feed; - -import java.io.IOException; - -import java.util.Date; -import java.util.Map; - -import org.restlet.data.Status; -import org.restlet.resource.ServerResource; -import org.restlet.representation.Representation; - -import org.restlet.ext.jackson.JacksonRepresentation; - -import org.apache.log4j.Logger; - -import com.p4square.grow.model.Message; - -/** - * ThreadResource manages the messages that make up a thread. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class ThreadResource extends ServerResource { - private static final Logger LOG = Logger.getLogger(ThreadResource.class); - - private FeedDataProvider mBackend; - private String mTopic; - private String mThreadId; - - @Override - public void doInit() { - super.doInit(); - - mBackend = (FeedDataProvider) getApplication(); - mTopic = getAttribute("topic"); - mThreadId = getAttribute("thread"); - } - - /** - * GET a list of messages in a thread. - */ - @Override - protected Representation get() { - // If the topic or threadId are missing, return a 404. - if (mTopic == null || mTopic.length() == 0 || - mThreadId == null || mThreadId.length() == 0) { - setStatus(Status.CLIENT_ERROR_NOT_FOUND); - return null; - } - - // TODO: Support limit query parameter. - - try { - String collectionKey = mTopic + "/" + mThreadId; - Map<String, Message> messages = mBackend.getMessageProvider().query(collectionKey); - return new JacksonRepresentation(messages.values()); - - } catch (IOException e) { - LOG.error("Unexpected exception: " + e.getMessage(), e); - setStatus(Status.SERVER_ERROR_INTERNAL); - return null; - } - } - - /** - * POST a new message to the thread. - */ - @Override - protected Representation post(Representation entity) { - // If the topic and thread are not provided, respond with not allowed. - // TODO: Check if the thread exists. - if (mTopic == null || !mBackend.TOPICS.contains(mTopic) || - mThreadId == null || mThreadId.length() == 0) { - setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); - return null; - } - - try { - JacksonRepresentation<Message> jsonRep = new JacksonRepresentation<Message>(entity, Message.class); - Message message = jsonRep.getObject(); - - // Force the thread id and message to be what we expect. - message.setThreadId(mThreadId); - message.setId(Message.generateId()); - - if (message.getCreated() == null) { - message.setCreated(new Date()); - } - - String collectionKey = mTopic + "/" + mThreadId; - mBackend.getMessageProvider().put(collectionKey, message.getId(), message); - - setLocationRef(mThreadId + "/" + message.getId()); - return new JacksonRepresentation(message); - - } catch (IOException e) { - LOG.error("Unexpected exception: " + e.getMessage(), e); - setStatus(Status.SERVER_ERROR_INTERNAL); - return null; - } - } -} diff --git a/src/com/p4square/grow/backend/feed/TopicResource.java b/src/com/p4square/grow/backend/feed/TopicResource.java deleted file mode 100644 index 24b6a92..0000000 --- a/src/com/p4square/grow/backend/feed/TopicResource.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.backend.feed; - -import java.io.IOException; - -import java.util.Date; -import java.util.Map; - -import org.restlet.data.Status; -import org.restlet.resource.ServerResource; -import org.restlet.representation.Representation; - -import org.restlet.ext.jackson.JacksonRepresentation; - -import org.apache.log4j.Logger; - -import com.p4square.grow.model.Message; -import com.p4square.grow.model.MessageThread; - -/** - * TopicResource manages the threads contained in a topic. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class TopicResource extends ServerResource { - private static final Logger LOG = Logger.getLogger(TopicResource.class); - - private FeedDataProvider mBackend; - private String mTopic; - - @Override - public void doInit() { - super.doInit(); - - mBackend = (FeedDataProvider) getApplication(); - mTopic = getAttribute("topic"); - } - - /** - * GET a list of threads in the topic. - */ - @Override - protected Representation get() { - // If no topic is provided, return a list of topics. - if (mTopic == null || mTopic.length() == 0) { - return new JacksonRepresentation(FeedDataProvider.TOPICS); - } - - // Parse limit query parameter. - int limit = -1; - String limitString = getQueryValue("limit"); - if (limitString != null) { - try { - limit = Integer.parseInt(limitString); - } catch (NumberFormatException e) { - setStatus(Status.CLIENT_ERROR_BAD_REQUEST); - return null; - } - } - - try { - Map<String, MessageThread> threads = mBackend.getThreadProvider().query(mTopic, limit); - return new JacksonRepresentation(threads.values()); - - } catch (IOException e) { - LOG.error("Unexpected exception: " + e.getMessage(), e); - setStatus(Status.SERVER_ERROR_INTERNAL); - return null; - } - } - - /** - * POST a new thread to the topic. - */ - @Override - protected Representation post(Representation entity) { - // If no topic is provided, respond with not allowed. - if (mTopic == null || !mBackend.TOPICS.contains(mTopic)) { - setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); - return null; - } - - try { - // Deserialize the incoming message. - JacksonRepresentation<MessageThread> jsonRep = - new JacksonRepresentation<MessageThread>(entity, MessageThread.class); - - // Get the message from the request. - // Throw away the wrapping MessageThread because we'll create our own later. - Message message = jsonRep.getObject().getMessage(); - if (message.getCreated() == null) { - message.setCreated(new Date()); - } - - // Create the new thread. - MessageThread newThread = MessageThread.createNew(); - - // Force the thread id and message to be what we expect. - message.setId(Message.generateId()); - message.setThreadId(newThread.getId()); - newThread.setMessage(message); - - mBackend.getThreadProvider().put(mTopic, newThread.getId(), newThread); - - setLocationRef(mTopic + "/" + newThread.getId()); - return new JacksonRepresentation(newThread); - - } catch (IOException e) { - LOG.error("Unexpected exception: " + e.getMessage(), e); - setStatus(Status.SERVER_ERROR_INTERNAL); - return null; - } - } -} diff --git a/src/com/p4square/grow/backend/resources/AccountResource.java b/src/com/p4square/grow/backend/resources/AccountResource.java deleted file mode 100644 index 2ac7061..0000000 --- a/src/com/p4square/grow/backend/resources/AccountResource.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.backend.resources; - -import java.io.IOException; - -import org.restlet.data.Status; -import org.restlet.resource.ServerResource; -import org.restlet.representation.Representation; - -import org.restlet.ext.jackson.JacksonRepresentation; - -import org.apache.log4j.Logger; - -import com.p4square.grow.model.UserRecord; -import com.p4square.grow.provider.Provider; -import com.p4square.grow.provider.ProvidesUserRecords; -import com.p4square.grow.provider.JsonEncodedProvider; - -/** - * Stores a document about a user. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class AccountResource extends ServerResource { - private static final Logger LOG = Logger.getLogger(AccountResource.class); - - private Provider<String, UserRecord> mUserRecordProvider; - - private String mUserId; - - @Override - public void doInit() { - super.doInit(); - - final ProvidesUserRecords backend = (ProvidesUserRecords) getApplication(); - mUserRecordProvider = backend.getUserRecordProvider(); - - mUserId = getAttribute("userId"); - } - - /** - * Handle GET Requests. - */ - @Override - protected Representation get() { - try { - UserRecord result = mUserRecordProvider.get(mUserId); - - if (result == null) { - setStatus(Status.CLIENT_ERROR_NOT_FOUND); - return null; - } - - JacksonRepresentation<UserRecord> rep = new JacksonRepresentation<UserRecord>(result); - rep.setObjectMapper(JsonEncodedProvider.MAPPER); - return rep; - - } catch (IOException e) { - setStatus(Status.SERVER_ERROR_INTERNAL); - return null; - } - } - - /** - * Handle PUT requests - */ - @Override - protected Representation put(Representation entity) { - try { - JacksonRepresentation<UserRecord> representation = - new JacksonRepresentation<>(entity, UserRecord.class); - representation.setObjectMapper(JsonEncodedProvider.MAPPER); - UserRecord record = representation.getObject(); - - mUserRecordProvider.put(mUserId, record); - setStatus(Status.SUCCESS_NO_CONTENT); - - } catch (IOException e) { - setStatus(Status.SERVER_ERROR_INTERNAL); - } - - return null; - } -} diff --git a/src/com/p4square/grow/backend/resources/BannerResource.java b/src/com/p4square/grow/backend/resources/BannerResource.java deleted file mode 100644 index 2b9c8e6..0000000 --- a/src/com/p4square/grow/backend/resources/BannerResource.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.backend.resources; - -import java.io.IOException; - -import org.restlet.data.Status; -import org.restlet.ext.jackson.JacksonRepresentation; -import org.restlet.representation.Representation; -import org.restlet.representation.StringRepresentation; -import org.restlet.resource.ServerResource; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import org.apache.log4j.Logger; - -import com.p4square.grow.backend.GrowBackend; -import com.p4square.grow.model.Banner; -import com.p4square.grow.provider.JsonEncodedProvider; -import com.p4square.grow.provider.Provider; - -/** - * Fetches or sets the banner string. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class BannerResource extends ServerResource { - private static final Logger LOG = Logger.getLogger(BannerResource.class); - - public static final ObjectMapper MAPPER = JsonEncodedProvider.MAPPER; - - private Provider<String, String> mStringProvider; - - @Override - public void doInit() { - super.doInit(); - - final GrowBackend backend = (GrowBackend) getApplication(); - mStringProvider = backend.getStringProvider(); - } - - /** - * Handle GET Requests. - */ - @Override - protected Representation get() { - String result = null; - try { - result = mStringProvider.get("banner"); - - } catch (IOException e) { - LOG.warn("Exception loading banner: " + e); - } - - if (result == null || result.length() == 0) { - result = "{\"html\":null}"; - } - - return new StringRepresentation(result); - } - - /** - * Handle PUT requests - */ - @Override - protected Representation put(Representation entity) { - try { - JacksonRepresentation<Banner> representation = - new JacksonRepresentation<>(entity, Banner.class); - representation.setObjectMapper(MAPPER); - - Banner banner = representation.getObject(); - - mStringProvider.put("banner", MAPPER.writeValueAsString(banner)); - setStatus(Status.SUCCESS_NO_CONTENT); - - } catch (IOException e) { - setStatus(Status.SERVER_ERROR_INTERNAL); - } - - return null; - } -} diff --git a/src/com/p4square/grow/backend/resources/SurveyResource.java b/src/com/p4square/grow/backend/resources/SurveyResource.java deleted file mode 100644 index 8723ee2..0000000 --- a/src/com/p4square/grow/backend/resources/SurveyResource.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.backend.resources; - -import java.io.IOException; - -import java.util.Map; -import java.util.HashMap; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import org.restlet.data.MediaType; -import org.restlet.data.Status; -import org.restlet.ext.jackson.JacksonRepresentation; -import org.restlet.representation.Representation; -import org.restlet.representation.StringRepresentation; -import org.restlet.resource.ServerResource; - -import org.apache.log4j.Logger; - -import com.p4square.grow.backend.GrowBackend; -import com.p4square.grow.model.Question; -import com.p4square.grow.provider.JsonEncodedProvider; -import com.p4square.grow.provider.Provider; - -/** - * This resource manages assessment questions. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class SurveyResource extends ServerResource { - private static final Logger LOG = Logger.getLogger(SurveyResource.class); - - private static final ObjectMapper MAPPER = JsonEncodedProvider.MAPPER; - - private Provider<String, Question> mQuestionProvider; - private Provider<String, String> mStringProvider; - - private String mQuestionId; - - @Override - public void doInit() { - super.doInit(); - - final GrowBackend backend = (GrowBackend) getApplication(); - mQuestionProvider = backend.getQuestionProvider(); - mStringProvider = backend.getStringProvider(); - - mQuestionId = getAttribute("questionId"); - } - - /** - * Handle GET Requests. - */ - @Override - protected Representation get() { - String result = "{}"; - - if (mQuestionId == null) { - // TODO: List all question ids - - } else if (mQuestionId.equals("first")) { - // Get the first question id from db? - Map<?, ?> questionSummary = getQuestionsSummary(); - mQuestionId = (String) questionSummary.get("first"); - - } else if (mQuestionId.equals("count")) { - // Get the first question id from db? - Map<?, ?> questionSummary = getQuestionsSummary(); - - return new StringRepresentation("{\"count\":" + - String.valueOf((Integer) questionSummary.get("count")) + "}"); - } - - if (mQuestionId != null) { - // Get a question by id - Question question = null; - try { - question = mQuestionProvider.get(mQuestionId); - } catch (IOException e) { - LOG.error("IOException loading question: " + e); - } - - if (question == null) { - // 404 - setStatus(Status.CLIENT_ERROR_NOT_FOUND); - return null; - } - - JacksonRepresentation<Question> rep = new JacksonRepresentation<>(question); - rep.setObjectMapper(MAPPER); - return rep; - } - - return new StringRepresentation(result); - } - - private Map<?, ?> getQuestionsSummary() { - try { - // TODO: This could be better. Quick fix for provider support. - String json = mStringProvider.get("/questions"); - - if (json != null) { - return MAPPER.readValue(json, Map.class); - } - - } catch (IOException e) { - LOG.info("Exception reading questions summary.", e); - } - - return null; - } -} diff --git a/src/com/p4square/grow/backend/resources/SurveyResultsResource.java b/src/com/p4square/grow/backend/resources/SurveyResultsResource.java deleted file mode 100644 index 7c15cfd..0000000 --- a/src/com/p4square/grow/backend/resources/SurveyResultsResource.java +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.backend.resources; - -import java.io.IOException; -import java.util.Map; -import java.util.HashMap; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import org.restlet.data.MediaType; -import org.restlet.data.Status; -import org.restlet.representation.Representation; -import org.restlet.representation.StringRepresentation; -import org.restlet.resource.ServerResource; - -import org.apache.log4j.Logger; - -import com.p4square.grow.backend.GrowBackend; -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.model.UserRecord; -import com.p4square.grow.provider.CollectionProvider; -import com.p4square.grow.provider.Provider; - - -/** - * Store the user's answers to the assessment and generate their score. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class SurveyResultsResource extends ServerResource { - private static final Logger LOG = Logger.getLogger(SurveyResultsResource.class); - - private static final ObjectMapper MAPPER = new ObjectMapper(); - - static enum RequestType { - ASSESSMENT, ANSWER - } - - private CollectionProvider<String, String, String> mAnswerProvider; - private Provider<String, Question> mQuestionProvider; - private Provider<String, UserRecord> mUserRecordProvider; - - private RequestType mRequestType; - private String mUserId; - private String mQuestionId; - - @Override - public void doInit() { - super.doInit(); - - final GrowBackend backend = (GrowBackend) getApplication(); - mAnswerProvider = backend.getAnswerProvider(); - mQuestionProvider = backend.getQuestionProvider(); - mUserRecordProvider = backend.getUserRecordProvider(); - - mUserId = getAttribute("userId"); - mQuestionId = getAttribute("questionId"); - - mRequestType = RequestType.ASSESSMENT; - if (mQuestionId != null) { - mRequestType = RequestType.ANSWER; - } - } - - /** - * Handle GET Requests. - */ - @Override - protected Representation get() { - try { - String result = null; - - switch (mRequestType) { - case ANSWER: - result = mAnswerProvider.get(mUserId, mQuestionId); - break; - - case ASSESSMENT: - result = mAnswerProvider.get(mUserId, "summary"); - if (result == null || result.length() == 0) { - result = buildAssessment(); - } - break; - } - - if (result == null) { - setStatus(Status.CLIENT_ERROR_NOT_FOUND); - return null; - } - - return new StringRepresentation(result); - } catch (IOException e) { - LOG.error("IOException getting answer: ", e); - setStatus(Status.SERVER_ERROR_INTERNAL); - return null; - } - } - - /** - * Handle PUT requests - */ - @Override - protected Representation put(Representation entity) { - boolean success = false; - - switch (mRequestType) { - case ANSWER: - try { - mAnswerProvider.put(mUserId, mQuestionId, entity.getText()); - mAnswerProvider.put(mUserId, "lastAnswered", mQuestionId); - mAnswerProvider.put(mUserId, "summary", null); - success = true; - - } catch (Exception e) { - LOG.warn("Caught exception putting answer: " + e.getMessage(), e); - } - break; - - default: - setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); - return null; - } - - if (success) { - setStatus(Status.SUCCESS_NO_CONTENT); - - } else { - setStatus(Status.SERVER_ERROR_INTERNAL); - } - - return null; - } - - /** - * Clear assessment results. - */ - @Override - protected Representation delete() { - boolean success = false; - - switch (mRequestType) { - case ANSWER: - try { - mAnswerProvider.put(mUserId, mQuestionId, null); - mAnswerProvider.put(mUserId, "summary", null); - success = true; - - } catch (Exception e) { - LOG.warn("Caught exception putting answer: " + e.getMessage(), e); - } - break; - - case ASSESSMENT: - try { - mAnswerProvider.put(mUserId, "summary", null); - mAnswerProvider.put(mUserId, "lastAnswered", null); - // TODO Delete answers - - UserRecord record = mUserRecordProvider.get(mUserId); - if (record != null) { - record.setLanding("assessment"); - mUserRecordProvider.put(mUserId, record); - } - - success = true; - - } catch (Exception e) { - LOG.warn("Caught exception putting answer: " + e.getMessage(), e); - } - break; - - default: - setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); - return null; - } - - if (success) { - setStatus(Status.SUCCESS_NO_CONTENT); - - } else { - setStatus(Status.SERVER_ERROR_INTERNAL); - } - - return null; - - } - - /** - * This method compiles assessment results. - */ - private String buildAssessment() throws IOException { - StringBuilder sb = new StringBuilder("{ "); - - // Last question answered - final String lastAnswered = mAnswerProvider.get(mUserId, "lastAnswered"); - if (lastAnswered != null && lastAnswered.length() > 0) { - sb.append("\"lastAnswered\": \"" + lastAnswered + "\", "); - } - - // Compute score - Map<String, String> row = mAnswerProvider.query(mUserId); - if (row.size() > 0) { - Score score = new Score(); - boolean scoringDone = false; - int totalAnswers = 0; - for (Map.Entry<String, String> c : row.entrySet()) { - if (c.getKey().equals("lastAnswered") || c.getKey().equals("summary")) { - continue; - } - - try { - Question question = mQuestionProvider.get(c.getKey()); - RecordedAnswer userAnswer = MAPPER.readValue(c.getValue(), RecordedAnswer.class); - - if (question == null) { - LOG.warn("Answer for unknown question: " + c.getKey()); - continue; - } - - LOG.debug("Scoring questionId: " + c.getKey()); - scoringDone = !question.scoreAnswer(score, userAnswer); - - } catch (Exception e) { - LOG.error("Failed to score question: {userid: \"" + mUserId + - "\", questionid:\"" + c.getKey() + - "\", userAnswer:\"" + c.getValue() + "\"}", e); - } - - totalAnswers++; - } - - sb.append("\"score\":" + score.getScore()); - sb.append(", \"sum\":" + score.getSum()); - sb.append(", \"count\":" + score.getCount()); - sb.append(", \"totalAnswers\":" + totalAnswers); - sb.append(", \"result\":\"" + score.toString() + "\""); - } - - sb.append(" }"); - String summary = sb.toString(); - - // Persist summary - mAnswerProvider.put(mUserId, "summary", summary); - - return summary; - } -} diff --git a/src/com/p4square/grow/backend/resources/TrainingRecordResource.java b/src/com/p4square/grow/backend/resources/TrainingRecordResource.java deleted file mode 100644 index 51ba56a..0000000 --- a/src/com/p4square/grow/backend/resources/TrainingRecordResource.java +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -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.fasterxml.jackson.databind.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.ext.jackson.JacksonRepresentation; - -import org.apache.log4j.Logger; - -import com.p4square.grow.backend.GrowBackend; - -import com.p4square.grow.model.Chapter; -import com.p4square.grow.model.Playlist; -import com.p4square.grow.model.VideoRecord; -import com.p4square.grow.model.TrainingRecord; - -import com.p4square.grow.provider.CollectionProvider; -import com.p4square.grow.provider.JsonEncodedProvider; -import com.p4square.grow.provider.Provider; -import com.p4square.grow.provider.ProvidesAssessments; -import com.p4square.grow.provider.ProvidesTrainingRecords; - -import com.p4square.grow.model.Score; - -/** - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class TrainingRecordResource extends ServerResource { - private static final Logger LOG = Logger.getLogger(TrainingRecordResource.class); - private static final ObjectMapper MAPPER = JsonEncodedProvider.MAPPER; - - static enum RequestType { - SUMMARY, VIDEO - } - - private Provider<String, TrainingRecord> mTrainingRecordProvider; - private CollectionProvider<String, String, String> mAnswerProvider; - - private RequestType mRequestType; - private String mUserId; - private String mVideoId; - private TrainingRecord mRecord; - - @Override - public void doInit() { - super.doInit(); - - mTrainingRecordProvider = ((ProvidesTrainingRecords) getApplication()).getTrainingRecordProvider(); - mAnswerProvider = ((ProvidesAssessments) getApplication()).getAnswerProvider(); - - mUserId = getAttribute("userId"); - mVideoId = getAttribute("videoId"); - - try { - Playlist defaultPlaylist = ((ProvidesTrainingRecords) getApplication()).getDefaultPlaylist(); - - mRecord = mTrainingRecordProvider.get(mUserId); - if (mRecord == null) { - mRecord = new TrainingRecord(); - mRecord.setPlaylist(defaultPlaylist); - skipAssessedChapters(mUserId, mRecord); - } else { - // Merge the playlist with the most recent version. - mRecord.getPlaylist().merge(defaultPlaylist); - } - - } catch (IOException e) { - LOG.error("IOException loading TrainingRecord: " + e.getMessage(), e); - mRecord = null; - } - - mRequestType = RequestType.SUMMARY; - if (mVideoId != null) { - mRequestType = RequestType.VIDEO; - } - } - - /** - * Handle GET Requests. - */ - @Override - protected Representation get() { - JacksonRepresentation<?> rep = null; - - if (mRecord == null) { - setStatus(Status.SERVER_ERROR_INTERNAL); - return null; - } - - switch (mRequestType) { - case VIDEO: - VideoRecord video = mRecord.getPlaylist().find(mVideoId); - if (video == null) { - break; // Fall through and return 404 - } - rep = new JacksonRepresentation<VideoRecord>(video); - break; - - case SUMMARY: - rep = new JacksonRepresentation<TrainingRecord>(mRecord); - break; - } - - if (rep == null) { - setStatus(Status.CLIENT_ERROR_NOT_FOUND); - return null; - - } else { - rep.setObjectMapper(JsonEncodedProvider.MAPPER); - return rep; - } - } - - /** - * Handle PUT requests - */ - @Override - protected Representation put(Representation entity) { - if (mRecord == null) { - setStatus(Status.SERVER_ERROR_INTERNAL); - return null; - } - - switch (mRequestType) { - case VIDEO: - try { - JacksonRepresentation<VideoRecord> representation = - new JacksonRepresentation<>(entity, VideoRecord.class); - representation.setObjectMapper(JsonEncodedProvider.MAPPER); - VideoRecord update = representation.getObject(); - VideoRecord video = mRecord.getPlaylist().find(mVideoId); - - if (video == null) { - // TODO: Video isn't on their playlist... - LOG.warn("Skipping video completion for video missing from playlist."); - - } else if (update.getComplete() && !video.getComplete()) { - // Video was newly completed - video.complete(); - mRecord.setLastVideo(mVideoId); - - mTrainingRecordProvider.put(mUserId, mRecord); - } - - setStatus(Status.SUCCESS_NO_CONTENT); - - } catch (Exception e) { - LOG.warn("Caught exception updating training record: " + e.getMessage(), e); - setStatus(Status.SERVER_ERROR_INTERNAL); - } - break; - - default: - setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); - } - - return null; - } - - private Score getAssessedScore(String userId) throws IOException { - // Get the user's score. - Score assessedScore = new Score(0, 0); - - String summaryString = mAnswerProvider.get(userId, "summary"); - if (summaryString == null) { - throw new IOException("Asked to create training record for unassessed user " + userId); - } - - Map<?,?> summary = MAPPER.readValue(summaryString, Map.class); - - if (summary.containsKey("sum") && summary.containsKey("count")) { - double sum = (Double) summary.get("sum"); - int count = (Integer) summary.get("count"); - assessedScore = new Score(sum, count); - } - - return assessedScore; - } - - /** - * Mark the chapters which the user assessed through as not required. - */ - private void skipAssessedChapters(String userId, TrainingRecord record) { - // Get the user's score. - Score assessedScore = new Score(0, 0); - - try { - assessedScore = getAssessedScore(userId); - } catch (IOException e) { - LOG.error("IOException fetching assessment record for " + userId, e); - return; - } - - // Mark the correct videos as not required. - Playlist playlist = record.getPlaylist(); - - for (Map.Entry<String, Chapter> entry : playlist.getChaptersMap().entrySet()) { - String chapterId = entry.getKey(); - Chapter chapter = entry.getValue(); - boolean required; - - if ("introduction".equals(chapter)) { - // Introduction chapter is always required - required = true; - - } else { - // Chapter required if the floor of the score is <= the chapter's numeric value. - required = assessedScore.floor() <= Score.numericScore(chapterId); - } - - if (!required) { - for (VideoRecord video : chapter.getVideos().values()) { - video.setRequired(required); - } - } - } - } -} diff --git a/src/com/p4square/grow/backend/resources/TrainingResource.java b/src/com/p4square/grow/backend/resources/TrainingResource.java deleted file mode 100644 index 6efdfab..0000000 --- a/src/com/p4square/grow/backend/resources/TrainingResource.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.backend.resources; - -import java.io.IOException; -import java.util.Map; - -import org.restlet.data.Status; -import org.restlet.resource.ServerResource; -import org.restlet.representation.Representation; -import org.restlet.representation.StringRepresentation; - -import org.apache.log4j.Logger; - -import com.p4square.grow.backend.GrowBackend; -import com.p4square.grow.backend.db.CassandraDatabase; - -import com.p4square.grow.provider.CollectionProvider; -/** - * This resource returns a listing of training items for a particular level. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class TrainingResource extends ServerResource { - private final static Logger LOG = Logger.getLogger(TrainingResource.class); - - private CollectionProvider<String, String, String> mVideoProvider; - - private String mLevel; - private String mVideoId; - - @Override - public void doInit() { - super.doInit(); - - GrowBackend backend = (GrowBackend) getApplication(); - mVideoProvider = backend.getVideoProvider(); - - mLevel = getAttribute("level"); - mVideoId = getAttribute("videoId"); - } - - /** - * Handle GET Requests. - */ - @Override - protected Representation get() { - String result = null; - - if (mLevel == null) { - setStatus(Status.CLIENT_ERROR_NOT_FOUND); - return null; - } - - try { - if (mVideoId == null) { - // Get all videos - // TODO: This could be improved, but this is the quickest way to get - // providers working. - Map<String, String> videos = mVideoProvider.query(mLevel); - if (videos.size() > 0) { - StringBuilder sb = new StringBuilder("{ \"level\": \"" + mLevel + "\""); - sb.append(", \"videos\": ["); - boolean first = true; - for (String value : videos.values()) { - if (!first) { - sb.append(", "); - } - sb.append(value); - first = false; - } - sb.append("] }"); - result = sb.toString(); - } - - } else { - // Get single video - result = mVideoProvider.get(mLevel, mVideoId); - } - - if (result == null) { - // 404 - setStatus(Status.CLIENT_ERROR_NOT_FOUND); - return null; - } - - return new StringRepresentation(result); - - } catch (IOException e) { - LOG.error("IOException fetch video: " + e.getMessage(), e); - setStatus(Status.SERVER_ERROR_INTERNAL); - return null; - } - } -} diff --git a/src/com/p4square/grow/ccb/CCBProgressReporter.java b/src/com/p4square/grow/ccb/CCBProgressReporter.java deleted file mode 100644 index d2826eb..0000000 --- a/src/com/p4square/grow/ccb/CCBProgressReporter.java +++ /dev/null @@ -1,104 +0,0 @@ -package com.p4square.grow.ccb; - -import com.p4square.ccbapi.CCBAPI; -import com.p4square.ccbapi.model.*; -import com.p4square.grow.frontend.ProgressReporter; -import com.p4square.grow.model.Score; -import org.apache.log4j.Logger; -import org.restlet.security.User; - -import java.io.IOException; -import java.time.LocalDate; -import java.time.ZoneId; -import java.util.Date; - -/** - * A ProgressReporter which records progress in CCB. - * - * Except not really, because it's not implemented yet. - * This is just a placeholder until ccb-api-client-java has support for updating an individual. - */ -public class CCBProgressReporter implements ProgressReporter { - - private static final Logger LOG = Logger.getLogger(CCBProgressReporter.class); - - private static final String GROW_LEVEL = "GrowLevelTrain"; - private static final String GROW_ASSESSMENT = "GrowLevelAsmnt"; - - private final CCBAPI mAPI; - private final CustomFieldCache mCache; - - public CCBProgressReporter(final CCBAPI api, final CustomFieldCache cache) { - mAPI = api; - mCache = cache; - } - - @Override - public void reportAssessmentComplete(final User user, final String level, final Date date, final String results) { - if (!(user instanceof CCBUser)) { - throw new IllegalArgumentException("Expected CCBUser but got " + user.getClass().getCanonicalName()); - } - final CCBUser ccbuser = (CCBUser) user; - - updateLevelAndDate(ccbuser, GROW_ASSESSMENT, level, date); - } - - @Override - public void reportChapterComplete(final User user, final String chapter, final Date date) { - if (!(user instanceof CCBUser)) { - throw new IllegalArgumentException("Expected CCBUser but got " + user.getClass().getCanonicalName()); - } - final CCBUser ccbuser = (CCBUser) user; - - // Only update the level if it is increasing. - final CustomPulldownFieldValue currentLevel = ccbuser.getProfile() - .getCustomPulldownFields().getByLabel(GROW_LEVEL); - - if (currentLevel != null) { - if (Score.numericScore(chapter) <= Score.numericScore(currentLevel.getSelection().getLabel())) { - LOG.info("Not updating level for " + user.getIdentifier() - + " because current level (" + currentLevel.getSelection().getLabel() - + ") is greater than new level (" + chapter + ")"); - return; - } - } - - updateLevelAndDate(ccbuser, GROW_LEVEL, chapter, date); - } - - private void updateLevelAndDate(final CCBUser user, final String field, final String level, final Date date) { - boolean modified = false; - - final UpdateIndividualProfileRequest req = new UpdateIndividualProfileRequest() - .withIndividualId(user.getProfile().getId()); - - final CustomField pulldownField = mCache.getIndividualPulldownByLabel(field); - if (pulldownField != null) { - final LookupTableType type = LookupTableType.valueOf(pulldownField.getName().toUpperCase()); - final LookupTableItem item = mCache.getPulldownItemByName(type, level); - if (item != null) { - req.withCustomPulldownField(pulldownField.getName(), item.getId()); - modified = true; - } - } - - final CustomField dateField = mCache.getDateFieldByLabel(field); - if (dateField != null) { - req.withCustomDateField(dateField.getName(), date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate()); - modified = true; - } - - try { - // Only update if a field exists. - if (modified) { - mAPI.updateIndividualProfile(req); - } - - } catch (IOException e) { - LOG.error("updateIndividual failed for " + user.getIdentifier() - + ", field " + field - + ", level " + level - + ", date " + date.toString()); - } - } -} diff --git a/src/com/p4square/grow/ccb/CCBUser.java b/src/com/p4square/grow/ccb/CCBUser.java deleted file mode 100644 index 7313172..0000000 --- a/src/com/p4square/grow/ccb/CCBUser.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.p4square.grow.ccb; - -import com.p4square.ccbapi.model.IndividualProfile; -import org.restlet.security.User; - -/** - * CCBUser is an adapter between a CCB IndividualProfile and a Restlet User. - * - * Note: CCBUser prefixes the user's identifier with "CCB-". This is done to - * ensure the identifier does not collide with identifiers from other - * systems. - */ -public class CCBUser extends User { - - private final IndividualProfile mProfile; - - /** - * Wrap an IndividualProfile inside a User object. - * - * @param profile The CCB IndividualProfile for the user. - */ - public CCBUser(final IndividualProfile profile) { - mProfile = profile; - - setIdentifier("CCB-" + mProfile.getId()); - setFirstName(mProfile.getFirstName()); - setLastName(mProfile.getLastName()); - setEmail(mProfile.getEmail()); - } - - /** - * @return The IndividualProfile of the user. - */ - public IndividualProfile getProfile() { - return mProfile; - } -} diff --git a/src/com/p4square/grow/ccb/CCBUserVerifier.java b/src/com/p4square/grow/ccb/CCBUserVerifier.java deleted file mode 100644 index db10b75..0000000 --- a/src/com/p4square/grow/ccb/CCBUserVerifier.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.p4square.grow.ccb; - -import com.p4square.ccbapi.CCBAPI; -import com.p4square.ccbapi.model.GetIndividualProfilesRequest; -import com.p4square.ccbapi.model.GetIndividualProfilesResponse; -import org.apache.log4j.Logger; -import org.restlet.Request; -import org.restlet.Response; -import org.restlet.security.Verifier; - -/** - * CCBUserVerifier authenticates a user through the CCB individual_profile_from_login_password API. - */ -public class CCBUserVerifier implements Verifier { - private static final Logger LOG = Logger.getLogger(CCBUserVerifier.class); - - private final CCBAPI mAPI; - - public CCBUserVerifier(final CCBAPI api) { - mAPI = api; - } - - @Override - public int verify(Request request, Response response) { - if (request.getChallengeResponse() == null) { - return RESULT_MISSING; // no credentials - } - - final String username = request.getChallengeResponse().getIdentifier(); - final char[] password = request.getChallengeResponse().getSecret(); - - try { - GetIndividualProfilesResponse resp = mAPI.getIndividualProfiles( - new GetIndividualProfilesRequest().withLoginPassword(username, password)); - - if (resp.getIndividuals().size() == 1) { - // Wrap the IndividualProfile up in an User and update the user on the request. - final CCBUser user = new CCBUser(resp.getIndividuals().get(0)); - LOG.info("Successfully authenticated " + user.getIdentifier()); - request.getClientInfo().setUser(user); - return RESULT_VALID; - } - - } catch (Exception e) { - LOG.error("CCB API Exception: " + e, e); - } - - return RESULT_INVALID; // Invalid credentials - } -} diff --git a/src/com/p4square/grow/ccb/ChurchCommunityBuilderIntegrationDriver.java b/src/com/p4square/grow/ccb/ChurchCommunityBuilderIntegrationDriver.java deleted file mode 100644 index fc6148f..0000000 --- a/src/com/p4square/grow/ccb/ChurchCommunityBuilderIntegrationDriver.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.p4square.grow.ccb; - -import com.codahale.metrics.MetricRegistry; -import com.p4square.ccbapi.CCBAPI; -import com.p4square.ccbapi.CCBAPIClient; -import com.p4square.grow.config.Config; -import com.p4square.grow.frontend.IntegrationDriver; -import com.p4square.grow.frontend.ProgressReporter; -import org.restlet.Context; -import org.restlet.security.Verifier; - -import java.net.URI; -import java.net.URISyntaxException; - -/** - * The ChurchCommunityBuilderIntegrationDriver is used to integrate Grow with Church Community Builder. - */ -public class ChurchCommunityBuilderIntegrationDriver implements IntegrationDriver { - - private final Context mContext; - private final MetricRegistry mMetricRegistry; - private final Config mConfig; - - private final CCBAPI mAPI; - - private final CCBProgressReporter mProgressReporter; - - public ChurchCommunityBuilderIntegrationDriver(final Context context) { - mContext = context; - mConfig = (Config) context.getAttributes().get("com.p4square.grow.config"); - mMetricRegistry = (MetricRegistry) context.getAttributes().get("com.p4square.grow.metrics"); - - try { - CCBAPI api = new CCBAPIClient(new URI(mConfig.getString("CCBAPIURL", "")), - mConfig.getString("CCBAPIUser", ""), - mConfig.getString("CCBAPIPassword", "")); - - if (mMetricRegistry != null) { - api = new MonitoredCCBAPI(api, mMetricRegistry); - } - - mAPI = api; - - final CustomFieldCache cache = new CustomFieldCache(mAPI); - mProgressReporter = new CCBProgressReporter(mAPI, cache); - - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - } - - @Override - public Verifier newUserAuthenticationVerifier() { - return new CCBUserVerifier(mAPI); - } - - @Override - public ProgressReporter getProgressReporter() { - return mProgressReporter; - } -} diff --git a/src/com/p4square/grow/ccb/CustomFieldCache.java b/src/com/p4square/grow/ccb/CustomFieldCache.java deleted file mode 100644 index d93e6d9..0000000 --- a/src/com/p4square/grow/ccb/CustomFieldCache.java +++ /dev/null @@ -1,126 +0,0 @@ -package com.p4square.grow.ccb; - -import com.p4square.ccbapi.CCBAPI; -import com.p4square.ccbapi.model.*; -import org.apache.log4j.Logger; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; - -/** - * CustomFieldCache maintains an index from custom field labels to names. - */ -public class CustomFieldCache { - - private static final Logger LOG = Logger.getLogger(CustomFieldCache.class); - - private final CCBAPI mAPI; - - private CustomFieldCollection<CustomField> mTextFields; - private CustomFieldCollection<CustomField> mDateFields; - private CustomFieldCollection<CustomField> mIndividualPulldownFields; - private CustomFieldCollection<CustomField> mGroupPulldownFields; - - private final Map<LookupTableType, Map<String, LookupTableItem>> mItemByNameTable; - - public CustomFieldCache(final CCBAPI api) { - mAPI = api; - mTextFields = new CustomFieldCollection<>(); - mDateFields = new CustomFieldCollection<>(); - mIndividualPulldownFields = new CustomFieldCollection<>(); - mGroupPulldownFields = new CustomFieldCollection<>(); - mItemByNameTable = new HashMap<>(); - } - - public CustomField getTextFieldByLabel(final String label) { - if (mTextFields.size() == 0) { - refresh(); - } - return mTextFields.getByLabel(label); - } - - public CustomField getDateFieldByLabel(final String label) { - if (mDateFields.size() == 0) { - refresh(); - } - return mDateFields.getByLabel(label); - } - - public CustomField getIndividualPulldownByLabel(final String label) { - if (mIndividualPulldownFields.size() == 0) { - refresh(); - } - return mIndividualPulldownFields.getByLabel(label); - } - - public CustomField getGroupPulldownByLabel(final String label) { - if (mGroupPulldownFields.size() == 0) { - refresh(); - } - return mGroupPulldownFields.getByLabel(label); - } - - public LookupTableItem getPulldownItemByName(final LookupTableType type, final String name) { - Map<String, LookupTableItem> items = mItemByNameTable.get(type); - if (items == null) { - if (!cacheLookupTable(type)) { - return null; - } - items = mItemByNameTable.get(type); - } - - return items.get(name.toLowerCase()); - } - - private synchronized void refresh() { - try { - // Get all of the custom fields. - final GetCustomFieldLabelsResponse resp = mAPI.getCustomFieldLabels(); - - final CustomFieldCollection<CustomField> newTextFields = new CustomFieldCollection<>(); - final CustomFieldCollection<CustomField> newDateFields = new CustomFieldCollection<>(); - final CustomFieldCollection<CustomField> newIndPulldownFields = new CustomFieldCollection<>(); - final CustomFieldCollection<CustomField> newGrpPulldownFields = new CustomFieldCollection<>(); - - for (final CustomField field : resp.getCustomFields()) { - if (field.getName().startsWith("udf_ind_text_")) { - newTextFields.add(field); - } else if (field.getName().startsWith("udf_ind_date_")) { - newDateFields.add(field); - } else if (field.getName().startsWith("udf_ind_pulldown_")) { - newIndPulldownFields.add(field); - } else if (field.getName().startsWith("udf_grp_pulldown_")) { - newGrpPulldownFields.add(field); - } else { - LOG.warn("Unknown custom field type " + field.getName()); - } - } - - this.mTextFields = newTextFields; - this.mDateFields = newDateFields; - this.mIndividualPulldownFields = newIndPulldownFields; - this.mGroupPulldownFields = newGrpPulldownFields; - - } catch (IOException e) { - // Error fetching labels. - LOG.error("Error fetching custom fields: " + e.getMessage(), e); - } - } - - private synchronized boolean cacheLookupTable(final LookupTableType type) { - try { - final GetLookupTableResponse resp = mAPI.getLookupTable(new GetLookupTableRequest().withType(type)); - mItemByNameTable.put(type, resp.getItems().stream().collect( - Collectors.toMap(item -> item.getName().toLowerCase(), Function.identity()))); - return true; - - } catch (IOException e) { - LOG.error("Exception caching lookup table of type " + type, e); - } - - return false; - } -} diff --git a/src/com/p4square/grow/ccb/MonitoredCCBAPI.java b/src/com/p4square/grow/ccb/MonitoredCCBAPI.java deleted file mode 100644 index 43b6433..0000000 --- a/src/com/p4square/grow/ccb/MonitoredCCBAPI.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.p4square.grow.ccb; - -import com.codahale.metrics.MetricRegistry; -import com.codahale.metrics.Timer; -import com.p4square.ccbapi.CCBAPI; -import com.p4square.ccbapi.model.*; - -import java.io.IOException; - -/** - * MonitoredCCBAPI is a CCBAPI decorator which records metrics for each API call. - */ -public class MonitoredCCBAPI implements CCBAPI { - - private final CCBAPI mAPI; - private final MetricRegistry mMetricRegistry; - - public MonitoredCCBAPI(final CCBAPI api, final MetricRegistry metricRegistry) { - if (api == null) { - throw new IllegalArgumentException("api must not be null."); - } - mAPI = api; - - if (metricRegistry == null) { - throw new IllegalArgumentException("metricRegistry must not be null."); - } - mMetricRegistry = metricRegistry; - } - - @Override - public GetCustomFieldLabelsResponse getCustomFieldLabels() throws IOException { - final Timer.Context timer = mMetricRegistry.timer("CCBAPI.getCustomFieldLabels.time").time(); - boolean success = false; - try { - final GetCustomFieldLabelsResponse resp = mAPI.getCustomFieldLabels(); - success = true; - return resp; - } finally { - timer.stop(); - mMetricRegistry.counter("CCBAPI.getCustomFieldLabels.success").inc(success ? 1 : 0); - mMetricRegistry.counter("CCBAPI.getCustomFieldLabels.failure").inc(!success ? 1 : 0); - } - } - - @Override - public GetLookupTableResponse getLookupTable(final GetLookupTableRequest request) throws IOException { - final Timer.Context timer = mMetricRegistry.timer("CCBAPI.getLookupTable.time").time(); - boolean success = false; - try { - final GetLookupTableResponse resp = mAPI.getLookupTable(request); - success = true; - return resp; - } finally { - timer.stop(); - mMetricRegistry.counter("CCBAPI.getLookupTable.success").inc(success ? 1 : 0); - mMetricRegistry.counter("CCBAPI.getLookupTable.failure").inc(!success ? 1 : 0); - } - } - - @Override - public GetIndividualProfilesResponse getIndividualProfiles(GetIndividualProfilesRequest request) - throws IOException { - final Timer.Context timer = mMetricRegistry.timer("CCBAPI.getIndividualProfiles").time(); - boolean success = false; - try { - final GetIndividualProfilesResponse resp = mAPI.getIndividualProfiles(request); - mMetricRegistry.counter("CCBAPI.getIndividualProfiles.count").inc(resp.getIndividuals().size()); - success = true; - return resp; - } finally { - timer.stop(); - mMetricRegistry.counter("CCBAPI.getIndividualProfiles.success").inc(success ? 1 : 0); - mMetricRegistry.counter("CCBAPI.getIndividualProfiles.failure").inc(!success ? 1 : 0); - } - } - - @Override - public UpdateIndividualProfileResponse updateIndividualProfile(UpdateIndividualProfileRequest request) throws IOException { - final Timer.Context timer = mMetricRegistry.timer("CCBAPI.updateIndividualProfile").time(); - boolean success = false; - try { - final UpdateIndividualProfileResponse resp = mAPI.updateIndividualProfile(request); - success = true; - return resp; - } finally { - timer.stop(); - mMetricRegistry.counter("CCBAPI.updateIndividualProfile.success").inc(success ? 1 : 0); - mMetricRegistry.counter("CCBAPI.updateIndividualProfile.failure").inc(!success ? 1 : 0); - } - } - - @Override - public void close() throws IOException { - mAPI.close(); - } -} diff --git a/src/com/p4square/grow/config/Config.java b/src/com/p4square/grow/config/Config.java deleted file mode 100644 index 2fc2ea3..0000000 --- a/src/com/p4square/grow/config/Config.java +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.config; - -import java.io.File; -import java.io.FileInputStream; -import java.io.InputStream; -import java.io.IOException; -import java.util.Properties; - -import org.apache.log4j.Logger; - -/** - * Manage configuration for an application. - * - * Config reads one or more property files as the application config. Duplicate - * properties loaded later override properties loaded earlier. Config has the - * concept of a domain to distinguish settings for development and production. - * The default domain is prod for production. Domain can be any String such as - * dev for development or test for testing. - * - * The property files are processed like java.util.Properties except that the - * keys are specified as DOMAIN.KEY. An asterisk (*) can be used in place of a - * domain to indicate it should apply to all domains. If a domain specific entry - * exists for the current domain, it will override any global config. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class Config { - private static final Logger LOG = Logger.getLogger(Config.class); - - private String mDomain; - private Properties mProperties; - - /** - * Construct a new Config object. - * - * Sets the domain to the value of the system property CONFIG_DOMAIN, if present. - * If the system property is not set then the environment variable CONFIG_DOMAIN is checked. - * If neither are set the domain defaults to prod. - */ - public Config() { - // Check the command line for a domain property. - mDomain = System.getProperty("CONFIG_DOMAIN"); - - // If the domain was not set with a property, check for an environment variable. - if (mDomain == null) { - mDomain = System.getenv("CONFIG_DOMAIN"); - } - - // If neither were set, default to prod - if (mDomain == null) { - mDomain = "prod"; - } - - mProperties = new Properties(); - } - - /** - * Change the domain from the default string "prod". - * - * @param domain The new domain. - */ - public void setDomain(String domain) { - LOG.info("Setting Config domain to " + domain); - mDomain = domain; - } - - /** - * @return the current domain. - */ - public String getDomain() { - return mDomain; - } - - /** - * Load properties from a file. - * Any exception are logged and suppressed. - */ - public void updateConfig(String propertyFilename) { - final File propFile = new File(propertyFilename); - - LOG.info("Loading properties from " + propFile); - - try { - InputStream in = new FileInputStream(propFile); - updateConfig(in); - - } catch (IOException e) { - LOG.error("Could not load properties file: " + e.getMessage(), e); - } - } - - /** - * Load properties from an InputStream. - * This method closes the InputStream when it completes. - * - * @param in The InputStream - */ - public void updateConfig(InputStream in) throws IOException { - LOG.info("Loading properties from InputStream"); - mProperties.load(in); - in.close(); - } - - /** - * Get a String from the config. - * - * @return The config value or null if it is not found. - */ - public String getString(String key) { - return getString(key, null); - } - - /** - * Get a String from the config. - * - * @return The config value or defaultValue if it can not be found. - */ - public String getString(final String key, final String defaultValue) { - String result; - - // Command line properties trump all. - result = System.getProperty(key); - if (result != null) { - LOG.debug("Reading System.getProperty(" + key + "). Got result = { " + result + " }"); - return result; - } - - // Environment variables can also override configs - result = System.getenv(key); - if (result != null) { - LOG.debug("Reading System.getenv(" + key + "). Got result = { " + result + " }"); - return result; - } - - final String domainKey = mDomain + "." + key; - result = mProperties.getProperty(domainKey); - if (result != null) { - LOG.debug("Reading config for key = { " + key + " }. Got result = { " + result + " }"); - return result; - } - - final String globalKey = "*." + key; - result = mProperties.getProperty(globalKey); - if (result != null) { - LOG.debug("Reading config for key = { " + key + " }. Got result = { " + result + " }"); - return result; - } - - LOG.debug("Reading config for key = { " + key + " }. Got default value = { " + defaultValue + " }"); - return defaultValue; - } - - /** - * Get an integer from the config. - * - * @return The config value or Integer.MIN_VALUE if the key is not present or the - * config can not be parsed. - */ - public int getInt(String key) { - return getInt(key, Integer.MIN_VALUE); - } - - /** - * Get an integer from the config. - * - * @return The config value or defaultValue if the key is not present or the - * config can not be parsed. - */ - public int getInt(String key, int defaultValue) { - final String propertyValue = getString(key); - - if (propertyValue != null) { - try { - final int result = Integer.valueOf(propertyValue); - return result; - - } catch (NumberFormatException e) { - LOG.warn("Expected property to be an integer: " - + key + " = { " + propertyValue + " }"); - } - } - - return defaultValue; - } - - public boolean getBoolean(String key) { - return getBoolean(key, false); - } - - public boolean getBoolean(String key, boolean defaultValue) { - final String propertyValue = getString(key); - - if (propertyValue != null) { - return (propertyValue.charAt(0) & 0xDF) == 'T'; - } - - return defaultValue; - } -} diff --git a/src/com/p4square/grow/frontend/AccountRedirectResource.java b/src/com/p4square/grow/frontend/AccountRedirectResource.java deleted file mode 100644 index be2ae65..0000000 --- a/src/com/p4square/grow/frontend/AccountRedirectResource.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.frontend; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -import org.restlet.data.Status; -import org.restlet.representation.Representation; -import org.restlet.representation.StringRepresentation; -import org.restlet.resource.ServerResource; - -import org.apache.log4j.Logger; - -import com.p4square.fmfacade.FreeMarkerPageResource; - -import com.p4square.grow.config.Config; -import com.p4square.grow.model.UserRecord; -import com.p4square.grow.provider.Provider; -import com.p4square.grow.provider.DelegateProvider; -import com.p4square.grow.provider.JsonEncodedProvider; - -/** - * This resource simply redirects the user to either the assessment - * or the training page. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class AccountRedirectResource extends ServerResource { - private static final Logger LOG = Logger.getLogger(AccountRedirectResource.class); - - private Config mConfig; - private Provider<String, UserRecord> mUserRecordProvider; - - // Fields pertaining to this request. - private String mUserId; - - @Override - public void doInit() { - super.doInit(); - - GrowFrontend growFrontend = (GrowFrontend) getApplication(); - mConfig = growFrontend.getConfig(); - - mUserRecordProvider = new DelegateProvider<String, String, UserRecord>( - new JsonRequestProvider<UserRecord>(getContext().getClientDispatcher(), - UserRecord.class)) { - @Override - public String makeKey(String userid) { - return getBackendEndpoint() + "/accounts/" + userid; - } - }; - - mUserId = getRequest().getClientInfo().getUser().getIdentifier(); - } - - /** - * Redirect to the correct landing. - */ - @Override - protected Representation get() { - if (mUserId == null || mUserId.length() == 0) { - // This shouldn't happen, but I want to be safe because of the DB insert below. - setStatus(Status.CLIENT_ERROR_FORBIDDEN); - return new ErrorPage("Not Authenticated!"); - } - - try { - // Fetch account Map. - UserRecord user = null; - try { - user = mUserRecordProvider.get(mUserId); - } catch (NotFoundException e) { - // User record doesn't exist, so create a new one. - user = new UserRecord(getRequest().getClientInfo().getUser()); - mUserRecordProvider.put(mUserId, user); - } - - // Check for the new believers cookie - String cookie = getRequest().getCookies().getFirstValue(NewBelieverResource.COOKIE_NAME); - if (cookie != null && cookie.length() != 0) { - user.setLanding("training"); - user.setNewBeliever(true); - mUserRecordProvider.put(mUserId, user); - } - - String landing = user.getLanding(); - if (landing == null) { - landing = "assessment"; - } - - String nextPage = mConfig.getString("dynamicRoot", ""); - nextPage += "/account/" + landing; - getResponse().redirectSeeOther(nextPage); - return new StringRepresentation("Redirecting to " + nextPage); - - } catch (Exception e) { - LOG.fatal("Could not render page: " + e.getMessage(), e); - setStatus(Status.SERVER_ERROR_INTERNAL); - return ErrorPage.RENDER_ERROR; - } - } - - /** - * @return The backend endpoint URI - */ - private String getBackendEndpoint() { - return mConfig.getString("backendUri", "riap://component/backend"); - } -} diff --git a/src/com/p4square/grow/frontend/AssessmentResetPage.java b/src/com/p4square/grow/frontend/AssessmentResetPage.java deleted file mode 100644 index 519b135..0000000 --- a/src/com/p4square/grow/frontend/AssessmentResetPage.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.frontend; - -import java.util.Map; - -import freemarker.template.Template; - -import org.restlet.data.MediaType; -import org.restlet.data.Status; -import org.restlet.representation.Representation; -import org.restlet.representation.StringRepresentation; -import org.restlet.ext.freemarker.TemplateRepresentation; - -import org.apache.log4j.Logger; - -import com.p4square.fmfacade.FreeMarkerPageResource; - -import com.p4square.fmfacade.json.JsonRequestClient; -import com.p4square.fmfacade.json.JsonResponse; -import com.p4square.fmfacade.json.ClientException; - -import com.p4square.grow.config.Config; - -/** - * This page delete's the current user's assessment. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class AssessmentResetPage extends FreeMarkerPageResource { - private static final Logger LOG = Logger.getLogger(AssessmentResetPage.class); - - private GrowFrontend mGrowFrontend; - private Config mConfig; - private JsonRequestClient mJsonClient; - - private String mUserId; - - @Override - public void doInit() { - super.doInit(); - - mGrowFrontend = (GrowFrontend) getApplication(); - mConfig = mGrowFrontend.getConfig(); - - mJsonClient = new JsonRequestClient(getContext().getClientDispatcher()); - - mUserId = getRequest().getClientInfo().getUser().getIdentifier(); - } - - /** - * Return the login page. - */ - @Override - protected Representation get() { - try { - // Get the assessment results - JsonResponse response = backendDelete("/accounts/" + mUserId + "/assessment"); - if (!response.getStatus().isSuccess()) { - setStatus(Status.SERVER_ERROR_INTERNAL); - return ErrorPage.BACKEND_ERROR; - } - - String nextPage = mConfig.getString("dynamicRoot", "") - + "/account/assessment/question/first"; - getResponse().redirectSeeOther(nextPage); - return new StringRepresentation("Redirecting to " + nextPage); - - } catch (Exception e) { - LOG.fatal("Could not render page: " + e.getMessage(), e); - setStatus(Status.SERVER_ERROR_INTERNAL); - return ErrorPage.RENDER_ERROR; - } - } - - /** - * @return The backend endpoint URI - */ - private String getBackendEndpoint() { - return mConfig.getString("backendUri", "riap://component/backend"); - } - - /** - * Helper method to send a GET to the backend. - */ - private JsonResponse backendDelete(final String uri) { - LOG.debug("Sending backend GET " + uri); - - final JsonResponse response = mJsonClient.delete(getBackendEndpoint() + uri); - final Status status = response.getStatus(); - if (!status.isSuccess() && !Status.CLIENT_ERROR_NOT_FOUND.equals(status)) { - LOG.warn("Error making backend request for '" + uri + "'. status = " + response.getStatus().toString()); - } - - return response; - } -} diff --git a/src/com/p4square/grow/frontend/AssessmentResultsPage.java b/src/com/p4square/grow/frontend/AssessmentResultsPage.java deleted file mode 100644 index f1c924b..0000000 --- a/src/com/p4square/grow/frontend/AssessmentResultsPage.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.frontend; - -import java.util.Date; -import java.util.Map; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.p4square.f1oauth.FellowshipOneIntegrationDriver; -import freemarker.template.Template; - -import org.restlet.data.MediaType; -import org.restlet.data.Status; -import org.restlet.representation.Representation; -import org.restlet.representation.StringRepresentation; -import org.restlet.ext.freemarker.TemplateRepresentation; - -import org.apache.log4j.Logger; - -import com.p4square.fmfacade.FreeMarkerPageResource; - -import com.p4square.fmfacade.json.JsonRequestClient; -import com.p4square.fmfacade.json.JsonResponse; -import com.p4square.fmfacade.json.ClientException; - -import com.p4square.f1oauth.Attribute; -import com.p4square.f1oauth.F1API; -import com.p4square.f1oauth.F1User; - -import com.p4square.grow.config.Config; -import com.p4square.grow.provider.JsonEncodedProvider; -import org.restlet.security.User; - -/** - * This page fetches the user's final score and displays the transitional page between - * the assessment and the videos. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class AssessmentResultsPage extends FreeMarkerPageResource { - private static final Logger LOG = Logger.getLogger(AssessmentResultsPage.class); - - private GrowFrontend mGrowFrontend; - private Config mConfig; - private JsonRequestClient mJsonClient; - - private String mUserId; - - @Override - public void doInit() { - super.doInit(); - - mGrowFrontend = (GrowFrontend) getApplication(); - mConfig = mGrowFrontend.getConfig(); - - mJsonClient = new JsonRequestClient(getContext().getClientDispatcher()); - - mUserId = getRequest().getClientInfo().getUser().getIdentifier(); - } - - /** - * Return the login page. - */ - @Override - protected Representation get() { - Template t = mGrowFrontend.getTemplate("templates/assessment-results.ftl"); - - try { - if (t == null) { - setStatus(Status.CLIENT_ERROR_NOT_FOUND); - return ErrorPage.TEMPLATE_NOT_FOUND; - } - - Map<String, Object> root = getRootObject(); - - // Get the assessment results - JsonResponse response = backendGet("/accounts/" + mUserId + "/assessment"); - if (!response.getStatus().isSuccess()) { - setStatus(Status.SERVER_ERROR_INTERNAL); - return ErrorPage.BACKEND_ERROR; - } - - final String score = (String) response.getMap().get("result"); - if (score == null) { - // Odd... send them to the first questions - String nextPage = mConfig.getString("dynamicRoot", "") - + "/account/assessment/question/first"; - getResponse().redirectSeeOther(nextPage); - return new StringRepresentation("Redirecting to " + nextPage); - } - - // Publish results in F1 - publishScoreInF1(response.getMap()); - - root.put("stage", score); - return new TemplateRepresentation(t, root, MediaType.TEXT_HTML); - - } catch (Exception e) { - LOG.fatal("Could not render page: " + e.getMessage(), e); - setStatus(Status.SERVER_ERROR_INTERNAL); - return ErrorPage.RENDER_ERROR; - } - } - - private void publishScoreInF1(Map results) { - final ProgressReporter reporter = mGrowFrontend.getThirdPartyIntegrationFactory().getProgressReporter(); - - try { - final User user = getRequest().getClientInfo().getUser(); - final String level = results.get("result").toString(); - final Date completionDate = new Date(); - final String data = JsonEncodedProvider.MAPPER.writeValueAsString(results); - - reporter.reportAssessmentComplete(user, level, completionDate, data); - - } catch (JsonProcessingException e) { - LOG.error("Failed to generate json " + e.getMessage(), e); - } - } - - /** - * @return The backend endpoint URI - */ - private String getBackendEndpoint() { - return mConfig.getString("backendUri", "riap://component/backend"); - } - - /** - * Helper method to send a GET to the backend. - */ - private JsonResponse backendGet(final String uri) { - LOG.debug("Sending backend GET " + uri); - - final JsonResponse response = mJsonClient.get(getBackendEndpoint() + uri); - final Status status = response.getStatus(); - if (!status.isSuccess() && !Status.CLIENT_ERROR_NOT_FOUND.equals(status)) { - LOG.warn("Error making backend request for '" + uri + "'. status = " - + response.getStatus().toString()); - } - - return response; - } -} diff --git a/src/com/p4square/grow/frontend/AuthenticatedResource.java b/src/com/p4square/grow/frontend/AuthenticatedResource.java deleted file mode 100644 index 800eb83..0000000 --- a/src/com/p4square/grow/frontend/AuthenticatedResource.java +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.frontend; - -import org.restlet.resource.ServerResource; -import org.restlet.representation.Representation; - -/** - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class AuthenticatedResource extends ServerResource { - protected Representation post() { - return null; - } -} diff --git a/src/com/p4square/grow/frontend/ChapterCompletePage.java b/src/com/p4square/grow/frontend/ChapterCompletePage.java deleted file mode 100644 index 35abc43..0000000 --- a/src/com/p4square/grow/frontend/ChapterCompletePage.java +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.frontend; - -import java.util.Date; -import java.util.Map; - -import com.p4square.f1oauth.FellowshipOneIntegrationDriver; -import freemarker.template.Template; - -import org.restlet.data.MediaType; -import org.restlet.data.Status; -import org.restlet.representation.Representation; -import org.restlet.representation.StringRepresentation; -import org.restlet.ext.freemarker.TemplateRepresentation; - -import org.apache.log4j.Logger; - -import com.p4square.fmfacade.FreeMarkerPageResource; - -import com.p4square.fmfacade.json.JsonRequestClient; -import com.p4square.fmfacade.json.JsonResponse; -import com.p4square.fmfacade.json.ClientException; - -import com.p4square.f1oauth.Attribute; -import com.p4square.f1oauth.F1API; -import com.p4square.f1oauth.F1User; - -import com.p4square.grow.config.Config; -import com.p4square.grow.model.TrainingRecord; -import com.p4square.grow.provider.Provider; -import com.p4square.grow.provider.TrainingRecordProvider; -import org.restlet.security.User; - -/** - * This resource displays the transitional page between chapters. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class ChapterCompletePage extends FreeMarkerPageResource { - private static final Logger LOG = Logger.getLogger(ChapterCompletePage.class); - - private GrowFrontend mGrowFrontend; - private Config mConfig; - private JsonRequestClient mJsonClient; - private Provider<String, TrainingRecord> mTrainingRecordProvider; - - private String mUserId; - private String mChapter; - - @Override - public void doInit() { - super.doInit(); - - mGrowFrontend = (GrowFrontend) getApplication(); - mConfig = mGrowFrontend.getConfig(); - - mJsonClient = new JsonRequestClient(getContext().getClientDispatcher()); - mTrainingRecordProvider = new TrainingRecordProvider<String>( - new JsonRequestProvider<TrainingRecord>( - getContext().getClientDispatcher(), - TrainingRecord.class)) { - @Override - public String makeKey(String userid) { - return getBackendEndpoint() + "/accounts/" + userid + "/training"; - } - }; - - mUserId = getRequest().getClientInfo().getUser().getIdentifier(); - - mChapter = getAttribute("chapter"); - } - - /** - * Return the login page. - */ - @Override - protected Representation get() { - try { - Map<String, Object> root = getRootObject(); - - // Get the training summary - TrainingRecord trainingRecord = mTrainingRecordProvider.get(mUserId); - if (trainingRecord == null) { - // Wait. What? Everyone has a training record... - setStatus(Status.SERVER_ERROR_INTERNAL); - return new ErrorPage("Could not retrieve your training record."); - } - - // Verify they completed the chapter. - Map<String, Boolean> chapters = trainingRecord.getPlaylist().getChapterStatuses(); - Boolean completed = chapters.get(mChapter); - if (completed == null || !completed) { - // Redirect back to training page... - String nextPage = mConfig.getString("dynamicRoot", ""); - nextPage += "/account/training/" + mChapter; - getResponse().redirectSeeOther(nextPage); - return new StringRepresentation("Redirecting to " + nextPage); - } - - // Publish the training chapter complete attribute. - assignAttribute(); - - // Find the next chapter - String nextChapter = null; - { - 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; - nextChapter = chapter.getKey(); - } - } - } - - String nextOverride = getQueryValue("next"); - if (nextOverride != null) { - nextChapter = nextOverride; - } - - root.put("stage", mChapter); - root.put("nextstage", nextChapter); - - /* - * We will display one of two transitional pages: - * - * If the next chapter has a forward page, display the forward page. - * Else, if this chapter is not "Introduction", display the chapter - * complete message. - */ - Template t = mGrowFrontend.getTemplate("templates/stage-" - + nextChapter + "-forward.ftl"); - - if (t == null) { - // Skip the chapter complete message for "Introduction" - if ("introduction".equals(mChapter)) { - String nextPage = mConfig.getString("dynamicRoot", ""); - nextPage += "/account/training/" + nextChapter; - getResponse().redirectSeeOther(nextPage); - return new StringRepresentation("Redirecting to " + nextPage); - } - - t = mGrowFrontend.getTemplate("templates/stage-complete.ftl"); - if (t == null) { - setStatus(Status.CLIENT_ERROR_NOT_FOUND); - return ErrorPage.TEMPLATE_NOT_FOUND; - } - } - - return new TemplateRepresentation(t, root, MediaType.TEXT_HTML); - - } catch (Exception e) { - LOG.fatal("Could not render page: " + e.getMessage(), e); - setStatus(Status.SERVER_ERROR_INTERNAL); - return ErrorPage.RENDER_ERROR; - } - } - - private void assignAttribute() { - final ProgressReporter reporter = mGrowFrontend.getThirdPartyIntegrationFactory().getProgressReporter(); - - final User user = getRequest().getClientInfo().getUser(); - final Date completionDate = new Date(); - - reporter.reportChapterComplete(user, mChapter, completionDate); - } - - /** - * @return The backend endpoint URI - */ - private String getBackendEndpoint() { - return mConfig.getString("backendUri", "riap://component/backend"); - } - - /** - * Helper method to send a GET to the backend. - */ - private JsonResponse backendGet(final String uri) { - LOG.debug("Sending backend GET " + uri); - - final JsonResponse response = mJsonClient.get(getBackendEndpoint() + uri); - final Status status = response.getStatus(); - if (!status.isSuccess() && !Status.CLIENT_ERROR_NOT_FOUND.equals(status)) { - LOG.warn("Error making backend request for '" + uri - + "'. status = " + response.getStatus().toString()); - } - - return response; - } - - int chapterIndex(String chapter) { - if ("leader".equals(chapter)) { - return 5; - } else if ("teacher".equals(chapter)) { - return 4; - } else if ("disciple".equals(chapter)) { - return 3; - } else if ("believer".equals(chapter)) { - return 2; - } else if ("seeker".equals(chapter)) { - return 1; - } else { - return Integer.MAX_VALUE; - } - } -} diff --git a/src/com/p4square/grow/frontend/ErrorPage.java b/src/com/p4square/grow/frontend/ErrorPage.java deleted file mode 100644 index 81abe74..0000000 --- a/src/com/p4square/grow/frontend/ErrorPage.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.frontend; - -import java.util.HashMap; -import java.util.Map; - -import java.io.IOException; -import java.io.Writer; - -import freemarker.template.Template; - -import org.restlet.data.MediaType; -import org.restlet.ext.freemarker.TemplateRepresentation; -import org.restlet.representation.Representation; -import org.restlet.representation.StringRepresentation; -import org.restlet.representation.WriterRepresentation; - -import com.p4square.fmfacade.FreeMarkerPageResource; - -/** - * ErrorPage wraps a String or Template Representation and displays the given - * error message. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class ErrorPage extends WriterRepresentation { - public static final ErrorPage TEMPLATE_NOT_FOUND = - new ErrorPage("Could not find the requested page template."); - - public static final ErrorPage RENDER_ERROR = - new ErrorPage("Error rendering page."); - - public static final ErrorPage BACKEND_ERROR = - new ErrorPage("Error communicating with backend."); - - public static final ErrorPage NOT_FOUND = - new ErrorPage("The requested URL could not be found."); - - private static Template cTemplate = null; - private static Map<String, Object> cRoot = null; - - private final String mMessage; - - public ErrorPage(String msg) { - this(msg, MediaType.TEXT_HTML); - } - - public ErrorPage(String msg, MediaType mediaType) { - super(mediaType); - - mMessage = msg; - } - - public static synchronized void setTemplate(Template template, Map<String, Object> root) { - cTemplate = template; - cRoot = root; - } - - protected Representation getRepresentation() { - if (cTemplate == null) { - return new StringRepresentation(mMessage); - - } else { - Map<String, Object> root = new HashMap<String, Object>(cRoot); - root.put("errorMessage", mMessage); - return new TemplateRepresentation(cTemplate, root, MediaType.TEXT_HTML); - } - } - - @Override - public void write(Writer writer) throws IOException { - getRepresentation().write(writer); - } -} diff --git a/src/com/p4square/grow/frontend/FeedData.java b/src/com/p4square/grow/frontend/FeedData.java deleted file mode 100644 index feb03a1..0000000 --- a/src/com/p4square/grow/frontend/FeedData.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.frontend; - -import java.io.IOException; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; - -import org.restlet.Context; -import org.restlet.Restlet; - -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.type.TypeFactory; - -import com.p4square.grow.config.Config; -import com.p4square.grow.frontend.JsonRequestProvider; -import com.p4square.grow.model.Message; -import com.p4square.grow.model.MessageThread; -import com.p4square.grow.provider.JsonEncodedProvider; -import com.p4square.grow.provider.Provider; - -/** - * Fetch feed data for a topic. - */ -public class FeedData { - - /** - * Allowed Topics. - */ - public static final HashSet<String> TOPICS = new HashSet(Arrays.asList("seeker", "believer", - "disciple", "teacher", "leader")); - - - private final Config mConfig; - private final String mBackendURI; - - // TODO: Elegantly merge the List and individual providers. - private final JsonRequestProvider<List<MessageThread>> mThreadsProvider; - private final JsonRequestProvider<MessageThread> mThreadProvider; - - private final JsonRequestProvider<List<Message>> mMessagesProvider; - private final JsonRequestProvider<Message> mMessageProvider; - - public FeedData(final Context context, final Config config) { - mConfig = config; - mBackendURI = mConfig.getString("backendUri", "riap://component/backend") + "/feed"; - - Restlet clientDispatcher = context.getClientDispatcher(); - - TypeFactory factory = JsonEncodedProvider.MAPPER.getTypeFactory(); - - JavaType threadType = factory.constructCollectionType(List.class, MessageThread.class); - mThreadsProvider = new JsonRequestProvider<List<MessageThread>>(clientDispatcher, threadType); - mThreadProvider = new JsonRequestProvider<MessageThread>(clientDispatcher, MessageThread.class); - - JavaType messageType = factory.constructCollectionType(List.class, Message.class); - mMessagesProvider = new JsonRequestProvider<List<Message>>(clientDispatcher, messageType); - mMessageProvider = new JsonRequestProvider<Message>(clientDispatcher, Message.class); - } - - /** - * Get the threads for a topic. - * - * @param topic The topic to request threads for. - * @param limit The maximum number of threads. - * @return A list of MessageThread objects. - */ - public List<MessageThread> getThreads(final String topic, final int limit) throws IOException { - return mThreadsProvider.get(makeUrl(limit, topic)); - } - - public List<Message> getMessages(final String topic, final String threadId) throws IOException { - return mMessagesProvider.get(makeUrl(topic, threadId)); - } - - public void createThread(final String topic, final Message message) throws IOException { - MessageThread thread = new MessageThread(); - thread.setMessage(message); - - mThreadProvider.post(makeUrl(topic), thread); - } - - public void createResponse(final String topic, final String thread, final Message message) - throws IOException { - - mMessageProvider.post(makeUrl(topic, thread), message); - } - - private String makeUrl(String... parts) { - String url = mBackendURI; - for (String part : parts) { - url += "/" + part; - } - - return url; - } - - private String makeUrl(int limit, String... parts) { - return makeUrl(parts) + "?limit=" + limit; - } -} diff --git a/src/com/p4square/grow/frontend/FeedResource.java b/src/com/p4square/grow/frontend/FeedResource.java deleted file mode 100644 index 13d0fa0..0000000 --- a/src/com/p4square/grow/frontend/FeedResource.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.frontend; - -import java.io.IOException; - -import org.restlet.data.Form; -import org.restlet.data.Status; -import org.restlet.representation.Representation; -import org.restlet.resource.ServerResource; - -import org.apache.log4j.Logger; - -import com.p4square.grow.config.Config; -import com.p4square.grow.model.Message; -import com.p4square.grow.model.UserRecord; - -/** - * This resource handles user interactions with the feed. - */ -public class FeedResource extends ServerResource { - private static final Logger LOG = Logger.getLogger(FeedResource.class); - - private Config mConfig; - - private FeedData mFeedData; - - // Fields pertaining to this request. - protected String mTopic; - protected String mThread; - - @Override - public void doInit() { - super.doInit(); - - GrowFrontend growFrontend = (GrowFrontend) getApplication(); - mConfig = growFrontend.getConfig(); - - mFeedData = new FeedData(getContext(), mConfig); - - mTopic = getAttribute("topic"); - if (mTopic != null) { - mTopic = mTopic.trim(); - } - - mThread = getAttribute("thread"); - if (mThread != null) { - mThread = mThread.trim(); - } - } - - /** - * Create a new MessageThread. - */ - @Override - protected Representation post(Representation entity) { - try { - if (mTopic == null || mTopic.length() == 0 || !FeedData.TOPICS.contains(mTopic)) { - setStatus(Status.CLIENT_ERROR_NOT_FOUND); - return ErrorPage.NOT_FOUND; - } - - Form form = new Form(entity); - - String question = form.getFirstValue("question"); - - Message message = new Message(); - message.setMessage(question); - - UserRecord user = new UserRecord(getRequest().getClientInfo().getUser()); - message.setAuthor(user); - - if (mThread != null && mThread.length() != 0) { - // Post a response - mFeedData.createResponse(mTopic, mThread, message); - - } else { - // Post a new thread - mFeedData.createThread(mTopic, message); - } - - /* - * Can't trust the referrer, so we'll send them to the - * appropriate part of the training page - * TODO: This could be better done. - */ - String nextPage = mConfig.getString("dynamicRoot", ""); - nextPage += "/account/training/" + mTopic; - getResponse().redirectSeeOther(nextPage); - return null; - - } catch (IOException e) { - LOG.fatal("Could not save message: " + e.getMessage(), e); - setStatus(Status.SERVER_ERROR_INTERNAL); - return ErrorPage.BACKEND_ERROR; - - } - } -} diff --git a/src/com/p4square/grow/frontend/GroupLeaderTrainingPageResource.java b/src/com/p4square/grow/frontend/GroupLeaderTrainingPageResource.java deleted file mode 100644 index 3ab140e..0000000 --- a/src/com/p4square/grow/frontend/GroupLeaderTrainingPageResource.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.frontend; - -/** - * Display the Group Leader training videos. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class GroupLeaderTrainingPageResource extends TrainingPageResource { - private static final String[] CHAPTERS = { "leader" }; - - @Override - public void doInit() { - super.doInit(); - - mChapter = "leader"; - } - - @Override - public String[] getChaptersInOrder() { - return CHAPTERS; - } -} diff --git a/src/com/p4square/grow/frontend/GrowFrontend.java b/src/com/p4square/grow/frontend/GrowFrontend.java deleted file mode 100644 index b5f62fb..0000000 --- a/src/com/p4square/grow/frontend/GrowFrontend.java +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan <jesse@jesterpm.net> - */ - -package com.p4square.grow.frontend; - -import java.io.File; -import java.io.IOException; -import java.lang.reflect.Constructor; - -import freemarker.template.Template; - -import org.restlet.Application; -import org.restlet.Component; -import org.restlet.Context; -import org.restlet.Restlet; -import org.restlet.data.Protocol; -import org.restlet.resource.Directory; -import org.restlet.routing.Redirector; -import org.restlet.routing.Router; -import org.restlet.security.Authenticator; - -import com.codahale.metrics.MetricRegistry; - -import org.apache.log4j.Logger; - -import com.p4square.fmfacade.FMFacade; -import com.p4square.fmfacade.FreeMarkerPageResource; - -import com.p4square.grow.config.Config; - -import com.p4square.restlet.metrics.MetricRouter; - -import com.p4square.session.SessionCheckingAuthenticator; -import com.p4square.session.SessionCreatingAuthenticator; -import org.restlet.security.Verifier; - -/** - * This is the Restlet Application implementing the Grow project front-end. - * It's implemented as an extension of FMFacade that connects interactive pages - * with various ServerResources. This class provides a main method to start a - * Jetty instance for testing. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class GrowFrontend extends FMFacade { - private static Logger LOG = Logger.getLogger(GrowFrontend.class); - - private final Config mConfig; - private final MetricRegistry mMetricRegistry; - - private IntegrationDriver mIntegrationFactory; - - public GrowFrontend() { - this(new Config(), new MetricRegistry()); - } - - public GrowFrontend(Config config, MetricRegistry metricRegistry) { - mConfig = config; - mMetricRegistry = metricRegistry; - } - - public Config getConfig() { - return mConfig; - } - - public MetricRegistry getMetrics() { - return mMetricRegistry; - } - - @Override - public synchronized void start() throws Exception { - Template errorTemplate = getTemplate("templates/error.ftl"); - if (errorTemplate != null) { - ErrorPage.setTemplate(errorTemplate, - FreeMarkerPageResource.baseRootObject(getContext(), this)); - } - - getContext().getAttributes().put("com.p4square.grow.config", mConfig); - getContext().getAttributes().put("com.p4square.grow.metrics", mMetricRegistry); - - super.start(); - } - - public synchronized IntegrationDriver getThirdPartyIntegrationFactory() { - if (mIntegrationFactory == null) { - final String driverClassName = getConfig().getString("integrationDriver", - "com.p4square.f1oauth.FellowshipOneIntegrationDriver"); - try { - Class<?> clazz = Class.forName(driverClassName); - Constructor<?> constructor = clazz.getConstructor(Context.class); - mIntegrationFactory = (IntegrationDriver) constructor.newInstance(getContext()); - } catch (Exception e) { - LOG.error("Failed to instantiate IntegrationDriver " + driverClassName); - } - } - - return mIntegrationFactory; - } - - @Override - protected Router createRouter() { - Router router = new MetricRouter(getContext(), mMetricRegistry); - - final Authenticator defaultGuard = new SessionCheckingAuthenticator(getContext(), true); - defaultGuard.setNext(FreeMarkerPageResource.class); - router.attachDefault(defaultGuard); - router.attach("/", new Redirector(getContext(), "index.html", Redirector.MODE_CLIENT_PERMANENT)); - router.attach("/login.html", LoginPageResource.class); - router.attach("/newaccount.html", NewAccountResource.class); - router.attach("/newbeliever", NewBelieverResource.class); - - final Router accountRouter = new MetricRouter(getContext(), mMetricRegistry); - accountRouter.attach("/authenticate", AuthenticatedResource.class); - accountRouter.attach("/logout", LogoutResource.class); - - accountRouter.attach("", AccountRedirectResource.class); - accountRouter.attach("/assessment/question/{questionId}", SurveyPageResource.class); - accountRouter.attach("/assessment/results", AssessmentResultsPage.class); - accountRouter.attach("/assessment/reset", AssessmentResetPage.class); - accountRouter.attach("/assessment", SurveyPageResource.class); - accountRouter.attach("/training/{chapter}/completed", ChapterCompletePage.class); - accountRouter.attach("/training/{chapter}/videos/{videoId}.json", VideosResource.class); - accountRouter.attach("/training/{chapter}", TrainingPageResource.class); - accountRouter.attach("/training", TrainingPageResource.class); - accountRouter.attach("/feed/{topic}", FeedResource.class); - accountRouter.attach("/feed/{topic}/{thread}", FeedResource.class); - - final Authenticator accountGuard = createAuthenticatorChain(accountRouter); - router.attach("/account", accountGuard); - - return router; - } - - private Authenticator createAuthenticatorChain(Restlet last) { - final Context context = getContext(); - final String loginPage = getConfig().getString("dynamicRoot", "") + "/login.html"; - final String loginPost = getConfig().getString("dynamicRoot", "") + "/account/authenticate"; - final String defaultPage = getConfig().getString("dynamicRoot", "") + "/account"; - - // This is used to check for an existing session - SessionCheckingAuthenticator sessionChk = new SessionCheckingAuthenticator(context, true); - - // This is used to authenticate the user - Verifier verifier = getThirdPartyIntegrationFactory().newUserAuthenticationVerifier(); - LoginFormAuthenticator loginAuth = new LoginFormAuthenticator(context, false, verifier); - loginAuth.setLoginFormUrl(loginPage); - loginAuth.setLoginPostUrl(loginPost); - loginAuth.setDefaultPage(defaultPage); - - // This is used to create a new session for a newly authenticated user. - SessionCreatingAuthenticator sessionCreate = new SessionCreatingAuthenticator(context); - - sessionChk.setNext(loginAuth); - loginAuth.setNext(sessionCreate); - - sessionCreate.setNext(last); - - return sessionChk; - } - - /** - * Stand-alone main for testing. - */ - public static void main(String[] args) { - // Start the HTTP Server - final Component component = new Component(); - component.getServers().add(Protocol.HTTP, 8085); - component.getClients().add(Protocol.HTTP); - component.getClients().add(Protocol.HTTPS); - component.getClients().add(Protocol.FILE); - //component.getClients().add(new Client(null, Arrays.asList(Protocol.HTTPS), "org.restlet.ext.httpclient.HttpClientHelper")); - - // Static content - try { - component.getDefaultHost().attach("/images/", new FileServingApp("./build/root/images/")); - component.getDefaultHost().attach("/scripts", new FileServingApp("./build/root/scripts")); - component.getDefaultHost().attach("/style.css", new FileServingApp("./build/root/style.css")); - component.getDefaultHost().attach("/favicon.ico", new FileServingApp("./build/root/favicon.ico")); - component.getDefaultHost().attach("/notfound.html", new FileServingApp("./build/root/notfound.html")); - component.getDefaultHost().attach("/error.html", new FileServingApp("./build/root/error.html")); - } catch (IOException e) { - LOG.error("Could not create directory for static resources: " - + e.getMessage(), e); - } - - // Setup App - GrowFrontend app = new GrowFrontend(); - - // Load an optional config file from the first argument. - app.getConfig().setDomain("dev"); - if (args.length == 1) { - app.getConfig().updateConfig(args[0]); - } - - component.getDefaultHost().attach(app); - - // Setup shutdown hook - Runtime.getRuntime().addShutdownHook(new Thread() { - public void run() { - try { - component.stop(); - } catch (Exception e) { - LOG.error("Exception during cleanup", e); - } - } - }); - - LOG.info("Starting server..."); - - try { - component.start(); - } catch (Exception e) { - LOG.fatal("Could not start: " + e.getMessage(), e); - } - } - - private static class FileServingApp extends Application { - private final String mPath; - - public FileServingApp(String path) throws IOException { - mPath = new File(path).getAbsolutePath(); - } - - @Override - public Restlet createInboundRoot() { - return new Directory(getContext(), "file://" + mPath); - } - } -} diff --git a/src/com/p4square/grow/frontend/IntegrationDriver.java b/src/com/p4square/grow/frontend/IntegrationDriver.java deleted file mode 100644 index b9c3508..0000000 --- a/src/com/p4square/grow/frontend/IntegrationDriver.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.p4square.grow.frontend; - -import org.restlet.security.Verifier; - -/** - * An IntegrationDriver is used to create implementations of various objects - * used to integration Grow with a particular Church Management System. - */ -public interface IntegrationDriver { - - /** - * Create a new Restlet Verifier to authenticate users when they login to the site. - * - * @return A Verifier. - */ - Verifier newUserAuthenticationVerifier(); - - /** - * Return a ProgressReporter for this Church Management System. - * - * The ProgressReporter should be thread-safe. - * - * @return The ProgressReporter. - */ - ProgressReporter getProgressReporter(); -} diff --git a/src/com/p4square/grow/frontend/JsonRequestProvider.java b/src/com/p4square/grow/frontend/JsonRequestProvider.java deleted file mode 100644 index bf3b2b3..0000000 --- a/src/com/p4square/grow/frontend/JsonRequestProvider.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.frontend; - -import java.io.IOException; - -import com.fasterxml.jackson.databind.JavaType; - -import org.restlet.Request; -import org.restlet.Response; -import org.restlet.Restlet; -import org.restlet.data.Method; -import org.restlet.data.Status; -import org.restlet.representation.Representation; -import org.restlet.representation.StringRepresentation; - -import com.p4square.grow.provider.Provider; -import com.p4square.grow.provider.JsonEncodedProvider; - -/** - * Fetch a JSON object via a Request. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class JsonRequestProvider<V> extends JsonEncodedProvider<V> implements Provider<String, V> { - - private final Restlet mDispatcher; - - public JsonRequestProvider(Restlet dispatcher, Class<V> clazz) { - super(clazz); - - mDispatcher = dispatcher; - } - - public JsonRequestProvider(Restlet dispatcher, JavaType type) { - super(type); - - mDispatcher = dispatcher; - } - - @Override - public V get(String url) throws IOException { - Request request = new Request(Method.GET, url); - Response response = mDispatcher.handle(request); - Representation representation = response.getEntity(); - - if (!response.getStatus().isSuccess()) { - if (representation != null) { - representation.release(); - } - - if (Status.CLIENT_ERROR_NOT_FOUND.equals(response.getStatus())) { - throw new NotFoundException("Could not get object. " + response.getStatus()); - } else { - throw new IOException("Could not get object. " + response.getStatus()); - } - } - - return decode(representation.getText()); - } - - @Override - public void put(String url, V obj) throws IOException { - final Request request = new Request(Method.PUT, url); - request.setEntity(new StringRepresentation(encode(obj))); - - final Response response = mDispatcher.handle(request); - - if (!response.getStatus().isSuccess()) { - throw new IOException("Could not put object. " + response.getStatus()); - } - } - - /** - * Variant of put() which makes a POST request to the url. - * - * This method may eventually be incorporated into Provider for - * creating new objects with auto-generated IDs. - * - * @param url The url to make the request to. - * @param obj The post to post. - * @throws IOException on failure. - */ - public void post(String url, V obj) throws IOException { - final Request request = new Request(Method.POST, url); - request.setEntity(new StringRepresentation(encode(obj))); - - final Response response = mDispatcher.handle(request); - - if (!response.getStatus().isSuccess()) { - throw new IOException("Could not put object. " + response.getStatus()); - } - } -} diff --git a/src/com/p4square/grow/frontend/LoginFormAuthenticator.java b/src/com/p4square/grow/frontend/LoginFormAuthenticator.java deleted file mode 100644 index 21c9097..0000000 --- a/src/com/p4square/grow/frontend/LoginFormAuthenticator.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.frontend; - -import org.apache.log4j.Logger; - -import org.restlet.Context; -import org.restlet.Request; -import org.restlet.Response; -import org.restlet.data.ChallengeResponse; -import org.restlet.data.ChallengeScheme; -import org.restlet.data.Form; -import org.restlet.data.Method; -import org.restlet.data.Reference; -import org.restlet.security.Authenticator; -import org.restlet.security.Verifier; - -/** - * LoginFormAuthenticator changes - * - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class LoginFormAuthenticator extends Authenticator { - private static final Logger LOG = Logger.getLogger(LoginFormAuthenticator.class); - - private final Verifier mVerifier; - - private String mLoginPage = "/login.html"; - private String mLoginPostUrl = "/authenticate"; - private String mDefaultRedirect = "/index.html"; - - public LoginFormAuthenticator(Context context, boolean optional, Verifier verifier) { - super(context, false, optional, null); - - mVerifier = verifier; - } - - public void setLoginFormUrl(String url) { - mLoginPage = url; - } - - public void setLoginPostUrl(String url) { - mLoginPostUrl = url; - } - - public void setDefaultPage(String url) { - mDefaultRedirect = url; - } - - @Override - protected int beforeHandle(Request request, Response response) { - if (!isLoginAttempt(request) && request.getClientInfo().isAuthenticated()) { - // TODO: Logout - LOG.debug("Already authenticated. Skipping"); - return CONTINUE; - - } else { - return super.beforeHandle(request, response); - } - } - - - @Override - protected boolean authenticate(Request request, Response response) { - boolean isLoginAttempt = isLoginAttempt(request); - - Form query = request.getOriginalRef().getQueryAsForm(); - String redirect = query.getFirstValue("redirect"); - if (redirect == null || redirect.length() == 0) { - if (isLoginAttempt) { - redirect = mDefaultRedirect; - } else { - redirect = request.getResourceRef().getPath(); - } - } - - boolean authenticationFailed = false; - - if (isLoginAttempt) { - LOG.debug("Attempting authentication"); - - // Process login form - final Form form = new Form(request.getEntity()); - final String email = form.getFirstValue("email"); - final String password = form.getFirstValue("password"); - - boolean authenticated = false; - - if (email != null && !"".equals(email) && - password != null && !"".equals(password)) { - - LOG.debug("Got login request from " + email); - - request.setChallengeResponse( - new ChallengeResponse(ChallengeScheme.HTTP_BASIC, email, password.toCharArray())); - - // We expect the verifier to setup the User object. - int result = mVerifier.verify(request, response); - if (result == Verifier.RESULT_VALID) { - return true; - } - } - - authenticationFailed = true; - } - - if (!isOptional() || authenticationFailed) { - Reference ref = new Reference(mLoginPage); - ref.addQueryParameter("redirect", redirect); - - if (authenticationFailed) { - ref.addQueryParameter("retry", "t"); - } - - LOG.debug("Redirecting to " + ref); - response.redirectSeeOther(ref.toString()); - } - LOG.debug("Failing authentication."); - return false; - } - - @Override - protected int authenticated(Request request, Response response) { - super.authenticated(request, response); - - Form query = request.getOriginalRef().getQueryAsForm(); - String redirect = query.getFirstValue("redirect"); - if (redirect == null || redirect.length() == 0) { - redirect = mDefaultRedirect; - } - - // TODO: Ensure redirect is a relative url. - LOG.debug("Redirecting to " + redirect); - response.redirectSeeOther(redirect); - - return CONTINUE; - } - - private boolean isLoginAttempt(Request request) { - String requestPath = request.getResourceRef().getPath(); - return request.getMethod() == Method.POST && mLoginPostUrl.equals(requestPath); - } -} diff --git a/src/com/p4square/grow/frontend/LoginPageResource.java b/src/com/p4square/grow/frontend/LoginPageResource.java deleted file mode 100644 index 38eba07..0000000 --- a/src/com/p4square/grow/frontend/LoginPageResource.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.frontend; - -import java.util.Map; - -import freemarker.template.Template; - -import org.restlet.data.Form; -import org.restlet.data.MediaType; -import org.restlet.data.Status; -import org.restlet.resource.ServerResource; -import org.restlet.representation.Representation; -import org.restlet.ext.freemarker.TemplateRepresentation; - -import org.apache.log4j.Logger; - -import com.p4square.fmfacade.FreeMarkerPageResource; - -/** - * LoginPageResource presents a login page template and processes the response. - * Upon successful authentication, the user is redirected to another page and - * a cookie is set. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class LoginPageResource extends FreeMarkerPageResource { - private static Logger cLog = Logger.getLogger(LoginPageResource.class); - - private GrowFrontend mGrowFrontend; - - private String mErrorMessage; - - @Override - public void doInit() { - super.doInit(); - - mGrowFrontend = (GrowFrontend) getApplication(); - - mErrorMessage = null; - } - - /** - * Return the login page. - */ - @Override - protected Representation get() { - Template t = mGrowFrontend.getTemplate("pages/login.html.ftl"); - - try { - if (t == null) { - setStatus(Status.CLIENT_ERROR_NOT_FOUND); - return null; - } - - Map<String, Object> root = getRootObject(); - - Form query = getRequest().getOriginalRef().getQueryAsForm(); - String redirect = query.getFirstValue("redirect"); - root.put("redirect", redirect); - String retry = query.getFirstValue("retry"); - if ("t".equals(retry)) { - root.put("errorMessage", "Invalid email or password."); - } - - return new TemplateRepresentation(t, root, MediaType.TEXT_HTML); - - } catch (Exception e) { - cLog.fatal("Could not render page: " + e.getMessage(), e); - setStatus(Status.SERVER_ERROR_INTERNAL); - return null; - } - } - -} diff --git a/src/com/p4square/grow/frontend/LogoutResource.java b/src/com/p4square/grow/frontend/LogoutResource.java deleted file mode 100644 index e26dcb7..0000000 --- a/src/com/p4square/grow/frontend/LogoutResource.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.frontend; - -import org.restlet.representation.Representation; -import org.restlet.representation.StringRepresentation; -import org.restlet.resource.ServerResource; - -import com.p4square.session.Sessions; - -import com.p4square.grow.config.Config; - -/** - * This Resource removes a user's session and session cookies. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class LogoutResource extends ServerResource { - private Config mConfig; - - @Override - protected void doInit() { - super.doInit(); - - GrowFrontend growFrontend = (GrowFrontend) getApplication(); - mConfig = growFrontend.getConfig(); - } - - @Override - protected Representation get() { - Sessions.getInstance().delete(getRequest(), getResponse()); - - String nextPage = mConfig.getString("dynamicRoot", ""); - nextPage += "/index.html"; - getResponse().redirectSeeOther(nextPage); - return new StringRepresentation("Redirecting to " + nextPage); - } -} diff --git a/src/com/p4square/grow/frontend/NewAccountResource.java b/src/com/p4square/grow/frontend/NewAccountResource.java deleted file mode 100644 index 5c13017..0000000 --- a/src/com/p4square/grow/frontend/NewAccountResource.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.frontend; - -import java.util.Map; - -import com.p4square.f1oauth.FellowshipOneIntegrationDriver; -import freemarker.template.Template; - -import org.restlet.data.Form; -import org.restlet.data.MediaType; -import org.restlet.data.Status; -import org.restlet.representation.Representation; -import org.restlet.representation.StringRepresentation; -import org.restlet.ext.freemarker.TemplateRepresentation; - -import org.apache.log4j.Logger; - -import com.p4square.f1oauth.F1Access; -import com.p4square.restlet.oauth.OAuthException; - -import com.p4square.fmfacade.FreeMarkerPageResource; - -/** - * This resource creates a new InFellowship account. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class NewAccountResource extends FreeMarkerPageResource { - private static Logger LOG = Logger.getLogger(NewAccountResource.class); - - private GrowFrontend mGrowFrontend; - private F1Access mHelper; - - private String mErrorMessage; - - private String mLoginPageUrl; - private String mVerificationPage; - - @Override - public void doInit() { - super.doInit(); - - mGrowFrontend = (GrowFrontend) getApplication(); - - final IntegrationDriver driver = mGrowFrontend.getThirdPartyIntegrationFactory(); - if (driver instanceof FellowshipOneIntegrationDriver) { - mHelper = ((FellowshipOneIntegrationDriver) driver).getF1Access(); - } else { - LOG.error("NewAccountResource only works with F1!"); - mHelper = null; - } - - mErrorMessage = ""; - - mLoginPageUrl = mGrowFrontend.getConfig().getString("postAccountCreationPage", - getRequest().getRootRef().toString()); - mVerificationPage = mGrowFrontend.getConfig().getString("dynamicRoot", "") - + "/verification.html"; - } - - /** - * Return the login page. - */ - @Override - protected Representation get() { - Template t = mGrowFrontend.getTemplate("pages/newaccount.html.ftl"); - - try { - if (t == null) { - setStatus(Status.CLIENT_ERROR_NOT_FOUND); - return ErrorPage.TEMPLATE_NOT_FOUND; - } - - Map<String, Object> root = getRootObject(); - if (mErrorMessage.length() > 0) { - root.put("errorMessage", mErrorMessage); - } - - return new TemplateRepresentation(t, root, MediaType.TEXT_HTML); - - } catch (Exception e) { - LOG.fatal("Could not render page: " + e.getMessage(), e); - setStatus(Status.SERVER_ERROR_INTERNAL); - return ErrorPage.RENDER_ERROR; - } - } - - @Override - protected Representation post(Representation rep) { - if (mHelper == null) { - mErrorMessage += "F1 support is not enabled! "; - return get(); - } - - Form form = new Form(rep); - - String firstname = form.getFirstValue("firstname"); - String lastname = form.getFirstValue("lastname"); - String email = form.getFirstValue("email"); - - if (isEmpty(firstname)) { - mErrorMessage += "First Name is a required field. "; - } - if (isEmpty(lastname)) { - mErrorMessage += "Last Name is a required field. "; - } - if (isEmpty(email)) { - mErrorMessage += "Email is a required field. "; - } - - if (mErrorMessage.length() > 0) { - return get(); - } - - try { - if (!mHelper.createAccount(firstname, lastname, email, mLoginPageUrl)) { - mErrorMessage = "An account with that address already exists."; - return get(); - } - - getResponse().redirectSeeOther(mVerificationPage); - return new StringRepresentation("Redirecting to " + mVerificationPage); - - } catch (OAuthException e) { - return new ErrorPage(e.getStatus().getDescription()); - } - } - - private boolean isEmpty(String s) { - return s == null || s.trim().length() == 0; - } -} diff --git a/src/com/p4square/grow/frontend/NewBelieverResource.java b/src/com/p4square/grow/frontend/NewBelieverResource.java deleted file mode 100644 index 8fe078a..0000000 --- a/src/com/p4square/grow/frontend/NewBelieverResource.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.frontend; - -import freemarker.template.Template; - -import org.restlet.data.CookieSetting; -import org.restlet.data.MediaType; -import org.restlet.data.Status; -import org.restlet.representation.Representation; -import org.restlet.ext.freemarker.TemplateRepresentation; - -import org.apache.log4j.Logger; - -import com.p4square.fmfacade.FreeMarkerPageResource; - -/** - * This resource displays the transitional page between chapters. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class NewBelieverResource extends FreeMarkerPageResource { - private static final Logger LOG = Logger.getLogger(NewBelieverResource.class); - - public static final String COOKIE_NAME = "seeker"; - - private GrowFrontend mGrowFrontend; - - @Override - public void doInit() { - super.doInit(); - - mGrowFrontend = (GrowFrontend) getApplication(); - } - - /** - * Display the New Believer page. - * - * The New Believer page creates a cookie to remember the user, - * explains what's going on, and then asks the user to go to the login - * page. - * - * When the user hits the {@link AccountRedirectResource} the cookie - * is read and the user is moved ahead to the training section. - */ - @Override - protected Representation get() { - Template t = mGrowFrontend.getTemplate("templates/newbeliever.ftl"); - - try { - if (t == null) { - setStatus(Status.CLIENT_ERROR_NOT_FOUND); - return ErrorPage.TEMPLATE_NOT_FOUND; - } - - // Set the new believer cookie - CookieSetting cookie = new CookieSetting(COOKIE_NAME, "true"); - cookie.setPath("/"); - getRequest().getCookies().add(cookie); - getResponse().getCookieSettings().add(cookie); - - return new TemplateRepresentation(t, getRootObject(), MediaType.TEXT_HTML); - - } catch (Exception e) { - LOG.fatal("Could not render page: " + e.getMessage(), e); - setStatus(Status.SERVER_ERROR_INTERNAL); - return ErrorPage.RENDER_ERROR; - } - } -} diff --git a/src/com/p4square/grow/frontend/NotFoundException.java b/src/com/p4square/grow/frontend/NotFoundException.java deleted file mode 100644 index dfa2a4c..0000000 --- a/src/com/p4square/grow/frontend/NotFoundException.java +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.frontend; - -import java.io.IOException; - -public class NotFoundException extends IOException { - public NotFoundException(final String message) { - super(message); - } -} diff --git a/src/com/p4square/grow/frontend/ProgressReporter.java b/src/com/p4square/grow/frontend/ProgressReporter.java deleted file mode 100644 index 2f36832..0000000 --- a/src/com/p4square/grow/frontend/ProgressReporter.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.p4square.grow.frontend; - -import org.restlet.security.User; - -import java.util.Date; - -/** - * A ProgressReporter is used to record a User's progress in a Church Management System. - */ -public interface ProgressReporter { - - /** - * Report that the User completed the assessment. - * - * @param user The user who completed the assessment. - * @param level The assessment level. - * @param date The completion date. - * @param results Result information (e.g. json of the results). - */ - void reportAssessmentComplete(User user, String level, Date date, String results); - - /** - * Report that the User completed the chapter. - * - * @param user The user who completed the chapter. - * @param chapter The chapter completed. - * @param date The completion date. - */ - void reportChapterComplete(User user, String chapter, Date date); -} diff --git a/src/com/p4square/grow/frontend/SurveyPageResource.java b/src/com/p4square/grow/frontend/SurveyPageResource.java deleted file mode 100644 index 3575fe3..0000000 --- a/src/com/p4square/grow/frontend/SurveyPageResource.java +++ /dev/null @@ -1,343 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.frontend; - -import java.io.IOException; - -import java.util.Map; -import java.util.HashMap; - -import freemarker.template.Template; - -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; - -import com.p4square.fmfacade.json.JsonRequestClient; -import com.p4square.fmfacade.json.JsonResponse; -import com.p4square.fmfacade.json.ClientException; - -import com.p4square.fmfacade.FreeMarkerPageResource; - -import com.p4square.grow.config.Config; -import com.p4square.grow.model.Question; -import com.p4square.grow.model.UserRecord; -import com.p4square.grow.provider.DelegateProvider; -import com.p4square.grow.provider.JsonEncodedProvider; -import com.p4square.grow.provider.Provider; - -/** - * SurveyPageResource handles rendering the survey and processing user's answers. - * - * This resource expects the user to be authenticated and the ClientInfo User object - * to be populated. Each question is requested from the backend along with the - * user's previous answer. Each answer is sent to the backend and the user is redirected - * to the next question. After the last question the user is sent to his results. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class SurveyPageResource extends FreeMarkerPageResource { - private static final Logger LOG = Logger.getLogger(SurveyPageResource.class); - - private Config mConfig; - private Template mSurveyTemplate; - private JsonRequestClient mJsonClient; - private Provider<String, Question> mQuestionProvider; - private Provider<String, UserRecord> mUserRecordProvider; - - // Fields pertaining to this request. - private String mQuestionId; - private String mUserId; - - @Override - public void doInit() { - super.doInit(); - - GrowFrontend growFrontend = (GrowFrontend) getApplication(); - mConfig = growFrontend.getConfig(); - mSurveyTemplate = growFrontend.getTemplate("templates/survey.ftl"); - if (mSurveyTemplate == null) { - LOG.fatal("Could not find survey template."); - setStatus(Status.SERVER_ERROR_INTERNAL); - } - - mJsonClient = new JsonRequestClient(getContext().getClientDispatcher()); - mQuestionProvider = new DelegateProvider<String, String, Question>( - new JsonRequestProvider<Question>(getContext().getClientDispatcher(), - Question.class)) { - @Override - public String makeKey(String questionId) { - return getBackendEndpoint() + "/assessment/question/" + questionId; - } - }; - - mUserRecordProvider = new DelegateProvider<String, String, UserRecord>( - new JsonRequestProvider<UserRecord>(getContext().getClientDispatcher(), - UserRecord.class)) { - @Override - public String makeKey(String userid) { - return getBackendEndpoint() + "/accounts/" + userid; - } - }; - - mQuestionId = getAttribute("questionId"); - mUserId = getRequest().getClientInfo().getUser().getIdentifier(); - } - - /** - * Return a page with a survey question. - */ - @Override - protected Representation get() { - try { - // Get the current question. - if (mQuestionId == null) { - // Get user's current question - mQuestionId = getCurrentQuestionId(); - - if (mQuestionId != null) { - Question lastQuestion = getQuestion(mQuestionId); - return redirectToNextQuestion(lastQuestion, getAnswer(mQuestionId)); - } - } - - // If we don't have a current question, get the first one. - if (mQuestionId == null) { - mQuestionId = "first"; - } - - Question question = getQuestion(mQuestionId); - if (question == null) { - setStatus(Status.CLIENT_ERROR_NOT_FOUND); - return new ErrorPage("Could not find the question."); - } - - // Set the real question id if a meta-id was used (i.e. first) - mQuestionId = question.getId(); - - // Get any previous answer to the question - String selectedAnswer = getAnswer(mQuestionId); - - Map root = getRootObject(); - root.put("question", question); - root.put("selectedAnswerId", selectedAnswer); - - // Get the question count and compute progress - { - JsonResponse response = backendGet("/assessment/question/count"); - if (response.getStatus().isSuccess()) { - Map countData = response.getMap(); - if (countData != null) { - response = backendGet("/accounts/" + mUserId + "/assessment"); - if (response.getStatus().isSuccess()) { - Integer completed = (Integer) response.getMap().get("totalAnswers"); - Integer total = (Integer) countData.get("count"); - - if (completed != null && total != null && total != 0) { - root.put("percentComplete", String.valueOf((int) (100.0 * completed) / total)); - } - } - } - } - } - - return new TemplateRepresentation(mSurveyTemplate, root, MediaType.TEXT_HTML); - - } catch (Exception e) { - LOG.fatal("Could not render page: " + e.getMessage(), e); - setStatus(Status.SERVER_ERROR_INTERNAL); - return ErrorPage.RENDER_ERROR; - } - } - - /** - * Record a survey answer and redirect to the next question. - */ - @Override - protected Representation post(Representation entity) { - final Form form = new Form(entity); - final String answerId = form.getFirstValue("answer"); - final String direction = form.getFirstValue("direction"); - boolean justGoBack = false; // FIXME: Ugly hack - - if (mQuestionId == null || answerId == null || answerId.length() == 0) { - if ("previous".equals(direction)) { - // Just go back - justGoBack = true; - - } else { - // Something is wrong. - setStatus(Status.CLIENT_ERROR_BAD_REQUEST); - return new ErrorPage("Question or answer messing."); - } - } - - try { - // Find the question - Question question = getQuestion(mQuestionId); - if (question == null) { - // User is answering a question which doesn't exist - setStatus(Status.CLIENT_ERROR_NOT_FOUND); - return new ErrorPage("Question not found."); - } - - // Store answer - if (!justGoBack) { - Map<String, String> answer = new HashMap<String, String>(); - answer.put("answerId", answerId); - JsonResponse response = backendPut("/accounts/" + mUserId + - "/assessment/answers/" + mQuestionId, answer); - - if (!response.getStatus().isSuccess()) { - // Something went wrong talking to the backend, error out. - LOG.fatal("Error recording survey answer " + response.getStatus()); - setStatus(Status.SERVER_ERROR_INTERNAL); - return ErrorPage.BACKEND_ERROR; - } - } - - // Find the next question or finish the assessment. - if ("previous".equals(direction)) { - return redirectToPreviousQuestion(question); - - } else { - return redirectToNextQuestion(question, answerId); - } - - } catch (Exception e) { - LOG.fatal("Could not render page: " + e.getMessage(), e); - setStatus(Status.SERVER_ERROR_INTERNAL); - return ErrorPage.RENDER_ERROR; - } - } - - private Question getQuestion(String id) { - try { - return mQuestionProvider.get(id); - - } catch (IOException e) { - LOG.warn("Error fetching question.", e); - return null; - } - } - - private String getAnswer(String questionId) { - try { - JsonResponse response = backendGet("/accounts/" + mUserId + "/assessment/answers/" + questionId); - if (response.getStatus().isSuccess()) { - return (String) response.getMap().get("answerId"); - } - - } catch (ClientException e) { - LOG.warn("Error fetching answer to question " + questionId, e); - } - - return null; - } - - private Representation redirectToNextQuestion(Question question, String answerid) { - String nextQuestionId = question.getNextQuestion(answerid); - - if (nextQuestionId == null) { - // Just finished the last question. Update the user's account - try { - UserRecord account = null; - try { - account = mUserRecordProvider.get(mUserId); - } catch (NotFoundException e) { - // User record doesn't exist, so create a new one. - account = new UserRecord(getRequest().getClientInfo().getUser()); - } - account.setLanding("training"); - mUserRecordProvider.put(mUserId, account); - } catch (IOException e) { - LOG.warn("IOException updating landing for " + mUserId, e); - } - - String nextPage = mConfig.getString("dynamicRoot", ""); - nextPage += "/account/assessment/results"; - getResponse().redirectSeeOther(nextPage); - return new StringRepresentation("Redirecting to " + nextPage); - } - - return redirectToQuestion(nextQuestionId); - } - - private Representation redirectToPreviousQuestion(Question question) { - String nextQuestionId = question.getPreviousQuestion(); - - if (nextQuestionId == null) { - nextQuestionId = (String) question.getId(); - } - - return redirectToQuestion(nextQuestionId); - } - - private Representation redirectToQuestion(String id) { - String nextPage = mConfig.getString("dynamicRoot", ""); - nextPage += "/account/assessment/question/" + id; - getResponse().redirectSeeOther(nextPage); - return new StringRepresentation("Redirecting to " + nextPage); - } - - private String getCurrentQuestionId() { - String id = null; - try { - JsonResponse response = backendGet("/accounts/" + mUserId + "/assessment"); - - if (response.getStatus().isSuccess()) { - return (String) response.getMap().get("lastAnswered"); - - } else { - LOG.warn("Failed to get assessment results: " + response.getStatus()); - } - - } catch (ClientException e) { - LOG.error("Exception getting assessment results.", e); - } - - return null; - } - - /** - * @return The backend endpoint URI - */ - private String getBackendEndpoint() { - return mConfig.getString("backendUri", "riap://component/backend"); - } - - /** - * Helper method to send a GET to the backend. - */ - private JsonResponse backendGet(final String uri) { - LOG.debug("Sending backend GET " + uri); - - final JsonResponse response = mJsonClient.get(getBackendEndpoint() + uri); - final Status status = response.getStatus(); - if (!status.isSuccess() && !Status.CLIENT_ERROR_NOT_FOUND.equals(status)) { - LOG.warn("Error making backend request for '" + uri + "'. status = " + response.getStatus().toString()); - } - - return response; - } - - protected JsonResponse backendPut(final String uri, final Map data) { - LOG.debug("Sending backend PUT " + uri); - - final JsonResponse response = mJsonClient.put(getBackendEndpoint() + uri, data); - final Status status = response.getStatus(); - if (!status.isSuccess() && !Status.CLIENT_ERROR_NOT_FOUND.equals(status)) { - LOG.warn("Error making backend request for '" + uri + "'. status = " + response.getStatus().toString()); - } - - return response; - } -} diff --git a/src/com/p4square/grow/frontend/TrainingPageResource.java b/src/com/p4square/grow/frontend/TrainingPageResource.java deleted file mode 100644 index a1e7789..0000000 --- a/src/com/p4square/grow/frontend/TrainingPageResource.java +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.frontend; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -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; - -import com.p4square.fmfacade.json.JsonRequestClient; -import com.p4square.fmfacade.json.JsonResponse; - -import com.p4square.fmfacade.FreeMarkerPageResource; - -import com.p4square.grow.config.Config; -import com.p4square.grow.model.TrainingRecord; -import com.p4square.grow.model.VideoRecord; -import com.p4square.grow.model.Playlist; -import com.p4square.grow.provider.TrainingRecordProvider; -import com.p4square.grow.provider.Provider; - -/** - * TrainingPageResource handles rendering the training page. - * - * This resource expects the user to be authenticated and the ClientInfo User object - * to be populated. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -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<Map<String, Object>> VIDEO_COMPARATOR = new Comparator<Map<String, Object>>() { - @Override - public int compare(Map<String, Object> left, Map<String, Object> 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); - - return Double.compare(leftNumber, rightNumber); - } - }; - - private Config mConfig; - private Template mTrainingTemplate; - private JsonRequestClient mJsonClient; - - private Provider<String, TrainingRecord> mTrainingRecordProvider; - private FeedData mFeedData; - - // Fields pertaining to this request. - protected String mChapter; - protected String mUserId; - - @Override - public void doInit() { - super.doInit(); - - GrowFrontend growFrontend = (GrowFrontend) getApplication(); - mConfig = growFrontend.getConfig(); - mTrainingTemplate = growFrontend.getTemplate("templates/training.ftl"); - if (mTrainingTemplate == null) { - LOG.fatal("Could not find training template."); - setStatus(Status.SERVER_ERROR_INTERNAL); - } - - mJsonClient = new JsonRequestClient(getContext().getClientDispatcher()); - mTrainingRecordProvider = new TrainingRecordProvider<String>(new JsonRequestProvider<TrainingRecord>(getContext().getClientDispatcher(), TrainingRecord.class)) { - @Override - public String makeKey(String userid) { - return getBackendEndpoint() + "/accounts/" + userid + "/training"; - } - }; - - mFeedData = new FeedData(getContext(), mConfig); - - mChapter = getAttribute("chapter"); - mUserId = getRequest().getClientInfo().getUser().getIdentifier(); - } - - /** - * Return a page of videos. - */ - @Override - protected Representation get() { - try { - // Get the training summary - TrainingRecord trainingRecord = mTrainingRecordProvider.get(mUserId); - if (trainingRecord == null) { - setStatus(Status.SERVER_ERROR_INTERNAL); - return new ErrorPage("Could not retrieve TrainingRecord."); - } - - Playlist playlist = trainingRecord.getPlaylist(); - Map<String, Boolean> chapters = playlist.getChapterStatuses(); - Map<String, Boolean> allowedChapters = new LinkedHashMap<String, Boolean>(); - - // The user is not allowed to view chapters after his highest completed chapter. - // In this loop we find which chapters are allowed and check if the user tried - // to skip ahead. - boolean allowUserToSkip = mConfig.getBoolean("allowUserToSkip", false) || getQueryValue("magicskip") != null; - String defaultChapter = null; - boolean userTriedToSkip = false; - int overallProgress = 0; - - boolean foundRequired = false; - for (String chapterId : getChaptersInOrder()) { - boolean allowed = true; - - Boolean completed = chapters.get(chapterId); - if (completed != null) { - if (!foundRequired) { - if (!completed) { - // The first incomplete chapter is the highest allowed chapter. - foundRequired = true; - defaultChapter = chapterId; - } - - } else { - allowed = allowUserToSkip; - - if (!allowUserToSkip && chapterId.equals(mChapter)) { - userTriedToSkip = true; - } - } - - allowedChapters.put(chapterId, allowed); - - if (completed) { - overallProgress++; - } - } - } - - // Overall progress is the percentage of chapters complete - overallProgress = (int) ((double) overallProgress / getChaptersInOrder().length * 100); - - if (defaultChapter == null) { - // Everything is completed... send them back to introduction. - defaultChapter = "introduction"; - } - - if (mChapter == null || userTriedToSkip) { - // No chapter was specified or the user tried to skip ahead. - // Either case, redirect. - String nextPage = mConfig.getString("dynamicRoot", ""); - nextPage += "/account/training/" + defaultChapter; - getResponse().redirectSeeOther(nextPage); - return new StringRepresentation("Redirecting to " + nextPage); - } - - - // Get videos for the chapter. - List<Map<String, Object>> videos = null; - { - JsonResponse response = backendGet("/training/" + mChapter); - if (!response.getStatus().isSuccess()) { - setStatus(Status.CLIENT_ERROR_NOT_FOUND); - return null; - } - videos = (List<Map<String, Object>>) response.getMap().get("videos"); - Collections.sort(videos, VIDEO_COMPARATOR); - } - - // Mark the completed videos as completed - int chapterProgress = 0; - for (Map<String, Object> video : videos) { - boolean completed = false; - VideoRecord record = playlist.find((String) video.get("id")); - LOG.info("VideoId: " + video.get("id")); - if (record != null) { - LOG.info("VideoRecord: " + record.getComplete()); - completed = record.getComplete(); - } - video.put("completed", completed); - - if (completed) { - chapterProgress++; - } - } - chapterProgress = chapterProgress * 100 / videos.size(); - - Map root = getRootObject(); - root.put("chapter", mChapter); - root.put("chapters", allowedChapters.keySet()); - root.put("isChapterAllowed", allowedChapters); - root.put("chapterProgress", chapterProgress); - root.put("overallProgress", overallProgress); - root.put("videos", videos); - root.put("allowUserToSkip", allowUserToSkip); - - // Determine if we should show the feed. - boolean showfeed = true; - - // Don't show the feed if the topic isn't allowed. - if (!FeedData.TOPICS.contains(mChapter)) { - showfeed = false; - } - - root.put("showfeed", showfeed); - if (showfeed) { - root.put("feeddata", mFeedData); - } - - return new TemplateRepresentation(mTrainingTemplate, root, MediaType.TEXT_HTML); - - } catch (Exception e) { - LOG.fatal("Could not render page: " + e.getMessage(), e); - setStatus(Status.SERVER_ERROR_INTERNAL); - return ErrorPage.RENDER_ERROR; - } - } - - /** - * This method returns a list of chapters in the correct order. - */ - protected String[] getChaptersInOrder() { - return CHAPTERS; - } - - /** - * @return The backend endpoint URI - */ - private String getBackendEndpoint() { - return mConfig.getString("backendUri", "riap://component/backend"); - } - - /** - * Helper method to send a GET to the backend. - */ - private JsonResponse backendGet(final String uri) { - LOG.debug("Sending backend GET " + uri); - - final JsonResponse response = mJsonClient.get(getBackendEndpoint() + uri); - final Status status = response.getStatus(); - if (!status.isSuccess() && !Status.CLIENT_ERROR_NOT_FOUND.equals(status)) { - LOG.warn("Error making backend request for '" + uri + "'. status = " + response.getStatus().toString()); - } - - return response; - } - -} diff --git a/src/com/p4square/grow/frontend/VideosResource.java b/src/com/p4square/grow/frontend/VideosResource.java deleted file mode 100644 index 2099a77..0000000 --- a/src/com/p4square/grow/frontend/VideosResource.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.frontend; - -import java.util.HashMap; -import java.util.Map; - -import freemarker.template.Template; - -import org.restlet.data.Form; -import org.restlet.data.MediaType; -import org.restlet.data.Status; -import org.restlet.ext.jackson.JacksonRepresentation; -import org.restlet.representation.Representation; -import org.restlet.resource.ServerResource; - -import org.apache.log4j.Logger; - -import com.p4square.fmfacade.json.JsonRequestClient; -import com.p4square.fmfacade.json.JsonResponse; - -import com.p4square.grow.config.Config; - -/** - * VideosResource returns JSON blobs with video information and records watched - * videos. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class VideosResource extends ServerResource { - private static Logger cLog = Logger.getLogger(VideosResource.class); - - private Config mConfig; - private JsonRequestClient mJsonClient; - - // Fields pertaining to this request. - private String mChapter; - private String mVideoId; - private String mUserId; - - @Override - public void doInit() { - super.doInit(); - - GrowFrontend growFrontend = (GrowFrontend) getApplication(); - mConfig = growFrontend.getConfig(); - - mJsonClient = new JsonRequestClient(getContext().getClientDispatcher()); - - mChapter = getAttribute("chapter"); - mVideoId = getAttribute("videoId"); - mUserId = getRequest().getClientInfo().getUser().getIdentifier(); - } - - /** - * Fetch a video record from the backend. - */ - @Override - protected Representation get() { - try { - JsonResponse response = backendGet("/training/" + mChapter + "/videos/" + mVideoId); - - if (response.getStatus().isSuccess()) { - return new JacksonRepresentation<Map>(response.getMap()); - - } else { - setStatus(response.getStatus()); - return null; - } - - } catch (Exception e) { - cLog.fatal("Could not render page: " + e.getMessage(), e); - setStatus(Status.SERVER_ERROR_INTERNAL); - return null; - } - } - - /** - * Mark a video as completed. - */ - @Override - protected Representation post(Representation entity) { - Map<String, Object> data = new HashMap<String, Object>(); - data.put("complete", "true"); - JsonResponse response = backendPut("/accounts/" + mUserId + "/training/videos/" + mVideoId, data); - - if (!response.getStatus().isSuccess()) { - // Something went wrong talking to the backend, error out. - cLog.fatal("Error recording completed video " + response.getStatus()); - setStatus(Status.SERVER_ERROR_INTERNAL); - return ErrorPage.BACKEND_ERROR; - } - - setStatus(Status.SUCCESS_NO_CONTENT); - return null; - } - - /** - * @return The backend endpoint URI - */ - private String getBackendEndpoint() { - return mConfig.getString("backendUri", "riap://component/backend"); - } - - /** - * Helper method to send a GET to the backend. - */ - private JsonResponse backendGet(final String uri) { - cLog.debug("Sending backend GET " + uri); - - final JsonResponse response = mJsonClient.get(getBackendEndpoint() + uri); - final Status status = response.getStatus(); - if (!status.isSuccess() && !Status.CLIENT_ERROR_NOT_FOUND.equals(status)) { - cLog.warn("Error making backend request for '" + uri + "'. status = " + response.getStatus().toString()); - } - - return response; - } - - private JsonResponse backendPut(final String uri, final Map data) { - cLog.debug("Sending backend PUT " + uri); - - final JsonResponse response = mJsonClient.put(getBackendEndpoint() + uri, data); - final Status status = response.getStatus(); - if (!status.isSuccess() && !Status.CLIENT_ERROR_NOT_FOUND.equals(status)) { - cLog.warn("Error making backend request for '" + uri + "'. status = " + response.getStatus().toString()); - } - - return response; - } -} diff --git a/src/com/p4square/grow/model/Answer.java b/src/com/p4square/grow/model/Answer.java deleted file mode 100644 index a818365..0000000 --- a/src/com/p4square/grow/model/Answer.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.model; - -import org.apache.log4j.Logger; - -/** - * This is the model of an assessment question's answer. - * - * @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 { - /** - * This question has no effect on the score. - */ - NONE, - - /** - * The score of this question is part of the average. - */ - AVERAGE, - - /** - * The score of this question is the total score, no other questions - * matter after this point. - */ - TRUMP; - - @Override - public String toString() { - return name().toLowerCase(); - } - } - - 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; - } - - /** - * 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.debug("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/Banner.java b/src/com/p4square/grow/model/Banner.java deleted file mode 100644 index b786b36..0000000 --- a/src/com/p4square/grow/model/Banner.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.model; - -/** - * Page Banner Data. - */ -public class Banner { - private String mHtml; - - public String getHtml() { - return mHtml; - } - - public void setHtml(final String html) { - mHtml = html; - } -} diff --git a/src/com/p4square/grow/model/Chapter.java b/src/com/p4square/grow/model/Chapter.java deleted file mode 100644 index 3a08e4c..0000000 --- a/src/com/p4square/grow/model/Chapter.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.model; - -import java.util.HashMap; -import java.util.Map; - -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonIgnore; - -/** - * Chapter is a list of VideoRecords in a Playlist. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class Chapter implements Cloneable { - private String mName; - private Map<String, VideoRecord> mVideos; - - public Chapter(String name) { - mName = name; - mVideos = new HashMap<String, VideoRecord>(); - } - - /** - * Private constructor for JSON decoding. - */ - private Chapter() { - this(null); - } - - /** - * @return The Chapter name. - */ - public String getName() { - return mName; - } - - /** - * Set the chapter name. - * - * @param name The name of the chapter. - */ - public void setName(final String name) { - mName = name; - } - - /** - * @return The VideoRecord for videoid or null if videoid is not in the chapter. - */ - public VideoRecord getVideoRecord(String videoid) { - return mVideos.get(videoid); - } - - /** - * @return A map of video ids to VideoRecords. - */ - @JsonAnyGetter - public Map<String, VideoRecord> getVideos() { - return mVideos; - } - - /** - * Set the VideoRecord for a video id. - * @param videoId the video id. - * @param video the VideoRecord. - */ - @JsonAnySetter - public void setVideoRecord(String videoId, VideoRecord video) { - mVideos.put(videoId, video); - } - - /** - * Remove the VideoRecord for a video id. - * @param videoId The id to remove. - */ - public void removeVideoRecord(String videoId) { - mVideos.remove(videoId); - } - - /** - * @return true if every required video has been completed. - */ - @JsonIgnore - public boolean isComplete() { - boolean complete = true; - - for (VideoRecord r : mVideos.values()) { - if (r.getRequired() && !r.getComplete()) { - return false; - } - } - - return complete; - } - - /** - * Deeply clone a chapter. - * - * @return a new Chapter object identical but independent of this one. - */ - public Chapter clone() throws CloneNotSupportedException { - Chapter c = new Chapter(mName); - for (Map.Entry<String, VideoRecord> videoEntry : mVideos.entrySet()) { - c.setVideoRecord(videoEntry.getKey(), videoEntry.getValue().clone()); - } - return c; - } -} diff --git a/src/com/p4square/grow/model/CircleQuestion.java b/src/com/p4square/grow/model/CircleQuestion.java deleted file mode 100644 index 71acc14..0000000 --- a/src/com/p4square/grow/model/CircleQuestion.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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 deleted file mode 100644 index d94c32c..0000000 --- a/src/com/p4square/grow/model/ImageQuestion.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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/Message.java b/src/com/p4square/grow/model/Message.java deleted file mode 100644 index 9d33320..0000000 --- a/src/com/p4square/grow/model/Message.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.model; - -import java.util.Date; -import java.util.UUID; - -/** - * A feed message. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class Message { - private String mThreadId; - private String mId; - private UserRecord mAuthor; - private Date mCreated; - private String mMessage; - - /** - * @return a new message id. - */ - public static String generateId() { - return String.format("%x-%s", System.currentTimeMillis(), UUID.randomUUID().toString()); - } - - /** - * @return The id of the thread that the message belongs to. - */ - public String getThreadId() { - return mThreadId; - } - - /** - * Set the id of the thread that the message belongs to. - * @param id The new thread id. - */ - public void setThreadId(String id) { - mThreadId = id; - } - - /** - * @return The id the message. - */ - public String getId() { - return mId; - } - - /** - * Set the id of the message. - * @param id The new message id. - */ - public void setId(String id) { - mId = id; - } - - /** - * @return The author of the message. - */ - public UserRecord getAuthor() { - return mAuthor; - } - - /** - * Set the author of the message. - * @param author The new author. - */ - public void setAuthor(UserRecord author) { - mAuthor = author; - } - - /** - * @return The Date the message was created. - */ - public Date getCreated() { - return mCreated; - } - - /** - * Set the Date the message was created. - * @param date The new creation date. - */ - public void setCreated(Date date) { - mCreated = date; - } - - /** - * @return The message text. - */ - public String getMessage() { - return mMessage; - } - - /** - * Set the message text. - * @param text The message text. - */ - public void setMessage(String text) { - mMessage = text; - } -} diff --git a/src/com/p4square/grow/model/MessageThread.java b/src/com/p4square/grow/model/MessageThread.java deleted file mode 100644 index 9542a18..0000000 --- a/src/com/p4square/grow/model/MessageThread.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.model; - -import java.util.UUID; - -/** - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class MessageThread { - private String mId; - private Message mMessage; - - /** - * Create a new thread with a probably unique id. - * - * @return the new thread. - */ - public static MessageThread createNew() { - MessageThread t = new MessageThread(); - // IDs are keyed to sort lexicographically from latest to oldest. - t.setId(String.format("%016x-%s", Long.MAX_VALUE - System.currentTimeMillis(), - UUID.randomUUID().toString())); - - return t; - } - - /** - * @return The id the message. - */ - public String getId() { - return mId; - } - - /** - * Set the id of the message. - * @param id The new message id. - */ - public void setId(String id) { - mId = id; - } - - /** - * @return The original message. - */ - public Message getMessage() { - return mMessage; - } - - /** - * Set the original message. - * @param id The new message. - */ - public void setMessage(Message message) { - mMessage = message; - } -} diff --git a/src/com/p4square/grow/model/Playlist.java b/src/com/p4square/grow/model/Playlist.java deleted file mode 100644 index 3e77ada..0000000 --- a/src/com/p4square/grow/model/Playlist.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.model; - -import java.util.Date; -import java.util.HashMap; -import java.util.Map; - -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonIgnore; - -/** - * Representation of a user's playlist. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class Playlist { - /** - * Map of Chapter ID to map of Video ID to VideoRecord. - */ - private Map<String, Chapter> mPlaylist; - - private Date mLastUpdated; - - /** - * Construct an empty playlist. - */ - public Playlist() { - mPlaylist = new HashMap<String, Chapter>(); - mLastUpdated = new Date(0); // Default to a prehistoric date if we don't have one. - } - - /** - * Find the VideoRecord for a video id. - */ - public VideoRecord find(String videoId) { - for (Chapter chapter : mPlaylist.values()) { - VideoRecord r = chapter.getVideoRecord(videoId); - - if (r != null) { - return r; - } - } - - return null; - } - - /** - * @param videoId The video to search for. - * @return the Chapter containing videoId. - */ - private Chapter findChapter(String videoId) { - for (Chapter chapter : mPlaylist.values()) { - VideoRecord r = chapter.getVideoRecord(videoId); - - if (r != null) { - return chapter; - } - } - - return null; - } - - /** - * @return The last modified date of the source playlist. - */ - public Date getLastUpdated() { - return mLastUpdated; - } - - /** - * Set the last updated date. - * @param date the new last updated date. - */ - public void setLastUpdated(Date date) { - mLastUpdated = date; - } - - /** - * Add a video to the playlist. - */ - public VideoRecord add(String chapterId, String videoId) { - Chapter chapter = mPlaylist.get(chapterId); - - if (chapter == null) { - chapter = new Chapter(chapterId); - mPlaylist.put(chapterId, chapter); - } - - VideoRecord r = new VideoRecord(); - chapter.setVideoRecord(videoId, r); - return r; - } - - /** - * Add a Chapter to the Playlist. - * @param chapterId The name of the chapter. - * @param chapter The Chapter object to add. - */ - @JsonAnySetter - public void addChapter(String chapterId, Chapter chapter) { - chapter.setName(chapterId); - mPlaylist.put(chapterId, chapter); - } - - /** - * @return a map of chapter id to chapter. - */ - @JsonAnyGetter - public Map<String, Chapter> getChaptersMap() { - return mPlaylist; - } - - /** - * @return The last chapter to be completed. - */ - @JsonIgnore - public Map<String, Boolean> getChapterStatuses() { - Map<String, Boolean> completed = new HashMap<String, Boolean>(); - - for (Map.Entry<String, Chapter> entry : mPlaylist.entrySet()) { - completed.put(entry.getKey(), entry.getValue().isComplete()); - } - - return completed; - } - - /** - * @return true if all required videos in the chapter have been watched. - */ - public boolean isChapterComplete(String chapterId) { - Chapter chapter = mPlaylist.get(chapterId); - if (chapter != null) { - return chapter.isComplete(); - } - - return false; - } - - /** - * Merge a playlist into this playlist. - * - * Merge is accomplished by adding all missing Chapters and VideoRecords to - * this playlist. - */ - public void merge(Playlist source) { - if (source.getLastUpdated().before(mLastUpdated)) { - // Already up to date. - return; - } - - for (Map.Entry<String, Chapter> entry : source.getChaptersMap().entrySet()) { - String chapterName = entry.getKey(); - Chapter theirChapter = entry.getValue(); - Chapter myChapter = mPlaylist.get(entry.getKey()); - - if (myChapter == null) { - // Add new chapter - myChapter = new Chapter(chapterName); - addChapter(chapterName, myChapter); - } - - // Check chapter for missing videos - for (Map.Entry<String, VideoRecord> videoEntry : theirChapter.getVideos().entrySet()) { - String videoId = videoEntry.getKey(); - VideoRecord myVideo = myChapter.getVideoRecord(videoId); - - if (myVideo == null) { - myVideo = find(videoId); - if (myVideo == null) { - // New Video - try { - myVideo = videoEntry.getValue().clone(); - myChapter.setVideoRecord(videoId, myVideo); - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); // Unexpected... - } - } else { - // Video moved - findChapter(videoId).removeVideoRecord(videoId); - myChapter.setVideoRecord(videoId, myVideo); - } - } - } - } - - mLastUpdated = source.getLastUpdated(); - } -} diff --git a/src/com/p4square/grow/model/Point.java b/src/com/p4square/grow/model/Point.java deleted file mode 100644 index e9fc0ca..0000000 --- a/src/com/p4square/grow/model/Point.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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 deleted file mode 100644 index a7b4179..0000000 --- a/src/com/p4square/grow/model/QuadQuestion.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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 deleted file mode 100644 index 33403b5..0000000 --- a/src/com/p4square/grow/model/QuadScoringEngine.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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 deleted file mode 100644 index f4b9458..0000000 --- a/src/com/p4square/grow/model/Question.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.model; - -import java.util.HashMap; -import java.util.Map; - -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonSubTypes.Type; -import com.fasterxml.jackson.annotation.JsonTypeInfo; - -/** - * Model of an assessment question. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -@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 String mQuestionId; - private QuestionType mType; - private String mQuestionText; - private Map<String, Answer> mAnswers; - - private String mPreviousQuestionId; - private String mNextQuestionId; - - public Question() { - mAnswers = new HashMap<String, Answer>(); - } - - /** - * @return the id String for this question. - */ - public String getId() { - return mQuestionId; - } - - /** - * Set the id String for this question. - * @param id New id - */ - public void setId(String id) { - mQuestionId = id; - } - - /** - * @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; - } - - /** - * 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; - } - - /** - * @return a map of Answer id Strings to Answer objects. - */ - public Map<String, Answer> getAnswers() { - return mAnswers; - } - - /** - * Determine the id of the next question based on the answer to this - * question. - * - * @param answerid - * The id of the selected answer. - * @return a question id or null if this is the last question. - */ - public String getNextQuestion(String answerid) { - String nextQuestion = null; - - Answer a = mAnswers.get(answerid); - if (a != null) { - nextQuestion = a.getNextQuestion(); - } - - if (nextQuestion == null) { - nextQuestion = mNextQuestionId; - } - - 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 deleted file mode 100644 index 7d9905d..0000000 --- a/src/com/p4square/grow/model/RecordedAnswer.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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 deleted file mode 100644 index 031c309..0000000 --- a/src/com/p4square/grow/model/Score.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * 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 decimal value for the given Score String. - * - * This method satisfies the invariant for Score x: - * numericScore(x.toString()) <= x.getScore() - */ - public static double numericScore(String score) { - score = score.toLowerCase(); - - if ("teacher".equals(score)) { - return 3.5; - } else if ("disciple".equals(score)) { - return 2.5; - } else if ("believer".equals(score)) { - return 1.5; - } else if ("seeker".equals(score)) { - return 0; - } else { - return Integer.MAX_VALUE; - } - } - - double sum; - int count; - - public Score() { - sum = 0; - count = 0; - } - - public Score(double sum, int count) { - this.sum = sum; - this.count = count; - } - - /** - * Copy Constructor. - */ - public Score(Score other) { - sum = other.sum; - count = other.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() { - if (count == 0) { - return 0; - } - - return sum / count; - } - - /** - * @return the lowest score in the same category as this score. - */ - public double floor() { - final double score = getScore(); - - if (score >= 3.5) { - return 3.5; // teacher - - } else if (score >= 2.5) { - return 2.5; // disciple - - } else if (score >= 1.5) { - return 1.5; // believer - - } else { - return 0; // seeker - } - } - - @Override - public String toString() { - final double score = getScore(); - - if (score >= 3.5) { - return "teacher"; - - } else if (score >= 2.5) { - return "disciple"; - - } else if (score >= 1.5) { - return "believer"; - - } else { - return "seeker"; - } - } - -} diff --git a/src/com/p4square/grow/model/ScoringEngine.java b/src/com/p4square/grow/model/ScoringEngine.java deleted file mode 100644 index 8ff18b3..0000000 --- a/src/com/p4square/grow/model/ScoringEngine.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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 deleted file mode 100644 index 6ef2dbb..0000000 --- a/src/com/p4square/grow/model/SimpleScoringEngine.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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 deleted file mode 100644 index f0861e3..0000000 --- a/src/com/p4square/grow/model/SliderQuestion.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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 deleted file mode 100644 index 2961e95..0000000 --- a/src/com/p4square/grow/model/SliderScoringEngine.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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) { - int numberOfAnswers = question.getAnswers().size(); - if (numberOfAnswers == 0) { - throw new IllegalArgumentException("Question has no answers."); - } - - double answer = Double.valueOf(userAnswer.getAnswerId()); - if (answer < 0 || answer > 1) { - throw new IllegalArgumentException("Answer out of bounds."); - } - - double delta = Math.max(1, Math.ceil(answer * numberOfAnswers) / numberOfAnswers * 4); - - 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 deleted file mode 100644 index 88c2a34..0000000 --- a/src/com/p4square/grow/model/TextQuestion.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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; - } -} diff --git a/src/com/p4square/grow/model/TrainingRecord.java b/src/com/p4square/grow/model/TrainingRecord.java deleted file mode 100644 index bc3ffa9..0000000 --- a/src/com/p4square/grow/model/TrainingRecord.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.model; - -/** - * Representation of a user's training record. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class TrainingRecord { - private String mLastVideo; - private Playlist mPlaylist; - - public TrainingRecord() { - mPlaylist = new Playlist(); - } - - /** - * @return Video id of the last video watched. - */ - public String getLastVideo() { - return mLastVideo; - } - - /** - * Set the video id for the last video watched. - * @param video The new video id. - */ - public void setLastVideo(String video) { - mLastVideo = video; - } - - /** - * @return the user's Playlist. - */ - public Playlist getPlaylist() { - return mPlaylist; - } - - /** - * Set the user's playlist. - * @param playlist The new playlist. - */ - public void setPlaylist(Playlist playlist) { - mPlaylist = playlist; - } -} diff --git a/src/com/p4square/grow/model/UserRecord.java b/src/com/p4square/grow/model/UserRecord.java deleted file mode 100644 index 4399282..0000000 --- a/src/com/p4square/grow/model/UserRecord.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.model; - -import java.io.IOException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -import org.apache.commons.codec.binary.Hex; - -import org.restlet.security.User; - -/** - * A simple user representation without any secrets. - */ -public class UserRecord { - private String mId; - private String mFirstName; - private String mLastName; - private String mEmail; - private String mLanding; - private boolean mNewBeliever; - - // Backend Access - private String mBackendPasswordHash; - - /** - * Create an empty UserRecord. - */ - public UserRecord() { - } - - /** - * Create a new UserRecord with the information from a User. - */ - public UserRecord(final User user) { - mId = user.getIdentifier(); - mFirstName = user.getFirstName(); - mLastName = user.getLastName(); - mEmail = user.getEmail(); - } - - /** - * @return The user's identifier. - */ - public String getId() { - return mId; - } - - /** - * Set the user's identifier. - * @param value The new id. - */ - public void setId(final String value) { - mId = value; - } - - /** - * @return The user's email. - */ - public String getEmail() { - return mEmail; - } - - /** - * Set the user's email. - * @param value The new email. - */ - public void setEmail(final String value) { - mEmail = value; - } - - /** - * @return The user's first name. - */ - public String getFirstName() { - return mFirstName; - } - - /** - * Set the user's first name. - * @param value The new first name. - */ - public void setFirstName(final String value) { - mFirstName = value; - } - - /** - * @return The user's last name. - */ - public String getLastName() { - return mLastName; - } - - /** - * Set the user's last name. - * @param value The new last name. - */ - public void setLastName(final String value) { - mLastName = value; - } - - /** - * @return The user's landing page. - */ - public String getLanding() { - return mLanding; - } - - /** - * Set the user's landing page. - * @param value The new landing page. - */ - public void setLanding(final String value) { - mLanding = value; - } - - /** - * @return true if the user came from the New Believer's landing. - */ - public boolean getNewBeliever() { - return mNewBeliever; - } - - /** - * Set the user's new believer flag. - * @param value The new flag. - */ - public void setNewBeliever(final boolean value) { - mNewBeliever = value; - } - - /** - * @return The user's backend password hash, null if he doesn't have - * access. - */ - public String getBackendPasswordHash() { - return mBackendPasswordHash; - } - - /** - * Set the user's backend password hash. - * @param value The new backend password hash or null to remove - * access. - */ - public void setBackendPasswordHash(final String value) { - mBackendPasswordHash = value; - } - - /** - * Set the user's backend password to the clear-text value given. - * @param value The new backend password. - */ - public void setBackendPassword(final String value) { - try { - mBackendPasswordHash = hashPassword(value); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } - } - - /** - * Hash the given secret. - */ - public static String hashPassword(final String secret) throws NoSuchAlgorithmException { - MessageDigest md = MessageDigest.getInstance("SHA-1"); - - // Convert the char[] to byte[] - // FIXME This approach is incorrectly truncating multibyte - // characters. - byte[] b = new byte[secret.length()]; - for (int i = 0; i < secret.length(); i++) { - b[i] = (byte) secret.charAt(i); - } - - md.update(b); - - byte[] hash = md.digest(); - return new String(Hex.encodeHex(hash)); - } -} diff --git a/src/com/p4square/grow/model/VideoRecord.java b/src/com/p4square/grow/model/VideoRecord.java deleted file mode 100644 index ec99d0d..0000000 --- a/src/com/p4square/grow/model/VideoRecord.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.model; - -import java.util.Date; - -import com.fasterxml.jackson.annotation.JsonIgnore; - -/** - * Simple bean containing video completion data. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class VideoRecord implements Cloneable { - private Boolean mComplete; - private Boolean mRequired; - private Date mCompletionDate; - - public VideoRecord() { - mComplete = null; - mRequired = null; - mCompletionDate = null; - } - - public boolean getComplete() { - if (mComplete == null) { - return false; - } - return mComplete; - } - - public void setComplete(boolean complete) { - mComplete = complete; - } - - @JsonIgnore - public boolean isCompleteSet() { - return mComplete != null; - } - - public boolean getRequired() { - if (mRequired == null) { - return true; - } - return mRequired; - } - - public void setRequired(boolean complete) { - mRequired = complete; - } - - @JsonIgnore - public boolean isRequiredSet() { - return mRequired != null; - } - - 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(); - } - - /** - * @return an identical clone of this record. - */ - public VideoRecord clone() throws CloneNotSupportedException { - VideoRecord r = (VideoRecord) super.clone(); - r.mComplete = mComplete; - r.mRequired = mRequired; - r.mCompletionDate = mCompletionDate; - return r; - } -} diff --git a/src/com/p4square/grow/provider/CollectionProvider.java b/src/com/p4square/grow/provider/CollectionProvider.java deleted file mode 100644 index e4e9040..0000000 --- a/src/com/p4square/grow/provider/CollectionProvider.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.provider; - -import java.io.IOException; -import java.util.Map; - -/** - * ListProvider is the logical extension of Provider for dealing with lists of - * items. - * - * @param C The type of the collection key. - * @param K The type of the item key. - * @param V The type of the value. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public interface CollectionProvider<C, K, V> { - /** - * Retrieve a specific object from the collection. - * - * @param collection The collection key. - * @param key The key for the object in the collection. - * @return The object or null if not found. - */ - V get(C collection, K key) throws IOException; - - /** - * Retrieve a collection. - * - * The returned map will never be null. - * - * @param collection The collection key. - * @return A Map of keys to values. - */ - Map<K, V> query(C collection) throws IOException; - - /** - * Retrieve a portion of a collection. - * - * The returned map will never be null. - * - * @param collection The collection key. - * @param limit Max number of items to return. - * @return A Map of keys to values. - */ - Map<K, V> query(C collection, int limit) throws IOException; - - /** - * Persist the object with the given key. - * - * @param collection The collection key. - * @param key The key for the object in the collection. - * @param obj The object to persist. - */ - void put(C collection, K key, V obj) throws IOException; -} diff --git a/src/com/p4square/grow/provider/DelegateCollectionProvider.java b/src/com/p4square/grow/provider/DelegateCollectionProvider.java deleted file mode 100644 index cf697ba..0000000 --- a/src/com/p4square/grow/provider/DelegateCollectionProvider.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.provider; - -import java.io.IOException; -import java.util.LinkedHashMap; -import java.util.Map; - -/** - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public abstract class DelegateCollectionProvider<C, DC, K, DK, V> - implements CollectionProvider<C, K, V> { - - private CollectionProvider<DC, DK, V> mProvider; - - public DelegateCollectionProvider(final CollectionProvider<DC, DK, V> provider) { - mProvider = provider; - } - - public V get(C collection, K key) throws IOException { - return mProvider.get(makeCollectionKey(collection), makeKey(key)); - } - - public Map<K, V> query(C collection) throws IOException { - return query(collection, -1); - } - - public Map<K, V> query(C collection, int limit) throws IOException { - Map<DK, V> delegateResult = mProvider.query(makeCollectionKey(collection), limit); - Map<K, V> result = new LinkedHashMap<>(); - for (Map.Entry<DK, V> entry : delegateResult.entrySet()) { - result.put(unmakeKey(entry.getKey()), entry.getValue()); - } - - return result; - } - - public void put(C collection, K key, V obj) throws IOException { - mProvider.put(makeCollectionKey(collection), makeKey(key), obj); - } - - /** - * Make a collection key for the delegated provider. - * - * @param input The pre-transform key. - * @return the post-transform key. - */ - protected abstract DC makeCollectionKey(final C input); - - /** - * Make a key for the delegated provider. - * - * @param input The pre-transform key. - * @return the post-transform key. - */ - protected abstract DK makeKey(final K input); - - /** - * Transform a key for the delegated provider to an input key. - * - * @param input The post-transform key. - * @return the pre-transform key. - */ - protected abstract K unmakeKey(final DK input); -} diff --git a/src/com/p4square/grow/provider/DelegateProvider.java b/src/com/p4square/grow/provider/DelegateProvider.java deleted file mode 100644 index 42dcc63..0000000 --- a/src/com/p4square/grow/provider/DelegateProvider.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.provider; - -import java.io.IOException; - -/** - * DelegateProvider wraps an existing Provider an transforms the key from - * type K to type D. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public abstract class DelegateProvider<K, D, V> implements Provider<K, V> { - - private Provider<D, V> mProvider; - - public DelegateProvider(final Provider<D, V> provider) { - mProvider = provider; - } - - @Override - public V get(final K key) throws IOException { - return mProvider.get(makeKey(key)); - } - - @Override - public void put(final K key, final V obj) throws IOException { - mProvider.put(makeKey(key), obj); - } - - /** - * Make a Key for the delegated provider. - * - * @param input The pre-transform key. - * @return the post-transform key. - */ - protected abstract D makeKey(final K input); -} diff --git a/src/com/p4square/grow/provider/JsonEncodedProvider.java b/src/com/p4square/grow/provider/JsonEncodedProvider.java deleted file mode 100644 index 500f761..0000000 --- a/src/com/p4square/grow/provider/JsonEncodedProvider.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.provider; - -import java.io.IOException; - -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; - -/** - * Provider provides a simple interface for loading and persisting - * objects. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public abstract class JsonEncodedProvider<V> { - public static final ObjectMapper MAPPER = new ObjectMapper(); - static { - MAPPER.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true); - MAPPER.configure(DeserializationFeature.READ_ENUMS_USING_TO_STRING, true); - MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - } - - protected final Class<V> mClazz; - protected final JavaType mType; - - public JsonEncodedProvider(Class<V> clazz) { - mClazz = clazz; - mType = null; - } - - public JsonEncodedProvider(JavaType type) { - mType = type; - mClazz = null; - } - - /** - * Encode the object as JSON. - * - * @param obj The object to encode. - * @return The JSON encoding of obj. - * @throws IOException if the object cannot be encoded. - */ - protected String encode(V obj) throws IOException { - if (mClazz == String.class) { - return (String) obj; - } - - return MAPPER.writeValueAsString(obj); - } - - /** - * Decode the JSON string as an object. - * - * @param blob The JSON data to decode. - * @return The decoded object or null if blob is null. - * @throws IOException If an object cannot be decoded. - */ - protected V decode(String blob) throws IOException { - if (blob == null) { - return null; - } - - if (mClazz == String.class) { - return (V) blob; - } - - V obj; - if (mClazz != null) { - obj = MAPPER.readValue(blob, mClazz); - - } else { - obj = MAPPER.readValue(blob, mType); - } - - return obj; - } -} - diff --git a/src/com/p4square/grow/provider/MapCollectionProvider.java b/src/com/p4square/grow/provider/MapCollectionProvider.java deleted file mode 100644 index 4c5cef6..0000000 --- a/src/com/p4square/grow/provider/MapCollectionProvider.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2015 Jesse Morgan - */ - -package com.p4square.grow.provider; - -import java.io.IOException; -import java.util.Iterator; -import java.util.Map; -import java.util.HashMap; - -/** - * In-memory CollectionProvider implementation, useful for tests. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class MapCollectionProvider<C, K, V> implements CollectionProvider<C, K, V> { - private final Map<C, Map<K, V>> mMap; - - public MapCollectionProvider() { - mMap = new HashMap<>(); - } - - @Override - public synchronized V get(C collection, K key) throws IOException { - Map<K, V> map = mMap.get(collection); - if (map != null) { - return map.get(key); - } - - return null; - } - - @Override - public synchronized Map<K, V> query(C collection) throws IOException { - Map<K, V> map = mMap.get(collection); - if (map == null) { - map = new HashMap<K, V>(); - } - - return map; - } - - @Override - public synchronized Map<K, V> query(C collection, int limit) throws IOException { - Map<K, V> map = query(collection); - - if (map.size() > limit) { - Map<K, V> smallMap = new HashMap<>(); - - Iterator<Map.Entry<K, V>> iterator = map.entrySet().iterator(); - for (int i = 0; i < limit; i++) { - Map.Entry<K, V> entry = iterator.next(); - smallMap.put(entry.getKey(), entry.getValue()); - } - - return smallMap; - - } else { - return map; - } - } - - @Override - public synchronized void put(C collection, K key, V obj) throws IOException { - Map<K, V> map = mMap.get(collection); - if (map == null) { - map = new HashMap<K, V>(); - mMap.put(collection, map); - } - - map.put(key, obj); - } -} diff --git a/src/com/p4square/grow/provider/MapProvider.java b/src/com/p4square/grow/provider/MapProvider.java deleted file mode 100644 index 40f8107..0000000 --- a/src/com/p4square/grow/provider/MapProvider.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2015 Jesse Morgan - */ - -package com.p4square.grow.provider; - -import java.io.IOException; -import java.util.Map; -import java.util.HashMap; - -/** - * In-memory Provider implementation, useful for tests. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class MapProvider<K, V> implements Provider<K, V> { - private final Map<K, V> mMap = new HashMap<K, V>(); - - @Override - public V get(K key) throws IOException { - return mMap.get(key); - } - - @Override - public void put(K key, V obj) throws IOException { - mMap.put(key, obj); - } -} diff --git a/src/com/p4square/grow/provider/Provider.java b/src/com/p4square/grow/provider/Provider.java deleted file mode 100644 index ca6af25..0000000 --- a/src/com/p4square/grow/provider/Provider.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.provider; - -import java.io.IOException; - -/** - * Provider provides a simple interface for loading and persisting - * objects. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public interface Provider<K, V> { - /** - * Retrieve the object with the given key. - * - * @param key The key for the object. - * @return The object or null if not found. - */ - V get(K key) throws IOException; - - /** - * Persist the object with the given key. - * - * @param key The key for the object. - * @param obj The object to persist. - */ - void put(K key, V obj) throws IOException; -} diff --git a/src/com/p4square/grow/provider/ProvidesAssessments.java b/src/com/p4square/grow/provider/ProvidesAssessments.java deleted file mode 100644 index 62ba8f6..0000000 --- a/src/com/p4square/grow/provider/ProvidesAssessments.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.provider; - -import com.p4square.grow.model.RecordedAnswer; - -/** - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public interface ProvidesAssessments { - /** - * Provides a collection of user assessments. - * The collection key is the user id. - * The key is the question id. - */ - CollectionProvider<String, String, String> getAnswerProvider(); -} diff --git a/src/com/p4square/grow/provider/ProvidesQuestions.java b/src/com/p4square/grow/provider/ProvidesQuestions.java deleted file mode 100644 index b43f649..0000000 --- a/src/com/p4square/grow/provider/ProvidesQuestions.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.provider; - -import com.p4square.grow.model.Question; - -/** - * Indicates the ability to provide a Question Provider. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public interface ProvidesQuestions { - /** - * @return A Provider of Questions keyed by question id. - */ - Provider<String, Question> getQuestionProvider(); -} diff --git a/src/com/p4square/grow/provider/ProvidesStrings.java b/src/com/p4square/grow/provider/ProvidesStrings.java deleted file mode 100644 index 5d9976e..0000000 --- a/src/com/p4square/grow/provider/ProvidesStrings.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.provider; - -/** - * Indicates the ability to provide a String provider. - * - * Strings are typically configuration settings stored as a String. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public interface ProvidesStrings { - /** - * @return A Provider of Questions keyed by question id. - */ - Provider<String, String> getStringProvider(); -}
\ No newline at end of file diff --git a/src/com/p4square/grow/provider/ProvidesTrainingRecords.java b/src/com/p4square/grow/provider/ProvidesTrainingRecords.java deleted file mode 100644 index 586e649..0000000 --- a/src/com/p4square/grow/provider/ProvidesTrainingRecords.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.provider; - -import java.io.IOException; - -import com.p4square.grow.model.TrainingRecord; -import com.p4square.grow.model.Playlist; - -/** - * Indicates the ability to provide a TrainingRecord Provider. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public interface ProvidesTrainingRecords { - /** - * @return A Provider of Questions keyed by question id. - */ - Provider<String, TrainingRecord> getTrainingRecordProvider(); - - /** - * @return the Default Playlist. - */ - Playlist getDefaultPlaylist() throws IOException; -} diff --git a/src/com/p4square/grow/provider/ProvidesUserRecords.java b/src/com/p4square/grow/provider/ProvidesUserRecords.java deleted file mode 100644 index d77c878..0000000 --- a/src/com/p4square/grow/provider/ProvidesUserRecords.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.provider; - -import com.p4square.grow.model.UserRecord; - -/** - * Indicates the ability to provide a UserRecord Provider. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public interface ProvidesUserRecords { - /** - * @return A Provider of Questions keyed by question id. - */ - Provider<String, UserRecord> getUserRecordProvider(); -} diff --git a/src/com/p4square/grow/provider/ProvidesVideos.java b/src/com/p4square/grow/provider/ProvidesVideos.java deleted file mode 100644 index 3d055d3..0000000 --- a/src/com/p4square/grow/provider/ProvidesVideos.java +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.provider; - -/** - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public interface ProvidesVideos { - /** - * @return A Provider of Questions keyed by question id. - */ - CollectionProvider<String, String, String> getVideoProvider(); -} diff --git a/src/com/p4square/grow/provider/TrainingRecordProvider.java b/src/com/p4square/grow/provider/TrainingRecordProvider.java deleted file mode 100644 index 44dba87..0000000 --- a/src/com/p4square/grow/provider/TrainingRecordProvider.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.provider; - -import java.io.IOException; - -import com.p4square.grow.model.TrainingRecord; - -/** - * TrainingRecordProvider wraps an existing Provider to get and put TrainingRecords. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public abstract class TrainingRecordProvider<K> implements Provider<String, TrainingRecord> { - - private Provider<K, TrainingRecord> mProvider; - - public TrainingRecordProvider(Provider<K, TrainingRecord> provider) { - mProvider = provider; - } - - @Override - public TrainingRecord get(String key) throws IOException { - return mProvider.get(makeKey(key)); - } - - @Override - public void put(String key, TrainingRecord obj) throws IOException { - mProvider.put(makeKey(key), obj); - } - - /** - * Make a Key for a TrainingRecord.. - * - * @param userId The user id. - * @return a key for the TrainingRecord of userid. - */ - protected abstract K makeKey(String userId); -} diff --git a/src/com/p4square/grow/tools/AssessmentStats.java b/src/com/p4square/grow/tools/AssessmentStats.java deleted file mode 100644 index ca83411..0000000 --- a/src/com/p4square/grow/tools/AssessmentStats.java +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright 2013 Jesse Morgan - */ - -package com.p4square.grow.tools; - - -import java.util.Map; -import java.util.HashMap; -import java.util.Queue; -import java.util.List; -import java.util.LinkedList; -import java.io.IOException; - -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.Path; - -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; -import com.p4square.grow.provider.JsonEncodedProvider; - -/** - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class AssessmentStats { - public static void main(String... args) throws Exception { - if (args.length == 0) { - System.out.println("Usage: AssessmentStats directory firstQuestionId"); - System.exit(1); - } - - Map<String, Question> questions; - questions = loadQuestions(args[0], args[1]); - - // Find the highest possible score - List<AnswerPath> scores = findHighestFromId(questions, args[1]); - - // Print Results - System.out.printf("Found %d different paths.\n", scores.size()); - int i = 0; - for (AnswerPath path : scores) { - Score s = path.mScore; - System.out.printf("Path %d: %f points, %d questions. Score: %f (%s)\n", - i++, s.getSum(), s.getCount(), s.getScore(), s.toString()); - System.out.println(" " + path.mPath); - System.out.println(" " + path.mScores); - } - } - - private static Map<String, Question> loadQuestions(String baseDir, String firstId) throws IOException { - FileQuestionProvider provider = new FileQuestionProvider(baseDir); - - // Questions to find... - Queue<String> queue = new LinkedList<>(); - queue.offer(firstId); - - Map<String, Question> questions = new HashMap<>(); - - - while (!queue.isEmpty()) { - Question q = provider.get(queue.poll()); - questions.put(q.getId(), q); - - if (q.getNextQuestion() != null) { - queue.offer(q.getNextQuestion()); - - } - - for (Answer a : q.getAnswers().values()) { - if (a.getNextQuestion() != null) { - queue.offer(a.getNextQuestion()); - } - } - - // Quick Sanity check - if (q.getPreviousQuestion() != null) { - if (questions.get(q.getPreviousQuestion()) == null) { - throw new IllegalStateException("Haven't seen previous question??"); - } - } - } - - return questions; - } - - private static List<AnswerPath> findHighestFromId(Map<String, Question> questions, String id) { - List<AnswerPath> scores = new LinkedList<>(); - doFindHighestFromId(questions, id, scores, new AnswerPath()); - return scores; - } - - private static void doFindHighestFromId(Map<String, Question> questions, String id, List<AnswerPath> scores, AnswerPath path) { - if (id == null) { - // End of the road! Save the score and return. - scores.add(path); - return; - } - - Question q = questions.get(id); - - // Find the best answer following this path and find other paths. - Score maxScore = path.mScore; - double max = 0; - - int answerCount = 1; - for (Map.Entry<String, Answer> entry : q.getAnswers().entrySet()) { - Answer a = entry.getValue(); - RecordedAnswer userAnswer = new RecordedAnswer(); - - if (q.getType() == Question.QuestionType.SLIDER) { - // Special Case - userAnswer.setAnswerId(String.valueOf((float) answerCount / q.getAnswers().size())); - - } else { - userAnswer.setAnswerId(entry.getKey()); - } - - Score tempScore = new Score(path.mScore); // Always start with the initial score. - boolean endOfRoad = !q.scoreAnswer(tempScore, userAnswer); - double thisScore = tempScore.getSum() - path.mScore.getSum(); - - if (endOfRoad) { - // End of Road is a fork too. Record and pick another answer. - AnswerPath fork = new AnswerPath(path); - fork.update(id, tempScore); - scores.add(fork); - - } else if (a.getNextQuestion() != null) { - // Found a new path, follow it. - // Remember to count this answer in the score. - AnswerPath fork = new AnswerPath(path); - fork.update(id, tempScore); - doFindHighestFromId(questions, a.getNextQuestion(), scores, fork); - - } else if (thisScore > max) { - // Found a higher option that isn't a new path. - maxScore = tempScore; - max = thisScore; - } - - answerCount++; - } - - path.update(id, maxScore); - doFindHighestFromId(questions, q.getNextQuestion(), scores, path); - } - - private static class FileQuestionProvider extends JsonEncodedProvider<Question> implements Provider<String, Question> { - private String mBaseDir; - - public FileQuestionProvider(String directory) { - super(Question.class); - mBaseDir = directory; - } - - @Override - public Question get(String key) throws IOException { - Path qfile = FileSystems.getDefault().getPath(mBaseDir, key + ".json"); - byte[] blob = Files.readAllBytes(qfile); - return decode(new String(blob)); - } - - @Override - public void put(String key, Question obj) throws IOException { - throw new UnsupportedOperationException("Not Implemented"); - } - } - - private static class AnswerPath { - String mPath; - String mScores; - Score mScore; - - public AnswerPath() { - mPath = null; - mScores = null; - mScore = new Score(); - } - - public AnswerPath(AnswerPath other) { - mPath = other.mPath; - mScores = other.mScores; - mScore = other.mScore; - } - - public void update(String questionId, Score newScore) { - String value; - - if (mScore.getCount() == newScore.getCount()) { - value = "n/a"; - - } else { - double delta = newScore.getSum() - mScore.getSum(); - if (delta < 0) { - value = "TRUMP"; - } else { - value = String.valueOf(delta); - } - } - - if (mPath == null) { - mPath = questionId; - mScores = value; - - } else { - mPath += ", " + questionId; - mScores += " + " + value; - } - - mScore = newScore; - } - } -} diff --git a/src/com/p4square/grow/tools/AttributeBackfillTool.java b/src/com/p4square/grow/tools/AttributeBackfillTool.java deleted file mode 100644 index d7fd2ff..0000000 --- a/src/com/p4square/grow/tools/AttributeBackfillTool.java +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.tools; - -import java.util.Arrays; -import java.util.Date; -import java.util.List; -import java.util.Map; - -import org.restlet.Client; -import org.restlet.Context; -import org.restlet.data.Protocol; - -import com.p4square.f1oauth.Attribute; -import com.p4square.f1oauth.F1API; -import com.p4square.f1oauth.F1Access; -import com.p4square.f1oauth.F1Exception; -import com.p4square.restlet.oauth.OAuthUser; - -import com.p4square.grow.backend.dynamo.DynamoDatabase; -import com.p4square.grow.backend.dynamo.DynamoKey; - -import com.p4square.grow.config.Config; - -import com.p4square.grow.model.Chapter; -import com.p4square.grow.model.Playlist; -import com.p4square.grow.model.TrainingRecord; -import com.p4square.grow.model.VideoRecord; -import com.p4square.grow.provider.JsonEncodedProvider; - -/** - * This utility is used to backfill F1 Attributes from the GROW database into F1. - * - * This tool currently reads from Dynamo directly. It should probably access the - * backend or use the {@link com.p4square.grow.backend.GrowData} abstraction instead. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class AttributeBackfillTool { - - private static Config mConfig; - private static F1API mF1API; - private static DynamoDatabase mDatabase; - - public static void usage() { - System.out.println("java com.p4square.grow.tools.AttributeBackfillTool <command>...\n"); - System.out.println("Commands:"); - System.out.println("\t--domain <domain> Set config domain"); - System.out.println("\t--dev Set config domain to dev"); - System.out.println("\t--config <file> Merge in config file"); - System.out.println("\t--assessments Backfill All Assessments"); - System.out.println("\t--training Backfill All Training Records"); - } - - public static void main(String... args) { - if (args.length == 0) { - usage(); - System.exit(1); - } - - mConfig = new Config(); - - try { - mConfig.updateConfig(AttributeTool.class.getResourceAsStream("/grow.properties")); - - int offset = 0; - while (offset < args.length) { - if ("--domain".equals(args[offset])) { - mConfig.setDomain(args[offset + 1]); - mF1API = null; - mDatabase = null; - offset += 2; - - } else if ("--dev".equals(args[offset])) { - mConfig.setDomain("dev"); - mF1API = null; - mDatabase = null; - offset += 1; - - } else if ("--config".equals(args[offset])) { - mConfig.updateConfig(args[offset + 1]); - mF1API = null; - mDatabase = null; - offset += 2; - - } else if ("--assessments".equals(args[offset])) { - offset = assessments(args, ++offset); - - } else if ("--training".equals(args[offset])) { - offset = training(args, ++offset); - - } else { - throw new IllegalArgumentException("Unknown command " + args[offset]); - } - } - } catch (Exception e) { - e.printStackTrace(); - System.exit(2); - } - } - - private static F1API getF1API() throws Exception { - if (mF1API == null) { - Context context = new Context(); - Client client = new Client(context, Arrays.asList(Protocol.HTTP, Protocol.HTTPS)); - context.setClientDispatcher(client); - - F1Access f1Access = new F1Access(context, - mConfig.getString("f1ConsumerKey"), - mConfig.getString("f1ConsumerSecret"), - mConfig.getString("f1BaseUrl"), - mConfig.getString("f1ChurchCode"), - F1Access.UserType.WEBLINK); - - // Gather Username and Password - String username = System.console().readLine("F1 Username: "); - char[] password = System.console().readPassword("F1 Password: "); - - OAuthUser user = f1Access.getAccessToken(username, new String(password)); - Arrays.fill(password, ' '); // Lost cause, but I'll still try. - - mF1API = f1Access.getAuthenticatedApi(user); - } - - return mF1API; - } - - private static DynamoDatabase getDatabase() { - if (mDatabase == null) { - mDatabase = new DynamoDatabase(mConfig); - } - - return mDatabase; - } - - private static int assessments(String[] args, int offset) throws Exception { - final F1API f1 = getF1API(); - final DynamoDatabase db = getDatabase(); - - DynamoKey key = DynamoKey.newKey("assessments", null); - - while (key != null) { - Map<DynamoKey, Map<String, String>> rows = db.getAll(key); - - key = null; - - for (Map.Entry<DynamoKey, Map<String, String>> row : rows.entrySet()) { - key = row.getKey(); - - String userId = key.getHashKey(); - - String summaryString = row.getValue().get("summary"); - if (summaryString == null || summaryString.length() == 0) { - System.out.printf("%s assessment incomplete\n", userId); - continue; - } - - try { - Map summary = JsonEncodedProvider.MAPPER.readValue(summaryString, Map.class); - - String result = (String) summary.get("result"); - if (result == null) { - System.out.printf("%s assessment incomplete\n", userId); - continue; - } - - String attributeName = "Assessment Complete - " + result; - - // Check if the user already has the attribute. - List<Attribute> attributes = f1.getAttribute(userId, attributeName); - - if (attributes.size() == 0) { - Attribute attribute = new Attribute(attributeName); - attribute.setStartDate(new Date()); - attribute.setComment(summaryString); - - if (f1.addAttribute(userId, attribute)) { - System.out.printf("%s attribute added\n", userId); - } else { - System.out.printf("%s failed to add attribute\n", userId); - } - } else { - System.out.printf("%s already has attribute\n", userId); - } - } catch (Exception e) { - System.out.printf("%s exception: %s\n", userId, e.getMessage()); - } - } - } - - return offset; - } - - private static int training(String[] args, int offset) throws Exception { - final F1API f1 = getF1API(); - final DynamoDatabase db = getDatabase(); - - DynamoKey key = DynamoKey.newKey("training", null); - - while (key != null) { - Map<DynamoKey, Map<String, String>> rows = db.getAll(key); - - key = null; - - for (Map.Entry<DynamoKey, Map<String, String>> row : rows.entrySet()) { - key = row.getKey(); - - String userId = key.getHashKey(); - - String valueString = row.getValue().get("value"); - if (valueString == null || valueString.length() == 0) { - System.out.printf("%s empty training record\n", userId); - continue; - } - - try { - TrainingRecord record = - JsonEncodedProvider.MAPPER.readValue(valueString, TrainingRecord.class); - Playlist playlist = record.getPlaylist(); - -chapters: - for (Map.Entry<String, Chapter> entry : playlist.getChaptersMap().entrySet()) { - Chapter chapter = entry.getValue(); - - // Find completion date - Date complete = new Date(0); - for (VideoRecord vr : chapter.getVideos().values()) { - if (!vr.getComplete()) { - continue chapters; - } - - Date recordCompletion = vr.getCompletionDate(); - if (recordCompletion != null && complete.before(recordCompletion)) { - complete = vr.getCompletionDate(); - } - } - - String attributeName = "Training Complete - " + entry.getKey(); - - // Check if the user already has the attribute. - List<Attribute> attributes = f1.getAttribute(userId, attributeName); - - if (attributes.size() == 0) { - Attribute attribute = new Attribute(attributeName); - attribute.setStartDate(complete); - - if (f1.addAttribute(userId, attribute)) { - System.out.printf("%s added %s\n", userId, attributeName); - } else { - System.out.printf("%s failed to add %s\n", userId, attributeName); - } - } else { - System.out.printf("%s already has %s\n", userId, attributeName); - } - } - - } catch (Exception e) { - System.out.printf("%s exception: %s\n", userId, e.getMessage()); - e.printStackTrace(); - } - } - } - - return offset; - } -} diff --git a/src/com/p4square/grow/tools/AttributeTool.java b/src/com/p4square/grow/tools/AttributeTool.java deleted file mode 100644 index 8e0540a..0000000 --- a/src/com/p4square/grow/tools/AttributeTool.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright 2014 Jesse Morgan - */ - -package com.p4square.grow.tools; - -import java.util.Arrays; -import java.util.Date; -import java.util.List; -import java.util.Map; - -import org.restlet.Client; -import org.restlet.Context; -import org.restlet.data.Protocol; - -import com.p4square.grow.config.Config; -import com.p4square.f1oauth.Attribute; -import com.p4square.f1oauth.F1Access; -import com.p4square.f1oauth.F1API; -import com.p4square.f1oauth.F1Exception; -import com.p4square.restlet.oauth.OAuthUser; - -/** - * Tool for manipulating F1 Attributes. - * - * @author Jesse Morgan <jesse@jesterpm.net> - */ -public class AttributeTool { - - private static Config mConfig; - private static F1API mF1API; - - public static void usage() { - System.out.println("java com.p4square.grow.tools.AttributeTool <command>...\n"); - System.out.println("Commands:"); - System.out.println("\t--domain <domain> Set config domain"); - System.out.println("\t--dev Set config domain to dev"); - System.out.println("\t--config <file> Merge in config file"); - System.out.println("\t--list List all attributes"); - System.out.println("\t--assign <userId> <attribute> <comment> Assign an attribute"); - System.out.println("\t--getall <userId> Get an attribute"); - System.out.println("\t--get <userId> <attribute> Get an attribute"); - } - - public static void main(String... args) { - if (args.length == 0) { - usage(); - System.exit(1); - } - - mConfig = new Config(); - - try { - mConfig.updateConfig(AttributeTool.class.getResourceAsStream("/grow.properties")); - - int offset = 0; - while (offset < args.length) { - if ("--domain".equals(args[offset])) { - mConfig.setDomain(args[offset + 1]); - mF1API = null; - offset += 2; - - } else if ("--dev".equals(args[offset])) { - mConfig.setDomain("dev"); - mF1API = null; - offset += 1; - - } else if ("--config".equals(args[offset])) { - mConfig.updateConfig(args[offset + 1]); - mF1API = null; - offset += 2; - - } else if ("--list".equals(args[offset])) { - offset = list(args, ++offset); - - } else if ("--assign".equals(args[offset])) { - offset = assign(args, ++offset); - - } else if ("--getall".equals(args[offset])) { - offset = getall(args, ++offset); - - } else if ("--get".equals(args[offset])) { - offset = get(args, ++offset); - - } else { - throw new IllegalArgumentException("Unknown command " + args[offset]); - } - } - } catch (Exception e) { - e.printStackTrace(); - System.exit(2); - } - } - - private static F1API getF1API() throws Exception { - if (mF1API == null) { - Context context = new Context(); - Client client = new Client(context, Arrays.asList(Protocol.HTTP, Protocol.HTTPS)); - context.setClientDispatcher(client); - - F1Access f1Access = new F1Access(context, - mConfig.getString("f1ConsumerKey"), - mConfig.getString("f1ConsumerSecret"), - mConfig.getString("f1BaseUrl"), - mConfig.getString("f1ChurchCode"), - F1Access.UserType.WEBLINK); - - // Gather Username and Password - String username = System.console().readLine("F1 Username: "); - char[] password = System.console().readPassword("F1 Password: "); - - OAuthUser user = f1Access.getAccessToken(username, new String(password)); - Arrays.fill(password, ' '); // Lost cause, but I'll still try. - - mF1API = f1Access.getAuthenticatedApi(user); - } - - return mF1API; - } - - private static int list(String[] args, int offset) throws Exception { - final F1API f1 = getF1API(); - - final Map<String, String> attributes = f1.getAttributeList(); - System.out.printf("%7s %s\n", "ID", "Name"); - for (Map.Entry<String, String> entry : attributes.entrySet()) { - System.out.printf("%7s %s\n", entry.getValue(), entry.getKey()); - } - - return offset; - } - - private static int assign(String[] args, int offset) throws Exception { - final String userId = args[offset++]; - final String attributeName = args[offset++]; - final String comment = args[offset++]; - - final F1API f1 = getF1API(); - - Attribute attribute = new Attribute(attributeName); - attribute.setStartDate(new Date()); - attribute.setComment(comment); - - if (f1.addAttribute(userId, attribute)) { - System.out.println("Added attribute " + attributeName + " for " + userId); - } else { - System.out.println("Failed to add attribute " + attributeName + " for " + userId); - } - - return offset; - } - - private static int getall(String[] args, int offset) throws Exception { - final String userId = args[offset++]; - - doGet(userId, null); - - return offset; - } - - private static int get(String[] args, int offset) throws Exception { - final String userId = args[offset++]; - final String attributeName = args[offset++]; - - doGet(userId, attributeName); - - return offset; - } - - private static void doGet(final String userId, final String attributeName) throws Exception { - final F1API f1 = getF1API(); - - List<Attribute> attributes = f1.getAttribute(userId, attributeName); - for (Attribute attribute : attributes) { - System.out.printf("%s %s %s %s %s\n%s\n\n", - userId, - attribute.getAttributeName(), - attribute.getId(), - attribute.getStartDate(), - attribute.getEndDate(), - attribute.getComment()); - } - } -} |