Skip to content

Commit

Permalink
Merge branch 'master' into branch-remarks
Browse files Browse the repository at this point in the history
  • Loading branch information
erv-teo authored Apr 1, 2024
2 parents 63de80d + 011b226 commit 9ea25ea
Show file tree
Hide file tree
Showing 29 changed files with 781 additions and 35 deletions.
35 changes: 35 additions & 0 deletions docs/DeveloperGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,41 @@ The following activity diagram summarizes what happens when a user executes a ne
* Pros: Easy to be more specific.
* Cons: Not relevant for the nurses use case. Allowing more wards also make it harder to filter fast.

### Find feature

#### Implementation

The filter by tags and/or ward mechanism is facilitated by `FindCommand`, `FindCommandParser`,
`NameContainsKeywordsPredicate` and
`IcContainsKeywordsPredicate`.

![FindClassDiagram](images/FindClassDiagram.png)

Additionally, it implements the following operations:

* `FindCommandParser#parse()` — Parses user input and creates a `FindCommand` object.

Given below is an example usage scenario and how the find by name or IC mechanism behaves at each step.

Step 1. The user executes `find n\Bob` command to find patient with the name Bob in the address book. The
`FindCommandParser` parses the user input, creating a new `FindCommand` and `NameContainsKeywordsPredicate` object.

Step 2. For each patient in the address book, the `predicate` object will be passed to
`Model#updateFilteredPersonList` check if the patient has Bob as part of his/her name. If the patient has the name Bob,
the patient will be shown in result.

#### Design considerations:

**Aspect: Find by full word or letters:**

* **Alternative 1 (current choice):** Allow finding by full word only.
* Pros: More meaningful to find by words instead of just letters.
* Cons: Harder to search when patient details is insufficient.

* **Alternative 2:** Allow finding by letters.
* Pros: Able to search even if lacking patient information.
* Cons: Harder to get specific patient as result will be a list.

### \[Proposed\] Data archiving

_{Explain here how the data archiving feature will be implemented}_
Expand Down
12 changes: 7 additions & 5 deletions docs/UserGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,9 @@ You now have 1 patient(s) in your address book.

Displays a list of all registered patients.

Format: `list`, `list INDEX`
Format: `list`, `list [w\WARD] [t\TAG]...`

* INDEX: Must be a positive integer not larger than the number of patients in the list.
* WARD: Must only list at most 1 ward.

Example command:

Expand All @@ -126,15 +126,17 @@ Here are the details of the 2 patients in your contact book:

Example command:

`list 2`
`list w\B4 t\SeverAllergies`
```
Here are the details of patient 2 in your contact book:
Listed all persons with:
Tags: FallRisk
Ward: B4
Jane Doe
IC: I2103210P
Date of Birth: 12 Nov 1999
Admission Date: 3 Mar 2024
Ward: B3
Ward: B4
Tags: SevereAllergies
```

Expand Down
18 changes: 18 additions & 0 deletions docs/diagrams/FindClassDiagram.puml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
@startuml
!include style.puml
skinparam arrowThickness 1.1
skinparam arrowColor LOGIC_COLOR_T4
skinparam classBackgroundColor LOGIC_COLOR

class State1 as "FindCommandParser"
class State2 as "FindCommand"
class State3 as "<<interface>>\nPredicate<Person> "
class State4 as "NameContainsKeywordsPredicate"
class State5 as "IcContainsKeywordsPredicate"

State1 .down.> "0..1" State2
State2 -right-> "1" State3
State4 .up.|> State3
State5 .up.|> State3

@enduml
Binary file added docs/images/FindClassDiagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/main/java/seedu/address/logic/Messages.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ public class Messages {
public static final String MESSAGE_DUPLICATE_FIELDS =
"Multiple values specified for the following single-valued field(s): ";

public static final String MESSAGE_DOB_LATER_THAN_ADMISSION =
"Date of birth should not be later than date of admission";
/**
* Returns an error message indicating the duplicate prefixes.
*/
Expand Down
12 changes: 11 additions & 1 deletion src/main/java/seedu/address/logic/commands/EditCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
import static seedu.address.logic.parser.CliSyntax.PREFIX_WARD;
import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -51,7 +53,7 @@ public class EditCommand extends Command {
+ "[" + PREFIX_TAG + "TAG]...\n"
+ "[" + PREFIX_REMARK + "REMARK]...\n"
+ "Example: " + COMMAND_WORD + " 1 "
+ PREFIX_IC + "T123456Q "
+ PREFIX_IC + "T1234567Q "
+ PREFIX_DOB + "12/08/1999";

public static final String MESSAGE_EDIT_PERSON_SUCCESS = "Edited Person: %1$s";
Expand Down Expand Up @@ -89,6 +91,14 @@ public CommandResult execute(Model model) throws CommandException {
throw new CommandException(MESSAGE_DUPLICATE_PERSON);
}

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");

LocalDate dobToCheck = LocalDate.parse(editedPerson.getDob().value, formatter);
LocalDate adToCheck = LocalDate.parse(editedPerson.getAdmissionDate().value, formatter);
if (dobToCheck.isAfter(adToCheck)) {
throw new CommandException(Messages.MESSAGE_DOB_LATER_THAN_ADMISSION);
}

model.setPerson(personToEdit, editedPerson);
model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, Messages.format(editedPerson)));
Expand Down
22 changes: 14 additions & 8 deletions src/main/java/seedu/address/logic/commands/FindCommand.java
Original file line number Diff line number Diff line change
@@ -1,28 +1,34 @@
package seedu.address.logic.commands;

