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()); + } +}