diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md
index 2385938a20c..75f4d15033e 100644
--- a/docs/DeveloperGuide.md
+++ b/docs/DeveloperGuide.md
@@ -1,10 +1,10 @@
---
- layout: default.md
- title: "Developer Guide"
- pageNav: 3
+layout: default.md
+title: "Developer Guide"
+pageNav: 3
---
-# AB-3 Developer Guide
+# EduContacts Developer Guide
@@ -13,7 +13,7 @@
## **Acknowledgements**
-_{ list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well }_
+This project is based on the AddressBook-Level3 project created by the [SE-EDU initiative](https://se-education.org) ([UG](https://se-education.org/addressbook-level3/UserGuide.html), [DG](https://se-education.org/addressbook-level3/DeveloperGuide.html), [GitHub Page](https://github.com/se-edu/addressbook-level3)).
--------------------------------------------------------------------------------------------------------------------
@@ -90,7 +90,7 @@ Here's a (partial) class diagram of the `Logic` component:
-The sequence diagram below illustrates the interactions within the `Logic` component, taking `execute("delete 1")` API call as an example.
+The sequence diagram below illustrates the interactions within the `Logic` component, taking `execute("delete 12345678")` API call as an example.
@@ -130,7 +130,7 @@ The `Model` component,
-**Note:** An alternative (arguably, a more OOP) model is given below. It has a `Tag` list in the `AddressBook`, which `Person` references. This allows `AddressBook` to only require one `Tag` object per unique tag, instead of each `Person` needing their own `Tag` objects.
+**Note:** An alternative (arguably, a more OOP) model is given below. It has a `Role` list in the `AddressBook`, which `Person` references. This allows `AddressBook` to only require one `Role` object per unique tag, instead of each `Person` needing their own `Role` objects.
@@ -176,11 +176,11 @@ Step 1. The user launches the application for the first time. The `VersionedAddr
-Step 2. The user executes `delete 5` command to delete the 5th person in the address book. The `delete` command calls `Model#commitAddressBook()`, causing the modified state of the address book after the `delete 5` command executes to be saved in the `addressBookStateList`, and the `currentStatePointer` is shifted to the newly inserted address book state.
+Step 2. The user executes `delete 12345678` command to delete the person with Student ID of `12345678` in the address book. The `delete` command calls `Model#commitAddressBook()`, causing the modified state of the address book after the `delete 12345678` command executes to be saved in the `addressBookStateList`, and the `currentStatePointer` is shifted to the newly inserted address book state.
-Step 3. The user executes `add n/David …` to add a new person. The `add` command also calls `Model#commitAddressBook()`, causing another modified address book state to be saved into the `addressBookStateList`.
+Step 3. The user executes `add id/12345678 …` to add a new person. The `add` command also calls `Model#commitAddressBook()`, causing another modified address book state to be saved into the `addressBookStateList`.
@@ -281,6 +281,7 @@ _{Explain here how the data archiving feature will be implemented}_
**Value proposition**: save important time through simplification of student-parent contact management, enhancement in communication tracking and integrated progress reports
+
### User stories
@@ -310,24 +311,26 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli
| `*` | long-time teacher | import contact data | load data from a file to restore lost or missing data |
| `*` | long-time teacher | access communication history | be well-prepared for upcoming meetings |
+
+
### Use cases
-(For all use cases below, the **System** is the `AddressBook` and the **Actor** is the `user`, unless specified otherwise)
+(For all use cases below, the **System** is the `EduContacts` and the **Actor** is the `user`, unless specified otherwise)
**Use case: UC01 - Add a student**
**MSS**
1. User adds a student to the list of contacts.
-2. AddressBook updates the list of contacts.
+2. EduContacts updates the list of contacts.
Use case ends.
**Extensions**
-* 1a. AddressBook detects an error in the given data.
+* 1a. EduContacts detects an error in the given data.
- * 1a1. AddressBook shows an error message.
+ * 1a1. EduContacts shows an error message.
Use case ends.
@@ -337,36 +340,29 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli
**MSS**
-1. User finds a student.
-2. AddressBook displays the student.
+1. User provides details of the student.
+2. EduContacts displays the student and their details.
Use case ends.
**Extensions**
-* 1a. AddressBook detects an error in the given data.
+* 1a. EduContacts detects an error in the given data.
- * 1a1. AddressBook shows an error message.
+ * 1a1. EduContacts shows an error message.
Use case ends.
-* 2a. AddressBook displays an empty list.
+* 2a. EduContacts is unable to find the student.
Use case ends.
-* 2b. AddressBook displays a list of multiple students.
-
- * 2b1. User manually locates the student in the list.
-
- Use case ends.
-
**Use case: UC03 - Add a grade for a student**
**MSS**
-
-1. User finds the student they wish to add a grade for (UC02).
+1. User finds the student (UC02) they wish to add a grade for.
2. User adds a grade for the student.
-3. AddressBook updates the list of contacts.
+3. EduContacts updates the list of contacts.
Use case ends.
@@ -376,9 +372,9 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli
Use case ends.
-* 2a. AddressBook detects an error in the given data.
+* 2a. EduContacts detects an error in the given data.
- * 2a1. AddressBook shows an error message.
+ * 2a1. EduContacts shows an error message.
Use case ends.
@@ -386,9 +382,9 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli
**MSS**
-1. User finds the student they wish to delete from the list (UC02).
+1. User finds the student (UC02) they wish to delete from the list.
2. User deletes the student in the list.
-3. AddressBook updates the list of contacts.
+3. EduContacts updates the list of contacts.
Use case ends.
@@ -398,9 +394,9 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli
Use case ends.
-* 2a. AddressBook detects an error in the given data.
+* 2a. EduContacts detects an error in the given data.
- * 2a1. AddressBook shows an error message.
+ * 2a1. EduContacts shows an error message.
Use case ends.
@@ -408,9 +404,9 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli
**MSS**
-1. User finds the student they wish to edit (UC02).
+1. User finds the student (UC02) they wish to edit.
2. User edits the details of the student in the list.
-3. AddressBook updates the list of contacts.
+3. EduContacts updates the list of contacts.
Use case ends.
@@ -420,19 +416,19 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli
Use case ends.
-* 2a. AddressBook detects an error in the given data.
+* 2a. EduContacts detects an error in the given data.
- * 2a1. AddressBook shows an error message.
+ * 2a1. EduContacts shows an error message.
Use case ends.
-**Use case: UC06 - Add a tag to a student**
+**Use case: UC06 - Add a module to a student**
**MSS**
-1. User finds the student they wish to edit the details of (UC02).
-2. User adds a tag to the student in the list.
-3. AddressBook adds the tag to the student and updates the list of contacts.
+1. User finds the student (UC02) they wish to add a module for.
+2. User adds a module to the student in the list.
+3. EduContacts updates the list of contacts.
Use case ends.
@@ -442,19 +438,53 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli
Use case ends.
-* 2a. AddressBook detects an error in the given data.
+* 2a. EduContacts detects an error in the given data.
- * 2a1. AddressBook shows an error message.
+ * 2a1. EduContacts shows an error message.
Use case ends.
-**Use case: UC07 - Add contacts of next-of-kins of a student**
+* 2b. Student already has the module.
+
+ * 2b1. EduContacts shows an error message.
+
+ Use case ends.
+
+**Use case: UC07 - Grade a student**
**MSS**
-1. User finds the student they wish to add contacts of next-of-kins for (UC02).
+1. User finds the student (UC02) they wish to grade.
+2. User grades a module the student is taking.
+3. EduContacts updates the list of contacts.
+
+ Use case ends.
+
+**Extensions**
+
+* 1a. User is unable to find the student.
+
+ Use case ends.
+
+* 2a. EduContacts detects an error in the given data.
+
+ * 2a1. EduContacts shows an error message.
+
+ Use case ends.
+
+* 2b. The module is already graded.
+
+ * 2b1. EduContacts overwrites the old grade with the new grade.
+
+ Use case ends.
+
+**Use case: UC08 - Add contacts of next-of-kins of a student**
+
+**MSS**
+
+1. User finds the student (UC02) they wish to add contacts of next-of-kins for.
2. User adds contacts of next-of-kins of the student in the list.
-3. AddressBook updates the list of contacts.
+3. EduContacts updates the list of contacts.
Use case ends.
@@ -464,12 +494,14 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli
Use case ends.
-* 2a. AddressBook detects an error in the given data.
+* 2a. EduContacts detects an error in the given data.
- * 2a1. AddressBook shows an error message.
+ * 2a1. EduContacts shows an error message.
Use case ends.
+
+
### Non-Functional Requirements
1. **Data Requirements**
- EduContacts must be capable of storing up to 1000 students’ contact details and academic data without significant performance degradation
@@ -510,14 +542,12 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli
- The system should support exporting and importing data in common file formats (e.g., CSV) for ease of use and integration.
13. **Disaster Recovery**
- The system should support manual and automatic backups to prevent data loss. In case of a critical failure, the data should be easily recoverable.
- - There should be clear steps for restoring data from a backup after a system failure, ensuring minimal downtime.
+ - There should be clear steps for restoring data from a backup after a system failure, ensuring minimal downtime.
14. **Fault Tolerance**
- All critical errors should be logged, allowing developers to troubleshoot and resolve issues. Minor errors should not crash the system but allow users to continue their tasks.
- In the event of a system fault, the system should continue operating in a degraded mode without losing functionality.
-
-
-
+
### Glossary
@@ -545,44 +575,85 @@ testers are expected to do more *exploratory* testing.
+
+
### Launch and shutdown
1. Initial launch
- 1. Download the jar file and copy into an empty folder
+ 1. Download the jar file and copy into an empty folder.
- 1. Double-click the jar file Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum.
+ 2. Double-click the jar file.
+ Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum.
-1. Saving window preferences
+2. Saving window preferences
1. Resize the window to an optimum size. Move the window to a different location. Close the window.
- 1. Re-launch the app by double-clicking the jar file.
+ 2. Re-launch the app by double-clicking the jar file.
Expected: The most recent window size and location is retained.
-1. _{ more test cases … }_
+
### Deleting a person
1. Deleting a person while all persons are being shown
- 1. Prerequisites: List all persons using the `list` command. Multiple persons in the list.
+ 1. Prerequisites: List all persons using the `list` command. Multiple persons in the list. One person in the list has Student ID `12345678`.
- 1. Test case: `delete 1`
- Expected: First contact is deleted from the list. Details of the deleted contact shown in the status message. Timestamp in the status bar is updated.
+ 2. Test case: `delete 12345678`
+ Expected: Person with Student ID `12345678` is deleted. Details of the deleted contact shown in the status message. Timestamp in the status bar is updated.
- 1. Test case: `delete 0`
+ 3. Test case: `delete 1234 5678`
Expected: No person is deleted. Error details shown in the status message. Status bar remains the same.
- 1. Other incorrect delete commands to try: `delete`, `delete x`, `...` (where x is larger than the list size)
+ 4. Other incorrect delete commands to try: `delete`, `delete x`, `...` (where x is a Student ID that no student in the list has)
Expected: Similar to previous.
-1. _{ more test cases … }_
+2. Deleting a person while only one person is being shown
+
+ 1. Prerequisites: Filter persons using the `filter` command until only one person remains. Multiple persons in the list. Person that remains has Student ID `12345678`. One person in the list has Student ID `11111111`
+
+ 2. Test case: `delete 12345678`
+ Expected: Person with Student ID `12345678` is deleted. Details of the deleted contact shown in the status message. Timestamp in the status bar is updated. List of persons shown is now blank.
+
+ 3. Test case: `delete 11111111`
+ Expected: No person is deleted. Error details shown in the status message. Status bar remains the same.
+
+3. Deleting a person while no persons are in the list
+
+ 1. Prerequisites: Delete all persons in the list using the `clear` command.
+
+ 2. Test case: `delete 12345678`
+ Expected: No person is deleted. Error details shown in the status message. Status bar remains the same.
+
+
### Saving data
-1. Dealing with missing/corrupted data files
+1. Dealing with missing data files
+
+ 1. To simulate a missing file, in the same folder as the jar file, navigate to the `data` folder and delete the `address.json` file in the folder.
+
+ 2. Launch EduContacts by double-clicking the jar file.
+ Expected: EduContacts is populated by a set of default list of persons. A new `address.json` file will be created in the `data` folder after closing the app or executing a command.
+
+2. Dealing with corrupted data files
+
+ 1. To simulate a corrupted file, navigate to the `data` folder and remove a curly brace at the end of the file.
+
+ 2. Launch EduContacts by double-clicking the jar file.
+ Expected: EduContacts has a blank list of persons. A new `address.json` file will be created in the `data` folder after closing the app or executing a command.
+
+--------------------------------------------------------------------------------------------------------------------
+
+## **Appendix: Effort**
+
+_{to work on in the future}_
+
+--------------------------------------------------------------------------------------------------------------------
+
+## **Appendix: Planned Enhancements**
- 1. _{explain how to simulate a missing/corrupted file, and the expected behavior}_
+_{to work on in the future}_
-1. _{ more test cases … }_
diff --git a/docs/SettingUp.md b/docs/SettingUp.md
index 9e30139d075..fbcdd2677cc 100644
--- a/docs/SettingUp.md
+++ b/docs/SettingUp.md
@@ -1,7 +1,7 @@
---
- layout: default.md
- title: "Setting up and getting started"
- pageNav: 3
+layout: default.md
+title: "Setting up and getting started"
+pageNav: 3
---
# Setting up and getting started
@@ -52,7 +52,7 @@ If you plan to use Intellij IDEA (highly recommended):
1. **Learn the design**
- When you are ready to start coding, we recommend that you get some sense of the overall design by reading about [AddressBook’s architecture](DeveloperGuide.md#architecture).
+ When you are ready to start coding, we recommend that you get some sense of the overall design by reading about [EduContacts’ architecture](DeveloperGuide.md#architecture).
1. **Do the tutorials**
These tutorials will help you get acquainted with the codebase.
diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/address/logic/Logic.java
index 92cd8fa605a..e96576b8023 100644
--- a/src/main/java/seedu/address/logic/Logic.java
+++ b/src/main/java/seedu/address/logic/Logic.java
@@ -33,6 +33,9 @@ public interface Logic {
/** Returns an unmodifiable view of the filtered list of persons */
ObservableList getFilteredPersonList();
+ /** Returns a Person object to display */
+ Person getPersonToDisplay();
+
/**
* Returns the user prefs' address book file path.
*/
diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/address/logic/LogicManager.java
index 5aa3b91c7d0..147a2dc3c8b 100644
--- a/src/main/java/seedu/address/logic/LogicManager.java
+++ b/src/main/java/seedu/address/logic/LogicManager.java
@@ -71,6 +71,11 @@ public ObservableList getFilteredPersonList() {
return model.getFilteredPersonList();
}
+ @Override
+ public Person getPersonToDisplay() {
+ return model.getPersonToDisplay();
+ }
+
@Override
public Path getAddressBookFilePath() {
return model.getAddressBookFilePath();
diff --git a/src/main/java/seedu/address/logic/commands/FindCommand.java b/src/main/java/seedu/address/logic/commands/FindCommand.java
new file mode 100644
index 00000000000..9598a8ed201
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/FindCommand.java
@@ -0,0 +1,92 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+
+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.person.Person;
+import seedu.address.model.person.StudentId;
+
+/**
+ * Finds a person identified using their Student ID and displays their details.
+ */
+public class FindCommand extends Command {
+
+ public static final String COMMAND_WORD = "find";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Finds the student identified by the Student ID used and displays their details.\n"
+ + "Parameters: "
+ + "ID\n"
+ + "Example: " + COMMAND_WORD + " "
+ + "12345678";
+
+ public static final String MESSAGE_FIND_PERSON_SUCCESS = "Found Student: %1$s";
+ public static final String MESSAGE_PERSON_NOT_FOUND = "No student is found with Student ID: %1$s";
+ private final StudentId studentId;
+
+ /**
+ * Creates a FindCommand to find the person identified by the specified {@code StudentId}.
+ *
+ * @param studentId The student ID of the person to find.
+ * @throws NullPointerException if the {@code studentId} is null.
+ */
+ public FindCommand(StudentId studentId) {
+ requireNonNull(studentId);
+ this.studentId = studentId;
+ }
+
+ /**
+ * Executes the find command and displays the student identified by the given studentID.
+ *
+ * @param model the model that contains the data of persons
+ * @return a CommandResult that shows the outcome of the command
+ * @throws CommandException if the studentID is invalid or not found
+ */
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ List lastShownList = model.getFilteredPersonList();
+ Person toFind = null;
+
+ for (Person person : lastShownList) {
+ if (person.getStudentId().equals(studentId)) {
+ toFind = person;
+ break;
+ }
+ }
+
+ if (toFind == null) {
+ throw new CommandException(String.format(MESSAGE_PERSON_NOT_FOUND, studentId));
+ }
+
+ model.setPersonToDisplay(toFind);
+ return new CommandResult(String.format(MESSAGE_FIND_PERSON_SUCCESS, Messages.format(toFind)));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof FindCommand)) {
+ return false;
+ }
+
+ FindCommand otherFindCommand = (FindCommand) other;
+ return studentId.equals(otherFindCommand.studentId);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("targetStudentId", studentId)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java
index 08512519f23..4167b2c78bb 100644
--- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java
+++ b/src/main/java/seedu/address/logic/parser/AddressBookParser.java
@@ -15,6 +15,7 @@
import seedu.address.logic.commands.EditCommand;
import seedu.address.logic.commands.ExitCommand;
import seedu.address.logic.commands.FilterCommand;
+import seedu.address.logic.commands.FindCommand;
import seedu.address.logic.commands.GradeCommand;
import seedu.address.logic.commands.HelpCommand;
import seedu.address.logic.commands.ListCommand;
@@ -85,6 +86,9 @@ public Command parseCommand(String userInput) throws ParseException {
case ModuleCommand.COMMAND_WORD:
return new ModuleCommandParser().parse(arguments);
+ case FindCommand.COMMAND_WORD:
+ return new FindCommandParser().parse(arguments);
+
default:
logger.finer("This user input caused a ParseException: " + userInput);
throw new ParseException(MESSAGE_UNKNOWN_COMMAND);
diff --git a/src/main/java/seedu/address/logic/parser/FindCommandParser.java b/src/main/java/seedu/address/logic/parser/FindCommandParser.java
new file mode 100644
index 00000000000..634d211c2cf
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/FindCommandParser.java
@@ -0,0 +1,29 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import seedu.address.logic.commands.FindCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.person.StudentId;
+
+/**
+ * Parses input arguments and creates a new FindCommand object
+ */
+public class FindCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the FindCommand
+ * and returns a FindCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public FindCommand parse(String args) throws ParseException {
+ String trimmedArg = args.trim();
+ if (trimmedArg.contains(" ")) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE));
+ }
+
+ StudentId studentId = ParserUtil.parseStudentId(trimmedArg);
+ return new FindCommand(studentId);
+ }
+}
diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java
index d54df471c1f..83f1a585c9f 100644
--- a/src/main/java/seedu/address/model/Model.java
+++ b/src/main/java/seedu/address/model/Model.java
@@ -84,4 +84,15 @@ public interface Model {
* @throws NullPointerException if {@code predicate} is null.
*/
void updateFilteredPersonList(Predicate predicate);
+
+ /**
+ * Displays the given person {@code personToDisplay} and their details.
+ * {@code target} must exist in the address book.
+ */
+ void setPersonToDisplay(Person personToDisplay);
+
+ /**
+ * Returns the Person object to display.
+ */
+ Person getPersonToDisplay();
}
diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java
index 57bc563fde6..6aa4c8ba38c 100644
--- a/src/main/java/seedu/address/model/ModelManager.java
+++ b/src/main/java/seedu/address/model/ModelManager.java
@@ -4,6 +4,7 @@
import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
import java.nio.file.Path;
+import java.util.Objects;
import java.util.function.Predicate;
import java.util.logging.Logger;
@@ -22,6 +23,7 @@ public class ModelManager implements Model {
private final AddressBook addressBook;
private final UserPrefs userPrefs;
private final FilteredList filteredPersons;
+ private Person personToDisplay;
/**
* Initializes a ModelManager with the given addressBook and userPrefs.
@@ -36,6 +38,20 @@ public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs
filteredPersons = new FilteredList<>(this.addressBook.getPersonList());
}
+ /**
+ * Initializes a ModelManager with the given addressBook, userPrefs and personToDisplay.
+ */
+ public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs, Person personToDisplay) {
+ requireAllNonNull(addressBook, userPrefs);
+
+ logger.fine("Initializing with address book: " + addressBook + " and user prefs " + userPrefs);
+
+ this.addressBook = new AddressBook(addressBook);
+ this.userPrefs = new UserPrefs(userPrefs);
+ filteredPersons = new FilteredList<>(this.addressBook.getPersonList());
+ setPersonToDisplay(personToDisplay);
+ }
+
public ModelManager() {
this(new AddressBook(), new UserPrefs());
}
@@ -80,6 +96,7 @@ public void setAddressBookFilePath(Path addressBookFilePath) {
@Override
public void setAddressBook(ReadOnlyAddressBook addressBook) {
this.addressBook.resetData(addressBook);
+ setPersonToDisplay(null);
}
@Override
@@ -96,11 +113,16 @@ public boolean hasPerson(Person person) {
@Override
public void deletePerson(Person target) {
addressBook.removePerson(target);
+ if (target.isSamePerson(personToDisplay)) {
+ setPersonToDisplay(null);
+ }
}
@Override
public void addPerson(Person person) {
addressBook.addPerson(person);
+
+ setPersonToDisplay(person);
updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
}
@@ -109,6 +131,7 @@ public void setPerson(Person target, Person editedPerson) {
requireAllNonNull(target, editedPerson);
addressBook.setPerson(target, editedPerson);
+ setPersonToDisplay(editedPerson);
}
//=========== Filtered Person List Accessors =============================================================
@@ -128,6 +151,20 @@ public void updateFilteredPersonList(Predicate predicate) {
filteredPersons.setPredicate(predicate);
}
+ //=========== Person To Display =========================================================================
+
+ @Override
+ public void setPersonToDisplay(Person personToDisplay) {
+ if (personToDisplay == null || addressBook.hasPerson(personToDisplay)) {
+ this.personToDisplay = personToDisplay;
+ }
+ }
+
+ @Override
+ public Person getPersonToDisplay() {
+ return personToDisplay;
+ }
+
@Override
public boolean equals(Object other) {
if (other == this) {
@@ -140,9 +177,11 @@ public boolean equals(Object other) {
}
ModelManager otherModelManager = (ModelManager) other;
+
return addressBook.equals(otherModelManager.addressBook)
&& userPrefs.equals(otherModelManager.userPrefs)
- && filteredPersons.equals(otherModelManager.filteredPersons);
+ && filteredPersons.equals(otherModelManager.filteredPersons)
+ && Objects.equals(personToDisplay, otherModelManager.personToDisplay);
}
}
diff --git a/src/test/java/seedu/address/logic/LogicManagerTest.java b/src/test/java/seedu/address/logic/LogicManagerTest.java
index e17d4d4a580..1da2ffb7fc9 100644
--- a/src/test/java/seedu/address/logic/LogicManagerTest.java
+++ b/src/test/java/seedu/address/logic/LogicManagerTest.java
@@ -90,6 +90,22 @@ public void getFilteredPersonList_modifyList_throwsUnsupportedOperationException
assertThrows(UnsupportedOperationException.class, () -> logic.getFilteredPersonList().remove(0));
}
+ @Test
+ public void getPersonToDisplay_validPerson_success() {
+ // store current model in a temporary variable
+ Model temp = model;
+
+ // set up Logic object with personToDisplay set as AMY
+ model.addPerson(AMY);
+ model = new ModelManager(model.getAddressBook(), model.getUserPrefs(), AMY);
+ setUp();
+
+ assertEquals(AMY, logic.getPersonToDisplay());
+
+ // reset model to old model after testing
+ model = temp;
+ }
+
/**
* Executes the command and confirms that
* - no exceptions are thrown
@@ -173,6 +189,7 @@ public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath)
Person expectedPerson = new PersonBuilder(AMY).emptyModuleList().build();
ModelManager expectedModel = new ModelManager();
expectedModel.addPerson(expectedPerson);
+ expectedModel.setPersonToDisplay(expectedPerson);
assertCommandFailure(addCommand, CommandException.class, expectedMessage, expectedModel);
}
}
diff --git a/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java b/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java
index 162a0c86031..bd44f49c202 100644
--- a/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java
+++ b/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java
@@ -32,6 +32,7 @@ public void execute_newPerson_success() {
Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs());
expectedModel.addPerson(validPerson);
+ expectedModel.setPersonToDisplay(validPerson);
assertCommandSuccess(new AddCommand(validPerson), model,
String.format(AddCommand.MESSAGE_SUCCESS, Messages.format(validPerson)),
diff --git a/src/test/java/seedu/address/logic/commands/AddCommandTest.java b/src/test/java/seedu/address/logic/commands/AddCommandTest.java
index 90e8253f48e..cee9c867cc2 100644
--- a/src/test/java/seedu/address/logic/commands/AddCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/AddCommandTest.java
@@ -42,6 +42,7 @@ public void execute_personAcceptedByModel_addSuccessful() throws Exception {
assertEquals(String.format(AddCommand.MESSAGE_SUCCESS, Messages.format(validPerson)),
commandResult.getFeedbackToUser());
assertEquals(Arrays.asList(validPerson), modelStub.personsAdded);
+ assertEquals(validPerson, modelStub.personDisplayed);
}
@Test
@@ -157,6 +158,16 @@ public ObservableList getFilteredPersonList() {
public void updateFilteredPersonList(Predicate predicate) {
throw new AssertionError("This method should not be called.");
}
+
+ @Override
+ public void setPersonToDisplay(Person personToDisplay) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public Person getPersonToDisplay() {
+ throw new AssertionError("This method should not be called.");
+ }
}
/**
@@ -182,6 +193,7 @@ public boolean hasPerson(Person person) {
*/
private class ModelStubAcceptingPersonAdded extends ModelStub {
final ArrayList personsAdded = new ArrayList<>();
+ private Person personDisplayed;
@Override
public boolean hasPerson(Person person) {
@@ -193,6 +205,7 @@ public boolean hasPerson(Person person) {
public void addPerson(Person person) {
requireNonNull(person);
personsAdded.add(person);
+ personDisplayed = person;
}
@Override
diff --git a/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java b/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java
index 7cb5b09ed3b..3cf436e7bf7 100644
--- a/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java
@@ -69,6 +69,39 @@ public void execute_validStudentIdFilteredList_success() {
assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel);
}
+ @Test
+ public void execute_personDisplayedDeleted_success() {
+ Person personToDelete = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ model.setPersonToDisplay(personToDelete);
+ StudentId studentIdToDelete = personToDelete.getStudentId();
+ DeleteCommand deleteCommand = new DeleteCommand(studentIdToDelete);
+
+ String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS,
+ Messages.format(personToDelete));
+
+ ModelManager expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs());
+ expectedModel.deletePerson(personToDelete);
+
+ assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void execute_personDisplayedNotDeleted_success() {
+ Person personToDelete = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ Person personToDisplay = model.getFilteredPersonList().get(INDEX_SECOND_PERSON.getZeroBased());
+ model.setPersonToDisplay(personToDisplay);
+ StudentId studentIdToDelete = personToDelete.getStudentId();
+ DeleteCommand deleteCommand = new DeleteCommand(studentIdToDelete);
+
+ String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS,
+ Messages.format(personToDelete));
+
+ ModelManager expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs(), personToDisplay);
+ expectedModel.deletePerson(personToDelete);
+
+ assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel);
+ }
+
@Test
public void equals() {
Person firstPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
diff --git a/src/test/java/seedu/address/logic/commands/EditCommandTest.java b/src/test/java/seedu/address/logic/commands/EditCommandTest.java
index d91af00b729..6cf96a24892 100644
--- a/src/test/java/seedu/address/logic/commands/EditCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/EditCommandTest.java
@@ -79,7 +79,7 @@ public void execute_noFieldSpecifiedUnfilteredList_success() {
String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, Messages.format(editedPerson));
- Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
+ Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs(), editedPerson);
assertCommandSuccess(editCommand, model, expectedMessage, expectedModel);
}
diff --git a/src/test/java/seedu/address/logic/commands/FindCommandTest.java b/src/test/java/seedu/address/logic/commands/FindCommandTest.java
new file mode 100644
index 00000000000..3f54e86e897
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/FindCommandTest.java
@@ -0,0 +1,107 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
+import static seedu.address.logic.commands.CommandTestUtil.showPersonAtIndex;
+import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
+import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON;
+import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.Messages;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.person.Person;
+import seedu.address.model.person.StudentId;
+
+/**
+ * Contains integration tests (interaction with the Model) and unit tests for
+ * {@code FindCommand}.
+ */
+public class FindCommandTest {
+
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ @Test
+ public void execute_validStudentIdUnfilteredList_success() {
+ Person personToFind = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ StudentId studentIdToFind = personToFind.getStudentId();
+ FindCommand findCommand = new FindCommand(studentIdToFind);
+
+ String expectedMessage = String.format(FindCommand.MESSAGE_FIND_PERSON_SUCCESS,
+ Messages.format(personToFind));
+
+ ModelManager expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs());
+ expectedModel.setPersonToDisplay(personToFind);
+
+ assertCommandSuccess(findCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void execute_invalidStudentIdUnfilteredList_throwsCommandException() {
+ StudentId invalidStudentId = new StudentId("12345679");
+ FindCommand findCommand = new FindCommand(invalidStudentId);
+ assertCommandFailure(
+ findCommand, model, String.format(FindCommand.MESSAGE_PERSON_NOT_FOUND, invalidStudentId));
+ }
+
+ @Test
+ public void execute_validStudentIdFilteredList_success() {
+ showPersonAtIndex(model, INDEX_FIRST_PERSON);
+
+ Person personToFind = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ StudentId studentIdToFind = personToFind.getStudentId();
+ FindCommand findCommand = new FindCommand(studentIdToFind);
+
+ String expectedMessage = String.format(FindCommand.MESSAGE_FIND_PERSON_SUCCESS,
+ Messages.format(personToFind));
+
+ Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs(), personToFind);
+ showPersonAtIndex(expectedModel, INDEX_FIRST_PERSON);
+
+ assertCommandSuccess(findCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void equals() {
+ Person firstPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ Person secondPerson = model.getFilteredPersonList().get(INDEX_SECOND_PERSON.getZeroBased());
+
+ StudentId firstStudentId = firstPerson.getStudentId();
+ StudentId secondStudentId = secondPerson.getStudentId();
+
+ FindCommand findFirstCommand = new FindCommand(firstStudentId);
+ FindCommand findSecondCommand = new FindCommand(secondStudentId);
+
+ // same object -> returns true
+ assertTrue(findFirstCommand.equals(findFirstCommand));
+
+ // same values -> returns true
+ FindCommand findFirstCommandCopy = new FindCommand(firstStudentId);
+ assertTrue(findFirstCommand.equals(findFirstCommandCopy));
+
+ // different types -> returns false
+ assertFalse(findFirstCommand.equals(1));
+
+ // null -> returns false
+ assertFalse(findFirstCommand.equals(null));
+
+ // different person -> returns false
+ assertFalse(findFirstCommand.equals(findSecondCommand));
+ }
+
+ @Test
+ public void toStringMethod() {
+ Person firstPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ StudentId targetStudentId = firstPerson.getStudentId();
+
+ FindCommand findCommand = new FindCommand(targetStudentId);
+ String expected = FindCommand.class.getCanonicalName() + "{targetStudentId=" + targetStudentId + "}";
+ assertEquals(expected, findCommand.toString());
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java b/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java
index 1d08eaa860f..7189a5b8b62 100644
--- a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java
+++ b/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java
@@ -23,6 +23,7 @@
import seedu.address.logic.commands.EditCommand.EditPersonDescriptor;
import seedu.address.logic.commands.ExitCommand;
import seedu.address.logic.commands.FilterCommand;
+import seedu.address.logic.commands.FindCommand;
import seedu.address.logic.commands.GradeCommand;
import seedu.address.logic.commands.HelpCommand;
import seedu.address.logic.commands.ListCommand;
@@ -142,6 +143,14 @@ public void parseCommand_grade() throws Exception {
assertEquals(new GradeCommand(validStudentId, validModule, validGrade), command);
}
+ @Test
+ public void parseCommand_find() throws Exception {
+ StudentId validStudentId = new StudentId(CommandTestUtil.VALID_STUDENTID_BOB);
+ FindCommand command = (FindCommand) parser.parseCommand(
+ FindCommand.COMMAND_WORD + " " + validStudentId);
+ assertEquals(new FindCommand(validStudentId), command);
+ }
+
@Test
public void parseCommand_unrecognisedInput_throwsParseException() {
assertThrows(ParseException.class, String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE), ()
diff --git a/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java b/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java
new file mode 100644
index 00000000000..39d051d64f0
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java
@@ -0,0 +1,39 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.CommandTestUtil;
+import seedu.address.logic.commands.FindCommand;
+import seedu.address.model.person.StudentId;
+
+/**
+ * As we are only doing white-box testing, our test cases do not cover path variations
+ * outside the FindCommand code. For example, inputs "1" and "1 abc" take the
+ * same path through the FindCommand, and therefore we test only one of them.
+ * The path variation for those two cases occur inside the ParserUtil, and
+ * therefore should be covered by the ParserUtilTest.
+ */
+public class FindCommandParserTest {
+
+ private FindCommandParser parser = new FindCommandParser();
+
+ @Test
+ public void parse_validArgs_returnsDeleteCommand() {
+ StudentId validStudentId = new StudentId(CommandTestUtil.VALID_STUDENTID_BOB);
+ assertParseSuccess(parser, validStudentId.toString(), new FindCommand(validStudentId));
+ }
+
+ @Test
+ public void parse_invalidArgs_throwsParseException() {
+ // multiple arguments
+ assertParseFailure(parser, "11111111 abc",
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE));
+
+ // invalid studentId
+ assertParseFailure(parser, "1", StudentId.MESSAGE_CONSTRAINTS);
+ }
+}
diff --git a/src/test/java/seedu/address/model/ModelManagerTest.java b/src/test/java/seedu/address/model/ModelManagerTest.java
index 2cf1418d116..453d0010a31 100644
--- a/src/test/java/seedu/address/model/ModelManagerTest.java
+++ b/src/test/java/seedu/address/model/ModelManagerTest.java
@@ -2,8 +2,8 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
import static seedu.address.testutil.Assert.assertThrows;
import static seedu.address.testutil.TypicalPersons.ALICE;
import static seedu.address.testutil.TypicalPersons.BENSON;
@@ -27,6 +27,7 @@ public void constructor() {
assertEquals(new UserPrefs(), modelManager.getUserPrefs());
assertEquals(new GuiSettings(), modelManager.getGuiSettings());
assertEquals(new AddressBook(), new AddressBook(modelManager.getAddressBook()));
+ assertEquals(null, modelManager.getPersonToDisplay());
}
@Test
@@ -93,6 +94,41 @@ public void getFilteredPersonList_modifyList_throwsUnsupportedOperationException
assertThrows(UnsupportedOperationException.class, () -> modelManager.getFilteredPersonList().remove(0));
}
+ @Test
+ public void setPersonToDisplay_validPerson_success() {
+ modelManager.addPerson(ALICE);
+ modelManager.setPersonToDisplay(ALICE);
+
+ // same person -> returns true
+ assertEquals(new ModelManager(modelManager.getAddressBook(), modelManager.getUserPrefs(), ALICE), modelManager);
+
+ // different person -> returns false
+ assertNotEquals(new ModelManager(modelManager.getAddressBook(), modelManager.getUserPrefs(), BENSON),
+ modelManager);
+ }
+
+ @Test
+ public void setPersonToDisplay_invalidPerson_nothingHappens() {
+ modelManager.addPerson(ALICE);
+ modelManager.setPersonToDisplay(ALICE);
+ modelManager.setPersonToDisplay(BENSON);
+
+ assertEquals(new ModelManager(modelManager.getAddressBook(),
+ modelManager.getUserPrefs(),
+ ALICE), modelManager);
+ }
+
+ @Test
+ public void getPersonToDisplay_personToDisplayEqualToPersonSet_returnsTrue() {
+ modelManager.addPerson(ALICE);
+ modelManager = new ModelManager(modelManager.getAddressBook(), modelManager.getUserPrefs(), ALICE);
+ // same person -> returns true
+ assertEquals(ALICE, modelManager.getPersonToDisplay());
+
+ // different person -> returns false
+ assertNotEquals(BENSON, modelManager.getPersonToDisplay());
+ }
+
@Test
public void equals() {
AddressBook addressBook = new AddressBookBuilder().withPerson(ALICE).withPerson(BENSON).build();
@@ -104,6 +140,10 @@ public void equals() {
ModelManager modelManagerCopy = new ModelManager(addressBook, userPrefs);
assertTrue(modelManager.equals(modelManagerCopy));
+ modelManager = new ModelManager(addressBook, userPrefs, ALICE);
+ modelManagerCopy = new ModelManager(addressBook, userPrefs, ALICE);
+ assertTrue(modelManager.equals(modelManagerCopy));
+
// same object -> returns true
assertTrue(modelManager.equals(modelManager));
@@ -121,8 +161,11 @@ public void equals() {
modelManager.updateFilteredPersonList(new NameContainsKeywordsPredicate(Arrays.asList(keywords)));
assertFalse(modelManager.equals(new ModelManager(addressBook, userPrefs)));
+ // different personToDisplay -> returns false
+ assertFalse(modelManager.equals(new ModelManager(addressBook, userPrefs)));
+
// resets modelManager to initial state for upcoming tests
- modelManager.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+ modelManager = new ModelManager(addressBook, userPrefs);
// different userPrefs -> returns false
UserPrefs differentUserPrefs = new UserPrefs();