Skip to content

Commit

Permalink
Merge pull request #144 from aliciamichellew/alicia/note-search-tags-…
Browse files Browse the repository at this point in the history
…feature

Add `search` by tags feature for Note
  • Loading branch information
aliciamichellew authored Oct 29, 2023
2 parents 50a7040 + 81f1bc2 commit f048bca
Show file tree
Hide file tree
Showing 11 changed files with 224 additions and 11 deletions.
28 changes: 19 additions & 9 deletions src/main/java/seedu/address/logic/Messages.java
Original file line number Diff line number Diff line change
Expand Up @@ -84,17 +84,28 @@ public static String format(University university) {
}

/**
* Formats the {@code university} for display to the user.
* Formats the {@code observableList} for display to the user.
* Overloaded method.
* @param universityObservableList
* @param observableList
* @return
*/
public static String format(ObservableList<University> universityObservableList) {
final StringBuilder builder = new StringBuilder("UniversityName: ");
String universityNames = universityObservableList.stream()
.map(University::getUniversityName).map(UniversityName::getName).collect(Collectors.joining(", "));
builder.append(universityNames);
return builder.toString();
public static <T> String format(ObservableList<T> observableList) {
T item = observableList.get(0);
if (item instanceof University) {
final StringBuilder builder = new StringBuilder("UniversityName: ");
String universityNames = observableList.stream().map(c -> (University) c)
.map(University::getUniversityName)
.map(UniversityName::getName).collect(Collectors.joining(", "));
builder.append(universityNames);
return builder.toString();
} else if (item instanceof Note) {
final StringBuilder builder = new StringBuilder("Note: ");
String tag = observableList.stream().findFirst().map(c -> (Note) c)
.map(Note::getTags).get().toString();
builder.append(tag);
return builder.toString();
}
return "default";
}

/**
Expand Down Expand Up @@ -140,5 +151,4 @@ public static String format(Note note) {
.append(note.getTags());
return builder.toString();
}

}
72 changes: 72 additions & 0 deletions src/main/java/seedu/address/logic/commands/NoteSearchCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package seedu.address.logic.commands;

import static java.util.Objects.requireNonNull;

import seedu.address.commons.util.ToStringBuilder;
import seedu.address.logic.Messages;
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.model.Model;
import seedu.address.model.SeplendidModel;
import seedu.address.model.notes.NoteTagContainsKeywordsPredicate;
import seedu.address.seplendidui.UiUtil;

/**
* Finds and lists all notes in SEPlendid whose tags contains any of the argument keywords.
* Keyword matching is case-insensitive.
*/
public class NoteSearchCommand extends NoteCommand {
public static final String ACTION_WORD = "search";

public static final String MESSAGE_SUCCESS = "Notes searched: %1$s";

public static final String MESSAGE_NONEXISTENT_NOTES = "This note does not exist in SEPlendid";

public static final String NOTE_SEARCH_MESSAGE_USAGE = COMMAND_WORD
+ " search [note_tag_keyword]: Search notes with the same tag keyword";
private final NoteTagContainsKeywordsPredicate predicate;

public NoteSearchCommand(NoteTagContainsKeywordsPredicate predicate) {
this.predicate = predicate;
}

@Override
public CommandResult execute(Model model) throws CommandException {
throw new CommandException("TBD: this is a stub and should be removed after morph.");
}

@Override
public CommandResult execute(SeplendidModel model) throws CommandException {
requireNonNull(model);

model.getSearchNoteIfExists(predicate);

if (model.getFilteredLocalCourseList().isEmpty()) {
throw new CommandException(MESSAGE_NONEXISTENT_NOTES);
}

return new CommandResult(String.format(MESSAGE_SUCCESS, Messages.format(model.getFilteredNoteList())),
UiUtil.ListViewModel.NOTE_LIST);
}

@Override
public boolean equals(Object other) {
if (other == this) {
return true;
}

// instanceof handles nulls
if (!(other instanceof NoteSearchCommand)) {
return false;
}

NoteSearchCommand otherNoteSearchCommand = (NoteSearchCommand) other;
return predicate.equals(otherNoteSearchCommand.predicate);
}

@Override
public String toString() {
return new ToStringBuilder(this)
.add("predicate", predicate)
.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package seedu.address.logic.parser;

import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import static seedu.address.logic.parser.CliSyntax.PARAMETER_TAGS;

import seedu.address.logic.commands.NoteSearchCommand;
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.notes.NoteTagContainsKeywordsPredicate;
import seedu.address.model.tag.Tag;

/**
* Parses the given {@code String} of arguments in the context of the SearchCommand
* and returns a SearchCommand object for execution.
* @throws ParseException if the user input does not conform the expected format
*/
public class NoteSearchCommandParser implements Parser<NoteSearchCommand> {
/**
* Parses the given {@code String} of arguments in the context of the NoteSearchCommand
* and returns a NoteSearchCommand object for execution.
* @throws ParseException if the user input does not conform the expected format
*/
public NoteSearchCommand parse(String args) throws ParseException {
String trimmedArgs = args.trim();
if (!ParserUtil.areValuesEnclosedAndNonEmpty(args)) {
throw new ParseException(
String.format(MESSAGE_INVALID_COMMAND_FORMAT,
NoteSearchCommand.NOTE_SEARCH_MESSAGE_USAGE)
);
}

SeplendidArgumentMap parameterToArgMap =
SeplendidArgumentTokenizer.tokenize(args,
PARAMETER_TAGS);

if (!ParserUtil.areArgumentsPresent(parameterToArgMap,
PARAMETER_TAGS)) {
throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
NoteSearchCommand.NOTE_SEARCH_MESSAGE_USAGE));
}

Tag tag = ParserUtil.parseTag(
parameterToArgMap.getValue(PARAMETER_TAGS).get());
return new NoteSearchCommand(new NoteTagContainsKeywordsPredicate(tag.toString()));

}
}
7 changes: 5 additions & 2 deletions src/main/java/seedu/address/logic/parser/SeplendidParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import seedu.address.logic.commands.NoteAddCommand;
import seedu.address.logic.commands.NoteCommand;
import seedu.address.logic.commands.NoteListCommand;
import seedu.address.logic.commands.NoteSearchCommand;
import seedu.address.logic.commands.PartnerCourseAddCommand;
import seedu.address.logic.commands.PartnerCourseCommand;
import seedu.address.logic.commands.PartnerCourseDeleteCommand;
Expand Down Expand Up @@ -127,7 +128,7 @@ public Command parseCommand(String userInput) throws ParseException {
return getPartnerCourseCommandWithArg(userInput, actionWord, arguments);

case NoteCommand.COMMAND_WORD:
return getNoteCommand(userInput, actionWord, arguments);
return getNoteCommandWithArg(userInput, actionWord, arguments);

case UniversityCommand.COMMAND_WORD:
return getUniversityCommandWithArg(userInput, actionWord, arguments);
Expand Down Expand Up @@ -240,11 +241,13 @@ private MappingCommand getMappingCommandWithoutArg(String userInput, String acti
}
}

private NoteAddCommand getNoteCommand(String userInput, String actionWord, String arguments)
private NoteCommand getNoteCommandWithArg(String userInput, String actionWord, String arguments)
throws ParseException {
switch (actionWord) {
case NoteAddCommand.ACTION_WORD:
return new NoteAddCommandParser().parse(arguments);
case NoteSearchCommand.ACTION_WORD:
return new NoteSearchCommandParser().parse(arguments);
default:
logger.finer("This user input caused a ParseException: " + userInput);
throw new ParseException(MESSAGE_UNKNOWN_COMMAND);
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/seedu/address/model/SeplendidModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import seedu.address.model.localcourse.LocalCourse;
import seedu.address.model.mapping.Mapping;
import seedu.address.model.notes.Note;
import seedu.address.model.notes.NoteTagContainsKeywordsPredicate;
import seedu.address.model.partnercourse.PartnerCode;
import seedu.address.model.partnercourse.PartnerCourse;
import seedu.address.model.university.University;
Expand Down Expand Up @@ -238,6 +239,13 @@ public interface SeplendidModel {
*/
void setNote(Note note, Note editedNote);

/**
* Gets the notes with the specific tags
* @param notePredicate
*/
void getSearchNoteIfExists(NoteTagContainsKeywordsPredicate notePredicate);


//=========== FilteredLocalCourseList Accessors =============================================================

/**
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/seedu/address/model/SeplendidModelManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import seedu.address.model.localcourse.LocalCourse;
import seedu.address.model.mapping.Mapping;
import seedu.address.model.notes.Note;
import seedu.address.model.notes.NoteTagContainsKeywordsPredicate;
import seedu.address.model.partnercourse.PartnerCode;
import seedu.address.model.partnercourse.PartnerCourse;
import seedu.address.model.university.University;
Expand Down Expand Up @@ -442,6 +443,13 @@ public void setNote(Note target, Note editedNote) {
noteCatalogue.setNote(target, editedNote);
}

@Override
public void getSearchNoteIfExists(NoteTagContainsKeywordsPredicate notePredicate) {
requireNonNull(notePredicate);
filteredNoteCatalogue.setPredicate(notePredicate);

}

//=========== FilteredNoteList Accessors =============================================================

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package seedu.address.model.notes;

import java.util.function.Predicate;

import seedu.address.commons.util.ToStringBuilder;

/**
* Tests that a {@code Note}'s {@code Tag} matches any of the keywords given.
*/
public class NoteTagContainsKeywordsPredicate implements Predicate<Note> {
private final String keyword;
public NoteTagContainsKeywordsPredicate(String keywords) {
this.keyword = keywords;
}

@Override
public boolean test(Note note) {
return note.getTags().toString().toLowerCase()
.contains(keyword.toLowerCase());
}

@Override
public boolean equals(Object other) {
if (other == this) {
return true;
}

// instanceof handles nulls
if (!(other instanceof seedu.address.model.notes.NoteTagContainsKeywordsPredicate)) {
return false;
}

NoteTagContainsKeywordsPredicate otherTagContainsKeywordsPredicate =
(NoteTagContainsKeywordsPredicate) other;
return keyword.equals(otherTagContainsKeywordsPredicate.keyword);
}

@Override
public String toString() {
return new ToStringBuilder(this).add("keyword", keyword).toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import seedu.address.model.localcourse.LocalCourse;
import seedu.address.model.mapping.Mapping;
import seedu.address.model.notes.Note;
import seedu.address.model.notes.NoteTagContainsKeywordsPredicate;
import seedu.address.model.partnercourse.PartnerCode;
import seedu.address.model.partnercourse.PartnerCourse;
import seedu.address.model.university.University;
Expand Down Expand Up @@ -347,6 +348,11 @@ public void setNote(Note note, Note editedNote) {
throw new AssertionError("This method should not be called.");
}

@Override
public void getSearchNoteIfExists(NoteTagContainsKeywordsPredicate notePredicate) {
throw new AssertionError("This method should not be called.");
}

@Override
public ObservableList<Note> getFilteredNoteList() {
throw new AssertionError("This method should not be called.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import seedu.address.model.localcourse.LocalCourse;
import seedu.address.model.mapping.Mapping;
import seedu.address.model.notes.Note;
import seedu.address.model.notes.NoteTagContainsKeywordsPredicate;
import seedu.address.model.partnercourse.PartnerCode;
import seedu.address.model.partnercourse.PartnerCourse;
import seedu.address.model.university.University;
Expand Down Expand Up @@ -348,6 +349,11 @@ public void setNote(Note note, Note editedNote) {
throw new AssertionError("This method should not be called.");
}

@Override
public void getSearchNoteIfExists(NoteTagContainsKeywordsPredicate notePredicate) {
throw new AssertionError("This method should not be called.");
}

@Override
public ObservableList<Note> getFilteredNoteList() {
throw new AssertionError("This method should not be called.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import seedu.address.model.localcourse.LocalCourse;
import seedu.address.model.mapping.Mapping;
import seedu.address.model.notes.Note;
import seedu.address.model.notes.NoteTagContainsKeywordsPredicate;
import seedu.address.model.partnercourse.PartnerCode;
import seedu.address.model.partnercourse.PartnerCourse;
import seedu.address.model.university.University;
Expand Down Expand Up @@ -337,6 +338,11 @@ public void setNote(Note note, Note editedNote) {
throw new AssertionError("This method should not be called.");
}

@Override
public void getSearchNoteIfExists(NoteTagContainsKeywordsPredicate notePredicate) {
throw new AssertionError("This method should not be called.");
}

@Override
public ObservableList<Note> getFilteredNoteList() {
throw new AssertionError("This method should not be called.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import seedu.address.model.localcourse.LocalCourse;
import seedu.address.model.mapping.Mapping;
import seedu.address.model.notes.Note;
import seedu.address.model.notes.NoteTagContainsKeywordsPredicate;
import seedu.address.model.partnercourse.PartnerCode;
import seedu.address.model.partnercourse.PartnerCourse;
import seedu.address.model.university.University;
Expand Down Expand Up @@ -346,6 +347,11 @@ public void setNote(Note note, Note editedNote) {
throw new AssertionError("This method should not be called.");
}

@Override
public void getSearchNoteIfExists(NoteTagContainsKeywordsPredicate notePredicate) {
throw new AssertionError("This method should not be called.");
}

@Override
public ObservableList<Note> getFilteredNoteList() {
throw new AssertionError("This method should not be called.");
Expand Down

0 comments on commit f048bca

Please sign in to comment.