import static java.util.Objects.requireNonNull;
import static seedu.address.logic.parser.CliSyntax.PREFIX_IC;
import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;

import java.util.function.Predicate;

import seedu.address.commons.util.ToStringBuilder;
import seedu.address.logic.Messages;
import seedu.address.model.Model;
import seedu.address.model.person.NameContainsKeywordsPredicate;
import seedu.address.model.person.Person;

/**
* Finds and lists all persons in address book whose name contains any of the argument keywords.
* Keyword matching is case insensitive.
* Keyword matching is case-insensitive.
*/
public class FindCommand extends Command {

public static final String COMMAND_WORD = "find";

public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all persons whose names contain any of "
+ "the specified keywords (case-insensitive) and displays them as a list with index numbers.\n"
+ "Parameters: KEYWORD [MORE_KEYWORDS]...\n"
+ "Example: " + COMMAND_WORD + " alice bob charlie";
public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds the person whose name or ic contain any of "
+ "the specified keywords (case-insensitive).\n"
+ "type 1 parameter: " + PREFIX_NAME + "KEYWORD [MORE_KEYWORDS]...\n"
+ "Example: " + COMMAND_WORD + PREFIX_NAME + "alice herman\n"
+ "type 2 parameter: " + PREFIX_IC + "KEYWORD\n"
+ "Example: " + COMMAND_WORD + PREFIX_IC + "A1234567W";

private final NameContainsKeywordsPredicate predicate;
private final Predicate<Person> predicate;

public FindCommand(NameContainsKeywordsPredicate predicate) {
public FindCommand(Predicate<Person> predicate) {
this.predicate = predicate;
}

Expand Down
52 changes: 50 additions & 2 deletions src/main/java/seedu/address/logic/commands/ListCommand.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package seedu.address.logic.commands;

import static java.util.Objects.requireNonNull;
import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
import static seedu.address.logic.parser.CliSyntax.PREFIX_WARD;
import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;

import seedu.address.commons.util.ToStringBuilder;
import seedu.address.model.Model;
import seedu.address.model.person.ListKeywordsPredicate;

/**
* Lists all persons in the address book to the user.
Expand All @@ -12,13 +16,57 @@ public class ListCommand extends Command {

public static final String COMMAND_WORD = "list";

public static final String MESSAGE_USAGE = COMMAND_WORD + ": Lists relevant contacts from the address book. "
+ "Parameters: "
+ "[" + PREFIX_TAG + "TAG]..."
+ "[" + PREFIX_WARD + "WARD]\n"
+ "Example: " + COMMAND_WORD + " "
+ PREFIX_TAG + "FallRisk "
+ PREFIX_WARD + "3 ";

public static final String MESSAGE_SUCCESS = "Listed all persons";
public static final String MESSAGE_SUCCESS_LIST = "Listed all persons with: %1$s";
private final ListKeywordsPredicate predicate;

public ListCommand(ListKeywordsPredicate predicate) {
this.predicate = predicate;
}

public ListCommand() {
this.predicate = null;
}

@Override
public CommandResult execute(Model model) {
requireNonNull(model);
model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
return new CommandResult(MESSAGE_SUCCESS);
if (predicate != null) {
model.updateFilteredPersonList(predicate);
return new CommandResult(String.format(MESSAGE_SUCCESS_LIST, predicate.getKeywordsString()));
} else {
model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
return new CommandResult(MESSAGE_SUCCESS);
}
}

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

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

ListCommand otherListCommand = (ListCommand) other;
return predicate.equals(otherListCommand.predicate);
}

@Override
public String toString() {
return new ToStringBuilder(this)
.add("predicate", predicate == null ? "null" : predicate)
.toString();
}
}
11 changes: 11 additions & 0 deletions src/main/java/seedu/address/logic/parser/AddCommandParser.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package seedu.address.logic.parser;

import static seedu.address.logic.Messages.MESSAGE_DOB_LATER_THAN_ADMISSION;
import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import static seedu.address.logic.parser.CliSyntax.PREFIX_ADMISSION_DATE;
import static seedu.address.logic.parser.CliSyntax.PREFIX_DOB;
Expand All @@ -9,6 +10,8 @@
import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
import static seedu.address.logic.parser.CliSyntax.PREFIX_WARD;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Set;
import java.util.stream.Stream;

Expand Down Expand Up @@ -54,6 +57,14 @@ public AddCommand parse(String args) throws ParseException {
Ward ward = ParserUtil.parseWard(argMultimap.getValue(PREFIX_WARD).orElse(null));
Remark remark = ParserUtil.parseRemark(argMultimap.getValue(PREFIX_REMARK).orElse(null));


DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
LocalDate dobDate = LocalDate.parse(dob.value, formatter);
LocalDate admissionDateDate = LocalDate.parse(admissionDate.value, formatter);
if (dobDate.isAfter(admissionDateDate)) {
throw new ParseException(MESSAGE_DOB_LATER_THAN_ADMISSION);
}

Person person = new Person(name, tagList, dob, ic, admissionDate, ward, remark);

return new AddCommand(person);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public Command parseCommand(String userInput) throws ParseException {
return new FindCommandParser().parse(arguments);

case ListCommand.COMMAND_WORD:
return new ListCommand();
return new ListCommandParser().parse(arguments);

case ExitCommand.COMMAND_WORD:
return new ExitCommand();
Expand Down
33 changes: 29 additions & 4 deletions src/main/java/seedu/address/logic/parser/FindCommandParser.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package seedu.address.logic.parser;

import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import static seedu.address.logic.parser.CliSyntax.PREFIX_IC;
import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;

import java.util.Arrays;
import java.util.Collections;

import seedu.address.logic.commands.FindCommand;
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.person.IcContainsKeywordPredicate;
import seedu.address.model.person.NameContainsKeywordsPredicate;

/**
Expand All @@ -24,10 +28,31 @@ public FindCommand parse(String args) throws ParseException {
throw new ParseException(
String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE));
}

String[] nameKeywords = trimmedArgs.split("\\s+");

return new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList(nameKeywords)));
assert trimmedArgs.length() > 1;

ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_IC);

argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_IC);

if (argMultimap.getValue(PREFIX_NAME).isPresent()) {
String keywords = argMultimap.getValue(PREFIX_NAME).get();
if (keywords.equals("")) {
return new FindCommand(new NameContainsKeywordsPredicate(Collections.emptyList()));
} else {
String[] nameKeywords = keywords.split("\\s+");
return new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList(nameKeywords)));
}
} else if (argMultimap.getValue(PREFIX_IC).isPresent()) {
String keyword = argMultimap.getValue(PREFIX_IC).get();
if (keyword.split("\\s+").length != 1) {
throw new ParseException(
String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE));
}
return new FindCommand(new IcContainsKeywordPredicate(keyword));
} else {
throw new ParseException(
String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE));
}
}

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

import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
import static seedu.address.logic.parser.CliSyntax.PREFIX_WARD;

import java.util.List;

import seedu.address.logic.commands.ListCommand;
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.person.ListKeywordsPredicate;

/**
* Parses input arguments and creates a new DeleteCommand object
*/
public class ListCommandParser implements Parser<ListCommand> {

/**
* Parses the given {@code String} of arguments in the context of the ListCommand
* and returns a ListCommand object for execution.
* @throws ParseException if the user input does not conform the expected format
*/
public ListCommand parse(String args) throws ParseException {
String trimmedArgs = args.trim();
if (trimmedArgs.isEmpty()) {
return new ListCommand(); // return a ListCommand object with no predicate
}

ArgumentMultimap argMultimap =
ArgumentTokenizer.tokenize(args, PREFIX_TAG, PREFIX_WARD);
argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_WARD);
List<String> tagList = ParserUtil
.parseTagsKeywords(argMultimap.getAllValues(PREFIX_TAG));
String ward = argMultimap.getValue(PREFIX_WARD).orElse("");

if (ward.isEmpty() && tagList.isEmpty()) {
throw new ParseException(
String.format(MESSAGE_INVALID_COMMAND_FORMAT, ListCommand.MESSAGE_USAGE));
}

assert !tagList.isEmpty() || !ward.isEmpty();

return new ListCommand(new ListKeywordsPredicate(tagList, ward));
}
}
Loading

0 comments on commit 9ea25ea

Please sign in to comment.