diff --git a/chat.json b/chat.json
new file mode 100644
index 00000000..cbd95a4f
--- /dev/null
+++ b/chat.json
@@ -0,0 +1 @@
+{"name":"My Conversation","messages":[{"content":"Hello there!","timestamp":1448470901,"senderId":"bob"},{"content":"how are you?","timestamp":1448470905,"senderId":"mike"},{"content":"I\u0027m good thanks, do you like pie?","timestamp":1448470906,"senderId":"bob"},{"content":"no, let me ask Angus...","timestamp":1448470910,"senderId":"mike"},{"content":"Hell yes! Are we buying some pie?","timestamp":1448470912,"senderId":"angus"},{"content":"No, just want to know if there\u0027s anybody else in the pie society...","timestamp":1448470914,"senderId":"bob"},{"content":"YES! I\u0027m the head pie eater there...","timestamp":1448470915,"senderId":"angus"}]}
\ No newline at end of file
diff --git a/out.json b/out.json
new file mode 100644
index 00000000..b7bf314e
--- /dev/null
+++ b/out.json
@@ -0,0 +1 @@
+{"name":"My Conversation","messages":[{"content":"Hello there!","timestamp":1448470901,"senderId":"bob"},{"content":"how are you?","timestamp":1448470905,"senderId":"mike"},{"content":"I\u0027m good thanks, do you like pie?","timestamp":1448470906,"senderId":"bob"},{"content":"no, let me ask Angus...","timestamp":1448470910,"senderId":"mike"},{"content":"Hell yes! Are we buying some pie?","timestamp":1448470912,"senderId":"angus"},{"content":"No, just want to know if there\u0027s anybody else in the pie society...","timestamp":1448470914,"senderId":"bob"},{"content":"YES! I\u0027m the head pie eater there...","timestamp":1448470915,"senderId":"angus"}],"activity":[{"name":"bob","count":3},{"name":"mike","count":2},{"name":"angus","count":2}]}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 577ea153..fc8f8659 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,6 +7,7 @@
com.mindlinksoft.recruitment.mychat
my-chat
1.0-SNAPSHOT
+ my-chat
1.8
@@ -31,6 +32,11 @@
gson
2.5
+
+ commons-cli
+ commons-cli
+ 1.0
+
@@ -57,9 +63,7 @@
- true
- lib/
- com.mindlinksoft.recruitment.mychat.ConversationExporter
+ com.mindlinksoft.recruitment.mychat.Main
diff --git a/src/main/java/META-INF/MANIFEST.MF b/src/main/java/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..8c702e52
--- /dev/null
+++ b/src/main/java/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Main-Class: com.mindlinksoft.recruitment.mychat.Main
+
diff --git a/src/main/java/com/mindlinksoft/recruitment/mychat/Conversation.java b/src/main/java/com/mindlinksoft/recruitment/mychat/Conversation.java
index d7809f00..7aebc6bf 100644
--- a/src/main/java/com/mindlinksoft/recruitment/mychat/Conversation.java
+++ b/src/main/java/com/mindlinksoft/recruitment/mychat/Conversation.java
@@ -1,6 +1,8 @@
package com.mindlinksoft.recruitment.mychat;
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.List;
/**
* Represents the model of a conversation.
@@ -9,12 +11,17 @@ public final class Conversation {
/**
* The name of the conversation.
*/
- public String name;
+ private final String name;
/**
* The messages in the conversation.
*/
- public Collection messages;
+ private final Collection messages;
+
+ /**
+ * NEW FEATURE: The messaging activity of the users in the conversation.
+ */
+ private List activity;
/**
* Initializes a new instance of the {@link Conversation} class.
@@ -25,4 +32,78 @@ public Conversation(String name, Collection messages) {
this.name = name;
this.messages = messages;
}
+
+ public void trackActivity() {
+ // cannot track activity on null messages
+ if (messages == null) {
+ return;
+ }
+
+ activity = new ArrayList<>();
+
+ int i;
+ boolean senderActivityAlreadyTracked;
+
+ for (Message message : messages) {
+ senderActivityAlreadyTracked = false;
+ for (i = 0; i < activity.size(); i++) {
+ // check if sender has already been tracked
+ if (message.getSenderId().equals(activity.get(i).getName())) {
+ senderActivityAlreadyTracked = true;
+ break;
+ }
+ }
+ // if the sender has not already been tracked, add to tracked activity
+ if (!senderActivityAlreadyTracked) {
+ activity.add(new SenderMessagePairing(message.getSenderId()));
+ }
+ // increment sender's message count
+ activity.get(i).incrementCount();
+ }
+ }
+
+ public void sortActivity() {
+ // cannot sort null activity
+ if (activity == null) {
+ return;
+ }
+ // sort senders in descending order of message count
+ activity.sort((a1, a2) -> (a2.count - a1.count));
+ }
+
+ // - Getters and Setters (changed the class variables to private)
+
+ public String getName() {
+ return name;
+ }
+
+ public Collection getMessages() {
+ return messages;
+ }
+
+ public List getActivity() {
+ return activity;
+ }
+
+ class SenderMessagePairing {
+ private String name;
+ private int count;
+
+ public SenderMessagePairing(String name) {
+ this.name = name;
+ this.count = 0;
+ }
+
+ public void incrementCount() {
+ this.count++;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public int getCount() {
+ return count;
+ }
+ }
}
diff --git a/src/main/java/com/mindlinksoft/recruitment/mychat/ConversationExporter.java b/src/main/java/com/mindlinksoft/recruitment/mychat/ConversationExporter.java
index bf5fa9bf..85172536 100644
--- a/src/main/java/com/mindlinksoft/recruitment/mychat/ConversationExporter.java
+++ b/src/main/java/com/mindlinksoft/recruitment/mychat/ConversationExporter.java
@@ -1,36 +1,34 @@
package com.mindlinksoft.recruitment.mychat;
-import com.google.gson.*;
-
+import com.mindlinksoft.recruitment.mychat.ConversationFilterers.BlacklistedWordFilterer;
+import com.mindlinksoft.recruitment.mychat.ConversationFilterers.KeywordFilterer;
+import com.mindlinksoft.recruitment.mychat.ConversationFilterers.UserFilterer;
import picocli.CommandLine;
import picocli.CommandLine.ParameterException;
import picocli.CommandLine.ParseResult;
import picocli.CommandLine.UnmatchedArgumentException;
-import java.io.*;
-import java.lang.reflect.Type;
-import java.time.Instant;
-import java.util.ArrayList;
-import java.util.List;
-
/**
* Represents a conversation exporter that can read a conversation and write it out in JSON.
*/
public class ConversationExporter {
+ private String user; // user whose messages are to be output
+ private String keyword; // keyword whose containing messages are to be output
+ private String[] blacklistedWords; // words to be redacted in the output
+ private boolean report; // flag determining whether or not to add the messaging activity to the output
+
/**
- * The application entry point.
- * @param args The command line arguments.
- * @throws Exception Thrown when something bad happens.
+ * Run method from which the rest of the exporter starts
*/
- public static void main(String[] args) throws Exception {
- // We use picocli to parse the command line - see https://picocli.info/
+ public void run(String[] args) {
ConversationExporterConfiguration configuration = new ConversationExporterConfiguration();
CommandLine cmd = new CommandLine(configuration);
+
try {
ParseResult parseResult = cmd.parseArgs(args);
-
+
if (parseResult.isUsageHelpRequested()) {
cmd.usage(cmd.getOut());
System.exit(cmd.getCommandSpec().exitCodeOnUsageHelp());
@@ -43,99 +41,150 @@ public static void main(String[] args) throws Exception {
return;
}
- ConversationExporter exporter = new ConversationExporter();
-
- exporter.exportConversation(configuration.inputFilePath, configuration.outputFilePath);
+ // get parameters from command line
+ getCLIParameters(configuration);
+ // start exporting conversation
+ exportConversation(configuration.inputFilePath, configuration.outputFilePath);
System.exit(cmd.getCommandSpec().exitCodeOnSuccess());
- } catch (ParameterException ex) {
- cmd.getErr().println(ex.getMessage());
- if (!UnmatchedArgumentException.printSuggestions(ex, cmd.getErr())) {
- ex.getCommandLine().usage(cmd.getErr());
+ } catch (ParameterException e) {
+ cmd.getErr().println(e.getMessage());
+ if (!UnmatchedArgumentException.printSuggestions(e, cmd.getErr())) {
+ e.getCommandLine().usage(cmd.getErr());
}
System.exit(cmd.getCommandSpec().exitCodeOnInvalidInput());
- } catch (Exception ex) {
- ex.printStackTrace(cmd.getErr());
+ } catch (Exception e) {
+ e.printStackTrace(cmd.getErr());
System.exit(cmd.getCommandSpec().exitCodeOnExecutionException());
}
}
+
/**
* Exports the conversation at {@code inputFilePath} as JSON to {@code outputFilePath}.
+ * @param configuration The configuration which contains the parameters for exportation.
+ */
+ public void getCLIParameters(ConversationExporterConfiguration configuration) {
+ // stores the arguments
+ this.user = configuration.user;
+ this.keyword = configuration.keyword;
+ this.blacklistedWords = configuration.blacklistedWords;
+ this.report = configuration.report;
+ }
+
+
+ /**
+ * Exports the conversation at {@code inputFilePath} as JSON to {@code outputFilePath}. Holds the sequence of stages of exportation.
* @param inputFilePath The input file path.
* @param outputFilePath The output file path.
* @throws Exception Thrown when something bad happens.
*/
public void exportConversation(String inputFilePath, String outputFilePath) throws Exception {
- Conversation conversation = this.readConversation(inputFilePath);
+ ConversationExporterIO exporterIO = new ConversationExporterIO();
+ long startTime;
+ double exportTime; // time taken to export conversation
+
+ // start timing exportation
+ startTime = System.nanoTime();
+
+ // read conversation from file
+ Conversation conversation = exporterIO.readConversation(inputFilePath);
+
+ // filter conversation based on specified parameters (user, keyword, blacklisted words)
+ conversation = filterConversation(conversation);
+
+ // track conversation activity if a report is specified
+ checkReport(conversation);
+
+ // write to JSon file
+ exporterIO.writeConversation(conversation, outputFilePath);
- this.writeConversation(conversation, outputFilePath);
+ // finish timing exportation and convert from nanoseconds to milliseconds
+ exportTime = ((double) (System.nanoTime() - startTime)) / 1_000_000.0;
- // TODO: Add more logging...
- System.out.println("Conversation exported from '" + inputFilePath + "' to '" + outputFilePath);
+ // report the completion of the exportation
+ reportExportCompletion(inputFilePath, outputFilePath, exportTime);
}
+
/**
- * Helper method to write the given {@code conversation} as JSON to the given {@code outputFilePath}.
- * @param conversation The conversation to write.
- * @param outputFilePath The file path where the conversation should be written.
- * @throws Exception Thrown when something bad happens.
+ * Reports on the completion of the exportation
+ * @param inputFilePath The input file path.
+ * @param outputFilePath The output file path.
+ * @param exportTime The time (in milliseconds) over which the exportation took place.
*/
- public void writeConversation(Conversation conversation, String outputFilePath) throws Exception {
- // TODO: Do we need both to be resources, or will buffered writer close the stream?
- try (OutputStream os = new FileOutputStream(outputFilePath, true);
- BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os))) {
-
- // TODO: Maybe reuse this? Make it more testable...
- GsonBuilder gsonBuilder = new GsonBuilder();
- gsonBuilder.registerTypeAdapter(Instant.class, new InstantSerializer());
-
- Gson g = gsonBuilder.create();
-
- bw.write(g.toJson(conversation));
- } catch (FileNotFoundException e) {
- // TODO: Maybe include more information?
- throw new IllegalArgumentException("The file was not found.");
- } catch (IOException e) {
- // TODO: Should probably throw different exception to be more meaningful :/
- throw new Exception("Something went wrong");
- }
+ public void reportExportCompletion(String inputFilePath, String outputFilePath, double exportTime) {
+ System.out.println("\nConversation exported from '" + inputFilePath + "' to '" + outputFilePath + "'.");
+ System.out.println("Time taken: " + exportTime + " milliseconds");
}
+
/**
- * Represents a helper to read a conversation from the given {@code inputFilePath}.
- * @param inputFilePath The path to the input file.
- * @return The {@link Conversation} representing by the input file.
- * @throws Exception Thrown when something bad happens.
+ * Filters conversation content based on the filter parameters (user, keyword, blacklist)
+ * @param conversation The conversation to be filtered.
*/
- public Conversation readConversation(String inputFilePath) throws Exception {
- try(InputStream is = new FileInputStream(inputFilePath);
- BufferedReader r = new BufferedReader(new InputStreamReader(is))) {
-
- List messages = new ArrayList();
+ public Conversation filterConversation(Conversation conversation) {
+ // if a user is specified, filter conversation to only feature messages by the specified user
+ if (this.user != null) {
+ UserFilterer userFilterer = new UserFilterer();
+ userFilterer.setUser(this.user);
+ conversation = userFilterer.filter(conversation);
+ }
- String conversationName = r.readLine();
- String line;
+ // if a keyword is specified, filter conversation to only feature messages containing the specified keyword
+ if (this.keyword != null) {
+ KeywordFilterer keywordFilterer = new KeywordFilterer();
+ keywordFilterer.setKeyword(this.keyword);
+ conversation = keywordFilterer.filter(conversation);
+ }
- while ((line = r.readLine()) != null) {
- String[] split = line.split(" ");
+ // if blacklisted words are specified, redact blacklisted words
+ if (this.blacklistedWords != null) {
+ BlacklistedWordFilterer blacklistedWordFilterer = new BlacklistedWordFilterer();
+ blacklistedWordFilterer.setBlacklistedWords(this.blacklistedWords);
+ conversation = blacklistedWordFilterer.filter(conversation);
+ }
- messages.add(new Message(Instant.ofEpochSecond(Long.parseUnsignedLong(split[0])), split[1], split[2]));
- }
+ return conversation;
+ }
- return new Conversation(conversationName, messages);
- } catch (FileNotFoundException e) {
- throw new IllegalArgumentException("The file was not found.");
- } catch (IOException e) {
- throw new Exception("Something went wrong");
+ public void checkReport(Conversation conversation) {
+ if (this.report) {
+ conversation.trackActivity();
+ conversation.sortActivity();
}
}
- class InstantSerializer implements JsonSerializer {
- @Override
- public JsonElement serialize(Instant instant, Type type, JsonSerializationContext jsonSerializationContext) {
- return new JsonPrimitive(instant.getEpochSecond());
- }
+ public String getUser() {
+ return user;
+ }
+
+ public String getKeyword() {
+ return keyword;
+ }
+
+ public String[] getBlacklistedWords() {
+ return blacklistedWords;
+ }
+
+ public boolean isReport() {
+ return report;
+ }
+
+ public void setUser(String user) {
+ this.user = user;
+ }
+
+ public void setKeyword(String keyword) {
+ this.keyword = keyword;
+ }
+
+ public void setBlacklistedWords(String[] blacklistedWords) {
+ this.blacklistedWords = blacklistedWords;
+ }
+
+ public void setReport(boolean report) {
+ this.report = report;
}
}
diff --git a/src/main/java/com/mindlinksoft/recruitment/mychat/ConversationExporterConfiguration.java b/src/main/java/com/mindlinksoft/recruitment/mychat/ConversationExporterConfiguration.java
index 5d785d40..eae899b8 100644
--- a/src/main/java/com/mindlinksoft/recruitment/mychat/ConversationExporterConfiguration.java
+++ b/src/main/java/com/mindlinksoft/recruitment/mychat/ConversationExporterConfiguration.java
@@ -1,13 +1,13 @@
package com.mindlinksoft.recruitment.mychat;
+import org.apache.commons.lang.ObjectUtils;
import picocli.CommandLine.Option;
import picocli.CommandLine.Command;
/**
* Represents the configuration for the exporter.
*/
-@Command(name = "export", mixinStandardHelpOptions = true, version = "exporter 1.0",
- description = "Exports a plain text chat log into a JSON file.")
+@Command(name = "export", mixinStandardHelpOptions = true, version = "exporter 1.0", description = "Exports a plain text chat log into a JSON file.")
public final class ConversationExporterConfiguration {
/**
* Gets the input file path.
@@ -20,4 +20,25 @@ public final class ConversationExporterConfiguration {
*/
@Option(names = { "-o", "--outputFilePath" }, description = "The path to the output JSON file.", required = true)
public String outputFilePath;
+
+ /**
+ * NEW FEATURE: Gets the specified user whose messages should be output.
+ */
+ @Option(names = { "-u", "--filterByUser" }, description = "Filter Messages by User - only messages sent by the specified user are output")
+ public String user = null;
+
+ /**
+ * NEW FEATURE: Gets the specified keyword whose containing messages should be output.
+ */
+ @Option(names = { "-k", "--filterByKeyword" }, description = "Filter Messages by Keyword - only messages containing the specified keyword are output")
+ public String keyword = null;
+
+ /**
+ * NEW FEATURE: Gets the list of blacklisted words who are redacted from the output.
+ */
+ @Option(names = { "-b", "--blacklist" }, description = "Blacklist Word - redacts any occurrence of the blacklisted word in the output")
+ public String[] blacklistedWords = null;
+
+ @Option(names = { "-r", "--report" }, description = "Include Report - adds a report detailing the number of messages sent by each user in the output")
+ public boolean report = false;
}
diff --git a/src/main/java/com/mindlinksoft/recruitment/mychat/ConversationExporterIO.java b/src/main/java/com/mindlinksoft/recruitment/mychat/ConversationExporterIO.java
new file mode 100644
index 00000000..58d91435
--- /dev/null
+++ b/src/main/java/com/mindlinksoft/recruitment/mychat/ConversationExporterIO.java
@@ -0,0 +1,88 @@
+package com.mindlinksoft.recruitment.mychat;
+
+import com.google.gson.*;
+
+import java.io.*;
+import java.lang.reflect.Type;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ConversationExporterIO {
+ /**
+ * Represents a helper to read a conversation from the given {@code inputFilePath}.
+ * @param inputFilePath The path to the input file.
+ * @return The {@link Conversation} representing by the input file.
+ * @throws Exception Thrown when something bad happens.
+ */
+ public Conversation readConversation(String inputFilePath) throws Exception {
+ try(InputStream is = new FileInputStream(inputFilePath);
+ BufferedReader r = new BufferedReader(new InputStreamReader(is))) {
+
+ List messages = new ArrayList<>();
+
+ String conversationName = r.readLine();
+ String line;
+
+ while ((line = r.readLine()) != null) {
+ String[] split = line.split(" ");
+ StringBuilder content = new StringBuilder();
+
+ // adds the rest of the splits to the message content
+ for (int i = 2; i < split.length; i++) {
+ content.append(split[i]);
+ // adds spaces in between all the splits (makes sure an extra one isn't added to the end)
+ if (i < split.length - 1) {
+ content.append(" ");
+ }
+ }
+
+ messages.add(new Message(Instant.ofEpochSecond(Long.parseUnsignedLong(split[0])), split[1], content.toString()));
+ }
+
+ return new Conversation(conversationName, messages);
+ } catch (FileNotFoundException e) {
+ throw new FileNotFoundException("Cannot find file named \'" + inputFilePath + "\'.");
+ } catch (IllegalArgumentException e) {
+ throw new IllegalArgumentException("Input buffer size is ef illegal value. Must be a non-zero value");
+ } catch (IOException e) {
+ throw new IOException("An I/O error has occurred.");
+ } catch (Exception e) {
+ throw new IOException("An error has occurred.");
+ }
+ }
+
+ /**
+ * Helper method to write the given {@code conversation} as JSON to the given {@code outputFilePath}.
+ * @param conversation The conversation to write.
+ * @param outputFilePath The file path where the conversation should be written.
+ * @throws Exception Thrown when something bad happens.
+ */
+ public void writeConversation(Conversation conversation, String outputFilePath) throws Exception {
+ try (OutputStream os = new FileOutputStream(outputFilePath, false);
+ BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os))) {
+
+ GsonBuilder gsonBuilder = new GsonBuilder();
+ gsonBuilder.registerTypeAdapter(Instant.class, new InstantSerializer());
+
+ Gson g = gsonBuilder.create();
+ bw.write(g.toJson(conversation));
+
+ } catch (FileNotFoundException e) {
+ throw new FileNotFoundException("Cannot find file named \'" + outputFilePath + "\'.");
+ } catch (IllegalArgumentException e) {
+ throw new IllegalArgumentException("Output buffer size is ef illegal value. Must be a non-zero value");
+ } catch (IOException e) {
+ throw new IOException("An I/O error has occurred.");
+ } catch (Exception e) {
+ throw new IOException("An error has occurred.");
+ }
+ }
+
+ class InstantSerializer implements JsonSerializer {
+ @Override
+ public JsonElement serialize(Instant instant, Type type, JsonSerializationContext jsonSerializationContext) {
+ return new JsonPrimitive(instant.getEpochSecond());
+ }
+ }
+}
diff --git a/src/main/java/com/mindlinksoft/recruitment/mychat/ConversationFilterers/BlacklistedWordFilterer.java b/src/main/java/com/mindlinksoft/recruitment/mychat/ConversationFilterers/BlacklistedWordFilterer.java
new file mode 100644
index 00000000..7a6a9b4a
--- /dev/null
+++ b/src/main/java/com/mindlinksoft/recruitment/mychat/ConversationFilterers/BlacklistedWordFilterer.java
@@ -0,0 +1,54 @@
+package com.mindlinksoft.recruitment.mychat.ConversationFilterers;
+
+import com.mindlinksoft.recruitment.mychat.Conversation;
+import com.mindlinksoft.recruitment.mychat.Message;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class BlacklistedWordFilterer extends Filterer {
+
+ private String[] blacklistedWords;
+
+ public void setBlacklistedWords(String[] blacklistedWords) {
+ this.blacklistedWords = blacklistedWords;
+ }
+
+ /**
+ * Filters a conversation to only contain messages that contain the specified keyword
+ * @param conversation The conversation to filter.
+ */
+ public Conversation filter(Conversation conversation) {
+ // if there are no blacklisted words, or if the array is null, return original conversation
+ if (blacklistedWords == null || blacklistedWords.length == 0) {
+ return conversation;
+ }
+
+ String messageBeforeWord, messageAfterWord, redactedString;
+ int wordStartIndex, wordEndIndex;
+ List filteredMessages = new ArrayList<>();
+
+ for (Message message : conversation.getMessages()) {
+ redactedString = message.getContent();
+ for (String blacklistedWord : blacklistedWords) {
+ if (redactedString.contains(blacklistedWord)) {
+ // indexes of the start and end of the blacklisted word in the message
+ wordStartIndex = redactedString.indexOf(blacklistedWord);
+ wordEndIndex = wordStartIndex + blacklistedWord.length();
+ // get the parts of the message that come before and after the blacklisted word
+ messageBeforeWord = redactedString.substring(0, wordStartIndex);
+ messageAfterWord = redactedString.substring(wordEndIndex);
+ // replace the message with post-redaction message
+ redactedString = messageBeforeWord + "*redacted*" + messageAfterWord;
+ }
+ }
+ // add redacted string to new list of filtered messages
+ filteredMessages.add(new Message(message.getTimestamp(), message.getSenderId(), redactedString));
+ }
+
+ // create a new conversation object with the filtered messages
+ Conversation filteredConversation = new Conversation(conversation.getName(), filteredMessages);
+
+ return filteredConversation;
+ }
+}
diff --git a/src/main/java/com/mindlinksoft/recruitment/mychat/ConversationFilterers/Filterer.java b/src/main/java/com/mindlinksoft/recruitment/mychat/ConversationFilterers/Filterer.java
new file mode 100644
index 00000000..f67bfb11
--- /dev/null
+++ b/src/main/java/com/mindlinksoft/recruitment/mychat/ConversationFilterers/Filterer.java
@@ -0,0 +1,7 @@
+package com.mindlinksoft.recruitment.mychat.ConversationFilterers;
+
+import com.mindlinksoft.recruitment.mychat.Conversation;
+
+public abstract class Filterer {
+ public abstract Conversation filter(Conversation conversation);
+}
diff --git a/src/main/java/com/mindlinksoft/recruitment/mychat/ConversationFilterers/KeywordFilterer.java b/src/main/java/com/mindlinksoft/recruitment/mychat/ConversationFilterers/KeywordFilterer.java
new file mode 100644
index 00000000..3426a949
--- /dev/null
+++ b/src/main/java/com/mindlinksoft/recruitment/mychat/ConversationFilterers/KeywordFilterer.java
@@ -0,0 +1,41 @@
+package com.mindlinksoft.recruitment.mychat.ConversationFilterers;
+
+import com.mindlinksoft.recruitment.mychat.Conversation;
+import com.mindlinksoft.recruitment.mychat.Message;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class KeywordFilterer extends Filterer {
+
+ private String keyword;
+
+ public void setKeyword(String keyword) {
+ this.keyword = keyword;
+ }
+
+ /**
+ * Filters a conversation to only contain messages that contain the specified keyword
+ * @param conversation The conversation to filter.
+ */
+ public Conversation filter(Conversation conversation) {
+ // if keyword is a null string or an empty string, return original conversation
+ if (keyword == null || keyword.equals("")) {
+ return conversation;
+ }
+
+ List filteredMessages = new ArrayList<>();
+
+ // add message to filtered messages list if it contains the keyword
+ for (Message message : conversation.getMessages()) {
+ if (message.getContent().contains(keyword)) {
+ filteredMessages.add(message);
+ }
+ }
+
+ // create new conversation object with the filtered messages
+ Conversation filteredConversation = new Conversation(conversation.getName(), filteredMessages);
+
+ return filteredConversation;
+ }
+}
diff --git a/src/main/java/com/mindlinksoft/recruitment/mychat/ConversationFilterers/UserFilterer.java b/src/main/java/com/mindlinksoft/recruitment/mychat/ConversationFilterers/UserFilterer.java
new file mode 100644
index 00000000..5d9197dc
--- /dev/null
+++ b/src/main/java/com/mindlinksoft/recruitment/mychat/ConversationFilterers/UserFilterer.java
@@ -0,0 +1,41 @@
+package com.mindlinksoft.recruitment.mychat.ConversationFilterers;
+
+import com.mindlinksoft.recruitment.mychat.Conversation;
+import com.mindlinksoft.recruitment.mychat.Message;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class UserFilterer extends Filterer {
+
+ private String user;
+
+ public void setUser(String user) {
+ this.user = user;
+ }
+
+ /**
+ * Filters a conversation to only contain messages sent by the specified user
+ * @param conversation The conversation to filter.
+ */
+ public Conversation filter(Conversation conversation) {
+ // if user is a null string or an empty string, return original conversation
+ if (user == null || user.equals("")) {
+ return conversation;
+ }
+
+ List filteredMessages = new ArrayList<>();
+
+ // if message sender is the same as the user specified, add message to filtered messages list (case insensitive)
+ for (Message message : conversation.getMessages()) {
+ if (message.getSenderId().equalsIgnoreCase(user)) {
+ filteredMessages.add(message);
+ }
+ }
+
+ // create a new conversation object with the filtered messages
+ Conversation filteredConversation = new Conversation(conversation.getName(), filteredMessages);
+
+ return filteredConversation;
+ }
+}
diff --git a/src/main/java/com/mindlinksoft/recruitment/mychat/Main.java b/src/main/java/com/mindlinksoft/recruitment/mychat/Main.java
new file mode 100644
index 00000000..309264da
--- /dev/null
+++ b/src/main/java/com/mindlinksoft/recruitment/mychat/Main.java
@@ -0,0 +1,15 @@
+package com.mindlinksoft.recruitment.mychat;
+
+import java.util.Arrays;
+
+public class Main {
+
+ /**s
+ * The application entry point.
+ * @param args The command line arguments.
+ */
+ public static void main(String[] args) {
+ ConversationExporter exporter = new ConversationExporter();
+ exporter.run(args);
+ }
+}
diff --git a/src/main/java/com/mindlinksoft/recruitment/mychat/Message.java b/src/main/java/com/mindlinksoft/recruitment/mychat/Message.java
index 4135d497..352a8073 100644
--- a/src/main/java/com/mindlinksoft/recruitment/mychat/Message.java
+++ b/src/main/java/com/mindlinksoft/recruitment/mychat/Message.java
@@ -9,17 +9,17 @@ public final class Message {
/**
* The message content.
*/
- public String content;
+ private final String content;
/**
* The message timestamp.
*/
- public Instant timestamp;
+ private final Instant timestamp;
/**
* The message sender.
*/
- public String senderId;
+ private final String senderId;
/**
* Initializes a new instance of the {@link Message} class.
@@ -32,4 +32,18 @@ public Message(Instant timestamp, String senderId, String content) {
this.timestamp = timestamp;
this.senderId = senderId;
}
+
+ // - Getters and Setters (changed the class variables to private)
+
+ public String getContent() {
+ return content;
+ }
+
+ public Instant getTimestamp() {
+ return timestamp;
+ }
+
+ public String getSenderId() {
+ return senderId;
+ }
}
diff --git a/src/test/java/com/mindlinksoft/recruitment/mychat/BlacklistedWordFiltererTests.java b/src/test/java/com/mindlinksoft/recruitment/mychat/BlacklistedWordFiltererTests.java
new file mode 100644
index 00000000..c744861b
--- /dev/null
+++ b/src/test/java/com/mindlinksoft/recruitment/mychat/BlacklistedWordFiltererTests.java
@@ -0,0 +1,94 @@
+package com.mindlinksoft.recruitment.mychat;
+
+import com.mindlinksoft.recruitment.mychat.ConversationFilterers.BlacklistedWordFilterer;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+
+public class BlacklistedWordFiltererTests {
+ /**
+ * Tests that redacting blacklisted words functions correctly
+ */
+ @Test
+ public void testRedactBlacklistedWords() throws Exception {
+ BlacklistedWordFilterer blacklistedWordFilterer = new BlacklistedWordFilterer();
+ ConversationExporterIO exporterIO = new ConversationExporterIO();
+ Conversation originalConversation;
+ Conversation c;
+ List originalMessages;
+ List ms;
+
+ // gets the original conversation (unfiltered)
+ originalConversation = exporterIO.readConversation("chat.txt");
+ originalMessages = (ArrayList) originalConversation.getMessages();
+
+ // - [PIE] ---------------------------------------------------------
+ c = exporterIO.readConversation("chat.txt");
+
+ blacklistedWordFilterer.setBlacklistedWords(new String[]{"pie"});
+ c = blacklistedWordFilterer.filter(c);
+ ms = (ArrayList) c.getMessages();
+
+ assertEquals("Hello there!", ms.get(0).getContent());
+ assertEquals("how are you?", ms.get(1).getContent());
+ assertEquals("I'm good thanks, do you like *redacted*?", ms.get(2).getContent());
+ assertEquals("no, let me ask Angus...", ms.get(3).getContent());
+ assertEquals("Hell yes! Are we buying some *redacted*?", ms.get(4).getContent());
+ assertEquals("No, just want to know if there's anybody else in the *redacted* society...", ms.get(5).getContent());
+ assertEquals("YES! I'm the head *redacted* eater there...", ms.get(6).getContent());
+
+ // - [I'M, YOU] -----------------------------------------------------
+ c = exporterIO.readConversation("chat.txt");
+
+ blacklistedWordFilterer.setBlacklistedWords(new String[]{"I'm", "you"});
+ c = blacklistedWordFilterer.filter(c);
+ ms = (ArrayList) c.getMessages();
+
+ assertEquals("Hello there!", ms.get(0).getContent());
+ assertEquals("how are *redacted*?", ms.get(1).getContent());
+ assertEquals("*redacted* good thanks, do *redacted* like pie?", ms.get(2).getContent());
+ assertEquals("no, let me ask Angus...", ms.get(3).getContent());
+ assertEquals("Hell yes! Are we buying some pie?", ms.get(4).getContent());
+ assertEquals("No, just want to know if there's anybody else in the pie society...", ms.get(5).getContent());
+ assertEquals("YES! *redacted* the head pie eater there...", ms.get(6).getContent());
+
+ // - [NON-EXISTENT] -----------------------------------------------------
+ c = exporterIO.readConversation("chat.txt");
+
+ blacklistedWordFilterer.setBlacklistedWords(new String[]{"non-existent"});
+ c = blacklistedWordFilterer.filter(c);
+ ms = (ArrayList) c.getMessages();
+
+ // messages should be the same as the original as the "non-existent" string has no occurrences in the conversation
+ for (int i = 0; i < ms.size(); i++) {
+ assertEquals(originalMessages.get(i).getContent(), ms.get(i).getContent());
+ }
+
+ // - NULL ---------------------------------------------------------------
+ c = exporterIO.readConversation("chat.txt");
+
+ blacklistedWordFilterer.setBlacklistedWords(null);
+ c = blacklistedWordFilterer.filter(c);
+ ms = (ArrayList) c.getMessages();
+
+ // messages should be the same as the original as the "non-existent" string has no occurrences in the conversation
+ for (int i = 0; i < ms.size(); i++) {
+ assertEquals(originalMessages.get(i).getContent(), ms.get(i).getContent());
+ }
+
+ // - EMPTY ARRAY --------------------------------------------------------
+ c = exporterIO.readConversation("chat.txt");
+
+ blacklistedWordFilterer.setBlacklistedWords(new String[]{});
+ c = blacklistedWordFilterer.filter(c);
+ ms = (ArrayList) c.getMessages();
+
+ // messages should be the same as the original as the "non-existent" string has no occurrences in the conversation
+ for (int i = 0; i < ms.size(); i++) {
+ assertEquals(originalMessages.get(i).getContent(), ms.get(i).getContent());
+ }
+ }
+}
diff --git a/src/test/java/com/mindlinksoft/recruitment/mychat/ConversationExporterConfigurationTests.java b/src/test/java/com/mindlinksoft/recruitment/mychat/ConversationExporterConfigurationTests.java
new file mode 100644
index 00000000..bf5d32b3
--- /dev/null
+++ b/src/test/java/com/mindlinksoft/recruitment/mychat/ConversationExporterConfigurationTests.java
@@ -0,0 +1,57 @@
+package com.mindlinksoft.recruitment.mychat;
+
+
+import org.junit.Test;
+import picocli.CommandLine;
+
+import java.util.Arrays;
+
+import static org.junit.Assert.*;
+
+/**
+ * Tests for the {@link ConversationExporter}.
+ */
+public class ConversationExporterConfigurationTests {
+ /**
+ * Tests that parameters are correctly parsed into the configuration
+ */
+ @Test
+ public void testParameterParsing() {
+ ConversationExporterConfiguration configuration;
+ CommandLine cmd;
+ String[] args;
+
+ // - JUST INPUT AND OUTPUT FILES -----------------------------------------------------
+ args = new String[]{"-i", "chat.txt", "-o", "out.json"};
+
+ configuration = new ConversationExporterConfiguration();
+ cmd = new CommandLine(configuration);
+ cmd.parseArgs(args);
+
+ // input and output file parameters
+ assertEquals("chat.txt", configuration.inputFilePath);
+ assertEquals("out.json", configuration.outputFilePath);
+ // extra feature parameters
+ assertNull(configuration.user);
+ assertNull(configuration.keyword);
+ assertNull(configuration.blacklistedWords);
+ assertFalse(configuration.report);
+
+
+ // - EXTRA PARAMETERS ----------------------------------------------------------------
+ args = new String[]{"-i", "chat2.txt", "-o", "out2.json", "-b", "you", "-u", "mike", "-k", "pie", "-b", "there", "-r"};
+
+ configuration = new ConversationExporterConfiguration();
+ cmd = new CommandLine(configuration);
+ cmd.parseArgs(args);
+
+ // input and output file parameters
+ assertEquals("chat2.txt", configuration.inputFilePath);
+ assertEquals("out2.json", configuration.outputFilePath);
+ // extra feature parameters
+ assertEquals("mike", configuration.user);
+ assertEquals("pie", configuration.keyword);
+ assertEquals(Arrays.toString(new String[]{"you, there"}), Arrays.toString(configuration.blacklistedWords));
+ assertTrue(configuration.report);
+ }
+}
diff --git a/src/test/java/com/mindlinksoft/recruitment/mychat/ConversationExporterIOTests.java b/src/test/java/com/mindlinksoft/recruitment/mychat/ConversationExporterIOTests.java
new file mode 100644
index 00000000..5e1bc234
--- /dev/null
+++ b/src/test/java/com/mindlinksoft/recruitment/mychat/ConversationExporterIOTests.java
@@ -0,0 +1,110 @@
+package com.mindlinksoft.recruitment.mychat;
+
+import org.junit.Test;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.time.Instant;
+import java.util.ArrayList;
+
+import static org.junit.Assert.*;
+
+public class ConversationExporterIOTests {
+ /**
+ * Tests that exporting a conversation will export the conversation correctly.
+ *
+ * @throws Exception When something bad happens.
+ */
+ @Test
+ public void testReadConversation() throws Exception {
+ ConversationExporterIO exporterIO = new ConversationExporterIO();
+ Conversation c;
+
+ // - reading from normal text file -------------------------------------------
+
+ c = exporterIO.readConversation("chat.txt");
+
+ assertEquals("My Conversation", c.getName());
+
+ assertEquals(7, c.getMessages().size());
+
+ Message[] ms = new Message[c.getMessages().size()];
+ c.getMessages().toArray(ms);
+
+ assertEquals(Instant.ofEpochSecond(1448470901), ms[0].getTimestamp());
+ assertEquals("bob", ms[0].getSenderId());
+ assertEquals("Hello there!", ms[0].getContent());
+
+ assertEquals(Instant.ofEpochSecond(1448470905), ms[1].getTimestamp());
+ assertEquals("mike", ms[1].getSenderId());
+ assertEquals("how are you?", ms[1].getContent());
+
+ assertEquals(Instant.ofEpochSecond(1448470906), ms[2].getTimestamp());
+ assertEquals("bob", ms[2].getSenderId());
+ assertEquals("I'm good thanks, do you like pie?", ms[2].getContent());
+
+ assertEquals(Instant.ofEpochSecond(1448470910), ms[3].getTimestamp());
+ assertEquals("mike", ms[3].getSenderId());
+ assertEquals("no, let me ask Angus...", ms[3].getContent());
+
+ assertEquals(Instant.ofEpochSecond(1448470912), ms[4].getTimestamp());
+ assertEquals("angus", ms[4].getSenderId());
+ assertEquals("Hell yes! Are we buying some pie?", ms[4].getContent());
+
+ assertEquals(Instant.ofEpochSecond(1448470914), ms[5].getTimestamp());
+ assertEquals("bob", ms[5].getSenderId());
+ assertEquals("No, just want to know if there's anybody else in the pie society...", ms[5].getContent());
+
+ assertEquals(Instant.ofEpochSecond(1448470915), ms[6].getTimestamp());
+ assertEquals("angus", ms[6].getSenderId());
+ assertEquals("YES! I'm the head pie eater there...", ms[6].getContent());
+
+ // - reading from file that does not exist -----------------------------------
+
+ try {
+ c = exporterIO.readConversation("does_not_exist.txt");
+ } catch (Exception e) {
+ assertEquals("Cannot find file named 'does_not_exist.txt'.", e.getMessage());
+ }
+
+ // - reading from a null file ------------------------------------------------
+
+ try {
+ c = exporterIO.readConversation(null);
+ } catch (Exception e) {
+ assertEquals("An error has occurred.", e.getMessage());
+ }
+ }
+
+ /**
+ * Tests that exporting a conversation will export the conversation correctly.
+ *
+ * @throws Exception When something bad happens.
+ */
+ @Test
+ public void testWriteConversation() throws Exception {
+ ConversationExporterIO exporterIO = new ConversationExporterIO();
+ Conversation dummyConversation = new Conversation("Empty Convo", new ArrayList<>());
+
+ // - writing to dummy .json file -----------------------------------------------------------------
+
+ exporterIO.writeConversation(dummyConversation, "dummy.json");
+
+ assertTrue((new File("dummy.json")).exists());
+
+ // - writing to dummy .txt file ------------------------------------------------------------------
+
+ exporterIO.writeConversation(dummyConversation, "dummy.txt");
+ // though it writes to a .txt file, it writes in .json format, so it is not incorrect
+ assertTrue((new File("dummy.txt")).exists());
+
+ // - writing to a null file ----------------------------------------------------------------------
+
+ try {
+ exporterIO.writeConversation(dummyConversation,null);
+ } catch (Exception e) {
+ assertEquals("An error has occurred.", e.getMessage());
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/mindlinksoft/recruitment/mychat/ConversationExporterTests.java b/src/test/java/com/mindlinksoft/recruitment/mychat/ConversationExporterTests.java
index ebd59fe0..6fae6f8a 100644
--- a/src/test/java/com/mindlinksoft/recruitment/mychat/ConversationExporterTests.java
+++ b/src/test/java/com/mindlinksoft/recruitment/mychat/ConversationExporterTests.java
@@ -2,13 +2,17 @@
import com.google.gson.*;
import org.junit.Test;
+import picocli.CommandLine;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.*;
/**
* Tests for the {@link ConversationExporter}.
@@ -31,42 +35,213 @@ public void testExportingConversationExportsConversation() throws Exception {
Conversation c = g.fromJson(new InputStreamReader(new FileInputStream("chat.json")), Conversation.class);
- assertEquals("My Conversation", c.name);
+ assertEquals("My Conversation", c.getName());
- assertEquals(7, c.messages.size());
+ assertEquals(7, c.getMessages().size());
- Message[] ms = new Message[c.messages.size()];
- c.messages.toArray(ms);
+ Message[] ms = new Message[c.getMessages().size()];
+ c.getMessages().toArray(ms);
- assertEquals(Instant.ofEpochSecond(1448470901), ms[0].timestamp);
- assertEquals("bob", ms[0].senderId);
- assertEquals("Hello there!", ms[0].content);
+ assertEquals(Instant.ofEpochSecond(1448470901), ms[0].getTimestamp());
+ assertEquals("bob", ms[0].getSenderId());
+ assertEquals("Hello there!", ms[0].getContent());
- assertEquals(Instant.ofEpochSecond(1448470905), ms[1].timestamp);
- assertEquals("mike", ms[1].senderId);
- assertEquals("how are you?", ms[1].content);
+ assertEquals(Instant.ofEpochSecond(1448470905), ms[1].getTimestamp());
+ assertEquals("mike", ms[1].getSenderId());
+ assertEquals("how are you?", ms[1].getContent());
- assertEquals(Instant.ofEpochSecond(1448470906), ms[2].timestamp);
- assertEquals("bob", ms[2].senderId);
- assertEquals("I'm good thanks, do you like pie?", ms[2].content);
+ assertEquals(Instant.ofEpochSecond(1448470906), ms[2].getTimestamp());
+ assertEquals("bob", ms[2].getSenderId());
+ assertEquals("I'm good thanks, do you like pie?", ms[2].getContent());
- assertEquals(Instant.ofEpochSecond(1448470910), ms[3].timestamp);
- assertEquals("mike", ms[3].senderId);
- assertEquals("no, let me ask Angus...", ms[3].content);
+ assertEquals(Instant.ofEpochSecond(1448470910), ms[3].getTimestamp());
+ assertEquals("mike", ms[3].getSenderId());
+ assertEquals("no, let me ask Angus...", ms[3].getContent());
- assertEquals(Instant.ofEpochSecond(1448470912), ms[4].timestamp);
- assertEquals("angus", ms[4].senderId);
- assertEquals("Hell yes! Are we buying some pie?", ms[4].content);
+ assertEquals(Instant.ofEpochSecond(1448470912), ms[4].getTimestamp());
+ assertEquals("angus", ms[4].getSenderId());
+ assertEquals("Hell yes! Are we buying some pie?", ms[4].getContent());
- assertEquals(Instant.ofEpochSecond(1448470914), ms[5].timestamp);
- assertEquals("bob", ms[5].senderId);
- assertEquals("No, just want to know if there's anybody else in the pie society...", ms[5].content);
+ assertEquals(Instant.ofEpochSecond(1448470914), ms[5].getTimestamp());
+ assertEquals("bob", ms[5].getSenderId());
+ assertEquals("No, just want to know if there's anybody else in the pie society...", ms[5].getContent());
- assertEquals(Instant.ofEpochSecond(1448470915), ms[6].timestamp);
- assertEquals("angus", ms[6].senderId);
- assertEquals("YES! I'm the head pie eater there...", ms[6].content);
+ assertEquals(Instant.ofEpochSecond(1448470915), ms[6].getTimestamp());
+ assertEquals("angus", ms[6].getSenderId());
+ assertEquals("YES! I'm the head pie eater there...", ms[6].getContent());
}
+ /**
+ * Tests that multiple filters concurrently function correctly
+ */
+ @Test
+ public void testMultipleFilters() throws Exception {
+ ConversationExporter exporter = new ConversationExporter();
+ ConversationExporterIO exporterIO = new ConversationExporterIO();
+ Conversation c;
+ List ms;
+
+ // - USER : BOB, KEYWORD : THERE ---------------------------------------
+ c = exporterIO.readConversation("chat.txt");
+
+ exporter.setUser("bob");
+ exporter.setKeyword("there");
+ exporter.setBlacklistedWords(null);
+
+ exporter.filterConversation(c);
+ ms = (ArrayList) c.getMessages();
+
+ assertEquals(Instant.ofEpochSecond(1448470901), ms.get(0).getTimestamp());
+ assertEquals("bob", ms.get(0).getSenderId());
+ assertEquals("Hello there!", ms.get(0).getContent());
+
+ assertEquals(Instant.ofEpochSecond(1448470914), ms.get(1).getTimestamp());
+ assertEquals("bob", ms.get(1).getSenderId());
+ assertEquals("No, just want to know if there's anybody else in the pie society...", ms.get(1).getContent());
+
+ assertEquals(2, ms.size());
+
+
+ // - USER : MIKE, BLACKLIST : [YOU, ANGUS] ------------------------------
+ c = exporterIO.readConversation("chat.txt");
+
+ exporter.setUser("mike");
+ exporter.setKeyword(null);
+ exporter.setBlacklistedWords(new String[]{"you", "Angus"});
+
+ exporter.filterConversation(c);
+ ms = (ArrayList) c.getMessages();
+
+ assertEquals(Instant.ofEpochSecond(1448470905), ms.get(0).getTimestamp());
+ assertEquals("mike", ms.get(0).getSenderId());
+ assertEquals("how are *redacted*?", ms.get(0).getContent());
+
+ assertEquals(Instant.ofEpochSecond(1448470910), ms.get(1).getTimestamp());
+ assertEquals("mike", ms.get(1).getSenderId());
+ assertEquals("no, let me ask *redacted*...", ms.get(1).getContent());
+
+ assertEquals(2, ms.size());
+
+
+ // - KEYWORD : PIE, BLACKLIST : [PIE] -----------------------------------
+ c = exporterIO.readConversation("chat.txt");
+
+ exporter.setUser(null);
+ exporter.setKeyword("pie");
+ exporter.setBlacklistedWords(new String[]{"pie"});
+
+ exporter.filterConversation(c);
+ ms = (ArrayList) c.getMessages();
+
+ assertEquals(Instant.ofEpochSecond(1448470906), ms.get(0).getTimestamp());
+ assertEquals("bob", ms.get(0).getSenderId());
+ assertEquals("I'm good thanks, do you like *redacted*?", ms.get(0).getContent());
+
+ assertEquals(Instant.ofEpochSecond(1448470912), ms.get(1).getTimestamp());
+ assertEquals("angus", ms.get(1).getSenderId());
+ assertEquals("Hell yes! Are we buying some *redacted*?", ms.get(1).getContent());
+
+ assertEquals(Instant.ofEpochSecond(1448470914), ms.get(2).getTimestamp());
+ assertEquals("bob", ms.get(2).getSenderId());
+ assertEquals("No, just want to know if there's anybody else in the *redacted* society...", ms.get(2).getContent());
+
+ assertEquals(Instant.ofEpochSecond(1448470915), ms.get(3).getTimestamp());
+ assertEquals("angus", ms.get(3).getSenderId());
+ assertEquals("YES! I'm the head *redacted* eater there...", ms.get(3).getContent());
+
+ assertEquals(4, ms.size());
+
+
+ // - USER : ANGUS, KEYWORD : YES, BLACKLIST : [SOME] --------------------
+ c = exporterIO.readConversation("chat.txt");
+
+ exporter.setUser("angus");
+ exporter.setKeyword("yes");
+ exporter.setBlacklistedWords(new String[]{"some"});
+
+ exporter.filterConversation(c);
+ ms = (ArrayList) c.getMessages();
+
+ assertEquals(Instant.ofEpochSecond(1448470912), ms.get(0).getTimestamp());
+ assertEquals("angus", ms.get(0).getSenderId());
+ assertEquals("Hell yes! Are we buying *redacted* pie?", ms.get(0).getContent());
+
+ assertEquals(1, ms.size());
+
+ }
+
+ /**
+ * Tests that exporter correctly reports on a conversation when asked to
+ */
+ @Test
+ public void testConversationReporting() throws Exception {
+ ConversationExporter exporter = new ConversationExporter();
+ ConversationExporterIO exporterIO = new ConversationExporterIO();
+ Conversation c;
+
+ // - do not report ---------------------------------------------------------
+ c = exporterIO.readConversation("chat.txt");
+ exporter.setReport(false);
+ exporter.checkReport(c);
+
+ assertNull(c.getActivity());
+
+ // - report on conversation ------------------------------------------------
+ c = exporterIO.readConversation("chat.txt");
+ exporter.setReport(true);
+ exporter.checkReport(c);
+
+ assertEquals("bob", c.getActivity().get(0).getName());
+ assertEquals(3, c.getActivity().get(0).getCount());
+
+ assertEquals("mike", c.getActivity().get(1).getName());
+ assertEquals(2, c.getActivity().get(1).getCount());
+
+ assertEquals("angus", c.getActivity().get(2).getName());
+ assertEquals(2, c.getActivity().get(2).getCount());
+ }
+
+ /**
+ * Tests that parameters are correctly parsed into the exporter's parameter variables
+ */
+ @Test
+ public void testParameterParsing() {
+ ConversationExporter exporter;
+ ConversationExporterConfiguration configuration;
+ CommandLine cmd;
+ String[] args;
+
+ // - JUST INPUT AND OUTPUT FILES -----------------------------------------------------
+ args = new String[]{"-i", "chat.txt", "-o", "out.json"};
+
+ exporter = new ConversationExporter();
+ configuration = new ConversationExporterConfiguration();
+ cmd = new CommandLine(configuration);
+ cmd.parseArgs(args);
+ exporter.getCLIParameters(configuration);
+
+ assertNull(exporter.getUser());
+ assertNull(exporter.getKeyword());
+ assertNull(exporter.getBlacklistedWords());
+ assertFalse(exporter.isReport());
+
+
+ // - EXTRA PARAMETERS ----------------------------------------------------------------
+ args = new String[]{"-i", "chat2.txt", "-o", "out2.json", "-b", "you", "-u", "mike", "-k", "pie", "-b", "there", "-r"};
+
+ exporter = new ConversationExporter();
+ configuration = new ConversationExporterConfiguration();
+ cmd = new CommandLine(configuration);
+ cmd.parseArgs(args);
+ exporter.getCLIParameters(configuration);
+
+ assertEquals("mike", exporter.getUser());
+ assertEquals("pie", exporter.getKeyword());
+ assertEquals(Arrays.toString(new String[]{"you, there"}), Arrays.toString(exporter.getBlacklistedWords()));
+ assertTrue(exporter.isReport());
+ }
+
+
class InstantDeserializer implements JsonDeserializer {
@Override
diff --git a/src/test/java/com/mindlinksoft/recruitment/mychat/ConversationTests.java b/src/test/java/com/mindlinksoft/recruitment/mychat/ConversationTests.java
new file mode 100644
index 00000000..47178fe4
--- /dev/null
+++ b/src/test/java/com/mindlinksoft/recruitment/mychat/ConversationTests.java
@@ -0,0 +1,160 @@
+package com.mindlinksoft.recruitment.mychat;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertEquals;
+
+public class ConversationTests {
+ /**
+ * Tests that a conversation's activity is generated correctly
+ */
+ @Test
+ public void testActivityGeneration() throws Exception {
+ Collection testMessages = new ArrayList<>();
+
+ testMessages.add(new Message(null, "Annie", "*content*"));
+ testMessages.add(new Message(null, "Carol", "*content*"));
+ testMessages.add(new Message(null, "Annie", "*content*"));
+ testMessages.add(new Message(null, "Carol", "*content*"));
+ testMessages.add(new Message(null, "Dave", "*content*"));
+ testMessages.add(new Message(null, "Annie", "*content*"));
+ testMessages.add(new Message(null, "Dave", "*content*"));
+ testMessages.add(new Message(null, "Carol", "*content*"));
+ testMessages.add(new Message(null, "Kieran", "*content*"));
+
+ Conversation c = new Conversation("Test", testMessages);
+
+ // - before tracking activity -----------------------------------------
+
+ assertNull(c.getActivity());
+
+ // - track activity ---------------------------------------------------
+
+ c.trackActivity();
+
+ assertEquals("Annie", c.getActivity().get(0).getName());
+ assertEquals(3, c.getActivity().get(0).getCount());
+
+ assertEquals("Carol", c.getActivity().get(1).getName());
+ assertEquals(3, c.getActivity().get(1).getCount());
+
+ assertEquals("Dave", c.getActivity().get(2).getName());
+ assertEquals(2, c.getActivity().get(2).getCount());
+
+ assertEquals("Kieran", c.getActivity().get(3).getName());
+ assertEquals(1, c.getActivity().get(3).getCount());
+
+ // - added more messages ----------------------------------------------
+
+ testMessages.add(new Message(null, "Carol", "*content*"));
+ testMessages.add(new Message(null, "Kieran", "*content*"));
+ testMessages.add(new Message(null, "Carol", "*content*"));
+ testMessages.add(new Message(null, "Dave", "*content*"));
+ testMessages.add(new Message(null, "Ella", "*content*"));
+ testMessages.add(new Message(null, "Kieran", "*content*"));
+
+ c.trackActivity();
+
+ assertEquals("Annie", c.getActivity().get(0).getName());
+ assertEquals(3, c.getActivity().get(0).getCount());
+
+ assertEquals("Carol", c.getActivity().get(1).getName());
+ assertEquals(5, c.getActivity().get(1).getCount());
+
+ assertEquals("Dave", c.getActivity().get(2).getName());
+ assertEquals(3, c.getActivity().get(2).getCount());
+
+ assertEquals("Kieran", c.getActivity().get(3).getName());
+ assertEquals(3, c.getActivity().get(3).getCount());
+
+ assertEquals("Ella", c.getActivity().get(4).getName());
+ assertEquals(1, c.getActivity().get(4).getCount());
+
+ // - new conversation -------------------------------------------------
+
+ testMessages = new ArrayList<>();
+ c = new Conversation("Test", testMessages);
+
+ c.trackActivity();
+
+ assertEquals(0, c.getActivity().size());
+
+ // - null conversation ------------------------------------------------
+
+ testMessages = null;
+ c = new Conversation("Test", testMessages);
+
+ c.trackActivity();
+
+ assertEquals(null, c.getActivity());
+ }
+
+ /**
+ * Tests that a conversation's activity is sorted correctly
+ */
+ @Test
+ public void testActivitySorting() throws Exception {
+ Collection testMessages = new ArrayList<>();
+
+ testMessages.add(new Message(null, "Annie", "*content*"));
+ testMessages.add(new Message(null, "Carol", "*content*"));
+ testMessages.add(new Message(null, "Annie", "*content*"));
+ testMessages.add(new Message(null, "Carol", "*content*"));
+ testMessages.add(new Message(null, "Dave", "*content*"));
+ testMessages.add(new Message(null, "Carol", "*content*"));
+ testMessages.add(new Message(null, "Dave", "*content*"));
+ testMessages.add(new Message(null, "Carol", "*content*"));
+ testMessages.add(new Message(null, "Kieran", "*content*"));
+
+ Conversation c = new Conversation("Test", testMessages);
+
+ // - track activity ---------------------------------------------------
+
+ c.trackActivity();
+ c.sortActivity();
+
+ assertEquals("Carol", c.getActivity().get(0).getName());
+ assertEquals(4, c.getActivity().get(0).getCount());
+
+ assertEquals("Annie", c.getActivity().get(1).getName());
+ assertEquals(2, c.getActivity().get(1).getCount());
+
+ assertEquals("Dave", c.getActivity().get(2).getName());
+ assertEquals(2, c.getActivity().get(2).getCount());
+
+ assertEquals("Kieran", c.getActivity().get(3).getName());
+ assertEquals(1, c.getActivity().get(3).getCount());
+
+ // - added more messages ----------------------------------------------
+
+ testMessages.add(new Message(null, "Carol", "*content*"));
+ testMessages.add(new Message(null, "Kieran", "*content*"));
+ testMessages.add(new Message(null, "Kieran", "*content*"));
+ testMessages.add(new Message(null, "Annie", "*content*"));
+ testMessages.add(new Message(null, "Ella", "*content*"));
+ testMessages.add(new Message(null, "Kieran", "*content*"));
+
+ c.trackActivity();
+ c.sortActivity();
+
+ assertEquals("Carol", c.getActivity().get(0).getName());
+ assertEquals(5, c.getActivity().get(0).getCount());
+
+ assertEquals("Kieran", c.getActivity().get(1).getName());
+ assertEquals(4, c.getActivity().get(1).getCount());
+
+ assertEquals("Annie", c.getActivity().get(2).getName());
+ assertEquals(3, c.getActivity().get(2).getCount());
+
+ assertEquals("Dave", c.getActivity().get(3).getName());
+ assertEquals(2, c.getActivity().get(3).getCount());
+
+ assertEquals("Ella", c.getActivity().get(4).getName());
+ assertEquals(1, c.getActivity().get(4).getCount());
+ }
+
+}
diff --git a/src/test/java/com/mindlinksoft/recruitment/mychat/KeywordFiltererTests.java b/src/test/java/com/mindlinksoft/recruitment/mychat/KeywordFiltererTests.java
new file mode 100644
index 00000000..7c992f1b
--- /dev/null
+++ b/src/test/java/com/mindlinksoft/recruitment/mychat/KeywordFiltererTests.java
@@ -0,0 +1,84 @@
+package com.mindlinksoft.recruitment.mychat;
+
+import com.mindlinksoft.recruitment.mychat.ConversationFilterers.KeywordFilterer;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+
+public class KeywordFiltererTests {
+ /**
+ * Tests that filtering for keywords functions correctly
+ */
+ @Test
+ public void testFilterConversationByKeyword() throws Exception {
+ KeywordFilterer keywordFilterer = new KeywordFilterer();
+ ConversationExporterIO exporterIO = new ConversationExporterIO();
+ Conversation c;
+ List ms;
+
+ // - PIE ---------------------------------------------------------
+ c = exporterIO.readConversation("chat.txt");
+
+ keywordFilterer.setKeyword("pie");
+ c = keywordFilterer.filter(c);
+ ms = (ArrayList) c.getMessages();
+
+ assertEquals("I'm good thanks, do you like pie?", ms.get(0).getContent());
+ assertEquals("Hell yes! Are we buying some pie?", ms.get(1).getContent());
+ assertEquals("No, just want to know if there's anybody else in the pie society...", ms.get(2).getContent());
+ assertEquals("YES! I'm the head pie eater there...", ms.get(3).getContent());
+
+ // - HELL ---------------------------------------------------------
+ c = exporterIO.readConversation("chat.txt");
+
+ keywordFilterer.setKeyword("Hell");
+ c = keywordFilterer.filter(c);
+ ms = (ArrayList) c.getMessages();
+
+ assertEquals("Hello there!", ms.get(0).getContent());
+ assertEquals("Hell yes! Are we buying some pie?", ms.get(1).getContent());
+
+ // - NOPE ---------------------------------------------------------
+ c = exporterIO.readConversation("chat.txt");
+
+ keywordFilterer.setKeyword("nope");
+ c = keywordFilterer.filter(c);
+ ms = (ArrayList) c.getMessages();
+
+ assertEquals(0, ms.size());
+
+ // - NULL ---------------------------------------------------------
+ c = exporterIO.readConversation("chat.txt");
+
+ keywordFilterer.setKeyword(null);
+ c = keywordFilterer.filter(c);
+ ms = (ArrayList) c.getMessages();
+
+ assertEquals("Hello there!", ms.get(0).getContent());
+ assertEquals("how are you?", ms.get(1).getContent());
+ assertEquals("I'm good thanks, do you like pie?", ms.get(2).getContent());
+ assertEquals("no, let me ask Angus...", ms.get(3).getContent());
+ assertEquals("Hell yes! Are we buying some pie?", ms.get(4).getContent());
+ assertEquals("No, just want to know if there's anybody else in the pie society...", ms.get(5).getContent());
+ assertEquals("YES! I'm the head pie eater there...", ms.get(6).getContent());
+
+ // - EMPTY STRING -------------------------------------------------
+ c = exporterIO.readConversation("chat.txt");
+
+ keywordFilterer.setKeyword("");
+ c = keywordFilterer.filter(c);
+ ms = (ArrayList) c.getMessages();
+
+ assertEquals("Hello there!", ms.get(0).getContent());
+ assertEquals("how are you?", ms.get(1).getContent());
+ assertEquals("I'm good thanks, do you like pie?", ms.get(2).getContent());
+ assertEquals("no, let me ask Angus...", ms.get(3).getContent());
+ assertEquals("Hell yes! Are we buying some pie?", ms.get(4).getContent());
+ assertEquals("No, just want to know if there's anybody else in the pie society...", ms.get(5).getContent());
+ assertEquals("YES! I'm the head pie eater there...", ms.get(6).getContent());
+ }
+
+}
diff --git a/src/test/java/com/mindlinksoft/recruitment/mychat/UserFiltererTests.java b/src/test/java/com/mindlinksoft/recruitment/mychat/UserFiltererTests.java
new file mode 100644
index 00000000..6a1d305f
--- /dev/null
+++ b/src/test/java/com/mindlinksoft/recruitment/mychat/UserFiltererTests.java
@@ -0,0 +1,78 @@
+package com.mindlinksoft.recruitment.mychat;
+
+import com.mindlinksoft.recruitment.mychat.ConversationFilterers.UserFilterer;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class UserFiltererTests {
+ /**
+ * Tests that filtering by username functions correctly
+ */
+ @Test
+ public void testFilterConversationByUser() throws Exception {
+ UserFilterer userFilterer = new UserFilterer();
+ ConversationExporterIO exporterIO = new ConversationExporterIO();
+ Conversation c;
+
+ // - BOB ---------------------------------------------------------
+ c = exporterIO.readConversation("chat.txt");
+
+ userFilterer.setUser("bob");
+ c = userFilterer.filter(c);
+
+ for (Message m : c.getMessages()) {
+ assertEquals("bob", m.getSenderId());
+ }
+
+ assertEquals(3, c.getMessages().size());
+
+ // - BOB (CAPITALISED) -----------------------------------------
+ c = exporterIO.readConversation("chat.txt");
+ // should work the same way as with its lowercase version
+ userFilterer.setUser("BOB");
+ c = userFilterer.filter(c);
+
+ for (Message m : c.getMessages()) {
+ assertEquals("bob", m.getSenderId());
+ }
+
+ assertEquals(3, c.getMessages().size());
+
+ // - MIKE --------------------------------------------------------
+ c = exporterIO.readConversation("chat.txt");
+
+ userFilterer.setUser("mike");
+ c = userFilterer.filter(c);
+
+ for (Message m : c.getMessages()) {
+ assertEquals("mike", m.getSenderId());
+ }
+
+ assertEquals(2, c.getMessages().size());
+
+ // - ALICE -------------------------------------------------------
+ c = exporterIO.readConversation("chat.txt");
+ // no alice in the conversation
+ userFilterer.setUser("alice");
+ c = userFilterer.filter(c);
+
+ assertEquals(0, c.getMessages().size());
+
+ // - NULL --------------------------------------------------------
+ c = exporterIO.readConversation("chat.txt");
+
+ userFilterer.setUser(null);
+ c = userFilterer.filter(c);
+
+ assertEquals(7, c.getMessages().size());
+
+ // - EMPTY STRING ------------------------------------------------
+ c = exporterIO.readConversation("chat.txt");
+
+ userFilterer.setUser("");
+ c = userFilterer.filter(c);
+
+ assertEquals(7, c.getMessages().size());
+ }
+}