diff --git a/.github/codecov.yml b/.github/codecov.yml
new file mode 100644
index 00000000000..8061d523e5c
--- /dev/null
+++ b/.github/codecov.yml
@@ -0,0 +1,3 @@
+ignore:
+ - "src/main/java/seedu/address/ui"
+ - "src/main/java/seedu/address/model/util/SampleDataUtil.java"
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
new file mode 100644
index 00000000000..1e1862b0e82
--- /dev/null
+++ b/.github/workflows/docs.yml
@@ -0,0 +1,25 @@
+name: MarkBind Action
+
+on:
+ push:
+ branches:
+ - master
+
+jobs:
+ build_and_deploy:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Install Graphviz
+ run: sudo apt-get install graphviz
+ - name: Install Java
+ uses: actions/setup-java@v3
+ with:
+ java-version: '11'
+ distribution: 'temurin'
+ - name: Build & Deploy MarkBind site
+ uses: MarkBind/markbind-action@v2
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ rootDirectory: './docs'
+ baseUrl: '/tp' # replace with your repo name
+ version: '^5.1.0'
diff --git a/.gitignore b/.gitignore
index 284c4ca7cd9..1308aecbe96 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,3 +21,6 @@ src/test/data/sandbox/
# MacOS custom attributes files created by Finder
.DS_Store
docs/_site/
+docs/_markbind/logs/
+
+/bin/
diff --git a/README.md b/README.md
index 13f5c77403f..9b572a3183e 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,12 @@
-[](https://github.com/se-edu/addressbook-level3/actions)
-
+[](https://github.com/AY2324S1-CS2103T-T12-4/tp/actions)
+[](https://codecov.io/gh/AY2324S1-CS2103T-T12-4/tp)

-* This is **a sample project for Software Engineering (SE) students**.
- Example usages:
- * as a starting point of a course project (as opposed to writing everything from scratch)
- * as a case study
-* The project simulates an ongoing software project for a desktop application (called _AddressBook_) used for managing contact details.
- * It is **written in OOP fashion**. It provides a **reasonably well-written** code base **bigger** (around 6 KLoC) than what students usually write in beginner-level SE modules, without being overwhelmingly big.
- * It comes with a **reasonable level of user and developer documentation**.
-* It is named `AddressBook Level 3` (`AB3` for short) because it was initially created as a part of a series of `AddressBook` projects (`Level 1`, `Level 2`, `Level 3` ...).
-* For the detailed documentation of this project, see the **[Address Book Product Website](https://se-education.org/addressbook-level3)**.
-* This project is a **part of the se-education.org** initiative. If you would like to contribute code to this project, see [se-education.org](https://se-education.org#https://se-education.org/#contributing) for more info.
+- This project is based on the AddressBook-Level3 project created by the [SE-EDU initiative](https://se-education.org).
+
+- It is named MedBook, a brownfield project from the AddressBook-Level3.
+- It can:
+ - streamline patient management for healthcare professionals
+ - simplify the process of accessing patients' contact information and medical records
+- To access the User Guide, click [here](/docs/UserGuide.md)
+- To access the Developer Guide, click [here](/docs/DeveloperGuide.md)
diff --git a/build.gradle b/build.gradle
index a2951cc709e..b0ac5e57864 100644
--- a/build.gradle
+++ b/build.gradle
@@ -66,7 +66,11 @@ dependencies {
}
shadowJar {
- archiveFileName = 'addressbook.jar'
+ archiveFileName = 'medbook.jar'
+}
+
+run {
+ enableAssertions = true
}
defaultTasks 'clean', 'test'
diff --git a/docs/.gitignore b/docs/.gitignore
new file mode 100644
index 00000000000..1748e487fbd
--- /dev/null
+++ b/docs/.gitignore
@@ -0,0 +1,23 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+lerna-debug.log*
+_markbind/logs/
+
+# Dependency directories
+node_modules/
+
+# Production build files (change if you output the build to a different directory)
+_site/
+
+# Env
+.env
+.env.local
+
+# IDE configs
+.vscode/
+.idea/*
+*.iml
diff --git a/docs/AboutUs.md b/docs/AboutUs.md
index 1c9514e966a..0336f94d7ff 100644
--- a/docs/AboutUs.md
+++ b/docs/AboutUs.md
@@ -1,59 +1,57 @@
---
-layout: page
-title: About Us
+ layout: default.md
+ title: "About Us"
---
+# About Us
+
We are a team based in the [School of Computing, National University of Singapore](http://www.comp.nus.edu.sg).
-You can reach us at the email `seer[at]comp.nus.edu.sg`
+You can reach us at the email `low.darren[at]u.nus.edu`
## Project team
-### John Doe
+### Darren Low
-
+
-[[homepage](http://www.comp.nus.edu.sg/~damithch)]
-[[github](https://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
+[[github](https://github.com/Darren159)]
+[[portfolio](team/darren159.md)]
-* Role: Project Advisor
+- Role: Developer
-### Jane Doe
+### Kim Hanjoo
-
+
-[[github](http://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
+[[github](https://github.com/hjoneweek)]
+[[portfolio](team/hjoneweek.md)]
-* Role: Team Lead
-* Responsibilities: UI
+- Role: Developer
-### Johnny Doe
+### Adam Ang Zi Jun
-
+
-[[github](http://github.com/johndoe)] [[portfolio](team/johndoe.md)]
+[[github](https://github.com/adammangzijun)]
+[[portfolio](team/adammangzijun.md)]
-* Role: Developer
-* Responsibilities: Data
+- Role: Developer
-### Jean Doe
+### Lin Yuxiang
-
+
-[[github](http://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
+[[github](http://github.com/Clin-lyx)]
+[[portfolio](team/clin-lyx.md)]
-* Role: Developer
-* Responsibilities: Dev Ops + Threading
+- Role: Developer
-### James Doe
+### Ryan Ong Wei Xian
-
+
-[[github](http://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
+[[github](https://github.com/ryanongwx)]
+[[portfolio](team/ryanongwx.md)]
-* Role: Developer
-* Responsibilities: UI
+- Role: Developer
diff --git a/docs/Configuration.md b/docs/Configuration.md
index 13cf0faea16..32f6255f3b9 100644
--- a/docs/Configuration.md
+++ b/docs/Configuration.md
@@ -1,6 +1,8 @@
---
-layout: page
-title: Configuration guide
+ layout: default.md
+ title: "Configuration guide"
---
+# Configuration guide
+
Certain properties of the application can be controlled (e.g user preferences file location, logging level) through the configuration file (default: `config.json`).
diff --git a/docs/DevOps.md b/docs/DevOps.md
index d2fd91a6001..736da195dc2 100644
--- a/docs/DevOps.md
+++ b/docs/DevOps.md
@@ -1,38 +1,39 @@
---
-layout: page
-title: DevOps guide
+ layout: default.md
+ title: "DevOps guide"
+ pageNav: 3
---
-* Table of Contents
-{:toc}
+# DevOps guide
---------------------------------------------------------------------------------------------------------------------
+
+
+
+
## Build automation
This project uses Gradle for **build automation and dependency management**. **You are recommended to read [this Gradle Tutorial from the se-edu/guides](https://se-education.org/guides/tutorials/gradle.html)**.
-
Given below are how to use Gradle for some important project tasks.
-
-* **`clean`**: Deletes the files created during the previous build tasks (e.g. files in the `build` folder).
+- **`clean`**: Deletes the files created during the previous build tasks (e.g. files in the `build` folder).
e.g. `./gradlew clean`
-* **`shadowJar`**: Uses the ShadowJar plugin to creat a fat JAR file in the `build/lib` folder, *if the current file is outdated*.
+- **`shadowJar`**: Uses the ShadowJar plugin to creat a fat JAR file in the `build/lib` folder, _if the current file is outdated_.
e.g. `./gradlew shadowJar`.
-* **`run`**: Builds and runs the application.
+- **`run`**: Builds and runs the application.
**`runShadow`**: Builds the application as a fat JAR, and then runs it.
-* **`checkstyleMain`**: Runs the code style check for the main code base.
+- **`checkstyleMain`**: Runs the code style check for the main code base.
**`checkstyleTest`**: Runs the code style check for the test code base.
-* **`test`**: Runs all tests.
- * `./gradlew test` — Runs all tests
- * `./gradlew clean test` — Cleans the project and runs tests
+- **`test`**: Runs all tests.
+ - `./gradlew test` — Runs all tests
+ - `./gradlew clean test` — Cleans the project and runs tests
---------------------------------------------------------------------------------------------------------------------
+---
## Continuous integration (CI)
@@ -58,22 +59,23 @@ Any warnings or errors will be printed out to the console.
**If adding new checks:**
-* Checks are implemented as executable `check-*` scripts within the `.github` directory. The `run-checks.sh` script will automatically pick up and run files named as such. That is, you can add more such files if you need and the CI will do the rest.
+- Checks are implemented as executable `check-*` scripts within the `.github` directory. The `run-checks.sh` script will automatically pick up and run files named as such. That is, you can add more such files if you need and the CI will do the rest.
+
+- Check scripts should print out errors in the format `SEVERITY:FILENAME:LINE: MESSAGE`
-* Check scripts should print out errors in the format `SEVERITY:FILENAME:LINE: MESSAGE`
- * SEVERITY is either ERROR or WARN.
- * FILENAME is the path to the file relative to the current directory.
- * LINE is the line of the file where the error occurred and MESSAGE is the message explaining the error.
+ - SEVERITY is either ERROR or WARN.
+ - FILENAME is the path to the file relative to the current directory.
+ - LINE is the line of the file where the error occurred and MESSAGE is the message explaining the error.
-* Check scripts must exit with a non-zero exit code if any errors occur.
+- Check scripts must exit with a non-zero exit code if any errors occur.
---------------------------------------------------------------------------------------------------------------------
+---
## Making a release
Here are the steps to create a new release.
-1. Update the version number in [`MainApp.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/MainApp.java).
+1. Update the version number in [`MainApp.java`](https://github.com/AY2324S1-CS2103T-T12-4/tp/blob/master/src/main/java/seedu/address/MainApp.java).
1. Generate a fat JAR file using Gradle (i.e., `gradlew shadowJar`).
1. Tag the repo with the version number. e.g. `v0.1`
1. [Create a new release using GitHub](https://help.github.com/articles/creating-releases/). Upload the JAR file you created.
diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md
index 8a861859bfd..f06619c0d1e 100644
--- a/docs/DeveloperGuide.md
+++ b/docs/DeveloperGuide.md
@@ -1,377 +1,1481 @@
---
-layout: page
-title: Developer Guide
+layout: default.md
+title: "Developer Guide"
+pageNav: 2
---
-* Table of Contents
-{:toc}
---------------------------------------------------------------------------------------------------------------------
+# Developer Guide
-## **Acknowledgements**
+## 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 has not used any third-party libraries.
---------------------------------------------------------------------------------------------------------------------
+---
-## **Setting up, getting started**
+## Setting up and Getting Started
-Refer to the guide [_Setting up and getting started_](SettingUp.md).
+For initial setup and getting started with the development, please refer to the guide: [_Setting up and getting started_](SettingUp.md).
---------------------------------------------------------------------------------------------------------------------
+---
-## **Design**
+## Design Overview
-
+### Architecture
-:bulb: **Tip:** The `.puml` files used to create diagrams in this document `docs/diagrams` folder. Refer to the [_PlantUML Tutorial_ at se-edu/guides](https://se-education.org/guides/tutorials/plantUml.html) to learn how to create and edit diagrams.
-
+
-### Architecture
+The **_Architecture Diagram_** above provides a high-level design overview of the application.
+
+Below is a quick overview of the main components and their interactions:
+
+---
+
+
+
+#### Main Components:
+
+- **`Main`**: Responsible for initializing other components in the correct sequence upon application launch, and ensures proper shut down procedures are followed. It consists of [`Main`](https://github.com/AY2324S1-CS2103T-T12-4/tp/blob/master/src/main/java/seedu/address/Main.java) and [`MainApp`](https://github.com/AY2324S1-CS2103T-T12-4/tp/blob/master/src/main/java/seedu/address/MainApp.java) classes.
+
+- **`UI`** ([Details](#ui-component)): Manages the User Interface of the app.
+- **`Logic`** ([Details](#logic-component)): Handles command execution.
+- **`Model`** ([Details](#model-component)): Manages in-memory data.
+- **`Storage`** ([Details](#storage-component)): Handles reading from and writing to the hard disk.
+
+- **`Commons`**: A collection of classes used by multiple components.
+
+#### Component Interactions:
+
+The sequence diagram below shows the interactions between components for the `delete 1` command:
+
+
+
+Each of the four main components:
+
+- Defines its API in an interface named after the component.
+- Implements its functionality using a `{Component Name}Manager` class, following the corresponding API interface.
+
+For example, the `Logic` component's API is defined in `Logic.java`, and its functionality is implemented in `LogicManager.java`.
+
+---
+
+### Detailed Component Descriptions
+
+#### UI Component
+
+- **API**: [`Ui.java`](https://github.com/AY2324S1-CS2103T-T12-4/tp/blob/master/src/main/java/seedu/address/ui/Ui.java)
+- The `UI` component is responsible for handling all user interface operations.
+
+- Core `UI` Components:
+
+
+- Supplementary `UI` Components:
+
+
+The `UI` is composed of various components such as `MainWindow`, `CommandBox`, `ResultDisplay`, `PersonListPanel`, and `StatusBarFooter`, all of which inherit from the `UiPart` class.
+
+The `UI` layouts are defined in corresponding `.fxml` files located in the `src/main/resources/view` folder.
+
+Key responsibilities include:
+
+- Executing user commands via the `Logic` component.
+- Listening for data changes in the `Model` and updating the `UI` accordingly.
+- Maintaining a reference to the `Logic` component for command execution.
+
+---
+
+
+
+#### Logic Component
+
+- **API**: [`Logic.java`](https://github.com/AY2324S1-CS2103T-T12-4/tp/blob/master/src/main/java/seedu/address/logic/Logic.java)
+- The Logic component is responsible for interpreting and executing user commands.
+
+Here's a partial class diagram of the `Logic` component:
+
+
+
+Key functionalities include:
+
+- Interpreting user input and generating the corresponding `Command` object.
+- Executing the command and generating a `CommandResult` object.
+- Depending on the `Model` component to perform data operations.
+- Managing various command parsers to handle specific command formats.
+
+---
+
+
+
+#### Model Component
+
+- **API**: [`Model.java`](https://github.com/AY2324S1-CS2103T-T12-4/tp/blob/master/src/main/java/seedu/address/model/Model.java)
+- The Model component manages the application's in-memory data.
+
+
+
+Key responsibilities include:
+
+- Storing address book data.
+- Managing user preferences.
+- Providing an unmodifiable view of lists of `Person`, `Record` and `Appointment` objects for UI binding.
+
+---
+
+
+
+#### Storage Component
+
+- **API**: [`Storage.java`](https://github.com/AY2324S1-CS2103T-T12-4/tp/blob/master/src/main/java/seedu/address/storage/Storage.java)
+- The Storage component manages data persistence.
+
+
+
+Key functionalities include:
+
+- Saving and retrieving address book data and user preferences in JSON format.
+- Implementing both `AddressBookStorage` and `UserPrefStorage` for flexibility.
+- Depending on certain `Model` classes for data object definitions.
+
+---
+
+## Common Classes
+
+Classes used by multiple components are housed in the `seedu.addressbook.commons` package.
+
+---
+
+
+
+## Patient Features
+
+### General Implementation Details
+
+
+
+A `Person` object encapsulates various attributes:
+
+- `Name`: Patient's first and last name (and middle name, if applicable)
+- `Nric`: Patient's NRIC
+- `Email`: Patient's email address
+- `Phone`: Patient's phone number
+- `Gender`: Patient's gender
+- `Age`: Patient's age
+- `BloodType`: Patient's blood type
+- `Set`: Patient's allergies
+- `UniqueRecordList`: Patient's records from past visits to the clinic.
+- `UniqueAppointmentList`: Patient's scheduled appointments.
+- `isPinned`: Patient's pin status.
+
+Uniqueness of person is maintained through the `UniquePersonList`.
+
+---
+
+### Adding a Patient
+
+#### Overview
+
+The `addpatient` command integrates a new `Person` object with the patient's details in MedBook.
+
+#### Related Classes and Methods
+
+- `AddCommandParser#parse(String)`
+- `AddCommand#execute(Model)`
+- `Model#addPerson(Person)`, `AddressBook#addPerson(Person)`, `UniquePersonList#add(Person)`
+- `PersonListPanel`, `PersonCard`
+
+#### Implementation Steps
+
+1. **Parse User Input**: `AddCommandParser` checks for necessary parameters and their validity.
+2. **Create Person Object**: A `Person` object is instantiated during `AddCommandParser#parse(String)` and passed over to the `AddCommand`.
+3. **Execute Command**: `AddPersonCommand#execute(Model)` adds the new `Person` to the `UniquePersonList` in the `Model`.
+
+
+
+---
+
+### Editing a Patient's Details
+
+#### Overview
+
+The `editpatient` command facilitates the modification of patient information by updating the fields of a `Person` object with new details.
+
+#### Related Classes and Methods
+
+- `EditCommandParser#parse(String)`
+- `EditPersonDescriptor`
+- `EditCommand#execute(Model)`
+- `ModelManager#setPerson(Person,Person)`, `AddressBook#setPerson(Person,Person)`, `UniquePersonList#setPerson(Person,Person)`
+- `PersonListPanel`, `PersonCard`
+
+#### Implementation Steps
+
+1. **Parse User Input**: `EditCommandParser` checks for necessary parameters and their validity.
+2. **Create Person Object**: A `Person` object with the edited details is instantiated during `EditCommandParser#parse(String)` and passed over to the `EditCommand`.
+3. **Execute Command**: `EditCommand#execute(Model)` replaces the `Person` object in `Model` with a new `Person` object that has edited details.
+
+
+
+#### Design Considerations
+
+**Alternative 1 (Current Choice)**: Implement an Edit-by-Cloning Strategy
+
+- _Pros_:
+ - **Scalability:** By cloning the `Person` object before editing, the system is better equipped to handle future enhancements that may require complex transactional operations.
+ - **Data Integrity:** This method ensures that the original `Person` object remains unaltered during the edit process, which reduces the risk of data corruption in the event of an operation failure.
+- _Cons_:
+ - Adds complexity and has potential performance issues.
+
+**Alternative 2**: Modify the `Person` object directly
+
+- _Pros_:
+ - **Simplicity:** This straightforward approach requires less code, making it easier to implement and understand.
+ - **Efficiency:** Operating directly on the `Person` object without cloning can be more performant, especially when dealing with simple edits that do not span multiple data fields.
+- _Cons_:
+ - **Limited Flexibility:** Direct modification constrains the ability to extend the system with complex transactional features or undo/redo capabilities without significant refactoring.
+ - **Data Risk:** Without the safeguard of working on a cloned instance, there's a higher risk of inadvertently corrupting data during edit operations.
+
+By considering these alternatives, the development team has chosen to prioritize a robust foundation for future development and data integrity, despite the trade-offs in complexity and potential impact on performance.
+
+---
+
+### Deleting a Patient
+
+#### Overview
+
+The `delete` command deletes an existing `Person` object from MedBook.
+
+#### Related Classes and Methods
+
+- `DeleteCommandParser#parse(String)`
+- `DeleteCommand#execute(Model)`
+- `ModelManager#deletePerson(Person)`, `AddressBook#removePerson(Person)`, `UniquePersonList#remove(Person)`
+- `PersonListPanel`, `PersonCard`
+
+#### Implementation Steps
-
+1. **Parse User Input**: `DeleteCommandParser` checks for the validity of the `Person` index.
+2. **Create Person Object**: An `Index` object of the `Person` is instantiated during `DeleteCommandParser#parse(String)` and passed over to the `DeleteCommand`.
+3. **Execute Command**: `DeleteCommand#execute(Model)` deletes the `Person` object from `Model`.
-The ***Architecture Diagram*** given above explains the high-level design of the App.
+
-Given below is a quick overview of main components and how they interact with each other.
+---
+
+### Searching for Patients
+
+#### Overview
+
+The `search` command filters the list of patients using one or more keywords.
+
+#### Related Classes and Methods
+
+- `FindCommandParser#parse(String)`
+- `FindCommand#execute(Model)`
+- `Model#updateFilteredList(Predicate)`
+- `NameContainsKeywordsPredicate#test(Person)`
+- `PersonListPanel`, `PersonCard`
+
+#### Implementation Steps
+
+1. **Parse User Input**: `FindCommandParser` checks for existence of the keyword(s) and creates an array of keywords.
+2. **Create Predicate Object**: A `NameContainsKeywordsPredicate` object is instantiated during `FindCommandParser#parse(String)` and passed over to the `FindCommand`.
+3. **Execute Command**: `FindCommand#execute(Model)` finds patients containing keywords using `NameContainsKeywordsPredicate#test(Record)` and updates `FilteredPersonList`.
+
+
+
+---
+
+### Pinning a Patient
+
+#### Overview
-**Main components of the architecture**
+The `pin` command pins a patient to the **Pinned Patient List**
-**`Main`** (consisting of classes [`Main`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/Main.java) and [`MainApp`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/MainApp.java)) is in charge of the app launch and shut down.
-* At app launch, it initializes the other components in the correct sequence, and connects them up with each other.
-* At shut down, it shuts down the other components and invokes cleanup methods where necessary.
+#### Related Classes and Methods
-The bulk of the app's work is done by the following four components:
+- `PinCommandParser#parse(String)`
+- `PinCommand#execute(Model)`
+- `Model#setPerson(Person, Person)`, `AddressBook#setPerson(Person, Person)`, `UniquePersonList#setPerson(Person, Person)`
+- `PinnedPersonListPanel`, `PersonCard`
-* [**`UI`**](#ui-component): The UI of the App.
-* [**`Logic`**](#logic-component): The command executor.
-* [**`Model`**](#model-component): Holds the data of the App in memory.
-* [**`Storage`**](#storage-component): Reads data from, and writes data to, the hard disk.
+#### Implementation Steps
-[**`Commons`**](#common-classes) represents a collection of classes used by multiple other components.
+1. **Parse User Input**: `PinCommandParser` checks for the validity of the `Person` index.
+2. **Create Index Object**: An `Index` object of the `Person` is instantiated during `PinCommandParser#parse(String)` and passed over to the `PinCommand`.
+3. **Execute Command**: `PinCommand#execute(Model)` replaces the `Person` object in `Model` with a new `Person` object that has `isPinned` set to `true`.
-**How the architecture components interact with each other**
+
-The *Sequence Diagram* below shows how the components interact with each other for the scenario where the user issues the command `delete 1`.
+---
-
+### Unpinning a Patient
-Each of the four main components (also shown in the diagram above),
+#### Overview
-* defines its *API* in an `interface` with the same name as the Component.
-* implements its functionality using a concrete `{Component Name}Manager` class (which follows the corresponding API `interface` mentioned in the previous point.
+The `unpin` command unpins a patient from the **Pinned Patient List**
-For example, the `Logic` component defines its API in the `Logic.java` interface and implements its functionality using the `LogicManager.java` class which follows the `Logic` interface. Other components interact with a given component through its interface rather than the concrete class (reason: to prevent outside component's being coupled to the implementation of a component), as illustrated in the (partial) class diagram below.
+#### Related Classes and Methods
-
+- `UnpinCommandParser#parse(String)`
+- `UnpinCommand#execute(Model)`
+- `Model#setPerson(Person, Person)`, `AddressBook#setPerson(Person, Person)`, `UniquePersonList#setPerson(Person, Person)`
+- `PinnedPersonListPanel`, `PersonCard`
-The sections below give more details of each component.
+#### Implementation Steps
-### UI component
+1. **Parse User Input**: `UnpinCommandParser` checks for the validity of the pinned `Person` index.
+2. **Create Index Object**: An `Index` object is instantiated during `UnpinCommandParser#parse(String)` and passed over to the `UnpinCommand`.
+3. **Execute Command**: `UnpinCommand#execute(Model)` replaces the `Person` object in `Model` with a new `Person` object that has `isPinned` set to `false`.
-The **API** of this component is specified in [`Ui.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/Ui.java)
+
-
+---
-The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `PersonListPanel`, `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class which captures the commonalities between classes that represent parts of the visible GUI.
+
-The `UI` component uses the JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files that are in the `src/main/resources/view` folder. For example, the layout of the [`MainWindow`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/MainWindow.java) is specified in [`MainWindow.fxml`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/resources/view/MainWindow.fxml)
+## Records Feature
-The `UI` component,
+### General Implementation Details
-* executes user commands using the `Logic` component.
-* listens for changes to `Model` data so that the UI can be updated with the modified data.
-* keeps a reference to the `Logic` component, because the `UI` relies on the `Logic` to execute commands.
-* depends on some classes in the `Model` component, as it displays `Person` object residing in the `Model`.
+
-### Logic component
+A `Record` object encapsulates various attributes:
-**API** : [`Logic.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/logic/Logic.java)
+- `DateTime`: Date and time of the patient's clinic visit
+- `List`: Patient's health conditions during the visit
+- `List`: Medications prescribed to the patient
-Here's a (partial) class diagram of the `Logic` component:
+Uniqueness of record is maintained through the `UniqueRecordList`.
-
+---
-The sequence diagram below illustrates the interactions within the `Logic` component, taking `execute("delete 1")` API call as an example.
+### Adding a Record
-
+#### Overview
-
:information_source: **Note:** The lifeline for `DeleteCommandParser` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
-
+The `addrecord` command integrates a new `Record` object with the record's details in MedBook.
-How the `Logic` component works:
+#### Related Classes and Methods
-1. When `Logic` is called upon to execute a command, it is passed to an `AddressBookParser` object which in turn creates a parser that matches the command (e.g., `DeleteCommandParser`) and uses it to parse the command.
-1. This results in a `Command` object (more precisely, an object of one of its subclasses e.g., `DeleteCommand`) which is executed by the `LogicManager`.
-1. The command can communicate with the `Model` when it is executed (e.g. to delete a person).
-1. The result of the command execution is encapsulated as a `CommandResult` object which is returned back from `Logic`.
+- `AddRecordCommandParser#parse(String)`
+- `AddRecordCommand#execute(Model)`
+- `UniqueRecordList#add(Record)`
+- `Model#setPerson(Person, Person)`, `AddressBook#setPerson(Person, Person)`, `UniquePersonList#setPerson(Person, Person)`
+- `Model#updateRecordList(Person)`, `AddressBook#setRecords(Person)`, `UniqueRecordList#setRecords(UniqueRecordList)`
+- `RecordListPanel`, `RecordCard`
-Here are the other classes in `Logic` (omitted from the class diagram above) that are used for parsing a user command:
+#### Implementation Steps
-
+1. **Parse User Input**: `AddRecordCommandParser` checks for necessary parameters and their validity.
+2. **Create Record Object**: A `Record` object is instantiated during `AddRecordCommandParser#parse(String)` and passed over to the `AddRecordCommand`.
+3. **Execute Command**: `AddRecordCommand#execute(Model)` replaces the `Person` object in `Model` with a new `Person` object that has the added `Record`. `Model#updateRecordList(Person)` replaces the `UniqueRecordList` in `Model` with the `UniqueRecordList` of the new `Person` object.
-How the parsing works:
-* When called upon to parse a user command, the `AddressBookParser` class creates an `XYZCommandParser` (`XYZ` is a placeholder for the specific command name e.g., `AddCommandParser`) which uses the other classes shown above to parse the user command and create a `XYZCommand` object (e.g., `AddCommand`) which the `AddressBookParser` returns back as a `Command` object.
-* All `XYZCommandParser` classes (e.g., `AddCommandParser`, `DeleteCommandParser`, ...) inherit from the `Parser` interface so that they can be treated similarly where possible e.g, during testing.
+
-### Model component
-**API** : [`Model.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/model/Model.java)
+---
-
+### Editing a Record's Details
+#### Overview
-The `Model` component,
+The `editpatient` command facilitates the modification of record information by updating the fields of a `Record` object with new details.
-* stores the address book data i.e., all `Person` objects (which are contained in a `UniquePersonList` object).
-* stores the currently 'selected' `Person` objects (e.g., results of a search query) as a separate _filtered_ list which is exposed to outsiders as an unmodifiable `ObservableList` that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change.
-* stores a `UserPref` object that represents the user’s preferences. This is exposed to the outside as a `ReadOnlyUserPref` objects.
-* does not depend on any of the other three components (as the `Model` represents data entities of the domain, they should make sense on their own without depending on other components)
+#### Related Classes and Methods
-
:information_source: **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.
+- `EditRecordCommandParser#parse(String)`
+- `EditRecordDescriptor`
+- `EditRecordCommand#execute(Model)`
+- `ModelManager#setPerson(Person,Person)`, `AddressBook#setPerson(Person,Person)`, `UniquePersonList#setPerson(Person,Person)`
+- `Model#updateRecordList(Person)`, `AddressBook#setRecords(Person)`, `UniqueRecordList#setRecords(UniqueRecordList)`, `UniqueRecordList#setRecord(Record)`
+- `RecordListPanel`, `RecordCard`
-
+#### Implementation Steps
-
+1. **Parse User Input**: `EditRecordCommandParser` checks for necessary parameters and their validity.
+2. **Create Record Object**: A `Record` object with the edited details is instantiated during `EditRecordCommandParser#parse(String)` and passed over to the `EditRecordCommand`.
+3. **Execute Command**: `EditRecordCommand#execute(Model)` replaces the `Person` object in `Model` with a new `Person` object that has the new edited `Record`. `Model#updateRecordList(Person)` replaces the `UniqueRecordList` in `Model` with the `UniqueRecordList` of the new `Person` object.
+
-### Storage component
+#### Design Considerations
-**API** : [`Storage.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/storage/Storage.java)
+Similar to editing patient, a clone is created and modified, and then replaces the original.
-
+**Alternative 1 (Current Choice):** Clone the `Record` object, modify the clone, and then replace the original.
-The `Storage` component,
-* can save both address book data and user preference data in JSON format, and read them back into corresponding objects.
-* inherits from both `AddressBookStorage` and `UserPrefStorage`, which means it can be treated as either one (if only the functionality of only one is needed).
-* depends on some classes in the `Model` component (because the `Storage` component's job is to save/retrieve objects that belong to the `Model`)
+- _Pros_:
-### Common classes
+ - **Data Integrity:** By working on a clone, we minimize the risk of corrupting the original data in case of an error during the update process.
+ - **Undo/Redo Capability:** This approach allows for an easier implementation of undo/redo functionalities as we have distinct before and after states.
+ - **Consistency:** It maintains a consistent methodology with the editpatient command, which uses a similar approach for updating patient details.
-Classes used by multiple components are in the `seedu.addressbook.commons` package.
+- _Cons_:
+ - **Performance Overhead:** Cloning objects can introduce a performance hit, especially if the record is large or if there are many fields to update.
+ - **Complexity:** The codebase complexity increases due to the additional steps required to manage the cloning and replacement process.
---------------------------------------------------------------------------------------------------------------------
+**Alternative 2:** Update the `Record` object directly.
-## **Implementation**
+- _Pros_:
+ - **Performance:** This approach is more performant since it involves direct manipulation of the object without the need to create a copy.
+ - **Simplicity:** The logic is more straightforward, as it doesn't involve cloning, making the code easier to understand and maintain.
+- _Cons_:
+ - **Risk to Data Integrity:** Any errors during the update can corrupt the original data, as changes are made in place.
+ - **Difficulty in Extending Functionality:** Future features such as undo/redo or real-time collaboration could be harder to implement as changes are not isolated.
-This section describes some noteworthy details on how certain features are implemented.
+In conclusion, the decision to proceed with Alternative 1 was made to prioritize the application's long-term robustness and maintainability, accepting the trade-offs in performance and immediate simplicity for the sake of a safer and more extensible editing feature.
-### \[Proposed\] Undo/redo feature
+---
-#### Proposed Implementation
+### Deleting a Record
-The proposed undo/redo mechanism is facilitated by `VersionedAddressBook`. It extends `AddressBook` with an undo/redo history, stored internally as an `addressBookStateList` and `currentStatePointer`. Additionally, it implements the following operations:
+#### Overview
-* `VersionedAddressBook#commit()` — Saves the current address book state in its history.
-* `VersionedAddressBook#undo()` — Restores the previous address book state from its history.
-* `VersionedAddressBook#redo()` — Restores a previously undone address book state from its history.
+The `deleterecord` command deletes an existing `Record` object from MedBook.
-These operations are exposed in the `Model` interface as `Model#commitAddressBook()`, `Model#undoAddressBook()` and `Model#redoAddressBook()` respectively.
+#### Related Classes and Methods
-Given below is an example usage scenario and how the undo/redo mechanism behaves at each step.
+- `DeleteRecordCommandParser#parse(String)`
+- `DeleteRecordCommand#execute(Model)`
+- `UniqueRecordList#remove(Record)`
+- `Model#setPerson(Person, Person)`, `AddressBook#setPerson(Person, Person)`, `UniquePersonList#setPerson(Person, Person)`
+- `Model#updateRecordList(Person)`, `AddressBook#setRecords(Person)`, `UniqueRecordList#setRecords(UniqueRecordList)`
+- `RecordListPanel`, `RecordCard`
-Step 1. The user launches the application for the first time. The `VersionedAddressBook` will be initialized with the initial address book state, and the `currentStatePointer` pointing to that single address book state.
+##### Implementation Steps
-
+1. **Parse User Input**: `DeleteRecordCommandParser` checks for the validity of the patient and record indices.
+2. **Create Index Object**: Two `Index` objects, patient index and record index, are instantiated during `DeleteRecordCommandParser#parse(String)` and passed over to the `DeleteRecordCommand`.
+3. **Execute Command**: `DeleteRecordCommand#execute(Model)` replaces the `Person` object in `Model` with a new `Person` object that has the updated `UniqueRecordList`. `Model#updateRecordList(Person)` replaces the `UniqueRecordList` in `Model` with the `UniqueRecordList` of the new `Person` object.
-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 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`.
+### Viewing a patient's medical records
-
+The `view` command displays the list of records of the patient being viewed.
-
:information_source: **Note:** If a command fails its execution, it will not call `Model#commitAddressBook()`, so the address book state will not be saved into the `addressBookStateList`.
+#### Related Classes and Methods
-
+- `ViewCommandParser#parse(String)`
+- `ViewCommand#execute(Model)`
+- `Model#updateRecordList(Person)`, `AddressBook#setRecords(Person)`, `UniqueRecordList#setRecords(UniqueRecordList)`
+- `Model#getPersonBeingViewed()`, `AddressBook#getPersonBeingViewed()`, `Logic#getPersonBeingViewed()`
+- `RecordListPanel`, `RecordCard`
-Step 4. The user now decides that adding the person was a mistake, and decides to undo that action by executing the `undo` command. The `undo` command will call `Model#undoAddressBook()`, which will shift the `currentStatePointer` once to the left, pointing it to the previous address book state, and restores the address book to that state.
+#### Implementation steps
-
+1. **Parse User Input**: `ViewCommandParser` checks for validity of the patient index.
+2. **Create Index Object**: An `Index` object is instantiated during `ViewCommandParser#parse(String)` and passed over to the `ViewCommand`.
+3. **Update and Display**: `ViewCommand#execute(Model)` invokes the `Model#updateRecordList(Person)` to update the record list of the `Model`. The record list of the specific patient is displayed.
-
:information_source: **Note:** If the `currentStatePointer` is at index 0, pointing to the initial AddressBook state, then there are no previous AddressBook states to restore. The `undo` command uses `Model#canUndoAddressBook()` to check if this is the case. If so, it will return an error to the user rather
-than attempting to perform the undo.
+
-
+---
-The following sequence diagram shows how the undo operation works:
+### Searching for Records
-
+#### Overview
-
:information_source: **Note:** The lifeline for `UndoCommand` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
+The `searchrecord` command filters the list of records of the patient being viewed using one or more keywords.
-
+#### Related Classes and Methods
-The `redo` command does the opposite — it calls `Model#redoAddressBook()`, which shifts the `currentStatePointer` once to the right, pointing to the previously undone state, and restores the address book to that state.
+- `FindRecordCommandParser#parse(String)`
+- `FindRecordCommand#execute(Model)`
+- `Model#updateFilteredRecordList(Predicate)`
+- `RecordContainsKeywordsPredicate#test(Record)`
+- `RecordListPanel`, `RecordCard`
-
:information_source: **Note:** If the `currentStatePointer` is at index `addressBookStateList.size() - 1`, pointing to the latest address book state, then there are no undone AddressBook states to restore. The `redo` command uses `Model#canRedoAddressBook()` to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo.
+#### Implementation Steps
-
+1. **Parse User Input**: `FindRecordCommandParser` checks for existence of the keyword(s) and creates an array of keywords.
+2. **Create Predicate Object**: A `RecordContainsKeywordsPredicate` object is instantiated during `FindRecordCommandParser#parse(String)` and passed over to the `FindRecordCommand`.
+3. **Execute Command**: `FindRecordCommand#execute(Model)` finds records containing keywords using `RecordContainsKeywordsPredicate#test(Record)` and updates `FilteredRecordList`.
-Step 5. The user then decides to execute the command `list`. Commands that do not modify the address book, such as `list`, will usually not call `Model#commitAddressBook()`, `Model#undoAddressBook()` or `Model#redoAddressBook()`. Thus, the `addressBookStateList` remains unchanged.
+
-
+---
+
+### Attaching Files to Patient Records
+
+#### Overview
-Step 6. The user executes `clear`, which calls `Model#commitAddressBook()`. Since the `currentStatePointer` is not pointing at the end of the `addressBookStateList`, all address book states after the `currentStatePointer` will be purged. Reason: It no longer makes sense to redo the `add n/David …` command. This is the behavior that most modern desktop applications follow.
+The feature allows users to associate files with patient records.
-
+#### Related Class and Methods
-The following activity diagram summarizes what happens when a user executes a new command:
+- `RecordCard#handleAttachFile(ActionEvent)`
+- `Record#setFilePath(FilePath, Index)`
+- `EditRecordCommand#setFilePath(FilePath)`
+- `StorageManager#saveAddressBook(Model)`
+- `RecordCard#handleOpenFile(ActionEvent)`
-
+#### Implementation Steps
-#### Design considerations:
+1. **User Button Press**: `RecordCard#handleAttachFile(ActionEvent)` opens file explorer, prompting user to select the file to attach.
+2. **Saving Filepath**: `EditRecordCommand#setFilePath(FilePath)` and `Record#setFilePath(FilePath, Index)` updates the filepath of the corresponding record.
+3. **User File Press**: `RecordCard#handleOpenFile(ActionEvent)` opens the file.
-**Aspect: How undo & redo executes:**
+#### Alternative Considerations
-* **Alternative 1 (current choice):** Saves the entire address book.
- * Pros: Easy to implement.
- * Cons: May have performance issues in terms of memory usage.
+In designing this feature, we considered two primary approaches:
-* **Alternative 2:** Individual command knows how to undo/redo by
- itself.
- * Pros: Will use less memory (e.g. for `delete`, just save the person being deleted).
- * Cons: We must ensure that the implementation of each individual command are correct.
+1. **Current Implementation (GUI)**:
-_{more aspects and alternatives to be added}_
+ - _Pros_: The graphical user interface (GUI) for file attachments is user-friendly, especially for individuals less familiar with command-line interfaces (CLI).
+ - _Cons_: It may be relatively slower for users proficient with CLI-based interactions.
-### \[Proposed\] Data archiving
+2. **Command-Line Interface (CLI)**:
+ - _Pros_: CLI-based file attachment would cater to users who prefer efficient, command-driven workflows.
+ - _Cons_: May require a learning curve for users less experienced with command-line interactions.
+
+Ultimately, the decision was made to implement the feature with a GUI to ensure accessibility and ease of use for a broader range of users, while still allowing for efficient management of patient records.
+
+---
-_{Explain here how the data archiving feature will be implemented}_
+
+## Appointments Feature
---------------------------------------------------------------------------------------------------------------------
+### General Implementation Details
-## **Documentation, logging, testing, configuration, dev-ops**
+
+
+An `Appointment` object encapsulates various attributes:
+
+- `Name`: Appointment title.
+- `DateTime`: Date and Time of the Appointment.
+- `Nric`: Nric of the patient involved in the Appointment.
+
+Uniqueness is enforced through a `UniqueAppointmentList`.
+
+---
+
+### Adding an Appointment
+
+#### Overview
+
+The `addappointment` command integrates a new `Appointment` object with the appointment's details in MedBook.
+
+#### Related Classes and Methods
+
+- `AddAppointmentCommandParser#parse(String)`
+- `AddAppointmentCommand#execute(Model)`
+- `UniqueAppointmentList#setAppointments(UniqueAppointmentList)`, `UniqueAppointmentList#add(Appointment)`
+- `Model#setPerson(Person, Person)`, `AddressBook#setPerson(Person, Person)`, `UniquePersonList#setPerson(Person, Person)`
+- `Model#resetAppointmentList()`
+- `AppointmentWindow`, `AppointmentCalendarPanel`, `AppointmentListPanel`, `AppointmentCard`
+
+#### Implementation Steps
+
+1. **Parse User Input**: `AddAppointmentCommandParser` checks for necessary parameters and their validity.
+2. **Create Appointment Object**: An `Appointment` object is instantiated during `AddAppointmentCommandParser#parse(String)` and passed over to the `AddAppointmentCommand`.
+3. **Execute Command**: `AddAppointmentCommand#execute(Model)` replaces the `Person` object in `Model` with a new `Person` object that has the added `Appointment`. `Model#resetAppointmentList()` resets the `UniqueAppointmentList` of the `Model`.
+
+
+
+---
+
+### Deleting an Appointment
+
+#### Overview
+
+The `deleteappointment` command deletes an existing `Appointment` from MedBook.
+
+#### Related Classes and Methods
+
+- `DeleteAppointmentCommandParser#parse(String)`
+- `DeleteAppointmentCommand#execute(Model)`
+- `UniqueAppointmentList#setAppointments(UniqueAppointmentList)`, `UniqueAppointmentList#remove(Appointment)`
+- `Model#setPerson(Person, Person)`, `AddressBook#setPerson(Person, Person)`, `UniquePersonList#setPerson(Person, Person)`
+- `Model#resetAppointmentList()`
+- `AppointmentWindow`, `AppointmentCalendarPanel`, `AppointmentListPanel`, `AppointmentCard`
+
+#### Implementation Steps
+
+1. **Parse User Input**: `DeleteAppointmentCommandParser` checks for the validity of the appointment index.
+2. **Create Index Object**: An `Index` object is instantiated during `DeleteAppointmentCommandParser#parse(String)` and passed over to the `DeleteAppointmentCommand`.
+3. **Execute Command**: `DeleteAppointmentCommand#execute(Model)` replaces the `Person` object in `Model` with a new `Person` object that has the updated `UniqueAppointmentList`. `Model#resetAppointmentList()` resets the `UniqueAppointmentList` of the `Model`.
+
+
+
+---
-* [Documentation guide](Documentation.md)
-* [Testing guide](Testing.md)
-* [Logging guide](Logging.md)
-* [Configuration guide](Configuration.md)
-* [DevOps guide](DevOps.md)
+### Viewing the Appointment Window
---------------------------------------------------------------------------------------------------------------------
+#### Overview
-## **Appendix: Requirements**
+The `viewappointment` command opens/focuses the `AppointmentsWindow`.
-### Product scope
+#### Related Classes and Methods
-**Target user profile**:
+- `ViewAppointmentCommand#execute(Model)`
+- `MainWindow#handleAppointments()`, `AppointmentsWindow#show()`, `AppointmentsWindow#fillInnerParts()`
+- `AppointmentWindow`, `AppointmentCalendarPanel`, `AppointmentListPanel`, `AppointmentCard`
-* has a need to manage a significant number of contacts
-* prefer desktop apps over other types
-* can type fast
-* prefers typing to mouse interactions
-* is reasonably comfortable using CLI apps
+#### Implementation Steps
-**Value proposition**: manage contacts faster than a typical mouse/GUI driven app
+1. **Execute Command**: `ViewAppointmentCommand#execute(Model)` returns a new `CommandResult` with `showAppointments` set to `true`.
+2. **Show Appointments Window**: `MainWindow#handleAppointments()` calls the `AppointmentsWindow#show()` method which opens/focuses the `AppointmentsWindow`.
+3. **Populate Appointments Data**: `AppointmentsWindow#fillInnerParts()` populates the `AppointmentCalendarPanel`, `AppointmentListPanel` with a `UniqueAppointmentList`.
+
-### User stories
+### Design Considerations
+
+**Alternative 1 (Current Choice)**: Each `Person` consists of a `UniqueAppointmentList` consisting of all `Appointment` objects assigned to the `Person`. Each `Appointment` object has the corresponding `Person` `NRIC` as a field.
+
+- _Pros_:
+
+ - **Direct Relationship**: It directly associates appointments with the individual patient, reflecting a real-world scenario where a patient has a list of their appointments.
+ - **Ease of Access**: Retrieving all appointments for a specific person is straightforward.
+
+- _Cons_:
+ - **Duplication**: Storing `NRIC` in both `Person` and `Appointment` leads to data duplication, going against the principles of data normalization.
+ - **Data Integrity Risks**: If an `NRIC` needs to be updated, it must be changed in each Appointment object, increasing the risk of data inconsistency.
+ - **Complexity**: It complicates the retrieval of information about the `Person` associated with the `Appointment` such as trying to display the `Person` `Name` in the **Appointment Card**.
+
+**Alternative 2**: Each `Appointment` consists of a a `Person` objects. Each `Person` object has the corresponding `Appointment` id as a field.
+
+- _Pros_:
+
+ - **Data Normalization**: This approach avoids duplicating person-specific data (like `NRIC`) within the appointment, adhering to data normalization principles.
+
+- _Cons_:
+
+ - **Complexity**: It complicates the retrieval of all appointments for a single person, requiring more complex queries and potentially impacting performance.
+ - **Data Redundancy**: If an appointments holds a `Person` object, there's a risk of redundancy as the same `Person` object could be stored across multiple appointments.
+ - **Difficult Person Creation**: The dependency of `Person` creation on `Appointment` objects complicates the process, potentially leading to issues when a new patient is registered without an appointment.
+
+Considering MedBook's primary focus on patient management, **Alternative 1** has been selected. This decision prioritizes the ease and intuitiveness of accessing and managing patient-specific information, despite some level of data redundancy, which is often a trade-off in software design for enhanced usability.
+
+---
+
+
+
+## User Stories
Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unlikely to have) - `*`
-| Priority | As a … | I want to … | So that I can… |
-| -------- | ------------------------------------------ | ------------------------------ | ---------------------------------------------------------------------- |
-| `* * *` | new user | see usage instructions | refer to instructions when I forget how to use the App |
-| `* * *` | user | add a new person | |
-| `* * *` | user | delete a person | remove entries that I no longer need |
-| `* * *` | user | find a person by name | locate details of persons without having to go through the entire list |
-| `* *` | user | hide private contact details | minimize chance of someone else seeing them by accident |
-| `*` | user with many persons in the address book | sort persons by name | locate a person easily |
+| Priority | As a … | I can … | So that I can … |
+| -------- | ------------- | ------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
+| `* * *` | user | _add_ a patient's detail including age, gender, or blood type etc. to the application | keep track of my patients |
+| `* * *` | user | _add_ a patient’s medical records with details including date, time, condition and prescribed medication etc. | manage patients' medical records efficiently |
+| `* * *` | user | _add_ a patient's appointments with details including date, time and title etc. with a patient | manage patients' appointments efficiently |
+| `* * *` | user | _view_ a list of all the patients in the app | have an overview of all my patients |
+| `* * *` | user | _view_ a list of all the medical records of a patient | have an overview of all the medical records of my patients |
+| `* * *` | user | _view_ a list of all the upcoming appointments | have an overview of all my upcoming medical appointments |
+| `* * *` | user | _edit_ an existing patient’s details | keep the patient's details accurate and up-to-date |
+| `* * *` | user | _edit_ an existing medical record's details | keep the medical record's details accurate and up-to-date |
+| `* * *` | user | _delete_ a specific patient from the app | remove patients that are no longer relevant or needed |
+| `* * *` | user | _delete_ a specific medical record from the app | remove erroneous medical records. |
+| `* * *` | user | _delete_ a specific appointment from the app | remove erroneous appointments. |
+| `* * *` | new user | _see_ the app populated with sample data | see how the app will look when it is in use |
+| `* * *` | new user | _access_ a “help” page to view the app’s basic commands | conveniently view basic commands within the app |
+| `* * *` | user | _save_ the address book automatically | prevent accidental loss of data |
+| `* *` | user | _search_ for specific patients using keywords such as the patient’s name or blood type etc. | find and filter specific patients from a long list of patients |
+| `* *` | user | _search_ for a specific medical record of a patient using keywords such as date, condition or medication | find and filter specific medical records from a long list of medical records |
+| `* *` | user | _pin_ a specific patient | conveniently view patients details |
+| `* *` | user | _unpin_ a specific patient | |
+| `* *` | user | _attach_ files such as lab reports and prescription images to a patient's medical records | keep documents in an organised manner |
+| `* *` | advanced user | _directly edit_ the MedBook data stored in the JSON file | make specific and controlled changes to the data |
+| `* *` | user | _edit_ an existing appointment's details | keep the appointment's details accurate and up-to-date |
+| `*` | user | _receive_ regular updates and bug fixes for the app | |
+| `*` | user | _receive_ reminders for upcoming patient appointments | be punctual for upcoming appointments |
+| `*` | user | _export_ patient data | share or transfer data between different systems |
+| `*` | user | _leave_ patient data encrypted | prevent unauthorised access to the data |
+| `*` | user | _sort_ existing appointments by date | quickly view the appointments which are soon upcoming |
+
+---
+
+## Use Cases
+
+### UC01 - Viewing Help
+
+- **Actor**: User
+- **System**: MedBook
+- **Main Success Scenario (MSS)**:
+ 1. User requests for help.
+ 2. MedBook displays help information.
+ Use case ends.
+
+---
+
+### UC02 - Adding a Patient
+
+- **Actor**: User
+- **System**: MedBook
+- **Main Success Scenario (MSS)**:
+ 1. User requests to add a new patient.
+ 2. User enters the patient's details.
+ 3. MedBook adds the patient to the system.
+ Use case ends.
+- **Extensions**:
+ - 2a. MedBook detects an error in the entered patient's details.
+ - 2a1. MedBook shows an error message.
+ - 2a2. User enters new patient details.
+ - Steps 2a1-2a2 are repeated until the patient details entered is correct.
+ - Use case resumes from step 3.
+
+---
+
+### UC03 - Listing All Patients
-*{More to be added}*
+- **Actor**: User
+- **System**: MedBook
+- **Preconditions**: There is at least one patient entry in the system.
+- **Main Success Scenario (MSS)**:
+ 1. User requests to list patients.
+ 2. MedBook shows a list of patients.
+ Use case ends.
-### Use cases
+---
-(For all use cases below, the **System** is the `AddressBook` and the **Actor** is the `user`, unless specified otherwise)
+### UC04 - Editing a Patient's Details
+
+- **Actor**: User
+- **System**: MedBook
+- **Preconditions**: There is at least one patient entry in the system.
+- **Main Success Scenario (MSS)**:
+ 1. User lists all patients (UC03).
+ 2. User requests to edit a patient's details.
+ 3. User enters the new patient details.
+ 4. MedBook updates the patient details.
+ Use case ends.
+- **Extensions**:
+ - 3a. MedBook detects an error in the entered patient's details.
+ - 3a1. MedBook shows an error message.
+ - 3a2. User enters new patient details.
+ - Steps 3a1-3a2 are repeated until the patient details entered is correct.
+ - Use case resumes from step 4.
-**Use case: Delete a person**
+---
-**MSS**
+### UC05 - Searching for a Specific Patient
+
+- **Actor**: User
+- **System**: MedBook
+- **Preconditions**: There is at least one patient entry in the system.
+- **Main Success Scenario (MSS)**:
+ 1. User lists all patients (UC03).
+ 2. User requests to search for a specific patient.
+ 3. User enters search criteria.
+ 4. MedBook performs a search and displays matching patients.
+ Use case ends.
+- **Extensions**:
+ - 4a. No matches found.
+ - 4a1. MedBook informs the user that there were no matches.
+ - Use case ends.
-1. User requests to list persons
-2. AddressBook shows a list of persons
-3. User requests to delete a specific person in the list
-4. AddressBook deletes the person
+---
- Use case ends.
+### UC06 - Delete a Patient
+
+- **Actor**: User
+- **System**: MedBook
+- **Preconditions**: There is at least one patient entry in the system.
+- **Main Success Scenario (MSS)**:
+ 1. User lists all patients (UC03).
+ 2. User requests to delete a specific patient.
+ 3. User enters patient ID.
+ 4. MedBook deletes the patient.
+ Use case ends.
+- **Extensions**:
+ - 3a. MedBook detects an error in the entered patient's ID.
+ - 3a1. MedBook shows an error message.
+ - 3a2. User enters new patient ID.
+ - Steps 3a1-3a2 are repeated until the patient ID entered is correct.
+ - Use case resumes from step 4.
-**Extensions**
+---
-* 2a. The list is empty.
+### UC07 - Pin a Patient
+
+- **Actor**: User
+- **System**: MedBook
+- **Preconditions**: There is at least one patient entry in the system.
+- **Main Success Scenario (MSS)**:
+ 1. User lists all patients (UC03).
+ 2. User requests to pin a specific patient.
+ 3. User enters patient ID.
+ 4. MedBook pins the patient.
+ Use case ends.
+- **Extensions**:
+ - 3a. MedBook detects an error in the entered patient's ID.
+ - 3a1. MedBook shows an error message.
+ - 3a2. User enters new patient's ID.
+ - Steps 3a1-3a2 are repeated until the patient ID entered is correct.
+ - Use case resumes from step 4.
- Use case ends.
+---
-* 3a. The given index is invalid.
+### UC08 - Unpin a Patient
+
+- **Actor**: User
+- **System**: MedBook
+- **Preconditions**: There is at least one pinned patient.
+- **Main Success Scenario (MSS)**:
+ 1. User requests to unpin a specific patient.
+ 2. User enters the patient's PINNEDID.
+ 3. MedBook unpins the patient.
+ Use case ends.
+- **Extensions**:
+ - 2a. MedBook detects an error in the entered PINNEDID.
+ - 2a1. MedBook shows an error message.
+ - 2a2. User enters new PINNEDID.
+ - Steps 2a1-2a2 are repeated until the PINNEDID entered is correct.
+ - Use case resumes from step 3.
- * 3a1. AddressBook shows an error message.
+---
- Use case resumes at step 2.
+### UC09 - Adding a Record to a Patient
+
+- **Actor**: User
+- **System**: MedBook
+- **Preconditions**: There is at least one patient entry in the system.
+- **Main Success Scenario (MSS)**:
+ 1. User lists all patients (UC03).
+ 2. User requests to add a new record to a patient.
+ 3. User enters the record's details.
+ 4. MedBook adds the record to the patient.
+ Use case ends.
+- **Extensions**:
+ - 3a. MedBook detects an error in the entered record's details.
+ - 3a1. MedBook shows an error message.
+ - 3a2. User enters new record details.
+ - Steps 3a1-3a2 are repeated until the record details entered is correct.
+ - Use case resumes from step 4.
-*{More to be added}*
+---
+
+### UC10 - View Patient's Medical Records
+
+- **Actor**: User
+- **System**: MedBook
+- **Main Success Scenario (MSS)**:
+ 1. User lists all patients (UC03).
+ 2. User requests to view a specific patient's records.
+ 3. User enters the patient ID.
+ 4. MedBook displays the records of the patient.
+ Use case ends.
+- **Extensions**:
+ - 3a. MedBook detects an error in the entered patient's ID.
+ - 3a1. MedBook shows an error message.
+ - 3a2. User enters new patient ID.
+ - Steps 3a1-3a2 are repeated until the patient ID entered is correct.
+ - Use case resumes from step 4.
+
+---
+
+### UC11 - Editing a Record
+
+- **Actor**: User
+- **System**: MedBook
+- **Preconditions**: There is at least one record entry in the patient.
+- **Main Success Scenario (MSS)**:
+ 1. User views a patient’s medical records (UC10).
+ 2. User requests to edit a patient’s record’s details.
+ 3. User enters new record details.
+ 4. MedBook updates the record’s details.
+ Use case ends.
+- **Extensions**:
+ - 3a. MedBook detects an error in the entered record’s details.
+ - 3a1. MedBook shows an error message.
+ - 3a2. User enters new record details.
+ - Steps 3a1-3a2 are repeated until the record details entered is correct.
+ - Use case resumes from step 4.
+
+---
+
+### UC12 - Deleting a Record under a Patient
+
+- **Actor**: User
+- **System**: MedBook
+- **Preconditions**: There is at least one record entry in the patient.
+- **Main Success Scenario (MSS)**:
+ 1. User views a patient’s medical records (UC10).
+ 2. User requests to delete a patient’s record.
+ 3. User enters the patient ID and record ID.
+ 4. MedBook deletes the record.
+ Use case ends.
+- **Extension**:
+ - 3a. MedBook detects an error in the entered patient ID and/or record ID.
+ - 3a1. MedBook shows an error message.
+ - 3a2. User enters new patient ID and record ID.
+ - Steps 3a1-3a2 are repeated until the patient ID and record ID entered is correct.
+ - Use case resumes from step 4.
+
+---
+
+### UC13 - Searching for Records
+
+- **Actor**: User
+- **System**: MedBook
+- **Preconditions**: There is at least one record entry in the patient.
+- **Main Success Scenario (MSS)**:
+ 1. User views a patient (UC10).
+ 2. User requests to search for a specific records.
+ 3. User enters search keywords.
+ 4. MedBook performs a search and displays matching record.
+ Use case ends.
+- **Extension**:
+ - 4a. No matches found.
+ - 4a1. MedBook informs the user that there were no matches.
+ - Use case ends.
+
+---
+
+### UC14 - Adding an Appointment to a Patient
+
+- **Actor**: User
+- **System**: MedBook
+- **Preconditions**: There is at least one patient entry in the system.
+- **Main Success Scenario (MSS)**:
+ 1. User requests to add a new appointment to a patient.
+ 2. User enters the appointment's details.
+ 3. MedBook adds the appointment to the patient.
+ Use case ends.
+- **Extensions**:
+ - 2a. MedBook detects an error in the entered appointment's details.
+ - 2a1. MedBook shows an error message.
+ - 2a2. User enters new appointment details.
+ - Steps 2a1-2a2 are repeated until the appointment details entered is correct.
+ - Use case resumes from step 3.
+
+---
+
+### UC15 - Viewing Appointments
+
+- **Actor**: User
+- **System**: MedBook
+- **Main Success Scenario (MSS)**:
+ 1. User requests to view appointments.
+ 2. MedBook shows the user all appointments.
+ Use case ends.
+
+---
+
+### UC16 - Deleting an Appointment
+
+- **Actor**: User
+- **System**: MedBook
+- **Preconditions**: There is at least one appointment entry in the system.
+- **Main Success Scenario (MSS)**:
+ 1. User views all appointments (UC15).
+ 2. User requests to delete a new appointment.
+ 3. User enters the appointment's ID.
+ 4. MedBook deletes the appointment.
+ Use case ends.
+- **Extensions**:
+ - 3a. MedBook detects an error in the entered appointment ID.
+ - 3a1. MedBook shows an error message.
+ - 3a2. User enters new appointment ID.
+ - Steps 3a1-3a2 are repeated until the appointment ID entered is correct.
+ - Use case resumes from step 4.
+
+---
+
+### UC17 - Attaching Files
+
+- **Actor**: User
+- **System**: MedBook
+- **Preconditions**: There is at least one record entry in the patient
+- **Main Success Scenario (MSS)**:
+
+ 1. User views a patient’s medical records (UC10).
+ 2. User requests to attach a file to a record of a patient.
+ 3. User selects a file.
+ 4. MedBook saves the file to the medical record.
+ Use case ends.
+
+- **Extension**:
+ - 3a. User does not choose a file.
+ - 3a1. MedBook displays an error message.
+ - Use case ends.
+
+---
+
+### UC18 - Opening Files
+
+- **Actor**: User
+- **System**: MedBook
+- **Preconditions**: There is at least one record entry with a file attached in the patient
+- **Main Success Scenario (MSS)**:
+
+ 1. User requests to view a file attached to a record.
+ 2. MedBook opens the file on the user’s default launcher.
+ Use case ends.
+
+- **Extension**:
+ - 1a. File does not exist in user’s local storage.
+ - 1a1. MedBook displays an error message.
+ - Use case ends.
+
+---
+
+## Glossary
+
+- **Mainstream OS**: Popular operating systems such as Windows, Linux, Unix, and OS-X.
+
+- **CLI (Command Line Interface)**: A user interface that allows users to interact with the software using text commands via a console or terminal.
+
+- **GUI (Graphical User Interface)**: A user interface that allows users to interact with the software through graphical icons and visual indicators, as opposed to text-based interfaces.
+
+- **Backward Compatibility**: The ability of the system to work with data and interfaces from earlier versions of the software.
+
+- **Healthcare Professional**: An individual who provides healthcare services, such as doctors, nurses, and medical staff.
+
+- **Performance Issues**: Any lag, delay, or inefficiency in the software’s response or processing time, especially noticeable when handling a large amount of data.
+
+- **Patient Information**: Data related to a patient, including but not limited to their personal details, medical history, contact information, and any other relevant information.
+
+---
+
+## Documentation, Logging, Testing, Configuration, Dev-Ops
+
+- [Documentation guide](Documentation.md)
+- [Testing guide](Testing.md)
+- [Logging guide](Logging.md)
+- [Configuration guide](Configuration.md)
+- [DevOps guide](DevOps.md)
+
+---
+
+## Appendix: Requirements
+
+### Product Scope
+
+**Target User Profile**:
+
+- **Primary Users**: Healthcare professionals in private clinics who handle patient information on a regular basis.
+- **Platform Preference**: Has a strong inclination towards using desktop applications, particularly those that support Command Line Interfaces.
+- **Technical Proficiency**: Is adept at typing.
+
+**Value Proposition**:
+
+- **Efficient Patient Management**: Provides a streamlined and efficient solution for managing patient information, ensuring that healthcare professionals can access and modify patient data quickly.
+- **Speed and Accessibility**: Designed to be significantly faster than conventional GUI applications, allowing users to execute commands and retrieve patient information in a matter of seconds.
+- **Command Line Efficiency**: Leverages the power of CLI to offer advanced users the ability to perform tasks in a more direct and efficient manner, while still maintaining accessibility for those who prefer graphical interfaces.
### Non-Functional Requirements
-1. Should work on any _mainstream OS_ as long as it has Java `11` or above installed.
-2. Should be able to hold up to 1000 persons without a noticeable sluggishness in performance for typical usage.
-3. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse.
+1. Should work on any mainstream OS with Java 11 or above.
+ - Rationale: Ensures accessibility for users on different platforms.
+ - Metric: Application functions correctly on Windows, macOS, and Linux.
+2. Supports up to 1000 patients without performance issues.
+ - Rationale: Ensures scalability for larger clinics.
+ - Metric: Application performs smoothly with a database of 1000 patients.
+3. Faster operation with commands than mouse for proficient typists.
+ - Rationale: Enhances productivity for users familiar with the command line.
+ - Metric: Common tasks are completed faster using keyboard shortcuts than GUI.
+4. Backward compatible with data from previous versions.
+ - Rationale: Ensures smooth transition for existing users upgrading to a new version.
+ - Metric: Users can open and interact with data files from previous versions without issues.
+5. Usable by novices.
+ - Rationale: Ensures the application is accessible to new users.
+ - Metric: New users can perform basic tasks without having to keep referring to the user manual.
+6. Provides comprehensive error messages and guidance for recovery.
+ - Rationale: Helps users understand what went wrong and how to fix it.
+ - Metric: Error messages include a description of the issue and steps for resolution.
+7. Comprehensive documentation and user guides available.
+ - Rationale: Provides users with resources to understand and use the application effectively.
+ - Metric: Documentation covers all features, includes screenshots, and is easy to navigate.
+
+---
+
+## Appendix: Instructions for Manual Testing
+
+
+
+**Note**: These instructions only provide a starting point for testers to work on; testers are expected to do more *exploratory* testing.
+
+
+### Launch and Shutdown
+
+#### Initial Launch
+
+1. Download and place the jar file in an empty folder.
+2. Launch the jar application via the terminal.
+ - Expected: GUI opens with sample contacts.
+
+---
+
+#### Saving Window Preferences
+
+1. Resize and reposition the window, then close it.
+2. Reopen the application.
+ - Expected: Window retains its size and position.
+
+---
+
+### Adding a New Patient
+
+1. Test Case: `addpatient n/John Doe i/T0000000Z e/johndoe@gmail.com p/98765432 g/M a/30 bt/AB+ al/Dust`
+
+ - Expected: New patient "John Doe" is added to the list, details are shown in the status message.
+
+2. Test Case: `addpatient n/John Doe i/0000000 e/johndoe@gmail.com p/98765432 g/M a/30 bt/AB+ al/Dust`
+ - Expected: Error message displayed, patient not added due to incorrect format of NRIC
+3. Test Case: `addpatient n/John Doe i/T0000000Z e/johndoegmail.com p/98765432 g/M a/30 bt/AB+ al/Dust`
+ - Expected: Error message displayed, patient not added due to incorrect format of email.
+4. Test Case: `addpatient n/John Doe i/T0000000Z e/johndoe@gmail.com p/12 g/M a/30 bt/AB+ al/Dust`
+ - Expected: Error message displayed, patient not added due to incorrect format of phone number.
+5. Test Case: `addpatient n/John Doe i/T0000000Z e/johndoe@gmail.com p/98765432 g/T a/30 bt/AB+ al/Dust`
+ - Expected: Error message displayed, patient not added because gender can only be `M` or `F`.
+6. Test Case: `addpatient n/John Doe i/T0000000Z e/johndoe@gmail.com p/98765432 g/M a/-1 bt/AB+ al/Dust`
+ - Expected: Error message displayed, patient not added because age can only be a non-negative integer.
+7. Test Case: `addpatient n/John Doe i/T0000000Z e/johndoe@gmail.com p/98765432 g/M a/30 bt/AP al/Dust`
+ - Expected: Error message displayed, patient not added due to incorrect blood type.
+
+---
+
+### Editing a Patient's Details
+
+1. Prerequisites: Ensure the patient list is displayed and contains the entry you wish to edit.
+2. Test Case: `editpatient 1 a/35`
+
+ - Expected: Patient at index 1 has their age updated to 35. Details shown in the status message.
+
+3. Test Case: `editpatient x a/35` (where x > list size)
+ - Expected: Error message displayed, patient's details unchanged.
+
+---
+
+### Deleting a Patient
+
+1. Prerequisites: Ensure the patient list is displayed and contains the entry you wish to delete.
+2. Test Case: `delete 1`
+
+ - Expected: Patient at index 1 is deleted. Details shown in status message.
+
+3. Test Case: `delete 0`
+ - Expected: Error message displayed, **Patient List** unchanged.
+4. Other Test Cases: `delete`, `delete x` (where x > list size)
+ - Expected: Error message displayed, **Patient List** unchanged.
+
+---
+
+### Pinning a Patient
+
+1. Prerequisites: Ensure the patient list is displayed and contains the entry you wish to pin.
+2. Test Case: `pin 1`
+
+ - Expected: Patient at index 1 is pinned to the **Pinned Patient List**. Details shown in the status message.
+
+3. Test Case: `pin x` (where x > list size)
+ - Expected: Error message displayed, **Pinned Patient List** unchanged.
+
+---
+
+### Unpinning a Patient
+
+1. Prerequisites: Ensure the pinned patient list contains the entry you wish to unpin.
+2. Test Case: `unpin 1`
+
+ - Expected: Patient at index 1 of the **Pinned Patient List** is unpinned and no longer displayed in the **Pinned Patient List**. Details shown in the status message.
+
+3. Test Case: `unpin x` (where x > list size)
+ - Expected: Error message displayed, **Pinned Patient List** unchanged.
+
+---
+
+### Searching for Patients
+
+1. Prerequisites: Ensure the patient list contains entries that will match your search term.
+2. Test Case: `search John`
+
+ - Expected: List of patients with details matching exactly "John" is displayed.
+
+---
+
+### Adding a Record under Patient
+
+1. Prerequisites: Ensure the patient list is displayed
+2. Test Case: `addrecord 1 d/12-11-2023 2200 c/Fever m/Ibuprofen`
+
+ - Expected: Adds the specified record to the first patient.
+
+3. Test Case: `addrecord x d/12-11-2023 2200 c/Fever m/Ibuprofen` (where x > size of patient list)
+ - Expected: Error message displayed, record not added.
+4. Test Case: `addrecord 1 d/12112023 c/Fever m/Ibuprofen`
+ - Expected: Error message displayed suggesting date and time should in the form of dd-mm-yyyy hhmm
+
+---
+
+### View Patient's Medical Records
+
+1. Prerequisites: Ensure the patient list is displayed and contains the entry you wish to view.
+2. Test Case: `view 1`
+
+ - Expected: Medical records of the first patient are displayed, details shown in the status message.
+
+3. Test Case: `view 0`
+
+ - Expected: Error message displayed, **Medical Record List** unchanged.
+
+4. Other Test Cases: `view`, `view x` (where x > list size)
+
+ - Expected: Error message displayed, **Medical Record List** unchanged.
+
+---
+
+### Editing a Record's Details
+
+1. Prerequisites: Ensure the record list of the patient is displayed and contains the entry you wish to edit.
+2. Test Case: `editrecord 1/1 c/Fever`
-*{More to be added}*
+ - Expected: The record at index 1 of the Patient at index 1 has its conditions updated to only "Fever". Details shown in the status message.
-### Glossary
+3. Test Case: `editrecord 1/1 c/Fever m/Paracetamol`
-* **Mainstream OS**: Windows, Linux, Unix, OS-X
-* **Private contact detail**: A contact detail that is not meant to be shared with others
+ - Expected: The record at index 1 of the Patient at index 1 has its conditions updated to only "Fever" and medications to only "Paracetamol". Details shown in the status message.
---------------------------------------------------------------------------------------------------------------------
+4. Test Case: `editrecord x/1 c/Fever` (where x > patient list size)
-## **Appendix: Instructions for manual testing**
+ - Expected: Error message displayed, record's details unchanged.
-Given below are instructions to test the app manually.
+5. Test Case: `editrecord 1/x c/Fever` (where x > record list size)
-
:information_source: **Note:** These instructions only provide a starting point for testers to work on;
-testers are expected to do more *exploratory* testing.
+ - Expected: Error message displayed, record's details unchanged.
-
+6. Test Case: `editrecord 1/1 d/12112023`
-### Launch and shutdown
+ - Expected: Error message displayed suggesting date and time should in the form of "dd-mm-yyyy hhmm".
+
+7. Test Case: `editrecord 1/1 d/12-11-2023 2200 d/13-11-2023 2200`
+ - Expected: Error message displayed suggesting multiple inputs of date are not allowed.
+
+---
+
+### Deleting a Record
+
+1. Prerequisites: Ensure the record list of the patient is displayed and contains the entry you wish to edit.
+2. Test Case: `deleterecord 1/1`
+
+ - Expected: Deletes the first record of the first patient.
+
+3. Test Case: `deleterecord x/1` (where x > size of patient list)
+
+ - Expected: Error message displayed, **Medical Record List** unchanged.
+
+4. Test Case: `deleterecord 1/y` (where y > size of record list of the first patient)
+ - Expected: Error message displayed, **Medical Record List** unchanged.
+
+---
+
+### Searching Records of the Patient Being Viewed
+
+1. Prerequisites: Ensure the record list of the patient is displayed.
+2. Test Case: `searchrecord Ibuprofen`
+
+ - Expected: List of records with "Ibuprofen" in the details is displayed.
+
+---
+
+### Attaching File to Record
+
+1. Prerequisites: Ensure the record list of the patient is displayed and it is not empty.
+2. Test Case: Click on “Attach Files” and select file from local storage.
+
+ - Expected: “File successfully attached” is displayed and file link is added to record.
+
+3. Test Case: Click on “Attach Files” and cancel the file explorer without selecting a file
+
+ - Expected: Error message displayed as no file was selected.
+
+---
+
+### Opening File in Record
+
+1. Prerequisites: Ensure the record list of the patient is displayed and it is not empty. Ensure the record has a file attached.
+2. Test Case: Click on Filepath link.
+
+ - Expected: File is opened using the user’s default launcher.
+
+3. Test Case: Delete or relocate file in local storage. Click on Filepath link.
+
+ - Expected: Error message displayed as file path no longer exists
+
+---
+
+### Adding an Appointment
+
+1. Prerequisites: Ensure the patient list is displayed and contains the entry you wish to add an appointment to.
+2. Test Case: `addappointment 1 n/Eye Exam d/18-10-2023 1900`
+
+ - Expected: New appointment, "Eye Exam" is added to the patient with index 1, details are shown in the status message.
+
+3. Test Case: `addappointment x n/Eye Exam d/18-10-2023 1900` (where x > list size)
+ - Expected: Error message displayed, appointment not added.
+4. Test Case: `addappointment 1 d/18-10-2023 1900`
+ - Expected: Error message displayed, appointment not added.
+5. Test Case: `addappointment 1 n/Eye Exam`
+ - Expected: Error message displayed, appointment not added.
+6. Test Case: `addappointment 1 n/Eye Exam d/18/10/2023 1900`
+ - Expected: Error message displayed, appointment not added.
+
+---
+
+### Viewing Appointments
+
+#### Appointment Window is closed
+
+1. Test Case: `viewappointment`
+ - Expected: Appointment Window opens.
+
+#### Appointment Window is open but not in focus
+
+1. Test Case: `viewappointment`
+ - Expected: Appointment Window focuses.
+
+---
+
+### Deleting an Appointment
+
+1. Prerequisites: Ensure the appointment list is displayed and contains the entry you wish to delete.
+2. Test Case: `deleteappointment 1`
+ - Expected: Appointment with index 1 is deleted, details are shown in status message.
+3. Test Case: `deleteappointment x` (where x > list size)
+ - Expected: Error message displayed, **Appointment List** unchanged.
+
+---
+
+### Verifying Patient Data Integrity
+
+#### After Operations
+
+1. Prerequisites: Perform operations like adding, deleting, and editing patients.
+2. Navigate to the folder where data is stored and open the data file.
+ - Expected: All changes should be accurately reflected in the data file.
+
+---
+
+### Handling Invalid Commands
+
+#### Input Mistakes
+
+1. Test Case: `addreocrd n/John Doe i/A0000000A a/30 g/M e/jd@example.com p/98776543 bt/AB+ al/Dust`
+
+ - Expected: Error message displayed stating unknown command.
+
+2. Test Case: `delet 1`
+ - Expected: Error message displayed stating unknown command.
+
+## Appendix: Planned Enhancements
+
+### Additional Navigation in Appointment Calender
+
+#### Current Implementation
+
+The current version of the Appointment Calendar allows users to view appointments in a calendar format. Navigation through the calendar is limited to sequential month traversal using 'Next' and 'Previous' buttons. This implementation, while functional, can be cumbersome for users who wish to view appointments that are several months away from the current date.
+
+#### Proposed Enhancement
+
+We plan to enhance the calendar navigation by introducing a more efficient way for users to view appointments. The enhancement will allow users to:
+
+**Manual Selection of Month and Year**: A dropdown menu or a picker control will be integrated into the UI, enabling users to quickly jump to a specific month and year without sequentially navigating through each month.
+
+**`viewcalender MM YYYY` command**: For users who prefer typing through the CLI, we will implement a command that allows them to view the calendar for a specific month and year. Users will be able to enter a command in the format viewcalendar MM YYYY (e.g., viewcalendar 12 2023 to view December 2023), and the calendar will update to display the selected month and year.
+
+---
+
+### Enhanced Appointment Calender UI
+
+#### Current Implementation
+
+The existing UI of the Appointment Calendar displays a truncated version of the appointment name and limits the visible appointments to only two per day. This restriction can lead to incomplete information visibility which is not optimal for efficiency and user experience.
+
+#### Proposed Enhancement
+
+To improve the user interface and overall user experience of the Appointment Calendar, the following enhancements are proposed:
+
+**Expanded Appointment Visibility**: Modify the calendar layout to allow for the display of more than two appointments per day. This could involve redesigning the day cells to accommodate more entries or implementing a scrolling mechanism within each day cell for days with numerous appointments.
+Ensure that the UI remains uncluttered and user-friendly, even with the inclusion of more appointment entries per day.
+
+**Full Appointment Name Display**: Adjust the UI to display the full name of each appointment, rather than a truncated version. This will enable users to quickly identify appointments at a glance without needing to hover over or click into the appointment for full details.
+Implement a dynamic text resizing or wrapping feature within each calendar entry to ensure that longer appointment names fit within the allocated space without compromising readability.
+
+**Responsive and Adaptive Design**: Enhance the calendar’s responsive design so it adapts effectively to different screen sizes and resolutions. This ensures that the increased information density does not negatively impact users on smaller screens or mobile devices.
+
+---
+
+### Enhanced NRIC Parameter for Patients
+
+#### Current Implementation
+
+In the current implementation the NRIC parameter for patient identification is limited in its format. It accepts an entry consisting of an alphabet, followed by seven digits, and then another alphabet. This format, while broadly useful, does not align completely with real-world scenarios, particularly in Singapore, and lacks the flexibility required for foreign patients.
+
+#### Proposed Enhancement
+
+To make the NRIC parameter more inclusive and reflective of real-world use cases, especially in Singapore, we propose enhancing the NRIC parameter with the following features:
+
+**Restricted First Alphabet**: The first alphabet in the NRIC will now be restricted to specific letters, such as 'S' and 'T', which are currently used in Singapore. This change aligns the system more closely with the actual format of NRICs in Singapore.
+
+**Optional Passport Number Support**: To accommodate foreign patients who do not have an NRIC, the system will be enhanced to accept passport numbers as an alternative identifier. This feature is particularly important for private clinics that cater to a diverse patient base, including non-residents and tourists.
+
+---
+
+### Accepting `/` in Name parameter
+
+#### Current Implementation
+
+Due to current constraints in the Parser which causes / to be parsed as tags, the "/" character cannot be entered into the name parameter. As such, users would currently not be able to enter "Muhammed Ali s/o Muhammed Ali".
+
+#### Proposed Enhancement
+
+Modify the parsing logic to differentiate between command tags and legitimate use of special characters in names. This could involve implementing an escape character mechanism or a more advanced parsing algorithm that can contextually understand the use of "/" in different scenarios.
+
+---
+
+### Patient Index Alignment
+
+#### Current Implementation
+
+The Patient ID in the patients in the **Pinned Patient List** and **Patient Being Viewed** is unaligned with the **Patient List** which might lead to confusion in users for which Patient ID to follow.
+
+#### Proposed Enhancement
+
+Modify the way panels create new person card entries. This could involve creating new lists to keep track of the patient index and update commands associated with the Pinned Patients and Patient Being Viewed.
+
+---
-1. Initial launch
+## Appendix: Effort
- 1. Download the jar file and copy into an empty folder
+This appendix aims to an insight into the total effort that went into the development of Medbook.
- 1. Double-click the jar file Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum.
+### Effort Overview
-1. Saving window preferences
+**Duration:** The project spanned approximately 2 months from initial conception to final release.
+**Team:** Consisted of 5 members.
- 1. Resize the window to an optimum size. Move the window to a different location. Close the window.
+#### Technical Complexity and Challenges
- 1. Re-launch the app by double-clicking the jar file.
- Expected: The most recent window size and location is retained.
+- **Data Security:** Implementing robust security measures such as data encryption to protect sensitive patient information was paramount and required extensive research and testing. (It was omitted due to constraints related to the Project)
+- **Defensive Coding:** We enforced immutability of `Person`, `Record`, and `Appointment` to maintain data integrity.
+- **User Interface:** Crafting an intuitive GUI that also supported a CLI for efficiency demanded iterative design and usability testing.
-1. _{ more test cases … }_
+#### Effort Quantification
-### Deleting a person
+- **Development:** The team invested over 300 hours in coding, with a focused effort on crafting intuitive and responsive GUI and CLI interfaces. This investment reflects our commitment to usability and accessibility, ensuring that both novice and experienced users can navigate the application with ease.
-1. Deleting a person while all persons are being shown
+- **Testing:** More than 100 hours were dedicated to a combination of manual and automated testing. This rigorous testing protocol was critical in validating the application's reliability and optimizing its performance across various user scenarios and system environments.
- 1. Prerequisites: List all persons using the `list` command. Multiple persons in the list.
+- **Documentation:** We allocated 50 hours to developing thorough user documentation. This comprehensive guide is pivotal for facilitating a quick adoption of the application by new users and serves as a reliable reference for existing users to leverage the application's full potential.
- 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.
+#### Reuse of Existing Solutions
- 1. Test case: `delete 0`
- Expected: No person is deleted. Error details shown in the status message. Status bar remains the same.
+- **Library Reuse:** Utilized the JavaFX library for the GUI, which saved much effort for front-end development.
- 1. Other incorrect delete commands to try: `delete`, `delete x`, `...` (where x is larger than the list size)
- Expected: Similar to previous.
+#### Effort Comparison with Reference Projects:
-1. _{ more test cases … }_
+- **Compared to AB3:** the development of Medbook demanded considerably more effort, primarily due to its capability to manage multiple entity types such as patients, records, and appointments. This multifaceted approach contrasts with AB3's design, which is centered around handling a single entity type.
+ Moreover, Medbook was built upon the AB3 architecture as a foundational base, necessitating a deep understanding of the existing complex framework. Even though certain features in Medbook were adapted from AB3 to suit our specific needs, significant effort was required to modify and extend these features. Tailoring pre-existing functionalities to fit into our more comprehensive application model involved intricate work, ensuring seamless integration and functionality within Medbook's broader scope.
-### Saving data
+#### Achievements
-1. Dealing with missing/corrupted data files
+- Despite the high complexity, the team managed to deliver Medbook on schedule.
+- User feedback has been overwhelmingly positive, especially regarding the ease of use and performance of the application.
- 1. _{explain how to simulate a missing/corrupted file, and the expected behavior}_
+#### Conclusion
-1. _{ more test cases … }_
+The successful development of Medbook is a testament to the well-coordinated effort, rigorous testing, and effective project management that adapted to challenges and complexity with innovative solutions and strategic planning.
diff --git a/docs/Documentation.md b/docs/Documentation.md
index 3e68ea364e7..082e652d947 100644
--- a/docs/Documentation.md
+++ b/docs/Documentation.md
@@ -1,29 +1,21 @@
---
-layout: page
-title: Documentation guide
+ layout: default.md
+ title: "Documentation guide"
+ pageNav: 3
---
-**Setting up and maintaining the project website:**
-
-* We use [**Jekyll**](https://jekyllrb.com/) to manage documentation.
-* The `docs/` folder is used for documentation.
-* To learn how set it up and maintain the project website, follow the guide [_[se-edu/guides] **Using Jekyll for project documentation**_](https://se-education.org/guides/tutorials/jekyll.html).
-* Note these points when adapting the documentation to a different project/product:
- * The 'Site-wide settings' section of the page linked above has information on how to update site-wide elements such as the top navigation bar.
- * :bulb: In addition to updating content files, you might have to update the config files `docs\_config.yml` and `docs\_sass\minima\_base.scss` (which contains a reference to `AB-3` that comes into play when converting documentation pages to PDF format).
-* If you are using Intellij for editing documentation files, you can consider enabling 'soft wrapping' for `*.md` files, as explained in [_[se-edu/guides] **Intellij IDEA: Useful settings**_](https://se-education.org/guides/tutorials/intellijUsefulSettings.html#enabling-soft-wrapping)
+# Documentation Guide
+* We use [**MarkBind**](https://markbind.org/) to manage documentation.
+* The `docs/` folder contains the source files for the documentation website.
+* To learn how set it up and maintain the project website, follow the guide [[se-edu/guides] Working with Forked MarkBind sites](https://se-education.org/guides/tutorials/markbind-forked-sites.html) for project documentation.
**Style guidance:**
* Follow the [**_Google developer documentation style guide_**](https://developers.google.com/style).
+* Also relevant is the [_se-edu/guides **Markdown coding standard**_](https://se-education.org/guides/conventions/markdown.html).
-* Also relevant is the [_[se-edu/guides] **Markdown coding standard**_](https://se-education.org/guides/conventions/markdown.html)
-
-**Diagrams:**
-
-* See the [_[se-edu/guides] **Using PlantUML**_](https://se-education.org/guides/tutorials/plantUml.html)
-**Converting a document to the PDF format:**
+**Converting to PDF**
-* See the guide [_[se-edu/guides] **Saving web documents as PDF files**_](https://se-education.org/guides/tutorials/savingPdf.html)
+* See the guide [_se-edu/guides **Saving web documents as PDF files**_](https://se-education.org/guides/tutorials/savingPdf.html).
diff --git a/docs/Gemfile b/docs/Gemfile
deleted file mode 100644
index c8385d85874..00000000000
--- a/docs/Gemfile
+++ /dev/null
@@ -1,10 +0,0 @@
-# frozen_string_literal: true
-
-source "https://rubygems.org"
-
-git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
-
-gem 'jekyll'
-gem 'github-pages', group: :jekyll_plugins
-gem 'wdm', '~> 0.1.0' if Gem.win_platform?
-gem 'webrick'
diff --git a/docs/Logging.md b/docs/Logging.md
index 5e4fb9bc217..589644ad5c6 100644
--- a/docs/Logging.md
+++ b/docs/Logging.md
@@ -1,8 +1,10 @@
---
-layout: page
-title: Logging guide
+ layout: default.md
+ title: "Logging guide"
---
+# Logging guide
+
* We are using `java.util.logging` package for logging.
* The `LogsCenter` class is used to manage the logging levels and logging destinations.
* The `Logger` for a class can be obtained using `LogsCenter.getLogger(Class)` which will log messages according to the specified logging level.
diff --git a/docs/SettingUp.md b/docs/SettingUp.md
index 275445bd551..03df0295bd2 100644
--- a/docs/SettingUp.md
+++ b/docs/SettingUp.md
@@ -1,27 +1,32 @@
---
-layout: page
-title: Setting up and getting started
+ layout: default.md
+ title: "Setting up and getting started"
+ pageNav: 3
---
-* Table of Contents
-{:toc}
+# Setting up and getting started
+
+
--------------------------------------------------------------------------------------------------------------------
## Setting up the project in your computer
-
:exclamation: **Caution:**
+
+**Caution:**
Follow the steps in the following guide precisely. Things will not work out if you deviate in some steps.
-
+
First, **fork** this repo, and **clone** the fork into your computer.
If you plan to use Intellij IDEA (highly recommended):
1. **Configure the JDK**: Follow the guide [_[se-edu/guides] IDEA: Configuring the JDK_](https://se-education.org/guides/tutorials/intellijJdk.html) to to ensure Intellij is configured to use **JDK 11**.
-1. **Import the project as a Gradle project**: Follow the guide [_[se-edu/guides] IDEA: Importing a Gradle project_](https://se-education.org/guides/tutorials/intellijImportGradleProject.html) to import the project into IDEA.
- :exclamation: Note: Importing a Gradle project is slightly different from importing a normal Java project.
+1. **Import the project as a Gradle project**: Follow the guide [_[se-edu/guides] IDEA: Importing a Gradle project_](https://se-education.org/guides/tutorials/intellijImportGradleProject.html) to import the project into IDEA.
+
+ Note: Importing a Gradle project is slightly different from importing a normal Java project.
+
1. **Verify the setup**:
1. Run the `seedu.address.Main` and try a few commands.
1. [Run the tests](Testing.md) to ensure they all pass.
@@ -34,10 +39,11 @@ If you plan to use Intellij IDEA (highly recommended):
If using IDEA, follow the guide [_[se-edu/guides] IDEA: Configuring the code style_](https://se-education.org/guides/tutorials/intellijCodeStyle.html) to set up IDEA's coding style to match ours.
-
:bulb: **Tip:**
+
+ **Tip:**
Optionally, you can follow the guide [_[se-edu/guides] Using Checkstyle_](https://se-education.org/guides/tutorials/checkstyle.html) to find how to use the CheckStyle within IDEA e.g., to report problems _as_ you write code.
-
+
1. **Set up CI**
diff --git a/docs/Testing.md b/docs/Testing.md
index 8a99e82438a..78ddc57e670 100644
--- a/docs/Testing.md
+++ b/docs/Testing.md
@@ -1,12 +1,15 @@
---
-layout: page
-title: Testing guide
+ layout: default.md
+ title: "Testing guide"
+ pageNav: 3
---
-* Table of Contents
-{:toc}
+# Testing guide
---------------------------------------------------------------------------------------------------------------------
+
+
+
+
## Running tests
@@ -19,8 +22,10 @@ There are two ways to run tests.
* **Method 2: Using Gradle**
* Open a console and run the command `gradlew clean test` (Mac/Linux: `./gradlew clean test`)
-
:link: **Link**: Read [this Gradle Tutorial from the se-edu/guides](https://se-education.org/guides/tutorials/gradle.html) to learn more about using Gradle.
-
+
+
+**Link**: Read [this Gradle Tutorial from the se-edu/guides](https://se-education.org/guides/tutorials/gradle.html) to learn more about using Gradle.
+
--------------------------------------------------------------------------------------------------------------------
diff --git a/docs/UserGuide.md b/docs/UserGuide.md
index 57437026c7b..8656f868fbd 100644
--- a/docs/UserGuide.md
+++ b/docs/UserGuide.md
@@ -1,197 +1,929 @@
---
-layout: page
-title: User Guide
+layout: default.md
+title: "User Guide"
+pageNav: 4
---
-AddressBook Level 3 (AB3) is a **desktop app for managing contacts, optimized for use via a Command Line Interface** (CLI) while still having the benefits of a Graphical User Interface (GUI). If you can type fast, AB3 can get your contact management tasks done faster than traditional GUI apps.
+# User Guide
-* Table of Contents
-{:toc}
+## Welcome to MedBook!
---------------------------------------------------------------------------------------------------------------------
+**_Elevate Your Clinic with Advanced Patient Management Solutions_**
+
+Welcome to MedBook, the revolutionary **desktop application** tailor-made for **doctors** and **medical administrative assistants** in **private clinics**. MedBook is your partner in digitizing patient management and medical records, ensuring efficiency, accessibility, and security.
+
+#### Why Choose MedBook?
+
+With MedBook, experience a new level of convenience and control:
+
+- **Streamlined Patient Management**: With intuitive tools, adding, editing, removing, and saving patient details becomes a task of mere seconds.
+- **Integrated Data Handling**: Effortlessly manage patient appointments and medical records from a single, unified platform.
+- **Instant Information Access**: Retrieve any required information with speed and precision.
+- **Comprehensive Patient Overview**: View patient details, medical history, and upcoming appointments in one comprehensive and easily navigable interface.
+
+#### Unlock the Potential of Digital Healthcare with MedBook
+
+- **Go Digital with Ease**: Transition your clinic to a digital platform effortlessly, backed by features tailored for the healthcare sector's unique needs.
+- **Optimized for Speed**: Catering to all skill levels, MedBook's **Command Line Interface (CLI)** and **Graphical User Interface (GUI)** provide options for rapid data entry and user-friendly navigation.
+- **Begin Your MedBook Journey**: New to MedBook? Click [here](#quick-start) for a step-by-step guide to revolutionizing your clinic's management system.
+
+---
+
+
+
+## Table of Contents
+
+- [Welcome to MedBook](#welcome-to-medbook)
+- [Table of Contents](#table-of-contents)
+- [Navigating the User Guide](#navigating-the-user-guide)
+- [Quick Start](#quick-start)
+- [Glossary](#glossary)
+- [Navigating the GUI](#navigating-the-gui)
+- [MedBook Tutorial](#medbook-tutorial)
+- [Features](#features)
+ - [Parameters](#parameters)
+ - [Patient Features](#patient-features)
+ - [Adding a patient](#adding-a-patient-addpatient)
+ - [Editing a patient](#editing-a-patient-editpatient)
+ - [Deleting a patient](#deleting-a-patient-delete)
+ - [Searching patients by keywords](#searching-patients-by-keywords-search)
+ - [Listing all patients](#listing-all-patients-list)
+ - [Pinning a patient](#pinning-a-patient-pin)
+ - [Unpinning a patient](#unpinning-a-patient-unpin)
+ - [Medical Record Features](#medical-record-features)
+ - [Adding a medical record](#adding-a-medical-record-addrecord)
+ - [Viewing patient medical records](#viewing-patient-medical-records-view)
+ - [Editing a medical record](#editing-a-medical-record-editrecord)
+ - [Deleting a medical record](#deleting-a-medical-record-deleterecord)
+ - [Searching medical records by keywords](#searching-medical-records-by-keywords-searchrecord)
+ - [Attaching files to a patient's medical record](#attaching-files-to-a-patient-s-medical-record)
+ - [Appointment Features](#appointment-features)
+ - [Adding an appointment](#adding-an-appointment-addappointment)
+ - [Deleting an appointment](#deleting-an-appointment-deleteappointment)
+ - [Viewing appointments](#viewing-appointments-viewappointment)
+ - [General Features](#general-features)
+ - [Viewing help](#viewing-help-help)
+ - [Exiting the program](#exiting-the-program-exit)
+ - [Saving the data](#saving-the-data)
+ - [Editing the data file](#editing-the-data-file)
+- [FAQ](#faq)
+- [Known Issues](#known-issues)
+- [Command Summary](#command-summary)
+
+---
+
+
+
+## Navigating the User Guide
+
+Welcome to the MedBook User Guide! Our goal is to empower you with the knowledge and confidence to make the most of MedBook's features.
+
+- **Effortless Navigation**: Use the table of contents for seamless navigation between sections.
+- **Quick Start for New Users**: If you're new to MedBook, start with the [Quick Start](#quick-start) section to get up and running.
+- **Explore Features**: Already familiar with the basics? Dive into the [Features](#features) section to discover all that our application has to offer.
+
+We're here to make your experience as user-friendly as possible. Let's get started!
+
+[Back to Table Of Contents](#table-of-contents)
+
+---
## Quick start
-1. Ensure you have Java `11` or above installed in your Computer.
+1. **Check Java Installation**:
+
+ - Ensure you have Java `11` or above installed on your computer:
+
+ - [How do I check my version of Java?](#faq)
+
+2. **Download MedBook**:
+
+ - Grab the latest version of MedBook (`medbook.jar`) from our [Github Release](https://github.com/AY2324S1-CS2103T-T12-4/tp/releases).
+
+3. **Set Up Your Workspace**:
+
+ - Choose a folder where you'd like to manage your patient data.
+
+
+
+ **Tip**: Create a new folder named `MedBook` for easy organization.
+
+
+
+ - Move `medbook.jar` into the folder as shown below.
+
+4. **Accessing Terminal**:
+
+ - **Windows Users**:
+
+ - Search for "Terminal" in the Windows search bar and launch it.
+
+ - **Mac Users**:
+ - Find **Terminal** in "Utilities" under "Applications". Or, you can use Spotlight Search (Command + Spacebar) and type "Terminal".
+
+5. **Launch MedBook**:
+
+ - Navigate to to the folder where you have stored `medbook.jar` using the `cd` command
+
+
+
+ **Tip**: Right-click the folder and select `New Terminal at Folder` (**Mac**) or `Open in Terminal` (**Windows**) to instantly navigate to the folder in your **Terminal**!
+
+
+
+ - Type `java -jar medbook.jar` and hit Enter to start MedBook.
+
+Once launched, MedBook will look like this:
+
+
+- **Exploring Further**:
+ - Learn more about navigating the GUI [here](#navigating-the-gui).
+ - For new users, learn how to use MedBook [here](#medbook-tutorial).
+ - For advanced users, view all the available features [here](#features).
-1. Download the latest `addressbook.jar` from [here](https://github.com/se-edu/addressbook-level3/releases).
+[Back to Table Of Contents](#table-of-contents)
-1. Copy the file to the folder you want to use as the _home folder_ for your AddressBook.
+---
+
+
+
+## Glossary
-1. Open a command terminal, `cd` into the folder you put the jar file in, and use the `java -jar addressbook.jar` command to run the application.
- A GUI similar to the below should appear in a few seconds. Note how the app contains some sample data.
- 
+Here are some definitions of the terms used throughout this user guide.
-1. Type the command in the command box and press Enter to execute it. e.g. typing **`help`** and pressing Enter will open the help window.
- Some example commands you can try:
+| **Term** | **Definition** |
+| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| Graphical User Interface (GUI) | The GUI allows you to interact with the application through things you can click, instead of typing commands. It also provides visual display for information stored in the application. |
+| Command Line Interface (CLI) | The CLI is a way of interacting with the application using typed text commands. |
+| Command | A command is a textual input that users type into the Command Line Interface to interact with the application. |
+| Parameter | A parameter is a specific piece of information or data that you provide to a command in a Command Line Interface to customize the command's action. |
+
+[Back to Table Of Contents](#table-of-contents)
+
+---
- * `list` : Lists all contacts.
+
- * `add n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01` : Adds a contact named `John Doe` to the Address Book.
+## Navigating the GUI
- * `delete 3` : Deletes the 3rd contact shown in the current list.
+**Main Window**:
+
- * `clear` : Deletes all contacts.
+| **Name of Component** | **Description** |
+| --------------------- | -------------------------------------------------------------------------------------------------------- |
+| Menu Bar | Displays the drop down menus for MedBook, such as **File** and **Help** |
+| Message Display Box | Display the message output for every command. It can either can be an error or success message |
+| Patient Card | Contains information of the patient such as name, age, and contact information |
+| Patient List | Displays a list of Patient Cards |
+| Patient Being Viewed | The Patient Card of the patient currently being viewed |
+| Record Card | Contains information of the medical record including date and time, condition, and prescribed medication |
+| Medical Records List | Displays a list of Record Cards belonging to the Patient Being Viewed |
+| Pinned Patient List | Displays a list of Patient Cards of pinned patients |
+| Command Input Box | A text box which allows users to input commands |
- * `exit` : Exits the app.
+
+
+**Appointment Window**
+
+
+| **Name of Component** | **Description** |
+| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| Appointment Card | Contains information of the appointment including name, date and time and NRIC |
+| Appointment List | Displays a list of Appointment Cards |
+| Calendar | Presents appointments on their specific dates. Each date shows the abbreviated `NAME` of up to the **first two** appointments from the **Appointment List**. |
+| Previous Button | Shifts the calendar to the previous month |
+| Next Button | Shifts the calendar to the next month |
+
+[Back to Table Of Contents](#table-of-contents)
+
+---
-1. Refer to the [Features](#features) below for details of each command.
+
---------------------------------------------------------------------------------------------------------------------
+## MedBook Tutorial
+
+1. **Starting MedBook**:
+
+ - Launch the MedBook application. You may refer to [here](#quick-start).
+
+
+
+**Note**: Upon starting, MedBook will display some sample data to help you get familiar.
+
+
+
+2. **Opening the Help Window**:
+ - Let us try opening the **Help Window**. Type `help` in the Command Input Box and press Enter.
+ - A **Help Window**, similar to the image below, will appear, listing all available commands.
+ Please refer to the [Features](#features) section for a more detailed explanation of the commands.
+3. **Adding a New Patient**:
+ - Now, let us try adding a new patient. Type in `addpatient n/John Doe i/A0000000B e/johndoe@gmail.com p/12345678 g/M a/26 bt/AB+ al/Penicillin` and press Enter.
+ - The Message Display Box will confirm the successful addition.
+4. **Practice Adding Patients**:
+ - Experiment by adding a few more patients to MedBook on your own!
+5. **Adding a Medical Record**:
+ - Now, let us try adding a medical record to a patient. Type in `addrecord 3 d/18-09-2023 1800 c/Fever m/Paracetamol` and press Enter.
+ - This will add a new medical record to the third patient shown in the **Patient List**.
+ - The Message Display Box will confirm the successful creation.
+6. **Add More Medical Records**:
+ - Try creating additional medical records for other patients in MedBook!
+7. **Viewing Patient Records**:
+ - Let us view the medical records of the first patient. Type in `view 1` and press Enter.
+ - If no medical records were added for the first patient, you will see an empty **Medical Record List** as shown.
+
+
+Congratulations! You are now ready to explore MedBook on your own. Experiment with different features in the following section and discover the potential of MedBook!
+
+[Back to Table Of Contents](#table-of-contents)
+
+---
+
+
## Features
-
+
-**:information_source: Notes about the command format:**
+**Notes about the command format:**
-* Words in `UPPER_CASE` are the parameters to be supplied by the user.
- e.g. in `add n/NAME`, `NAME` is a parameter which can be used as `add n/John Doe`.
+- **Upper Case Words**: Words in `UPPER_CASE` indicate parameters to be supplied by the user.
-* Items in square brackets are optional.
- e.g `n/NAME [t/TAG]` can be used as `n/John Doe t/friend` or as `n/John Doe`.
+ - Example: in `addpatient n/NAME`, `NAME` is a parameter which can be used as `add n/John Doe`.
-* Items with `…` after them can be used multiple times including zero times.
- e.g. `[t/TAG]…` can be used as ` ` (i.e. 0 times), `t/friend`, `t/friend t/family` etc.
+- **Optional Items**: Square brackets `[ ]` denote optional elements.
-* Parameters can be in any order.
- e.g. if the command specifies `n/NAME p/PHONE_NUMBER`, `p/PHONE_NUMBER n/NAME` is also acceptable.
+ - Example: `n/NAME [al/ALLERGIES]` can be entered as either `n/John Doe al/Pencillin` or just `n/John Doe`.
-* Extraneous parameters for commands that do not take in parameters (such as `help`, `list`, `exit` and `clear`) will be ignored.
- e.g. if the command specifies `help 123`, it will be interpreted as `help`.
+- **Repeated Use**: Ellipsis `…` after an item means it can be used multiple times.
-* If you are using a PDF version of this document, be careful when copying and pasting commands that span multiple lines as space characters surrounding line-breaks may be omitted when copied over to the application.
-
+ - Example: `[al/ALLERGIES]…` can be used as `al/Pencillin`, `al/Pollen al/Dust` or not at all.
-### Viewing help : `help`
+- **Flexible Parameter Order**: The order of parameters in a command can vary.
-Shows a message explaning how to access the help page.
+ - Example: Both `n/NAME p/PHONE_NUMBER` and `p/PHONE_NUMBER n/NAME` are valid.
-
+- **Extraneous parameters**: Additional parameters for commands that do not require them (such as `help`, `list`, and `exit`) will be ignored.
-Format: `help`
+ - Example: `help 123` will be processed just as `help`.
+
+- **Unique Identifiers**: **Must be a positive integer** like 1, 2, 3, …
+
+ - `PATIENTID`: The unique identification for a patient shown in the **Patient List**.
+
+ - `RECORDID`: The unique identification for a medical record shown in the displayed **Medical Record List** of a specific patient.
+
+ - `APPOINTMENTID`: The unique identification for an appointment shown in the displayed **Appointment List**.
+
+ - `PINNEDID`: The unique identification for a patient shown in the displayed **Pinned Patient List**.
+
+
+
+
+
+### Parameters
+
+| FIELD | PREFIX | CONSTRAINTS |
+| ------------ | ------ | ----------------------------------------------------------------------------------------------------- |
+| `NAME` | n | Alphanumeric characters, dashes, dots and spaces only |
+| `NRIC` | i | Starts with a letter, followed by seven digits, and ends with a letter. Letters are case-insensitive. |
+| `EMAIL` | e | Must follow the format **local-part@domain** |
+| `GENDER` | g | Either **M** (Male) or **F** (Female) |
+| `PHONE` | p | A sequence of at least 3 digits long |
+| `AGE` | a | A non-negative integer |
+| `BLOODTYPE` | bt | Must be one of the following: **A-**, **A+**, **B-**, **B+**, **AB-**, **AB+**, **O-**, **O+** |
+| `ALLERGY` | al | Alphanumeric characters only |
+| `DATETIME` | d | Must follow the format **dd-MM-yyyy HHmm** |
+| `CONDITION` | c | Alphanumeric characters, dashes and spaces |
+| `MEDICATION` | m | Alphanumeric characters, dashes and spaces |
+
+
+
+**Notes for `NAME` format:**
+
+- If the patient's name contains the `/` character, use the `-` character instead. For example, use `s-o` instead of `s/o`.
+
+
+
+
+**Notes for `NRIC` format:**
+
+- If the patient does not have an NRIC, eg. foreigners, a placeholder NRIC which is not valid such as `A1234567A` can be used instead.
+
+
+
+
+**Notes for `EMAIL` format:**
+
+1. The **local-part** should only contain alphanumeric characters and these special characters: `+`, `_`, `.`, `-`. The **local-part** may not start or end with any special characters.
+2. This is followed by an `@` and then a **domain** name. The **domain** name is made up of **domain** labels separated by periods.
-### Adding a person: `add`
+The **domain** name must:
-Adds a person to the address book.
+- end with a **domain** label at least 2 characters long
-Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…`
+- have each **domain** label start and end with alphanumeric characters
-
:bulb: **Tip:**
-A person can have any number of tags (including 0)
-
+- have each **domain** label consist of alphanumeric characters, separated only by hyphens, if any.
+
+
+
+
+**Notes for `ALLERGY` format:**
+
+- Allergies that consist of multiple words should be consolidated into a single word with each word's initial letter capitalized. For example, use `RedMeat` instead of `Red Meat`.
+
+
+[Back to Table Of Contents](#table-of-contents)
+
+---
+
+
+
+### Patient Features
+
+#### Adding a patient: `addpatient`
+
+Adds a new patient to the system.
+
+Format: `addpatient n/NAME i/NRIC e/EMAIL p/PHONE g/GENDER a/AGE bt/BLOODTYPE [al/ALLERGY]...`
+
+- Adds a patient with information including: `NAME`, `NRIC`, `EMAIL`, `PHONE`, `GENDER`, `AGE`, `BLOODTYPE`, `ALLERGY`.
+
+
+
+**Tip**: Want to add a new patient to MedBook? Try out the `addpatient` command!
+
+
+
+Example:
+
+`addpatient n/John Doe i/T1234567A e/johndoe@gmail.com p/12345678 g/M a/26 bt/AB+ al/Penicillin`
+Adds a **Male** patient named **John Doe** whose NRIC is **T1234567A** , **26** years old, has **AB+** Blood Type, and is allergic to **Penicillin**.
+His email and phone number is **johndoe@gmail.com** and **12345678** respectively.
+
+Screenshots:
+
+
+Command
+
+
+Output
+
+
+[Back to Table Of Contents](#table-of-contents)
+
+---
+
+
+
+#### Editing a patient: `editpatient`
+
+Edits the details of an existing patient.
+
+Format: `editpatient PATIENTID PREFIX/NEWVALUE...`
+
+- Edits the details of the patient with the corresponding `PATIENTID`.
+
+- Acceptable fields: `NAME`, `EMAIL`, `PHONE`, `GENDER`, `AGE`, `BLOODTYPE`, `ALLERGY`.
+
+
+
+**Note**: `NRIC` cannot be edited.
+
+
+
+- Existing values in the respective fields will be updated to the `NEWVALUE`.
+
+- Multiple entries of `ALLERGY` are allowed i.e `editpatient 1 al/Seafood al/Dust`.
+
+
+
+**Note**: When editing `ALLERGY`, the existing allergies of the patient will be removed i.e adding of allergies is not cumulative.
+
+
+
+Examples:
+
+- `editpatient 1 e/johndoe_updated@gmail.com` Edits the `EMAIL` of the patient with the `PATIENTID` of **1** to **johndoe_updated@gmail.com**.
+
+- `editpatient 2 p/92345678` Edits the `PHONE` of the patient with the `PATIENTID` of **2** to **92345678**.
+
+- `editpatient 3 al/aspirin` Edits the `ALLERGY` of the patient with the `PATIENTID` of **3** to **aspirin**.
+
+- `editpatient 2 p/92345678 al/aspirin` Edits the `PHONE` and `ALLERGY` of the patient with the `PATIENTID` of **2** to **92345678** and **aspirin**, respectively.
+
+Screenshots:
+
+
+Command
+
+
+Output
+
+
+[Back to Table Of Contents](#table-of-contents)
+
+---
+
+#### Deleting a patient: `delete`
+
+Deletes an existing patient from the system.
+
+Format: `delete PATIENTID`
+
+- Deletes the patient with the corresponding `PATIENTID`.
+
+Example:
+
+- `delete 2` Deletes the patient with the `PATIENTID` of **2**.
+
+Screenshots:
+
+
+Command
+
+
+Output
+
+
+[Back to Table Of Contents](#table-of-contents)
+
+---
+
+
+
+#### Searching patients by keywords: `search`
+
+Searches for patients with details containing the corresponding `KEYWORD`.
+
+Format: `search KEYWORD [MOREKEYWORDS]...`
+
+- The search is not case-sensitive. e.g **Penicillin** will match **penicillin**.
+
+- The order of the keywords does not matter. e.g. **Hans Bo** will match **Bo Hans**.
+
+- Patients matching at least one keyword will be returned. e.g. **Hans Bo** will return **Hans Gruber**, **Bo Yang**.
+
+- Patients with detail that are not exactly the same as the `KEYWORD` searched will not appear. e.g. **Han** will not return **Hans**.
+
+
+
+**Note**: The details of the patient's medical records will not be searched.
+
+
+
+
+
+**Tip**: Utilise the `search` command to quickly locate patients. It is perfect for filtering patient lists - for instance, identifying patients with specific allergies.
+
+
Examples:
-* `add n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01`
-* `add n/Betsy Crowe t/friend e/betsycrowe@example.com a/Newgate Prison p/1234567 t/criminal`
-### Listing all persons : `list`
+- `search Alex` Will display patients with **Alex** in their details.
-Shows a list of all persons in the address book.
+- `search Alex M` Will display patients with **Alex** or **M** in their details.
+
+Screenshots:
+
+
+Command
+
+
+Output
+
+
+[Back to Table Of Contents](#table-of-contents)
+
+---
+
+
+
+#### Listing all patients: `list`
+
+Shows a list of all patients.
Format: `list`
-### Editing a person : `edit`
+- **All** patients will be shown in the **Patient List**.
+
+
+
+**Tip**: The `list` command allows you to easily view all the patients in the **Patient List**. For instance, after you've used the `search` command to find specific patients, you can use the `list` command to gather all the patients conveniently!
-Edits an existing person in the address book.
+
-Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…`
+[Back to Table Of Contents](#table-of-contents)
-* Edits the person at the specified `INDEX`. The index refers to the index number shown in the displayed person list. The index **must be a positive integer** 1, 2, 3, …
-* At least one of the optional fields must be provided.
-* Existing values will be updated to the input values.
-* When editing tags, the existing tags of the person will be removed i.e adding of tags is not cumulative.
-* You can remove all the person’s tags by typing `t/` without
- specifying any tags after it.
+---
+
+#### Pinning a patient: `pin`
+
+Pins an existing patient.
+
+Format: `pin PATIENTID`
+
+- Pins the patient with the corresponding `PATIENTID` to the **Pinned Patient List**.
+
+
+
+**Tip**: A patient requires follow-up? Use the `pin` command to help you remember to contact them!
+
+
+
+Example:
+
+- `pin 2` Pins the patient with the `PATIENTID` of **2** to the **Pinned Patient List**.
+
+Screenshots:
+
+
+Command
+
+
+Output
+
+
+[Back to Table Of Contents](#table-of-contents)
+
+---
+
+
+
+#### Unpinning a patient: `unpin`
+
+Unpins a pinned patient.
+
+Format: `unpin PINNEDID`
+
+- Unpins the patient with the corresponding `PINNEDID` from the **Pinned Patient List**.
+
+Examples:
+
+- `unpin 2` Unpins the patient with the `PINNEDID` of **2** from the **Pinned Patient List**.
+
+[Back to Table Of Contents](#table-of-contents)
+
+---
+
+
+
+### Medical Record Features
+
+#### Adding a medical record: `addrecord`
+
+Adds a new medical record to the system.
+
+Format: `addrecord PATIENTID d/DATETIME c/CONDITIONS... m/MEDICATIONS...`
+
+- Adds a medical record to the patient with the corresponding `PATIENTID`.
+
+- Duplicate entries for `CONDITIONS` and `MEDICATIONS` are allowed and will be considered as separate entries.
+ - `addrecord 1 d/10-11-2023 1800 c/Flu c/Flu m/Ibuprofen m/Ibuprofen` will store **[Flu, Flu]** and **[Ibuprofen, Ibuprofen]** for `CONDITIONS` and `MEDICATIONS`, respectively.
+
+Example:
+
+- `addrecord 3 d/18-09-2023 1800 c/Fever m/Paracetamol`
+ Adds a medical record to the patient with the `PATIENTID` of **3**.
+ The patient visited the clinic on **September 18th, 2023** at **6:00PM** with a **Fever** and was prescribed **Paracetamol**.
+
+Screenshots:
+
+
+Command
+
+
+Output
+
+
+[Back to Table Of Contents](#table-of-contents)
+
+---
+
+
+
+#### Viewing patient medical records: `view`
+
+Displays the medical records of a specific patient.
+
+Format: `view PATIENTID`
+
+- The medical records of the patient with the corresponding `PATIENTID` will be displayed on screen in the **Medical Record List**.
+
+- The corresponding patient's **Patient Card** will be displayed in the **Patient Being Viewed** section.
Examples:
-* `edit 1 p/91234567 e/johndoe@example.com` Edits the phone number and email address of the 1st person to be `91234567` and `johndoe@example.com` respectively.
-* `edit 2 n/Betsy Crower t/` Edits the name of the 2nd person to be `Betsy Crower` and clears all existing tags.
-### Locating persons by name: `find`
+- `view 2` displays the medical records of the patient with the `PATIENTID` of **2**. The medical records will be displayed in the **Medical Record List** and patient information will be in the **Patient Being Viewed** section.
+
+
+
+**Note**: The `PATIENTID` in the **Patient Being Viewed** section will display **1**. If you wish to perform commands on the patient, use the `PATIENTID` displayed in the **Patient List**.
+
+
+
+Screenshots:
+
+
+Command
+
+
+Output
+
+
+[Back to Table Of Contents](#table-of-contents)
+
+---
+
+
+
+#### Editing a medical record: `editrecord`
+
+Edits the details of an existing medical record.
+
+Format: `editrecord PATIENTID/RECORDID PREFIX/NEWVALUE...`
+
+- Edits the details of the medical record with the corresponding `RECORDID` of the patient with the corresponding `PATIENTID`.
+
+- Acceptable fields : `DATETIME`, `CONDITION`, `MEDICATION`.
+
+- Existing values in the respective fields will be updated to the `NEWVALUE`.
+
+- Multiple entries of `CONDITION` and `MEDICATION` are allowed i.e `editrecord 1/1 c/Flu c/Fever m/Panadol m/Ibuprofen`.
-Finds persons whose names contain any of the given keywords.
+
-Format: `find KEYWORD [MORE_KEYWORDS]`
+**Note**: When editing `CONDITION` and `MEDICATION`, the existing conditions and medications of the record will be removed i.e adding of conditions and medications is not cumulative.
-* The search is case-insensitive. e.g `hans` will match `Hans`
-* The order of the keywords does not matter. e.g. `Hans Bo` will match `Bo Hans`
-* Only the name is searched.
-* Only full words will be matched e.g. `Han` will not match `Hans`
-* Persons matching at least one keyword will be returned (i.e. `OR` search).
- e.g. `Hans Bo` will return `Hans Gruber`, `Bo Yang`
+
+
+
+
+**Tip**: Spotted an error in the medical record? Fret not, simply use `editrecord` to quickly make changes!
+
+
Examples:
-* `find John` returns `john` and `John Doe`
-* `find alex david` returns `Alex Yeoh`, `David Li`
- 
-### Deleting a person : `delete`
+- `editrecord 1/1 d/25-10-2023 1200` Edits the `DATETIME` of the medical record with the `RECORDID` of **1** of the patient with the `PATIENTID` of **1** to **25-10-2023 1200**.
+
+- `editrecord 1/1 c/Headache c/Flu` Edits the `CONDITION` of the medical record with the `RECORDID` of **1** of the patient with the `PATIENTID` of **1** to **Headache, Flu**.
+
+- `editrecord 1/1 c/Headache m/Paracetamol` Edits the `CONDITION` and `MEDICATION` of the medical record with the `RECORDID` of **1** of the patient with the `PATIENTID` of **1** to **Headache** and **Paracetamol**, respectively.
+
+[Back to Table Of Contents](#table-of-contents)
+
+---
+
+
+
+#### Deleting a medical record: `deleterecord`
+
+Deletes an existing medical record from the system.
+
+Format: `deleterecord PATIENTID/RECORDID`
+
+- Deletes the medical record with the corresponding `RECORDID` from the patient with the corresponding `PATIENTID`.
+
+Example:
+
+- `deleterecord 2/1` Deletes the medical record with the `RECORDID` of **2** from the patient with the `PATIENTID` of **1**.
+
+[Back to Table Of Contents](#table-of-contents)
+
+---
+
+#### Searching medical records by keywords: `searchrecord`
+
+Searches for medical records of the Patient Being Viewed with details containing the corresponding `KEYWORD`.
-Deletes the specified person from the address book.
+Format: `searchrecord KEYWORD [MOREKEYWORDS]...`
-Format: `delete INDEX`
+- The search is not case-sensitive. e.g **Penicillin** will match **penicillin**.
-* Deletes the person at the specified `INDEX`.
-* The index refers to the index number shown in the displayed person list.
-* The index **must be a positive integer** 1, 2, 3, …
+- Medical records matching at least one **KEYWORD** will be returned. e.g. **Mild Fever** will return **Mild Flu**, **High Fever**
+
+- Medical records with detail that are not exactly the same as the **KEYWORD** searched will not appear. e.g. **Head** will not return **Headache**.
+
+
+
+ **Important**: The **Patient Being Viewed** section should not be empty. Otherwise `searchrecord` command will not return any medical records.
+
+
Examples:
-* `list` followed by `delete 2` deletes the 2nd person in the address book.
-* `find Betsy` followed by `delete 1` deletes the 1st person in the results of the `find` command.
-### Clearing all entries : `clear`
+- `searchrecord Penicillin` Will display medical records of the Patient Being Viewed with **Penicillin** in their details.
+
+- `searchrecord Fever Cough` Will display medical records of the Patient Being Viewed with **Fever** or **Cough** in their details.
+
+- `searchrecord 19-10-2023` Will display medical records of the Patient Being Viewed with **19-10-2023** in their details.
+
+[Back to Table Of Contents](#table-of-contents)
+
+---
+
+
+
+#### Attaching files to a patient's medical record
+
+Attach local files to a patient's medical record by clicking on the **Attach** button located within each **Record Card**.
+
+
+
+**Tip**: A patient's report just comes out? The **Attach File** feature can help you keep documents in an organised manner!
+
+
+
+
+
+**Caution**:
+
+- Refrain from deleting/relocating the local file. If necessary, re-attach the new file after relocation/deletion.
+- Ensure that you have selected a default launcher for the selected file type.
+
+
+
+Screenshots:
+
+
+Command
+
+
+Selecting File
+
+
+Output
+
+
+[Back to Table Of Contents](#table-of-contents)
+
+---
+
+
+
+### Appointment Features
+
+#### Adding an appointment: `addappointment`
+
+Adds a new appointment to the system.
+
+Format: `addappointment PATIENTID n/NAME d/DATETIME`
+
+- Adds an appointment to the patient with the corresponding `PATIENTID`.
+
+Example:
+
+- `addappointment 2 n/Eye Examination d/10-10-2023 1800` Adds an `Eye Examination` to the patient with the `PATIENTID` of **2**. The appointment is schedueld for the **10th of October 2023** at **6:00PM**.
+
+Screenshots:
+
+
+Command
+
+
+Output
+
+
+[Back to Table Of Contents](#table-of-contents)
+
+---
+
+
+
+#### Deleting an appointment: `deleteappointment`
+
+Deletes an appointment from the system.
+
+Format: `deleteappointment APPOINTMENTID`
-Clears all entries from the address book.
+- Deletes the appointment with the corresponding `APPOINTMENTID`.
-Format: `clear`
+
-### Exiting the program : `exit`
+**Tip**: A patient cannot attend an appointment? Use `deleteappointment` to remove it from your schedule!
+
+
+
+Example:
+
+- `deleteappointment 1` Deletes the appointment with the `APPOINTMENTID` of **1**.
+
+[Back to Table Of Contents](#table-of-contents)
+
+---
+
+#### Viewing appointments: `viewappointment`
+
+Opens/focuses the **Appointment Window**.
+
+Format: `viewappointment`
+
+
+
+**Tip**: Want to know about upcoming appointments? Simply type in `viewappointment`!
+
+
+
+[Back to Table Of Contents](#table-of-contents)
+
+---
+
+
+
+### General Features
+
+#### Viewing help : `help`
+
+Opens/focuses the **Help Window** displaying all commands.
+
+Format: `help`
+
+
+
+**Tip**: If you get stuck while using MedBook, use the `help` command to get a quick overview of all commands!
+
+
+
+[Back to Table Of Contents](#table-of-contents)
+
+---
+
+#### Exiting the program: `exit`
Exits the program.
Format: `exit`
-### Saving the data
+[Back to Table Of Contents](#table-of-contents)
+
+---
+
+#### Saving the data
-AddressBook data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually.
+MedBook data is saved in the `data` folder automatically after any command that changes the data. This ensures the safety of your data. When MedBook is restarted, the data is loaded back into the application automatically.
-### Editing the data file
+[Back to Table Of Contents](#table-of-contents)
-AddressBook data are saved automatically as a JSON file `[JAR file location]/data/addressbook.json`. Advanced users are welcome to update data directly by editing that data file.
+---
+
+#### Editing the data file
+
+MedBook data is saved automatically as a JSON file located in `[jar file location]/data/medbook.json`. The file can be edited directly to make changes to MedBook data.
+
+
+
+**Caution**: Only advanced users are encouraged to make changes to the data file as wrong data formatting may result in the application not working as intended.
-
:exclamation: **Caution:**
-If your changes to the data file makes its format invalid, AddressBook will discard all data and start with an empty data file at the next run. Hence, it is recommended to take a backup of the file before editing it.
-
+
-### Archiving data files `[coming in v2.0]`
+[Back to Table Of Contents](#table-of-contents)
-_Details coming soon ..._
+---
---------------------------------------------------------------------------------------------------------------------
+
## FAQ
+**Q**: How do I check my java version?
+**A**: Open a **Terminal** and enter `java -version`. If you do not have Java installed, you can download it [here](https://www.oracle.com/java/technologies/downloads/#java11).
+
**Q**: How do I transfer my data to another Computer?
-**A**: Install the app in the other computer and overwrite the empty data file it creates with the file that contains the data of your previous AddressBook home folder.
+**A**: Install the app in the other computer and copy the `data` folder over to the folder of the installed jar file in the other computer.
---------------------------------------------------------------------------------------------------------------------
+[Back to Table Of Contents](#table-of-contents)
+
+---
## Known issues
1. **When using multiple screens**, if you move the application to a secondary screen, and later switch to using only the primary screen, the GUI will open off-screen. The remedy is to delete the `preferences.json` file created by the application before running the application again.
---------------------------------------------------------------------------------------------------------------------
+[Back to Table Of Contents](#table-of-contents)
-## Command summary
+---
-Action | Format, Examples
---------|------------------
-**Add** | `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…` e.g., `add n/James Ho p/22224444 e/jamesho@example.com a/123, Clementi Rd, 1234665 t/friend t/colleague`
-**Clear** | `clear`
-**Delete** | `delete INDEX` e.g., `delete 3`
-**Edit** | `edit INDEX [n/NAME] [p/PHONE_NUMBER] [e/EMAIL] [a/ADDRESS] [t/TAG]…` e.g.,`edit 2 n/James Lee e/jameslee@example.com`
-**Find** | `find KEYWORD [MORE_KEYWORDS]` e.g., `find James Jake`
-**List** | `list`
-**Help** | `help`
+## Command Summary
+
+| Action | Format, Examples |
+| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| **Help** | `help` |
+| **Add Patient** | `addpatient n/NAME i/NRIC e/EMAIL p/PHONE g/GENDER a/AGE bt/BLOODTYPE [al/ALLERGY]...` e.g., `addpatient n/John Doe i/T1234567A e/johndoe@gmail.com p/12345678 g/M a/26 bt/AB+ al/Penicillin` |
+| **Add Medical Record** | `addrecord PATIENTID d/DATETIME c/CONDITION... m/MEDICATION...` e.g., `addrecord 2 d/10-10-2020 1900 c/Fever m/Painkiller` |
+| **Add Appointment** | `addappointment PATIENTID n/NAME d/DATETIME` e.g., `addappointment 2 n/Eye Exam d/10-10-2020 1900` |
+| **List** | `list` |
+| **View Medical Records** | `view PATIENTID` e.g., `view 2` |
+| **View Appointments** | `viewappointment` |
+| **Edit Patient** | `editpatient PATIENTID PREFIX/NEWVALUE...` e.g.,`editpatient 1 e/johndoe_updated@gmail.com` |
+| **Edit Medical Record** | `editrecord PATIENTID/RECORDID PREFIX/NEWVALUE...` e.g.,`editrecord 1/1 d/25-10-2023 1200` |
+| **Search Patients** | `search KEYWORD [MOREKEYWORDS]...` e.g., `search James` |
+| **Search Medical Records** | `searchrecord KEYWORD [MOREKEYWORDS]...` e.g., `searchrecord Headache` |
+| **Delete Patient** | `delete PATIENTID` e.g., `delete 3` |
+| **Delete Medical Record** | `deleterecord PATIENTID/RECORDID` e.g., `deleterecord 2/1` |
+| **Delete Appointment** | `deleteappointment APPOINTMENTID` e.g., `deleteappointment 1` |
+| **Pin Patient** | `pin PATIENTID` e.g., `pin 2` |
+| **Unpin Patient** | `unpin PINNEDID` e.g. `unpin 2` |
+| **Exit** | `exit` |
+
+[Back to Table Of Contents](#table-of-contents)
diff --git a/docs/_config.yml b/docs/_config.yml
deleted file mode 100644
index 6bd245d8f4e..00000000000
--- a/docs/_config.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-title: "AB-3"
-theme: minima
-
-header_pages:
- - UserGuide.md
- - DeveloperGuide.md
- - AboutUs.md
-
-markdown: kramdown
-
-repository: "se-edu/addressbook-level3"
-github_icon: "images/github-icon.png"
-
-plugins:
- - jemoji
diff --git a/docs/_data/projects.yml b/docs/_data/projects.yml
deleted file mode 100644
index 8f3e50cb601..00000000000
--- a/docs/_data/projects.yml
+++ /dev/null
@@ -1,23 +0,0 @@
-- name: "AB-1"
- url: https://se-edu.github.io/addressbook-level1
-
-- name: "AB-2"
- url: https://se-edu.github.io/addressbook-level2
-
-- name: "AB-3"
- url: https://se-edu.github.io/addressbook-level3
-
-- name: "AB-4"
- url: https://se-edu.github.io/addressbook-level4
-
-- name: "Duke"
- url: https://se-edu.github.io/duke
-
-- name: "Collate"
- url: https://se-edu.github.io/collate
-
-- name: "Book"
- url: https://se-edu.github.io/se-book
-
-- name: "Resources"
- url: https://se-edu.github.io/resources
diff --git a/docs/_includes/custom-head.html b/docs/_includes/custom-head.html
deleted file mode 100644
index 8559a67ffad..00000000000
--- a/docs/_includes/custom-head.html
+++ /dev/null
@@ -1,6 +0,0 @@
-{% comment %}
- Placeholder to allow defining custom head, in principle, you can add anything here, e.g. favicons:
-
- 1. Head over to https://realfavicongenerator.net/ to add your own favicons.
- 2. Customize default _includes/custom-head.html in your source directory and insert the given code snippet.
-{% endcomment %}
diff --git a/docs/_includes/head.html b/docs/_includes/head.html
deleted file mode 100644
index 83ac5326933..00000000000
--- a/docs/_includes/head.html
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
- {%- include custom-head.html -%}
-
- {{page.title}}
-
-
diff --git a/docs/_includes/header.html b/docs/_includes/header.html
deleted file mode 100644
index 33badcd4f99..00000000000
--- a/docs/_includes/header.html
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
-
+
-:information_source: Don’t forget to update `AddressBookParser` to use our new `RemarkCommandParser`!
+Don’t forget to update `AddressBookParser` to use our new `RemarkCommandParser`!
-
+
If you are stuck, check out the sample
[here](https://github.com/se-edu/addressbook-level3/commit/dc6d5139d08f6403da0ec624ea32bd79a2ae0cbf#diff-8bf239e8e9529369b577701303ddd96af93178b4ed6735f91c2d8488b20c6b4a).
@@ -244,7 +247,7 @@ Simply add the following to [`seedu.address.ui.PersonCard`](https://github.com/s
**`PersonCard.java`:**
-``` java
+```java
@FXML
private Label remark;
```
@@ -276,11 +279,11 @@ We change the constructor of `Person` to take a `Remark`. We will also need to d
Unfortunately, a change to `Person` will cause other commands to break, you will have to modify these commands to use the updated `Person`!
-
+
-:bulb: Use the `Find Usages` feature in IntelliJ IDEA on the `Person` class to find these commands.
+Use the `Find Usages` feature in IntelliJ IDEA on the `Person` class to find these commands.
-
+
Refer to [this commit](https://github.com/se-edu/addressbook-level3/commit/ce998c37e65b92d35c91d28c7822cd139c2c0a5c) and check that you have got everything in order!
@@ -291,11 +294,11 @@ AddressBook stores data by serializing `JsonAdaptedPerson` into `json` with the
While the changes to code may be minimal, the test data will have to be updated as well.
-
+
-:exclamation: You must delete AddressBook’s storage file located at `/data/addressbook.json` before running it! Not doing so will cause AddressBook to default to an empty address book!
+You must delete AddressBook’s storage file located at `/data/addressbook.json` before running it! Not doing so will cause AddressBook to default to an empty address book!
-
+
Check out [this commit](https://github.com/se-edu/addressbook-level3/commit/556cbd0e03ff224d7a68afba171ad2eb0ce56bbf)
to see what the changes entail.
@@ -308,7 +311,7 @@ Just add [this one line of code!](https://github.com/se-edu/addressbook-level3/c
**`PersonCard.java`:**
-``` java
+```java
public PersonCard(Person person, int displayedIndex) {
//...
remark.setText(person.getRemark().value);
@@ -328,7 +331,7 @@ save it with `Model#setPerson()`.
**`RemarkCommand.java`:**
-``` java
+```java
//...
public static final String MESSAGE_ADD_REMARK_SUCCESS = "Added remark to Person: %1$s";
public static final String MESSAGE_DELETE_REMARK_SUCCESS = "Removed remark from Person: %1$s";
diff --git a/docs/tutorials/RemovingFields.md b/docs/tutorials/RemovingFields.md
index f29169bc924..c73bd379e5e 100644
--- a/docs/tutorials/RemovingFields.md
+++ b/docs/tutorials/RemovingFields.md
@@ -1,8 +1,11 @@
---
-layout: page
-title: "Tutorial: Removing Fields"
+ layout: default.md
+ title: "Tutorial: Removing Fields"
+ pageNav: 3
---
+# Tutorial: Removing Fields
+
> Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.
>
> — Antoine de Saint-Exupery
@@ -10,17 +13,17 @@ title: "Tutorial: Removing Fields"
When working on an existing code base, you will most likely find that some features that are no longer necessary.
This tutorial aims to give you some practice on such a code 'removal' activity by removing the `address` field from `Person` class.
-
+
**If you have done the [Add `remark` command tutorial](AddRemark.html) already**, you should know where the code had to be updated to add the field `remark`. From that experience, you can deduce where the code needs to be changed to _remove_ that field too. The removing of the `address` field can be done similarly.
However, if you have no such prior knowledge, removing a field can take a quite a bit of detective work. This tutorial takes you through that process. **At least have a read even if you don't actually do the steps yourself.**
-
+
-* Table of Contents
-{:toc}
+
+
## Safely deleting `Address`
@@ -50,10 +53,10 @@ Let’s try removing references to `Address` in `EditPersonDescriptor`.
1. Remove the usages of `address` and select `Do refactor` when you are done.
-
+
- :bulb: **Tip:** Removing usages may result in errors. Exercise discretion and fix them. For example, removing the `address` field from the `Person` class will require you to modify its constructor.
-
+ **Tip:** Removing usages may result in errors. Exercise discretion and fix them. For example, removing the `address` field from the `Person` class will require you to modify its constructor.
+
1. Repeat the steps for the remaining usages of `Address`
@@ -71,7 +74,7 @@ A quick look at the `PersonCard` class and its `fxml` file quickly reveals why i
**`PersonCard.java`**
-``` java
+```java
...
@FXML
private Label address;
diff --git a/docs/tutorials/TracingCode.md b/docs/tutorials/TracingCode.md
index 4fb62a83ef6..2b1b0f2d6b7 100644
--- a/docs/tutorials/TracingCode.md
+++ b/docs/tutorials/TracingCode.md
@@ -1,26 +1,30 @@
---
-layout: page
-title: "Tutorial: Tracing code"
+ layout: default.md
+ title: "Tutorial: Tracing code"
+ pageNav: 3
---
+# Tutorial: Tracing code
+
+
> Indeed, the ratio of time spent reading versus writing is well over 10 to 1. We are constantly reading old code as part of the effort to write new code. …\[Therefore,\] making it easy to read makes it easier to write.
>
> — Robert C. Martin Clean Code: A Handbook of Agile Software Craftsmanship
When trying to understand an unfamiliar code base, one common strategy used is to trace some representative execution path through the code base. One easy way to trace an execution path is to use a debugger to step through the code. In this tutorial, you will be using the IntelliJ IDEA’s debugger to trace the execution path of a specific user command.
-* Table of Contents
-{:toc}
+
+
## Before we start
Before we jump into the code, it is useful to get an idea of the overall structure and the high-level behavior of the application. This is provided in the 'Architecture' section of the developer guide. In particular, the architecture diagram (reproduced below), tells us that the App consists of several components.
-
+
It also has a sequence diagram (reproduced below) that tells us how a command propagates through the App.
-
+
Note how the diagram shows only the execution flows _between_ the main components. That is, it does not show details of the execution path *inside* each component. By hiding those details, the diagram aims to inform the reader about the overall execution path of a command without overwhelming the reader with too much details. In this tutorial, you aim to find those omitted details so that you get a more in-depth understanding of how the code works.
@@ -37,16 +41,16 @@ As you know, the first step of debugging is to put in a breakpoint where you wan
In our case, we would want to begin the tracing at the very point where the App start processing user input (i.e., somewhere in the UI component), and then trace through how the execution proceeds through the UI component. However, the execution path through a GUI is often somewhat obscure due to various *event-driven mechanisms* used by GUI frameworks, which happens to be the case here too. Therefore, let us put the breakpoint where the `UI` transfers control to the `Logic` component.
-
+
According to the sequence diagram you saw earlier (and repeated above for reference), the `UI` component yields control to the `Logic` component through a method named `execute`. Searching through the code base for an `execute()` method that belongs to the `Logic` component yields a promising candidate in `seedu.address.logic.Logic`.
-
+
-:bulb: **Intellij Tip:** The ['**Search Everywhere**' feature](https://www.jetbrains.com/help/idea/searching-everywhere.html) can be used here. In particular, the '**Find Symbol**' ('Symbol' here refers to methods, variables, classes etc.) variant of that feature is quite useful here as we are looking for a _method_ named `execute`, not simply the text `execute`.
-
+**Intellij Tip:** The ['**Search Everywhere**' feature](https://www.jetbrains.com/help/idea/searching-everywhere.html) can be used here. In particular, the '**Find Symbol**' ('Symbol' here refers to methods, variables, classes etc.) variant of that feature is quite useful here as we are looking for a _method_ named `execute`, not simply the text `execute`.
+
A quick look at the `seedu.address.logic.Logic` (an extract given below) confirms that this indeed might be what we’re looking for.
@@ -67,14 +71,14 @@ public interface Logic {
But apparently, this is an interface, not a concrete implementation.
That should be fine because the [Architecture section of the Developer Guide](../DeveloperGuide.html#architecture) tells us that components interact through interfaces. Here's the relevant diagram:
-
+
Next, let's find out which statement(s) in the `UI` code is calling this method, thus transferring control from the `UI` to the `Logic`.
-
+
-:bulb: **Intellij Tip:** The ['**Find Usages**' feature](https://www.jetbrains.com/help/idea/find-highlight-usages.html#find-usages) can find from which parts of the code a class/method/variable is being used.
-
+**Intellij Tip:** The ['**Find Usages**' feature](https://www.jetbrains.com/help/idea/find-highlight-usages.html#find-usages) can find from which parts of the code a class/method/variable is being used.
+

@@ -87,10 +91,10 @@ Now let’s set the breakpoint. First, double-click the item to reach the corres
Recall from the User Guide that the `edit` command has the format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…` For this tutorial we will be issuing the command `edit 1 n/Alice Yeoh`.
-
+
-:bulb: **Tip:** Over the course of the debugging session, you will encounter every major component in the application. Try to keep track of what happens inside the component and where the execution transfers to another component.
-
+**Tip:** Over the course of the debugging session, you will encounter every major component in the application. Try to keep track of what happens inside the component and where the execution transfers to another component.
+
1. To start the debugging session, simply `Run` \> `Debug Main`
@@ -110,7 +114,7 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [
**LogicManager\#execute().**
- ``` java
+ ```java
@Override
public CommandResult execute(String commandText)
throws CommandException, ParseException {
@@ -142,7 +146,7 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [

1. _Step into_ the line where user input in parsed from a String to a Command, which should bring you to the `AddressBookParser#parseCommand()` method (partial code given below):
- ``` java
+ ```java
public Command parseCommand(String userInput) throws ParseException {
...
final String commandWord = matcher.group("commandWord");
@@ -157,7 +161,7 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [
1. Stepping through the `switch` block, we end up at a call to `EditCommandParser().parse()` as expected (because the command we typed is an edit command).
- ``` java
+ ```java
...
case EditCommand.COMMAND_WORD:
return new EditCommandParser().parse(arguments);
@@ -166,8 +170,10 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [
1. Let’s see what `EditCommandParser#parse()` does by stepping into it. You might have to click the 'step into' button multiple times here because there are two method calls in that statement: `EditCommandParser()` and `parse()`.
-
:bulb: **Intellij Tip:** Sometimes, you might end up stepping into functions that are not of interest. Simply use the `step out` button to get out of them!
-
+
+
+ **Intellij Tip:** Sometimes, you might end up stepping into functions that are not of interest. Simply use the `step out` button to get out of them!
+
1. Stepping through the method shows that it calls `ArgumentTokenizer#tokenize()` and `ParserUtil#parseIndex()` to obtain the arguments and index required.
@@ -175,17 +181,17 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [

1. As you just traced through some code involved in parsing a command, you can take a look at this class diagram to see where the various parsing-related classes you encountered fit into the design of the `Logic` component.
-
+
1. Let’s continue stepping through until we return to `LogicManager#execute()`.
The sequence diagram below shows the details of the execution path through the Logic component. Does the execution path you traced in the code so far match the diagram?
- 
+
1. Now, step over until you read the statement that calls the `execute()` method of the `EditCommand` object received, and step into that `execute()` method (partial code given below):
**`EditCommand#execute()`:**
- ``` java
+ ```java
@Override
public CommandResult execute(Model model) throws CommandException {
...
@@ -205,25 +211,28 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [
* it uses the `updateFilteredPersonList` method to ask the `Model` to populate the 'filtered list' with _all_ persons.
FYI, The 'filtered list' is the list of persons resulting from the most recent operation that will be shown to the user immediately after. For the `edit` command, we populate it with all the persons so that the user can see the edited person along with all other persons. If this was a `find` command, we would be setting that list to contain the search results instead.
To provide some context, given below is the class diagram of the `Model` component. See if you can figure out where the 'filtered list' of persons is being tracked.
-
+
* :bulb: This may be a good time to read through the [`Model` component section of the DG](../DeveloperGuide.html#model-component)
1. As you step through the rest of the statements in the `EditCommand#execute()` method, you'll see that it creates a `CommandResult` object (containing information about the result of the execution) and returns it.
Advancing the debugger by one more step should take you back to the middle of the `LogicManager#execute()` method.
1. Given that you have already seen quite a few classes in the `Logic` component in action, see if you can identify in this partial class diagram some of the classes you've encountered so far, and see how they fit into the class structure of the `Logic` component:
-
+
+
* :bulb: This may be a good time to read through the [`Logic` component section of the DG](../DeveloperGuide.html#logic-component)
1. Similar to before, you can step over/into statements in the `LogicManager#execute()` method to examine how the control is transferred to the `Storage` component and what happens inside that component.
-
:bulb: **Intellij Tip:** When trying to step into a statement such as `storage.saveAddressBook(model.getAddressBook())` which contains multiple method calls, Intellij will let you choose (by clicking) which one you want to step into.
-
+
+
+ **Intellij Tip:** When trying to step into a statement such as `storage.saveAddressBook(model.getAddressBook())` which contains multiple method calls, Intellij will let you choose (by clicking) which one you want to step into.
+
-1. As you step through the code inside the `Storage` component, you will eventually arrive at the `JsonAddressBook#saveAddressBook()` method which calls the `JsonSerializableAddressBook` constructor, to create an object that can be _serialized_ (i.e., stored in storage medium) in JSON format. That constructor is given below (with added line breaks for easier readability):
+1. As you step through the code inside the `Storage` component, you will eventually arrive at the `JsonAddressBook#saveAddressBook()` method which calls the `JsonSerializableAddressBook` constructor, to create an object that can be _serialized_ (i.e., stored in storage medium) in JSON format. That constructor is given below (with added line breaks for easier readability):
**`JsonSerializableAddressBook` constructor:**
- ``` java
+ ```java
/**
* Converts a given {@code ReadOnlyAddressBook} into this class for Jackson use.
*
@@ -243,7 +252,8 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [
This is because regular Java objects need to go through an _adaptation_ for them to be suitable to be saved in JSON format.
1. While you are stepping through the classes in the `Storage` component, here is the component's class diagram to help you understand how those classes fit into the structure of the component.
-
+
+
* :bulb: This may be a good time to read through the [`Storage` component section of the DG](../DeveloperGuide.html#storage-component)
1. We can continue to step through until you reach the end of the `LogicManager#execute()` method and return to the `MainWindow#executeCommand()` method (the place where we put the original breakpoint).
@@ -251,7 +261,7 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [
1. Stepping into `resultDisplay.setFeedbackToUser(commandResult.getFeedbackToUser());`, we end up in:
**`ResultDisplay#setFeedbackToUser()`**
- ``` java
+ ```java
public void setFeedbackToUser(String feedbackToUser) {
requireNonNull(feedbackToUser);
resultDisplay.setText(feedbackToUser);
diff --git a/src/main/java/seedu/address/MainApp.java b/src/main/java/seedu/address/MainApp.java
index 3d6bd06d5af..05452ecae7e 100644
--- a/src/main/java/seedu/address/MainApp.java
+++ b/src/main/java/seedu/address/MainApp.java
@@ -48,7 +48,7 @@ public class MainApp extends Application {
@Override
public void init() throws Exception {
- logger.info("=============================[ Initializing AddressBook ]===========================");
+ logger.info("=============================[ Initializing MedBook ]===========================");
super.init();
AppParameters appParameters = AppParameters.parse(getParameters());
@@ -68,9 +68,12 @@ public void init() throws Exception {
}
/**
- * Returns a {@code ModelManager} with the data from {@code storage}'s address book and {@code userPrefs}.
- * The data from the sample address book will be used instead if {@code storage}'s address book is not found,
- * or an empty address book will be used instead if errors occur when reading {@code storage}'s address book.
+ * Returns a {@code ModelManager} with the data from {@code storage}'s address
+ * book and {@code userPrefs}.
+ * The data from the sample address book will be used instead if
+ * {@code storage}'s address book is not found,
+ * or an empty address book will be used instead if errors occur when reading
+ * {@code storage}'s address book.
*/
private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) {
logger.info("Using data file : " + storage.getAddressBookFilePath());
@@ -127,7 +130,8 @@ protected Config initConfig(Path configFilePath) {
initializedConfig = new Config();
}
- //Update config file in case it was missing to begin with or there are new/unused fields
+ // Update config file in case it was missing to begin with or there are
+ // new/unused fields
try {
ConfigUtil.saveConfig(initializedConfig, configFilePathUsed);
} catch (IOException e) {
@@ -137,7 +141,8 @@ protected Config initConfig(Path configFilePath) {
}
/**
- * Returns a {@code UserPrefs} using the file at {@code storage}'s user prefs file path,
+ * Returns a {@code UserPrefs} using the file at {@code storage}'s user prefs
+ * file path,
* or a new {@code UserPrefs} with default configuration if errors occur when
* reading from the file.
*/
@@ -158,7 +163,8 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) {
initializedPrefs = new UserPrefs();
}
- //Update prefs file in case it was missing to begin with or there are new/unused fields
+ // Update prefs file in case it was missing to begin with or there are
+ // new/unused fields
try {
storage.saveUserPrefs(initializedPrefs);
} catch (IOException e) {
@@ -170,7 +176,7 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) {
@Override
public void start(Stage primaryStage) {
- logger.info("Starting AddressBook " + MainApp.VERSION);
+ logger.info("Starting MedBook " + MainApp.VERSION);
ui.start(primaryStage);
}
diff --git a/src/main/java/seedu/address/commons/core/GuiSettings.java b/src/main/java/seedu/address/commons/core/GuiSettings.java
index a97a86ee8d7..5dced64a674 100644
--- a/src/main/java/seedu/address/commons/core/GuiSettings.java
+++ b/src/main/java/seedu/address/commons/core/GuiSettings.java
@@ -12,8 +12,8 @@
*/
public class GuiSettings implements Serializable {
- private static final double DEFAULT_HEIGHT = 600;
- private static final double DEFAULT_WIDTH = 740;
+ private static final double DEFAULT_HEIGHT = 720;
+ private static final double DEFAULT_WIDTH = 1280;
private final double windowWidth;
private final double windowHeight;
diff --git a/src/main/java/seedu/address/commons/core/LogsCenter.java b/src/main/java/seedu/address/commons/core/LogsCenter.java
index 8cf8e15a0f0..86305e85430 100644
--- a/src/main/java/seedu/address/commons/core/LogsCenter.java
+++ b/src/main/java/seedu/address/commons/core/LogsCenter.java
@@ -20,7 +20,7 @@
public class LogsCenter {
private static final int MAX_FILE_COUNT = 5;
private static final int MAX_FILE_SIZE_IN_BYTES = (int) (Math.pow(2, 20) * 5); // 5MB
- private static final String LOG_FILE = "addressbook.log";
+ private static final String LOG_FILE = "medbook.log";
private static final Logger logger; // logger for this class
private static Logger baseLogger; // to be used as the parent of all other loggers created by this class.
private static Level currentLogLevel = Level.INFO;
diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/address/logic/Logic.java
index 92cd8fa605a..0424928ab9c 100644
--- a/src/main/java/seedu/address/logic/Logic.java
+++ b/src/main/java/seedu/address/logic/Logic.java
@@ -8,7 +8,9 @@
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.ReadOnlyAddressBook;
+import seedu.address.model.appointment.Appointment;
import seedu.address.model.person.Person;
+import seedu.address.model.record.Record;
/**
* API of the Logic component
@@ -16,10 +18,11 @@
public interface Logic {
/**
* Executes the command and returns the result.
+ *
* @param commandText The command as entered by the user.
* @return the result of the command execution.
* @throws CommandException If an error occurs during command execution.
- * @throws ParseException If an error occurs during parsing.
+ * @throws ParseException If an error occurs during parsing.
*/
CommandResult execute(String commandText) throws CommandException, ParseException;
@@ -33,6 +36,15 @@ public interface Logic {
/** Returns an unmodifiable view of the filtered list of persons */
ObservableList getFilteredPersonList();
+ /** Returns an unmodifiable view of the pinned list of persons */
+ ObservableList getPinnedPersonList();
+
+ /** Returns an unmodifiable view of the filtered list of appointments */
+ ObservableList getFilteredAppointmentList();
+
+ /** Returns an unmodifiable view of the filtered list of records */
+ ObservableList getFilteredRecordList();
+
/**
* Returns the user prefs' address book file path.
*/
@@ -47,4 +59,9 @@ public interface Logic {
* Set the user prefs' GUI settings.
*/
void setGuiSettings(GuiSettings guiSettings);
+
+ ObservableList getRecordList();
+
+ ObservableList getPersonBeingViewed();
+
}
diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/address/logic/LogicManager.java
index 5aa3b91c7d0..c7489eee3b8 100644
--- a/src/main/java/seedu/address/logic/LogicManager.java
+++ b/src/main/java/seedu/address/logic/LogicManager.java
@@ -15,7 +15,9 @@
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.Model;
import seedu.address.model.ReadOnlyAddressBook;
+import seedu.address.model.appointment.Appointment;
import seedu.address.model.person.Person;
+import seedu.address.model.record.Record;
import seedu.address.storage.Storage;
/**
@@ -24,8 +26,8 @@
public class LogicManager implements Logic {
public static final String FILE_OPS_ERROR_FORMAT = "Could not save data due to the following error: %s";
- public static final String FILE_OPS_PERMISSION_ERROR_FORMAT =
- "Could not save data to file %s due to insufficient permissions to write to the file or the folder.";
+ public static final String FILE_OPS_PERMISSION_ERROR_FORMAT = "Could not save data to file %s due to "
+ + "insufficient permissions to write to the file or the folder.";
private final Logger logger = LogsCenter.getLogger(LogicManager.class);
@@ -34,7 +36,8 @@ public class LogicManager implements Logic {
private final AddressBookParser addressBookParser;
/**
- * Constructs a {@code LogicManager} with the given {@code Model} and {@code Storage}.
+ * Constructs a {@code LogicManager} with the given {@code Model} and
+ * {@code Storage}.
*/
public LogicManager(Model model, Storage storage) {
this.model = model;
@@ -71,6 +74,30 @@ public ObservableList getFilteredPersonList() {
return model.getFilteredPersonList();
}
+ @Override
+ public ObservableList getPinnedPersonList() {
+ return model.getPinnedPersonList();
+ }
+
+ @Override
+ public ObservableList getFilteredAppointmentList() {
+ return model.getAppointmentList();
+ }
+
+ @Override
+ public ObservableList getRecordList() {
+ return model.getRecordList();
+ }
+
+ public ObservableList getFilteredRecordList() {
+ return model.getFilteredRecordList();
+ }
+
+ @Override
+ public ObservableList getPersonBeingViewed() {
+ return model.getPersonBeingViewed();
+ }
+
@Override
public Path getAddressBookFilePath() {
return model.getAddressBookFilePath();
diff --git a/src/main/java/seedu/address/logic/Messages.java b/src/main/java/seedu/address/logic/Messages.java
index ecd32c31b53..07dfcc19d41 100644
--- a/src/main/java/seedu/address/logic/Messages.java
+++ b/src/main/java/seedu/address/logic/Messages.java
@@ -5,7 +5,9 @@
import java.util.stream.Stream;
import seedu.address.logic.parser.Prefix;
+import seedu.address.model.appointment.Appointment;
import seedu.address.model.person.Person;
+import seedu.address.model.record.Record;
/**
* Container for user visible messages.
@@ -15,9 +17,13 @@ public class Messages {
public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command";
public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s";
public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "The person index provided is invalid";
+ public static final String MESSAGE_INVALID_APPOINTMENT_DISPLAYED_INDEX =
+ "The appointment index provided is invalid";
+ public static final String MESSAGE_INVALID_RECORD_DISPLAYED_INDEX = "The record index provided is invalid";
public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!";
+ public static final String MESSAGE_RECORDS_LISTED_OVERVIEW = "%1$d records listed!";
public static final String MESSAGE_DUPLICATE_FIELDS =
- "Multiple values specified for the following single-valued field(s): ";
+ "Multiple values specified for the following single-valued field(s): ";
/**
* Returns an error message indicating the duplicate prefixes.
@@ -25,8 +31,7 @@ public class Messages {
public static String getErrorMessageForDuplicatePrefixes(Prefix... duplicatePrefixes) {
assert duplicatePrefixes.length > 0;
- Set duplicateFields =
- Stream.of(duplicatePrefixes).map(Prefix::toString).collect(Collectors.toSet());
+ Set duplicateFields = Stream.of(duplicatePrefixes).map(Prefix::toString).collect(Collectors.toSet());
return MESSAGE_DUPLICATE_FIELDS + String.join(" ", duplicateFields);
}
@@ -37,15 +42,51 @@ public static String getErrorMessageForDuplicatePrefixes(Prefix... duplicatePref
public static String format(Person person) {
final StringBuilder builder = new StringBuilder();
builder.append(person.getName())
- .append("; Phone: ")
- .append(person.getPhone())
+ .append("; NRIC: ")
+ .append(person.getNric())
.append("; Email: ")
.append(person.getEmail())
- .append("; Address: ")
- .append(person.getAddress())
- .append("; Tags: ");
- person.getTags().forEach(builder::append);
+ .append("; Phone: ")
+ .append(person.getPhone())
+ .append("; Gender: ")
+ .append(person.getGender())
+ .append("; Age: ")
+ .append(person.getAge())
+ .append("; BloodType: ")
+ .append(person.getBloodType())
+ .append("; Allergies: ");
+ person.getAllergies().forEach(builder::append);
+ return builder.toString();
+ }
+
+ /**
+ * Formats the {@code appointment} for display to the user.
+ */
+ public static String format(Appointment appointment, Person person) {
+ final StringBuilder builder = new StringBuilder();
+ builder.append("Patient: ")
+ .append(person.getName())
+ .append("; Appointment: ")
+ .append(appointment.getName())
+ .append("; Date & Time: ")
+ .append(appointment.getDateTime());
+
return builder.toString();
}
+ /**
+ * Formats the {@code record} for display to the user.
+ */
+ public static String format(Record record, Person person) {
+ final StringBuilder builder = new StringBuilder();
+ builder.append("Patient: ")
+ .append(person.getName())
+ .append("; Conditions: ")
+ .append(record.getConditions())
+ .append("; Date & Time: ")
+ .append(record.getDateTime())
+ .append("; Medications: ")
+ .append(record.getMedications());
+ return builder.toString();
+ }
}
diff --git a/src/main/java/seedu/address/logic/commands/AddAppointmentCommand.java b/src/main/java/seedu/address/logic/commands/AddAppointmentCommand.java
new file mode 100644
index 00000000000..c07becc1b21
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/AddAppointmentCommand.java
@@ -0,0 +1,101 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+
+import java.util.List;
+
+import seedu.address.commons.core.index.Index;
+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.appointment.Appointment;
+import seedu.address.model.appointment.UniqueAppointmentList;
+import seedu.address.model.person.Person;
+import seedu.address.model.shared.Nric;
+
+/**
+ * Adds an appointment to the address book.
+ */
+public class AddAppointmentCommand extends Command {
+
+ public static final String COMMAND_WORD = "addappointment";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds an appointment to the address book.\n"
+ + "Parameters: INDEX (must be a positive integer) "
+ + PREFIX_NAME + "NAME "
+ + PREFIX_DATE + "DATETIME\n"
+ + "Example: " + COMMAND_WORD + " 1 "
+ + PREFIX_NAME + "Eye Examination "
+ + PREFIX_DATE + "18-09-2023 1800 ";
+
+ public static final String MESSAGE_SUCCESS = "New appointment added: %1$s";
+ public static final String MESSAGE_DUPLICATE_APPOINTMENT = "This appointment already exists in the address book";
+
+ private final Index index;
+ private final Appointment toAdd;
+
+ /**
+ * Creates an AddAppointmentCommand to add the specified {@code Appointment}
+ */
+ public AddAppointmentCommand(Index index, Appointment appointment) {
+ requireNonNull(index);
+ requireNonNull(appointment);
+ this.toAdd = appointment;
+ this.index = index;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ List lastShownList = model.getFilteredPersonList();
+
+ if (index.getZeroBased() >= lastShownList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+
+ Person patient = lastShownList.get(index.getZeroBased());
+ Nric patientId = patient.getNric();
+ Appointment newAppointment = new Appointment(toAdd.getName(), toAdd.getDateTime(), patientId);
+ if (patient.hasAppointment(newAppointment)) {
+ throw new CommandException(MESSAGE_DUPLICATE_APPOINTMENT);
+ }
+
+ UniqueAppointmentList newAppointmentList = new UniqueAppointmentList();
+ newAppointmentList.setAppointments(patient.getAppointments());
+ newAppointmentList.add(newAppointment);
+
+ Person newPatient = new Person(patient.getName(), patient.getNric(), patient.getEmail(),
+ patient.getPhone(), patient.getGender(), patient.getAge(), patient.getBloodType(),
+ patient.getAllergies(), patient.getRecords(), newAppointmentList, patient.isPinned());
+
+ model.setPerson(patient, newPatient);
+
+ model.resetAppointmentList();
+ return new CommandResult(String.format(MESSAGE_SUCCESS, Messages.format(newAppointment, newPatient)));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof AddAppointmentCommand)) {
+ return false;
+ }
+
+ AddAppointmentCommand otherAddCommand = (AddAppointmentCommand) other;
+ return toAdd.equals(otherAddCommand.toAdd) && index.equals(otherAddCommand.index);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("toAdd", toAdd)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/address/logic/commands/AddCommand.java
index 5d7185a9680..4f6a617d15c 100644
--- a/src/main/java/seedu/address/logic/commands/AddCommand.java
+++ b/src/main/java/seedu/address/logic/commands/AddCommand.java
@@ -1,11 +1,14 @@
package seedu.address.logic.commands;
import static java.util.Objects.requireNonNull;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_AGE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ALLERGIES;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_BLOODTYPE;
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GENDER;
import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NRIC;
import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
import seedu.address.commons.util.ToStringBuilder;
import seedu.address.logic.Messages;
@@ -18,25 +21,31 @@
*/
public class AddCommand extends Command {
- public static final String COMMAND_WORD = "add";
+ public static final String COMMAND_WORD = "addpatient";
- public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a person to the address book. "
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a patient to the address book.\n"
+ "Parameters: "
+ PREFIX_NAME + "NAME "
- + PREFIX_PHONE + "PHONE "
+ + PREFIX_NRIC + "NRIC "
+ PREFIX_EMAIL + "EMAIL "
- + PREFIX_ADDRESS + "ADDRESS "
- + "[" + PREFIX_TAG + "TAG]...\n"
+ + PREFIX_PHONE + "PHONE "
+ + PREFIX_GENDER + "GENDER "
+ + PREFIX_AGE + "AGE "
+ + PREFIX_BLOODTYPE + "BLOODTYPE "
+ + "[" + PREFIX_ALLERGIES + "ALLERGY]...\n"
+ "Example: " + COMMAND_WORD + " "
+ PREFIX_NAME + "John Doe "
- + PREFIX_PHONE + "98765432 "
+ + PREFIX_NRIC + "S1234567A "
+ PREFIX_EMAIL + "johnd@example.com "
- + PREFIX_ADDRESS + "311, Clementi Ave 2, #02-25 "
- + PREFIX_TAG + "friends "
- + PREFIX_TAG + "owesMoney";
+ + PREFIX_PHONE + "98765432 "
+ + PREFIX_GENDER + "M "
+ + PREFIX_AGE + "18 "
+ + PREFIX_BLOODTYPE + "A+ "
+ + PREFIX_ALLERGIES + "Penicillin ";
+
public static final String MESSAGE_SUCCESS = "New person added: %1$s";
- public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book";
+ public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the Medbook";
private final Person toAdd;
diff --git a/src/main/java/seedu/address/logic/commands/AddRecordCommand.java b/src/main/java/seedu/address/logic/commands/AddRecordCommand.java
new file mode 100644
index 00000000000..21d7290b663
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/AddRecordCommand.java
@@ -0,0 +1,104 @@
+package seedu.address.logic.commands;
+
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_CONDITION;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_MEDICATION;
+
+import java.util.List;
+
+import seedu.address.commons.core.index.Index;
+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.record.Record;
+import seedu.address.model.record.UniqueRecordList;
+
+/**
+ * Adds a record to a patient in the MedBook
+ */
+public class AddRecordCommand extends Command {
+
+ public static final String COMMAND_WORD = "addrecord";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a patient record to the address book.\n"
+ + "Parameters: INDEX (must be a positive integer) "
+ + PREFIX_DATE + "DATETIME "
+ + PREFIX_CONDITION + "CONDITION "
+ + PREFIX_MEDICATION + "MEDICATION " + "\n"
+ + "Example: " + COMMAND_WORD + " 1 "
+ + PREFIX_DATE + "18-09-2023 1800 "
+ + PREFIX_CONDITION + "Fever "
+ + PREFIX_MEDICATION + "Tylenol";
+
+ public static final String MESSAGE_SUCCESS = "New record added: %1$s";
+
+ public static final String MESSAGE_DUPLICATE_RECORDS = "This record already exists in the record of the patient.";
+
+ private final Record record;
+
+ private final Index index;
+
+ /**
+ * Creates a record under a patient of specified index
+ */
+ public AddRecordCommand(Index index, Record record) {
+ requireAllNonNull(index, record);
+ this.index = index;
+ this.record = record;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+
+ List lastShownList = model.getFilteredPersonList();
+
+ if (index.getZeroBased() >= lastShownList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+
+ if (model.hasRecord(record, index)) {
+ throw new CommandException(MESSAGE_DUPLICATE_RECORDS);
+ }
+
+ Person personToAddRecord = lastShownList.get(index.getZeroBased());
+ UniqueRecordList newRecords = new UniqueRecordList();
+ newRecords.setRecords(personToAddRecord.getRecords());
+ newRecords.add(record);
+ Person personWithAddedRecord = new Person(personToAddRecord.getName(), personToAddRecord.getNric(),
+ personToAddRecord.getEmail(), personToAddRecord.getPhone(), personToAddRecord.getGender(),
+ personToAddRecord.getAge(), personToAddRecord.getBloodType(), personToAddRecord.getAllergies(),
+ newRecords, personToAddRecord.getAppointments(), personToAddRecord.isPinned());
+
+ model.setPerson(personToAddRecord, personWithAddedRecord);
+ model.updateRecordList(personWithAddedRecord);
+
+ return new CommandResult(String.format(MESSAGE_SUCCESS, Messages.format(record, personWithAddedRecord)));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof AddRecordCommand)) {
+ return false;
+ }
+
+ AddRecordCommand otherAddRecordCommand = (AddRecordCommand) other;
+ return record.equals(otherAddRecordCommand.record)
+ && index.equals(otherAddRecordCommand.index);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("index", index)
+ .add("record", record)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/ClearCommand.java b/src/main/java/seedu/address/logic/commands/ClearCommand.java
deleted file mode 100644
index 9c86b1fa6e4..00000000000
--- a/src/main/java/seedu/address/logic/commands/ClearCommand.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package seedu.address.logic.commands;
-
-import static java.util.Objects.requireNonNull;
-
-import seedu.address.model.AddressBook;
-import seedu.address.model.Model;
-
-/**
- * Clears the address book.
- */
-public class ClearCommand extends Command {
-
- public static final String COMMAND_WORD = "clear";
- public static final String MESSAGE_SUCCESS = "Address book has been cleared!";
-
-
- @Override
- public CommandResult execute(Model model) {
- requireNonNull(model);
- model.setAddressBook(new AddressBook());
- return new CommandResult(MESSAGE_SUCCESS);
- }
-}
diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/seedu/address/logic/commands/CommandResult.java
index 249b6072d0d..0276875de7b 100644
--- a/src/main/java/seedu/address/logic/commands/CommandResult.java
+++ b/src/main/java/seedu/address/logic/commands/CommandResult.java
@@ -16,15 +16,19 @@ public class CommandResult {
/** Help information should be shown to the user. */
private final boolean showHelp;
+ /** Appointments should be shown to the user. */
+ private final boolean showAppointments;
+
/** The application should exit. */
private final boolean exit;
/**
* Constructs a {@code CommandResult} with the specified fields.
*/
- public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) {
+ public CommandResult(String feedbackToUser, boolean showHelp, boolean showAppointments, boolean exit) {
this.feedbackToUser = requireNonNull(feedbackToUser);
this.showHelp = showHelp;
+ this.showAppointments = showAppointments;
this.exit = exit;
}
@@ -33,7 +37,7 @@ public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) {
* and other fields set to their default value.
*/
public CommandResult(String feedbackToUser) {
- this(feedbackToUser, false, false);
+ this(feedbackToUser, false, false, false);
}
public String getFeedbackToUser() {
@@ -44,6 +48,10 @@ public boolean isShowHelp() {
return showHelp;
}
+ public boolean isShowAppointments() {
+ return showAppointments;
+ }
+
public boolean isExit() {
return exit;
}
@@ -62,12 +70,13 @@ public boolean equals(Object other) {
CommandResult otherCommandResult = (CommandResult) other;
return feedbackToUser.equals(otherCommandResult.feedbackToUser)
&& showHelp == otherCommandResult.showHelp
+ && showAppointments == otherCommandResult.showAppointments
&& exit == otherCommandResult.exit;
}
@Override
public int hashCode() {
- return Objects.hash(feedbackToUser, showHelp, exit);
+ return Objects.hash(feedbackToUser, showHelp, showAppointments, exit);
}
@Override
@@ -75,6 +84,7 @@ public String toString() {
return new ToStringBuilder(this)
.add("feedbackToUser", feedbackToUser)
.add("showHelp", showHelp)
+ .add("showAppointments", showAppointments)
.add("exit", exit)
.toString();
}
diff --git a/src/main/java/seedu/address/logic/commands/DeleteAppointmentCommand.java b/src/main/java/seedu/address/logic/commands/DeleteAppointmentCommand.java
new file mode 100644
index 00000000000..94398cafb4b
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/DeleteAppointmentCommand.java
@@ -0,0 +1,97 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+
+import seedu.address.commons.core.index.Index;
+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.appointment.Appointment;
+import seedu.address.model.appointment.UniqueAppointmentList;
+import seedu.address.model.person.Person;
+import seedu.address.model.shared.Nric;
+
+/**
+ * Deletes an appointment identified using it's displayed index from the address
+ * book.
+ */
+public class DeleteAppointmentCommand extends Command {
+
+ public static final String COMMAND_WORD = "deleteappointment";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Deletes the appointment identified by the index number used in the displayed appointment list.\n"
+ + "Parameters: INDEX (must be a positive integer)\n"
+ + "Example: " + COMMAND_WORD + " 1";
+
+ public static final String MESSAGE_DELETE_APPOINTMENT_SUCCESS = "Deleted Appointment: %1$s";
+
+ public static final String MESSAGE_INVALID_NRIC = "The Patient with the corresponding NRIC no longer exists";
+
+ private final Index targetIndex;
+
+ public DeleteAppointmentCommand(Index targetIndex) {
+ this.targetIndex = targetIndex;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ List appointmentList = model.getAppointmentList();
+ List personList = model.getFilteredPersonList();
+
+ if (targetIndex.getZeroBased() >= appointmentList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_APPOINTMENT_DISPLAYED_INDEX);
+ }
+
+ Appointment appointmentToDelete = appointmentList.get(targetIndex.getZeroBased());
+ Nric patientNric = appointmentToDelete.getNric();
+ Person personWithAppointment = personList.stream()
+ .filter(person -> person.getNric().equals(patientNric))
+ .findFirst()
+ .orElse(null);
+ if (personWithAppointment == null) {
+ throw new CommandException(MESSAGE_INVALID_NRIC);
+ }
+ UniqueAppointmentList newList = new UniqueAppointmentList();
+ newList.setAppointments(personWithAppointment.getAppointments());
+ newList.remove(appointmentToDelete);
+
+ Person newPatient = new Person(personWithAppointment.getName(), personWithAppointment.getNric(),
+ personWithAppointment.getEmail(),
+ personWithAppointment.getPhone(), personWithAppointment.getGender(), personWithAppointment.getAge(),
+ personWithAppointment.getBloodType(),
+ personWithAppointment.getAllergies(), personWithAppointment.getRecords(), newList,
+ personWithAppointment.isPinned());
+
+ model.setPerson(personWithAppointment, newPatient);
+ model.resetAppointmentList();
+ return new CommandResult(String.format(MESSAGE_DELETE_APPOINTMENT_SUCCESS,
+ Messages.format(appointmentToDelete, personWithAppointment)));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof DeleteAppointmentCommand)) {
+ return false;
+ }
+
+ DeleteAppointmentCommand otherDeleteAppointmentCommand = (DeleteAppointmentCommand) other;
+ return targetIndex.equals(otherDeleteAppointmentCommand.targetIndex);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("targetIndex", targetIndex)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/DeleteCommand.java b/src/main/java/seedu/address/logic/commands/DeleteCommand.java
index 1135ac19b74..54e84f9fe6d 100644
--- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java
+++ b/src/main/java/seedu/address/logic/commands/DeleteCommand.java
@@ -42,6 +42,7 @@ public CommandResult execute(Model model) throws CommandException {
Person personToDelete = lastShownList.get(targetIndex.getZeroBased());
model.deletePerson(personToDelete);
+ model.resetAppointmentList();
return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, Messages.format(personToDelete)));
}
diff --git a/src/main/java/seedu/address/logic/commands/DeleteRecordCommand.java b/src/main/java/seedu/address/logic/commands/DeleteRecordCommand.java
new file mode 100644
index 00000000000..3ff1580b19a
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/DeleteRecordCommand.java
@@ -0,0 +1,88 @@
+package seedu.address.logic.commands;
+
+import java.util.List;
+
+import seedu.address.commons.core.index.Index;
+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.record.Record;
+import seedu.address.model.record.UniqueRecordList;
+
+/**
+ * Deletes a record identified using it's displayed patient index and record
+ * index from the address book.
+ */
+public class DeleteRecordCommand extends Command {
+ public static final String COMMAND_WORD = "deleterecord";
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Deletes the record identified by the index number used "
+ + "in the displayed patient list and record list.\n"
+ + "Parameters: PATIENT INDEX/RECORD INDEX (Both must be positive integers)\n"
+ + "Example: " + COMMAND_WORD + " 1/1";
+
+ public static final String MESSAGE_DELETE_RECORD_SUCCESS = "Deleted Record: %1$s";
+
+ private final Index targetPatientIndex;
+ private final Index targetRecordIndex;
+
+ /**
+ * Creates DeleteRecordCommand object
+ */
+ public DeleteRecordCommand(Index targetPatientIndex, Index targetRecordIndex) {
+ this.targetPatientIndex = targetPatientIndex;
+ this.targetRecordIndex = targetRecordIndex;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ List lastShownList = model.getFilteredPersonList();
+
+ if (targetPatientIndex.getZeroBased() >= lastShownList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+ Person targetPatient = lastShownList.get(targetPatientIndex.getZeroBased());
+ UniqueRecordList newRecordsList = new UniqueRecordList();
+ newRecordsList.setRecords(targetPatient.getRecords());
+
+ if (targetRecordIndex.getZeroBased() >= newRecordsList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_RECORD_DISPLAYED_INDEX);
+ }
+
+ Record targetRecord = newRecordsList.get(targetRecordIndex.getZeroBased());
+ newRecordsList.remove(targetRecord);
+ Person patientWithDeletedRecord = new Person(targetPatient.getName(), targetPatient.getNric(),
+ targetPatient.getEmail(), targetPatient.getPhone(), targetPatient.getGender(), targetPatient.getAge(),
+ targetPatient.getBloodType(), targetPatient.getAllergies(), newRecordsList,
+ targetPatient.getAppointments(), targetPatient.isPinned());
+ model.setPerson(targetPatient, patientWithDeletedRecord);
+ model.updateRecordList(patientWithDeletedRecord);
+ return new CommandResult(String.format(MESSAGE_DELETE_RECORD_SUCCESS,
+ Messages.format(targetRecord, targetPatient)));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+
+ if (!(other instanceof DeleteRecordCommand)) {
+ return false;
+ }
+
+ DeleteRecordCommand otherDeleteRecordCommand = (DeleteRecordCommand) other;
+ return this.targetPatientIndex.equals(otherDeleteRecordCommand.targetPatientIndex)
+ && this.targetRecordIndex.equals(otherDeleteRecordCommand.targetRecordIndex);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("targetPatientIndex", targetPatientIndex)
+ .add("targetRecordIndex", targetRecordIndex)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/EditCommand.java b/src/main/java/seedu/address/logic/commands/EditCommand.java
index 4b581c7331e..ab8e47db92c 100644
--- a/src/main/java/seedu/address/logic/commands/EditCommand.java
+++ b/src/main/java/seedu/address/logic/commands/EditCommand.java
@@ -1,11 +1,13 @@
package seedu.address.logic.commands;
import static java.util.Objects.requireNonNull;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_AGE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ALLERGIES;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_BLOODTYPE;
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GENDER;
import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
import java.util.Collections;
@@ -21,29 +23,36 @@
import seedu.address.logic.Messages;
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.model.Model;
-import seedu.address.model.person.Address;
+import seedu.address.model.appointment.UniqueAppointmentList;
+import seedu.address.model.person.Age;
+import seedu.address.model.person.Allergy;
+import seedu.address.model.person.BloodType;
import seedu.address.model.person.Email;
-import seedu.address.model.person.Name;
+import seedu.address.model.person.Gender;
import seedu.address.model.person.Person;
import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
+import seedu.address.model.record.UniqueRecordList;
+import seedu.address.model.shared.Name;
+import seedu.address.model.shared.Nric;
/**
* Edits the details of an existing person in the address book.
*/
public class EditCommand extends Command {
- public static final String COMMAND_WORD = "edit";
+ public static final String COMMAND_WORD = "editpatient";
public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the person identified "
+ "by the index number used in the displayed person list. "
+ "Existing values will be overwritten by the input values.\n"
+ "Parameters: INDEX (must be a positive integer) "
+ "[" + PREFIX_NAME + "NAME] "
- + "[" + PREFIX_PHONE + "PHONE] "
+ "[" + PREFIX_EMAIL + "EMAIL] "
- + "[" + PREFIX_ADDRESS + "ADDRESS] "
- + "[" + PREFIX_TAG + "TAG]...\n"
+ + "[" + PREFIX_PHONE + "PHONE] "
+ + "[" + PREFIX_GENDER + "GENDER] "
+ + "[" + PREFIX_AGE + "AGE] "
+ + "[" + PREFIX_BLOODTYPE + "BLOODTYPE "
+ + "[" + PREFIX_ALLERGIES + "ALLERGY]...\n"
+ "Example: " + COMMAND_WORD + " 1 "
+ PREFIX_PHONE + "91234567 "
+ PREFIX_EMAIL + "johndoe@example.com";
@@ -56,8 +65,10 @@ public class EditCommand extends Command {
private final EditPersonDescriptor editPersonDescriptor;
/**
- * @param index of the person in the filtered person list to edit
- * @param editPersonDescriptor details to edit the person with
+ * @param index
+ * of the person in the filtered person list to edit
+ * @param editPersonDescriptor
+ * details to edit the person with
*/
public EditCommand(Index index, EditPersonDescriptor editPersonDescriptor) {
requireNonNull(index);
@@ -85,7 +96,9 @@ public CommandResult execute(Model model) throws CommandException {
model.setPerson(personToEdit, editedPerson);
model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
- return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, Messages.format(editedPerson)));
+ model.updateRecordList(editedPerson);
+ return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS,
+ Messages.format(editedPerson)));
}
/**
@@ -96,12 +109,19 @@ private static Person createEditedPerson(Person personToEdit, EditPersonDescript
assert personToEdit != null;
Name updatedName = editPersonDescriptor.getName().orElse(personToEdit.getName());
- Phone updatedPhone = editPersonDescriptor.getPhone().orElse(personToEdit.getPhone());
+ Nric updatedNric = editPersonDescriptor.getNric().orElse(personToEdit.getNric());
Email updatedEmail = editPersonDescriptor.getEmail().orElse(personToEdit.getEmail());
- Address updatedAddress = editPersonDescriptor.getAddress().orElse(personToEdit.getAddress());
- Set updatedTags = editPersonDescriptor.getTags().orElse(personToEdit.getTags());
-
- return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedTags);
+ Phone updatedPhone = editPersonDescriptor.getPhone().orElse(personToEdit.getPhone());
+ Gender updatedGender = editPersonDescriptor.getGender().orElse(personToEdit.getGender());
+ Age updatedAge = editPersonDescriptor.getAge().orElse(personToEdit.getAge());
+ BloodType updatedBloodType = editPersonDescriptor.getBloodType().orElse(personToEdit.getBloodType());
+ Set updatedAllergies = editPersonDescriptor.getAllergies().orElse(personToEdit.getAllergies());
+ UniqueRecordList updatedRecords = personToEdit.getRecords();
+ UniqueAppointmentList updatedAppointments = personToEdit.getAppointments();
+ Boolean updatedisPinned = personToEdit.isPinned();
+
+ return new Person(updatedName, updatedNric, updatedEmail, updatedPhone, updatedGender,
+ updatedAge, updatedBloodType, updatedAllergies, updatedRecords, updatedAppointments, updatedisPinned);
}
@Override
@@ -129,84 +149,117 @@ public String toString() {
}
/**
- * Stores the details to edit the person with. Each non-empty field value will replace the
+ * Stores the details to edit the person with. Each non-empty field value will
+ * replace the
* corresponding field value of the person.
*/
public static class EditPersonDescriptor {
private Name name;
- private Phone phone;
+ private Nric nric;
private Email email;
- private Address address;
- private Set tags;
+ private Phone phone;
+ private Gender gender;
+ private Age age;
+ private BloodType bloodType;
+ private Set allergies;
- public EditPersonDescriptor() {}
+ public EditPersonDescriptor() {
+ }
/**
* Copy constructor.
- * A defensive copy of {@code tags} is used internally.
+ * A defensive copy of {@code allergies} is used internally.
*/
public EditPersonDescriptor(EditPersonDescriptor toCopy) {
setName(toCopy.name);
- setPhone(toCopy.phone);
+ setNric(toCopy.nric);
setEmail(toCopy.email);
- setAddress(toCopy.address);
- setTags(toCopy.tags);
+ setPhone(toCopy.phone);
+ setGender(toCopy.gender);
+ setAge(toCopy.age);
+ setBloodType(toCopy.bloodType);
+ setAllergies(toCopy.allergies);
}
/**
* Returns true if at least one field is edited.
*/
public boolean isAnyFieldEdited() {
- return CollectionUtil.isAnyNonNull(name, phone, email, address, tags);
+ return CollectionUtil.isAnyNonNull(name, email, phone, gender, age, bloodType, allergies);
+ }
+
+ public Optional getName() {
+ return Optional.ofNullable(name);
}
public void setName(Name name) {
this.name = name;
}
- public Optional getName() {
- return Optional.ofNullable(name);
+ public Optional getNric() {
+ return Optional.ofNullable(nric);
}
- public void setPhone(Phone phone) {
- this.phone = phone;
+ public void setNric(Nric nric) {
+ this.nric = nric;
}
public Optional getPhone() {
return Optional.ofNullable(phone);
}
- public void setEmail(Email email) {
- this.email = email;
+ public void setPhone(Phone phone) {
+ this.phone = phone;
}
public Optional getEmail() {
return Optional.ofNullable(email);
}
- public void setAddress(Address address) {
- this.address = address;
+ public void setEmail(Email email) {
+ this.email = email;
+ }
+
+ public Optional getGender() {
+ return Optional.ofNullable(gender);
+ }
+
+ public void setGender(Gender gender) {
+ this.gender = gender;
+ }
+
+ public Optional getAge() {
+ return Optional.ofNullable(age);
}
- public Optional getAddress() {
- return Optional.ofNullable(address);
+ public void setAge(Age age) {
+ this.age = age;
+ }
+
+ public Optional getBloodType() {
+ return Optional.ofNullable(bloodType);
+ }
+
+ public void setBloodType(BloodType bloodType) {
+ this.bloodType = bloodType;
}
/**
- * Sets {@code tags} to this object's {@code tags}.
- * A defensive copy of {@code tags} is used internally.
+ * Returns an unmodifiable Allergy set, which throws
+ * {@code UnsupportedOperationException}
+ * if modification is attempted.
+ * Returns {@code Optional#empty()} if {@code allergies} is null.
*/
- public void setTags(Set tags) {
- this.tags = (tags != null) ? new HashSet<>(tags) : null;
+ public Optional> getAllergies() {
+ return (allergies != null) ? Optional.of(Collections.unmodifiableSet(allergies)) : Optional.empty();
}
/**
- * Returns an unmodifiable tag set, which throws {@code UnsupportedOperationException}
- * if modification is attempted.
- * Returns {@code Optional#empty()} if {@code tags} is null.
+ * Sets {@code allergies} to this object's {@code tags}.
+ * A defensive copy of {@code allergies} is used internally.
*/
- public Optional> getTags() {
- return (tags != null) ? Optional.of(Collections.unmodifiableSet(tags)) : Optional.empty();
+ public void setAllergies(Set allergies) {
+ this.allergies = (allergies != null) ? new HashSet<>(allergies) : null;
}
@Override
@@ -222,20 +275,26 @@ public boolean equals(Object other) {
EditPersonDescriptor otherEditPersonDescriptor = (EditPersonDescriptor) other;
return Objects.equals(name, otherEditPersonDescriptor.name)
- && Objects.equals(phone, otherEditPersonDescriptor.phone)
+ && Objects.equals(nric, otherEditPersonDescriptor.nric)
&& Objects.equals(email, otherEditPersonDescriptor.email)
- && Objects.equals(address, otherEditPersonDescriptor.address)
- && Objects.equals(tags, otherEditPersonDescriptor.tags);
+ && Objects.equals(phone, otherEditPersonDescriptor.phone)
+ && Objects.equals(gender, otherEditPersonDescriptor.gender)
+ && Objects.equals(age, otherEditPersonDescriptor.age)
+ && Objects.equals(bloodType, otherEditPersonDescriptor.bloodType)
+ && Objects.equals(allergies, otherEditPersonDescriptor.allergies);
}
@Override
public String toString() {
return new ToStringBuilder(this)
.add("name", name)
- .add("phone", phone)
+ .add("nric", nric)
.add("email", email)
- .add("address", address)
- .add("tags", tags)
+ .add("phone", phone)
+ .add("gender", gender)
+ .add("age", age)
+ .add("bloodType", bloodType)
+ .add("allergies", allergies)
.toString();
}
}
diff --git a/src/main/java/seedu/address/logic/commands/EditRecordCommand.java b/src/main/java/seedu/address/logic/commands/EditRecordCommand.java
new file mode 100644
index 00000000000..cadd25d52b4
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/EditRecordCommand.java
@@ -0,0 +1,292 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_CONDITION;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_MEDICATION;
+
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.commons.util.CollectionUtil;
+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.record.Condition;
+import seedu.address.model.record.Medication;
+import seedu.address.model.record.Record;
+import seedu.address.model.record.UniqueRecordList;
+import seedu.address.model.shared.DateTime;
+
+/**
+ * Edits the details of an existing record of a Person in the address book.
+ */
+public class EditRecordCommand extends Command {
+
+ public static final String COMMAND_WORD = "editrecord";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits a patient's record in the address book "
+ + "by the patient's index number and the record's index number.\n"
+ + "Existing values will be overwritten by the input values.\n"
+ + "Parameters: PATIENT'S INDEX/RECORD INDEX (both must be a positive integer) "
+ + "[" + PREFIX_DATE + "DATE] "
+ + "[" + PREFIX_CONDITION + "CONDITION] " + "\n"
+ + "Example: " + COMMAND_WORD + " 1/2 "
+ + PREFIX_DATE + "21092023 1800 "
+ + PREFIX_CONDITION + "Cold"
+ + PREFIX_MEDICATION + "Ibuprofen";
+
+ public static final String MESSAGE_EDIT_RECORD_SUCCESS = "Edited record: %1$s";
+ public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided.";
+ public static final String MESSAGE_DUPLICATE_RECORD = "This record already exists in the record of the patient.";
+
+ private final Index patientIndex;
+ private final Index recordIndex;
+ private final EditRecordCommand.EditRecordDescriptor editRecordDescriptor;
+
+ /**
+ * @param patientIndex index of the person in the filtered person list
+ * to edit
+ * @param recordIndex index of the record of the targeted patient
+ * @param editRecordDescriptor details to edit the record with
+ */
+ public EditRecordCommand(Index patientIndex, Index recordIndex,
+ EditRecordCommand.EditRecordDescriptor editRecordDescriptor) {
+ requireNonNull(patientIndex);
+ requireNonNull(recordIndex);
+ requireNonNull(editRecordDescriptor);
+
+ this.patientIndex = patientIndex;
+ this.recordIndex = recordIndex;
+ this.editRecordDescriptor = new EditRecordCommand.EditRecordDescriptor(editRecordDescriptor);
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ List lastShownPersonList = model.getFilteredPersonList();
+
+ if (patientIndex.getZeroBased() >= lastShownPersonList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+
+ Person personToEdit = lastShownPersonList.get(patientIndex.getZeroBased());
+
+ UniqueRecordList uniqueRecordList = personToEdit.getRecords();
+ List lastShownRecordList = uniqueRecordList.asUnmodifiableObservableList();
+
+ if (recordIndex.getZeroBased() >= lastShownRecordList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_RECORD_DISPLAYED_INDEX);
+ }
+
+ Record recordToEdit = lastShownRecordList.get(recordIndex.getZeroBased());
+ Record editedRecord = createEditedRecord(recordToEdit, editRecordDescriptor);
+
+ if (recordToEdit.equals(editedRecord) || uniqueRecordList.contains(editedRecord)) {
+ throw new CommandException(MESSAGE_DUPLICATE_RECORD);
+ }
+
+ UniqueRecordList newList = new UniqueRecordList();
+ newList.setRecords(uniqueRecordList);
+ newList.setRecord(recordToEdit, editedRecord);
+ Person editedPerson = createdEditedPerson(personToEdit, newList);
+
+ if (!personToEdit.isSamePerson(editedPerson) && model.hasPerson(editedPerson)) {
+ throw new CommandException(EditCommand.MESSAGE_DUPLICATE_PERSON);
+ }
+
+ model.setPerson(personToEdit, editedPerson);
+ model.updateRecordList(editedPerson);
+ return new CommandResult(String.format(MESSAGE_EDIT_RECORD_SUCCESS,
+ Messages.format(editedRecord, personToEdit)));
+ }
+
+ /**
+ * Creates and returns a {@code Record} with the details of {@code recordToEdit}
+ * edited with {@code editRecordDescriptor}.
+ */
+ private Record createEditedRecord(Record recordToEdit,
+ EditRecordCommand.EditRecordDescriptor editRecordDescriptor) {
+ assert recordToEdit != null;
+
+ DateTime updatedDateTime = editRecordDescriptor.getDateTime().orElse(recordToEdit.getDateTime());
+ List updatedConditions = editRecordDescriptor.getConditions().orElse(recordToEdit.getConditions());
+ Path filePath = editRecordDescriptor.getFilePath().orElse(recordToEdit.getFilePath());
+ List updatedMedications = editRecordDescriptor.getMedications()
+ .orElse(recordToEdit.getMedications());
+
+ return new Record(updatedDateTime, updatedConditions,
+ updatedMedications, filePath, patientIndex.getZeroBased());
+ }
+
+ private static Person createdEditedPerson(Person personToEdit, UniqueRecordList records) {
+ assert personToEdit != null;
+ Person editedPerson = new Person(personToEdit.getName(), personToEdit.getNric(), personToEdit.getEmail(),
+ personToEdit.getPhone(), personToEdit.getGender(),
+ personToEdit.getAge(), personToEdit.getBloodType(),
+ personToEdit.getAllergies(), records, personToEdit.getAppointments(),
+ personToEdit.isPinned());
+
+ return editedPerson;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof EditRecordCommand)) {
+ return false;
+ }
+
+ EditRecordCommand otherEditRecordCommand = (EditRecordCommand) other;
+ return patientIndex.equals(otherEditRecordCommand.patientIndex)
+ && recordIndex.equals(otherEditRecordCommand.recordIndex)
+ && editRecordDescriptor.equals(otherEditRecordCommand.editRecordDescriptor);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("patientIndex", patientIndex)
+ .add("recordIndex", recordIndex)
+ .add("editRecordDescriptor", editRecordDescriptor)
+ .toString();
+ }
+
+ /**
+ * Stores the details to edit the record with. Each non-empty field value will
+ * replace the
+ * corresponding field value of the record.
+ */
+ public static class EditRecordDescriptor {
+ private DateTime dateTime;
+ private List conditions;
+ private Path filePath;
+ private Integer patientIndex;
+ private List medications;
+
+ public EditRecordDescriptor() {
+ }
+
+ /**
+ * Copy constructor.
+ * Defensive copies of {@code conditions} and {@code medications} are used
+ * internally.
+ */
+ public EditRecordDescriptor(EditRecordCommand.EditRecordDescriptor toCopy) {
+ setDateTime(toCopy.dateTime);
+ setConditions(toCopy.conditions);
+ setFilePath(toCopy.filePath);
+ setPatientIndex(toCopy.patientIndex);
+ setMedications(toCopy.medications);
+ }
+
+ /**
+ * Returns true if at least one field is edited.
+ */
+ public boolean isAnyFieldEdited() {
+ return CollectionUtil.isAnyNonNull(dateTime, conditions, filePath, medications);
+ }
+
+ public void setDateTime(DateTime dateTime) {
+ this.dateTime = dateTime;
+ }
+
+ public void setFilePath(Path filePath) {
+ this.filePath = filePath;
+ }
+
+ public Optional getFilePath() {
+ return Optional.ofNullable(filePath);
+ }
+
+ public Optional getPatientIndex() {
+ return Optional.ofNullable(patientIndex);
+ }
+
+ public void setPatientIndex(Integer patientIndex) {
+ this.patientIndex = patientIndex;
+ }
+
+ public Optional getDateTime() {
+ return Optional.ofNullable(dateTime);
+ }
+
+ /**
+ * Sets {@code conditions} to this record's {@code conditions}.
+ * A defensive copy of {@code condtions} is used internally.
+ */
+ public void setConditions(List conditions) {
+ this.conditions = (conditions != null) ? new ArrayList<>(conditions) : null;
+ }
+
+ /**
+ * Returns an unmodifiable condition list, which throws
+ * {@code UnsupportedOperationException}
+ * if modification is attempted.
+ * Returns {@code Optional#empty()} if {@code conditions} is null.
+ */
+ public Optional> getConditions() {
+ return (conditions != null) ? Optional.of(Collections.unmodifiableList(conditions)) : Optional.empty();
+ }
+
+ /**
+ * Sets {@code medications} to this record's {@code medications}.
+ * A defensive copy of {@code medications} is used internally.
+ */
+ public void setMedications(List medications) {
+ this.medications = (medications != null) ? new ArrayList<>(medications) : null;
+ }
+
+ /**
+ * Returns an unmodifiable medication list, which throws
+ * {@code UnsupportedOperationException}
+ * if modification is attempted.
+ * Returns {@code Optional#empty()} if {@code medications} is null.
+ */
+ public Optional> getMedications() {
+ return (medications != null) ? Optional.of(Collections.unmodifiableList(medications)) : Optional.empty();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof EditRecordCommand.EditRecordDescriptor)) {
+ return false;
+ }
+
+ EditRecordCommand.EditRecordDescriptor otherEditRecordDescriptor =
+ (EditRecordCommand.EditRecordDescriptor) other;
+ return Objects.equals(dateTime, otherEditRecordDescriptor.dateTime)
+ && Objects.equals(conditions, otherEditRecordDescriptor.conditions)
+ && Objects.equals(filePath, otherEditRecordDescriptor.filePath)
+ && Objects.equals(patientIndex, otherEditRecordDescriptor.patientIndex)
+ && Objects.equals(medications, otherEditRecordDescriptor.medications);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("dateTime", dateTime)
+ .add("conditions", conditions)
+ .add("medications", medications)
+ .add("filePath", filePath)
+ .add("patientIndex", patientIndex)
+ .toString();
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/seedu/address/logic/commands/ExitCommand.java
index 3dd85a8ba90..eb7560413f9 100644
--- a/src/main/java/seedu/address/logic/commands/ExitCommand.java
+++ b/src/main/java/seedu/address/logic/commands/ExitCommand.java
@@ -13,7 +13,7 @@ public class ExitCommand extends Command {
@Override
public CommandResult execute(Model model) {
- return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true);
+ return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, false, true);
}
}
diff --git a/src/main/java/seedu/address/logic/commands/FindCommand.java b/src/main/java/seedu/address/logic/commands/FindCommand.java
index 72b9eddd3a7..7bc13a3f4ce 100644
--- a/src/main/java/seedu/address/logic/commands/FindCommand.java
+++ b/src/main/java/seedu/address/logic/commands/FindCommand.java
@@ -8,12 +8,13 @@
import seedu.address.model.person.NameContainsKeywordsPredicate;
/**
- * Finds and lists all persons in address book whose name contains any of the argument keywords.
+ * Finds and lists all persons in address book whose name contains any of the
+ * argument keywords.
* Keyword matching is case insensitive.
*/
public class FindCommand extends Command {
- public static final String COMMAND_WORD = "find";
+ public static final String COMMAND_WORD = "search";
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"
diff --git a/src/main/java/seedu/address/logic/commands/FindRecordCommand.java b/src/main/java/seedu/address/logic/commands/FindRecordCommand.java
new file mode 100644
index 00000000000..e71ac50a92b
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/FindRecordCommand.java
@@ -0,0 +1,53 @@
+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.record.RecordContainsKeywordsPredicate;
+
+
+/**
+ * Finds and lists all records of a patient in address book that contain any of the arguement keywords.
+ * Keyword matching is not case sensitive.
+ */
+public class FindRecordCommand extends Command {
+ public static final String COMMAND_WORD = "searchrecord";
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all records of a patient that 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 + "Tylenol";
+ private final RecordContainsKeywordsPredicate predicate;
+ public FindRecordCommand(RecordContainsKeywordsPredicate predicate) {
+ this.predicate = predicate;
+ }
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ model.updateFilteredRecordList(predicate);
+ return new CommandResult(
+ String.format(Messages.MESSAGE_RECORDS_LISTED_OVERVIEW, model.getFilteredRecordList().size())
+ );
+ }
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+
+ if (!(other instanceof FindRecordCommand)) {
+ return false;
+ }
+
+ FindRecordCommand otherFindRecordCommand = (FindRecordCommand) other;
+ return this.predicate.equals(otherFindRecordCommand.predicate);
+ }
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("predicate", predicate)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/HelpCommand.java b/src/main/java/seedu/address/logic/commands/HelpCommand.java
index bf824f91bd0..07d26e2a23c 100644
--- a/src/main/java/seedu/address/logic/commands/HelpCommand.java
+++ b/src/main/java/seedu/address/logic/commands/HelpCommand.java
@@ -16,6 +16,6 @@ public class HelpCommand extends Command {
@Override
public CommandResult execute(Model model) {
- return new CommandResult(SHOWING_HELP_MESSAGE, true, false);
+ return new CommandResult(SHOWING_HELP_MESSAGE, true, false, false);
}
}
diff --git a/src/main/java/seedu/address/logic/commands/PinCommand.java b/src/main/java/seedu/address/logic/commands/PinCommand.java
new file mode 100644
index 00000000000..ed6e80bdfdd
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/PinCommand.java
@@ -0,0 +1,73 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+
+import seedu.address.commons.core.index.Index;
+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;
+
+/**
+ * Pins the person identified using it's displayed index from the address book.
+ */
+public class PinCommand extends Command {
+
+ public static final String COMMAND_WORD = "pin";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Pins the person identified by the index number used in the displayed person list.\n"
+ + "Parameters: INDEX (must be a positive integer)\n"
+ + "Example: " + COMMAND_WORD + " 1";
+
+ public static final String MESSAGE_PIN_PERSON_SUCCESS = "Pinned Person: %1$s";
+
+ private final Index targetIndex;
+
+ public PinCommand(Index targetIndex) {
+ this.targetIndex = targetIndex;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ List lastShownList = model.getFilteredPersonList();
+
+ if (targetIndex.getZeroBased() >= lastShownList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+
+ Person personToPin = lastShownList.get(targetIndex.getZeroBased());
+ Person pinnedPerson = new Person(personToPin.getName(), personToPin.getNric(), personToPin.getEmail(),
+ personToPin.getPhone(), personToPin.getGender(), personToPin.getAge(), personToPin.getBloodType(),
+ personToPin.getAllergies(), personToPin.getRecords(), personToPin.getAppointments(), true);
+
+ model.setPerson(personToPin, pinnedPerson);
+ return new CommandResult(String.format(MESSAGE_PIN_PERSON_SUCCESS, Messages.format(personToPin)));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof PinCommand)) {
+ return false;
+ }
+
+ PinCommand otherPinCommand = (PinCommand) other;
+ return targetIndex.equals(otherPinCommand.targetIndex);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("targetIndex", targetIndex)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/UnpinCommand.java b/src/main/java/seedu/address/logic/commands/UnpinCommand.java
new file mode 100644
index 00000000000..d4ed01e380c
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/UnpinCommand.java
@@ -0,0 +1,75 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+
+import seedu.address.commons.core.index.Index;
+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;
+
+/**
+ * Unpins the person identified using it's displayed index from the address
+ * book.
+ */
+public class UnpinCommand extends Command {
+
+ public static final String COMMAND_WORD = "unpin";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Unpins the person identified by the index number used in the displayed pinned list.\n"
+ + "Parameters: INDEX (must be a positive integer)\n"
+ + "Example: " + COMMAND_WORD + " 1";
+
+ public static final String MESSAGE_UNPIN_PERSON_SUCCESS = "Unpinned Person: %1$s";
+
+ private final Index targetIndex;
+
+ public UnpinCommand(Index targetIndex) {
+ this.targetIndex = targetIndex;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ List lastShownPinnedList = model.getPinnedPersonList();
+
+ if (targetIndex.getZeroBased() >= lastShownPinnedList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+
+ Person personToUnpin = lastShownPinnedList.get(targetIndex.getZeroBased());
+ Person unpinnedPerson = new Person(personToUnpin.getName(), personToUnpin.getNric(), personToUnpin.getEmail(),
+ personToUnpin.getPhone(), personToUnpin.getGender(), personToUnpin.getAge(),
+ personToUnpin.getBloodType(), personToUnpin.getAllergies(), personToUnpin.getRecords(),
+ personToUnpin.getAppointments(), false);
+
+ model.setPerson(personToUnpin, unpinnedPerson);
+ return new CommandResult(String.format(MESSAGE_UNPIN_PERSON_SUCCESS, Messages.format(personToUnpin)));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof UnpinCommand)) {
+ return false;
+ }
+
+ UnpinCommand otherUnpinCommand = (UnpinCommand) other;
+ return targetIndex.equals(otherUnpinCommand.targetIndex);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("targetIndex", targetIndex)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/ViewAppointmentCommand.java b/src/main/java/seedu/address/logic/commands/ViewAppointmentCommand.java
new file mode 100644
index 00000000000..1086a75403f
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/ViewAppointmentCommand.java
@@ -0,0 +1,21 @@
+package seedu.address.logic.commands;
+
+import seedu.address.model.Model;
+
+/**
+ * Opens the Appointments window.
+ */
+public class ViewAppointmentCommand extends Command {
+
+ public static final String COMMAND_WORD = "viewappointment";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Opens Appointments window.\n"
+ + "Example: " + COMMAND_WORD;
+
+ public static final String SHOWING_APPOINTMENTS_MESSAGE = "Opened appointments window.";
+
+ @Override
+ public CommandResult execute(Model model) {
+ return new CommandResult(SHOWING_APPOINTMENTS_MESSAGE, false, true, false);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/ViewCommand.java b/src/main/java/seedu/address/logic/commands/ViewCommand.java
new file mode 100644
index 00000000000..6784cb63471
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/ViewCommand.java
@@ -0,0 +1,69 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.person.Person;
+
+/**
+ * Views patient detailed information at the specified index
+ * and list medical records of the patient.
+ */
+
+public class ViewCommand extends Command {
+
+ public static final String COMMAND_WORD = "view";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": display the detailed information of the patient identified by the index given"
+ + " and list all the existing medical records of patient.\n"
+ + "Parameters: INDEX (must be a positive integer)\n"
+ + "Example: " + COMMAND_WORD + " 1";
+
+ public static final String MESSAGE_VIEW_PERSON_SUCCESS = "View Patient: %1$s";
+
+ private final Index targetIndex;
+
+ /**
+ * Constructor for ViewCommand class
+ *
+ * @param targetIndex the patient Index to view
+ */
+ public ViewCommand(Index targetIndex) {
+ requireNonNull(targetIndex);
+ this.targetIndex = targetIndex;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ List lastShownList = model.getFilteredPersonList();
+
+ if (targetIndex.getZeroBased() >= lastShownList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+
+ Person patientToView = lastShownList.get(targetIndex.getZeroBased());
+ model.updateRecordList(patientToView);
+ return new CommandResult(String.format(MESSAGE_VIEW_PERSON_SUCCESS, Messages.format(patientToView)));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof ViewCommand)) {
+ return false;
+ }
+
+ ViewCommand v = (ViewCommand) other;
+ return this.targetIndex.equals(v.targetIndex);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/AddAppointmentCommandParser.java b/src/main/java/seedu/address/logic/parser/AddAppointmentCommandParser.java
new file mode 100644
index 00000000000..4e9db2b4cf7
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/AddAppointmentCommandParser.java
@@ -0,0 +1,65 @@
+package seedu.address.logic.parser;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+
+import java.util.stream.Stream;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.AddAppointmentCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.appointment.Appointment;
+import seedu.address.model.shared.DateTime;
+import seedu.address.model.shared.Name;
+
+/**
+ * Parses input arguments and creates a new AddAppointmentCommand object
+ */
+public class AddAppointmentCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the
+ * AddAppointmentCommand
+ * and returns an AddAppointmentCommand object for execution.
+ *
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public AddAppointmentCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_DATE);
+
+ Index index;
+
+ try {
+ index = ParserUtil.parseIndex(argMultimap.getPreamble());
+ } catch (ParseException pe) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ AddAppointmentCommand.MESSAGE_USAGE), pe);
+ }
+
+ if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_DATE)) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ AddAppointmentCommand.MESSAGE_USAGE));
+ }
+
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_DATE);
+
+ Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get());
+ DateTime dateTime = ParserUtil.parseDateTime(argMultimap.getValue(PREFIX_DATE).get());
+
+ Appointment appointment = new Appointment(name, dateTime, null);
+
+ return new AddAppointmentCommand(index, appointment);
+ }
+
+ /**
+ * Returns true if none of the prefixes contains empty {@code Optional} values
+ * in the given
+ * {@code ArgumentMultimap}.
+ */
+ private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) {
+ return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent());
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/seedu/address/logic/parser/AddCommandParser.java
index 4ff1a97ed77..717f1f39d9c 100644
--- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/AddCommandParser.java
@@ -1,23 +1,31 @@
package seedu.address.logic.parser;
import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_AGE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ALLERGIES;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_BLOODTYPE;
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GENDER;
import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NRIC;
import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
import java.util.Set;
import java.util.stream.Stream;
import seedu.address.logic.commands.AddCommand;
import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.person.Address;
+import seedu.address.model.appointment.UniqueAppointmentList;
+import seedu.address.model.person.Age;
+import seedu.address.model.person.Allergy;
+import seedu.address.model.person.BloodType;
import seedu.address.model.person.Email;
-import seedu.address.model.person.Name;
+import seedu.address.model.person.Gender;
import seedu.address.model.person.Person;
import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
+import seedu.address.model.record.UniqueRecordList;
+import seedu.address.model.shared.Name;
+import seedu.address.model.shared.Nric;
/**
* Parses input arguments and creates a new AddCommand object
@@ -27,31 +35,40 @@ public class AddCommandParser implements Parser {
/**
* Parses the given {@code String} of arguments in the context of the AddCommand
* and returns an AddCommand object for execution.
+ *
* @throws ParseException if the user input does not conform the expected format
*/
public AddCommand parse(String args) throws ParseException {
- ArgumentMultimap argMultimap =
- ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG);
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_NRIC, PREFIX_EMAIL,
+ PREFIX_PHONE, PREFIX_GENDER, PREFIX_AGE,
+ PREFIX_BLOODTYPE, PREFIX_ALLERGIES);
- if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL)
+ if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_NRIC, PREFIX_EMAIL, PREFIX_PHONE, PREFIX_GENDER,
+ PREFIX_EMAIL, PREFIX_AGE, PREFIX_BLOODTYPE)
|| !argMultimap.getPreamble().isEmpty()) {
throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE));
}
- argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS);
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_NRIC, PREFIX_EMAIL, PREFIX_PHONE, PREFIX_GENDER,
+ PREFIX_EMAIL, PREFIX_AGE, PREFIX_BLOODTYPE);
Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get());
- Phone phone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get());
+ Nric nric = ParserUtil.parseNric(argMultimap.getValue(PREFIX_NRIC).get());
Email email = ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get());
- Address address = ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get());
- Set tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG));
+ Phone phone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get());
+ Gender gender = ParserUtil.parseGender(argMultimap.getValue(PREFIX_GENDER).get());
+ Age age = ParserUtil.parseAge(argMultimap.getValue(PREFIX_AGE).get());
+ BloodType bloodType = ParserUtil.parseBloodType(argMultimap.getValue(PREFIX_BLOODTYPE).get());
+ Set allergies = ParserUtil.parseAllergies(argMultimap.getAllValues(PREFIX_ALLERGIES));
- Person person = new Person(name, phone, email, address, tagList);
+ Person person = new Person(name, nric, email, phone, gender, age, bloodType, allergies,
+ new UniqueRecordList(), new UniqueAppointmentList(), false);
return new AddCommand(person);
}
/**
- * Returns true if none of the prefixes contains empty {@code Optional} values in the given
+ * Returns true if none of the prefixes contains empty {@code Optional} values
+ * in the given
* {@code ArgumentMultimap}.
*/
private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) {
diff --git a/src/main/java/seedu/address/logic/parser/AddRecordCommandParser.java b/src/main/java/seedu/address/logic/parser/AddRecordCommandParser.java
new file mode 100644
index 00000000000..4f72e9333bd
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/AddRecordCommandParser.java
@@ -0,0 +1,53 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_CONDITION;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_MEDICATION;
+
+import java.util.List;
+import java.util.stream.Stream;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.AddRecordCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.record.Condition;
+import seedu.address.model.record.Medication;
+import seedu.address.model.record.Record;
+import seedu.address.model.shared.DateTime;
+
+/**
+ * Parses a user input and creates a AddRecordCommand object
+ */
+public class AddRecordCommandParser implements Parser {
+ @Override
+ public AddRecordCommand parse(String userInput) throws ParseException {
+ ArgumentMultimap argMultimap =
+ ArgumentTokenizer.tokenize(userInput, PREFIX_DATE, PREFIX_CONDITION, PREFIX_MEDICATION);
+
+ if (!arePrefixesPresent(argMultimap, PREFIX_DATE, PREFIX_CONDITION, PREFIX_MEDICATION)
+ || argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddRecordCommand.MESSAGE_USAGE));
+ }
+
+ Index index = ParserUtil.parseIndex(argMultimap.getPreamble());
+
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_DATE);
+
+ DateTime dateTime = ParserUtil.parseDateTime(argMultimap.getValue(PREFIX_DATE).get());
+ List conditions = ParserUtil.parseConditions(argMultimap.getAllValues(PREFIX_CONDITION));
+ List medications = ParserUtil.parseMedications(argMultimap.getAllValues(PREFIX_MEDICATION));
+
+ Record record = new Record(dateTime, conditions, medications, null, index.getZeroBased());
+ return new AddRecordCommand(index, record);
+ }
+
+ /**
+ * Returns true if none of the prefixes contains empty {@code Optional} values
+ * in the given
+ * {@code ArgumentMultimap}.
+ */
+ private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) {
+ return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent());
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java
index 3149ee07e0b..8af52a80639 100644
--- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java
+++ b/src/main/java/seedu/address/logic/parser/AddressBookParser.java
@@ -8,15 +8,24 @@
import java.util.regex.Pattern;
import seedu.address.commons.core.LogsCenter;
+import seedu.address.logic.commands.AddAppointmentCommand;
import seedu.address.logic.commands.AddCommand;
-import seedu.address.logic.commands.ClearCommand;
+import seedu.address.logic.commands.AddRecordCommand;
import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.DeleteAppointmentCommand;
import seedu.address.logic.commands.DeleteCommand;
+import seedu.address.logic.commands.DeleteRecordCommand;
import seedu.address.logic.commands.EditCommand;
+import seedu.address.logic.commands.EditRecordCommand;
import seedu.address.logic.commands.ExitCommand;
import seedu.address.logic.commands.FindCommand;
+import seedu.address.logic.commands.FindRecordCommand;
import seedu.address.logic.commands.HelpCommand;
import seedu.address.logic.commands.ListCommand;
+import seedu.address.logic.commands.PinCommand;
+import seedu.address.logic.commands.UnpinCommand;
+import seedu.address.logic.commands.ViewAppointmentCommand;
+import seedu.address.logic.commands.ViewCommand;
import seedu.address.logic.parser.exceptions.ParseException;
/**
@@ -33,9 +42,11 @@ public class AddressBookParser {
/**
* Parses user input into command for execution.
*
- * @param userInput full user input string
+ * @param userInput
+ * full user input string
* @return the command based on the user input
- * @throws ParseException if the user input does not conform the expected format
+ * @throws ParseException
+ * if the user input does not conform the expected format
*/
public Command parseCommand(String userInput) throws ParseException {
final Matcher matcher = BASIC_COMMAND_FORMAT.matcher(userInput.trim());
@@ -46,7 +57,8 @@ public Command parseCommand(String userInput) throws ParseException {
final String commandWord = matcher.group("commandWord");
final String arguments = matcher.group("arguments");
- // Note to developers: Change the log level in config.json to enable lower level (i.e., FINE, FINER and lower)
+ // Note to developers: Change the log level in config.json to enable lower level
+ // (i.e., FINE, FINER and lower)
// log messages such as the one below.
// Lower level log messages are used sparingly to minimize noise in the code.
logger.fine("Command word: " + commandWord + "; Arguments: " + arguments);
@@ -56,18 +68,45 @@ public Command parseCommand(String userInput) throws ParseException {
case AddCommand.COMMAND_WORD:
return new AddCommandParser().parse(arguments);
+ case AddRecordCommand.COMMAND_WORD:
+ return new AddRecordCommandParser().parse(arguments);
+
case EditCommand.COMMAND_WORD:
return new EditCommandParser().parse(arguments);
+ case EditRecordCommand.COMMAND_WORD:
+ return new EditRecordCommandParser().parse(arguments);
+
case DeleteCommand.COMMAND_WORD:
return new DeleteCommandParser().parse(arguments);
- case ClearCommand.COMMAND_WORD:
- return new ClearCommand();
+ case DeleteRecordCommand.COMMAND_WORD:
+ return new DeleteRecordCommandParser().parse(arguments);
+
+ case PinCommand.COMMAND_WORD:
+ return new PinCommandParser().parse(arguments);
+
+ case UnpinCommand.COMMAND_WORD:
+ return new UnpinCommandParser().parse(arguments);
+
+ case AddAppointmentCommand.COMMAND_WORD:
+ return new AddAppointmentCommandParser().parse(arguments);
+
+ case DeleteAppointmentCommand.COMMAND_WORD:
+ return new DeleteAppointmentCommandParser().parse(arguments);
+
+ case ViewCommand.COMMAND_WORD:
+ return new ViewCommandParser().parse(arguments);
+
+ case ViewAppointmentCommand.COMMAND_WORD:
+ return new ViewAppointmentCommand();
case FindCommand.COMMAND_WORD:
return new FindCommandParser().parse(arguments);
+ case FindRecordCommand.COMMAND_WORD:
+ return new FindRecordCommandParser().parse(arguments);
+
case ListCommand.COMMAND_WORD:
return new ListCommand();
diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java
index 75b1a9bf119..58b6c912aa8 100644
--- a/src/main/java/seedu/address/logic/parser/CliSyntax.java
+++ b/src/main/java/seedu/address/logic/parser/CliSyntax.java
@@ -7,9 +7,14 @@ public class CliSyntax {
/* Prefix definitions */
public static final Prefix PREFIX_NAME = new Prefix("n/");
- public static final Prefix PREFIX_PHONE = new Prefix("p/");
+ public static final Prefix PREFIX_NRIC = new Prefix("i/");
public static final Prefix PREFIX_EMAIL = new Prefix("e/");
- public static final Prefix PREFIX_ADDRESS = new Prefix("a/");
- public static final Prefix PREFIX_TAG = new Prefix("t/");
-
+ public static final Prefix PREFIX_PHONE = new Prefix("p/");
+ public static final Prefix PREFIX_GENDER = new Prefix("g/");
+ public static final Prefix PREFIX_AGE = new Prefix("a/");
+ public static final Prefix PREFIX_BLOODTYPE = new Prefix("bt/");
+ public static final Prefix PREFIX_ALLERGIES = new Prefix("al/");
+ public static final Prefix PREFIX_DATE = new Prefix("d/");
+ public static final Prefix PREFIX_CONDITION = new Prefix("c/");
+ public static final Prefix PREFIX_MEDICATION = new Prefix("m/");
}
diff --git a/src/main/java/seedu/address/logic/parser/DeleteAppointmentCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteAppointmentCommandParser.java
new file mode 100644
index 00000000000..cfda28809a2
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/DeleteAppointmentCommandParser.java
@@ -0,0 +1,31 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.DeleteAppointmentCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new DeleteAppointmentCommand object
+ */
+public class DeleteAppointmentCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the
+ * DeleteAppointmentCommand
+ * and returns a DeleteAppointmentCommand object for execution.
+ *
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public DeleteAppointmentCommand parse(String args) throws ParseException {
+ try {
+ Index index = ParserUtil.parseIndex(args);
+ return new DeleteAppointmentCommand(index);
+ } catch (ParseException pe) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteAppointmentCommand.MESSAGE_USAGE), pe);
+ }
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/parser/DeleteRecordCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteRecordCommandParser.java
new file mode 100644
index 00000000000..b3185c660d0
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/DeleteRecordCommandParser.java
@@ -0,0 +1,32 @@
+package seedu.address.logic.parser;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.DeleteRecordCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new DeleteRecordCommand object
+ */
+public class DeleteRecordCommandParser implements Parser {
+
+ @Override
+ public DeleteRecordCommand parse(String userInput) throws ParseException {
+ requireNonNull(userInput);
+ Index patientIndex;
+ Index recordIndex;
+
+ try {
+ String trimmedUserInput = userInput.trim();
+ patientIndex = ParserUtil.parsePatientIndex(trimmedUserInput);
+ recordIndex = ParserUtil.parseRecordIndex(trimmedUserInput);
+ } catch (ParseException pe) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ DeleteRecordCommand.MESSAGE_USAGE), pe);
+ }
+
+ return new DeleteRecordCommand(patientIndex, recordIndex);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/EditCommandParser.java b/src/main/java/seedu/address/logic/parser/EditCommandParser.java
index 46b3309a78b..c91d3f676bd 100644
--- a/src/main/java/seedu/address/logic/parser/EditCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/EditCommandParser.java
@@ -2,11 +2,13 @@
import static java.util.Objects.requireNonNull;
import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_AGE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ALLERGIES;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_BLOODTYPE;
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GENDER;
import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
import java.util.Collection;
import java.util.Collections;
@@ -17,7 +19,7 @@
import seedu.address.logic.commands.EditCommand;
import seedu.address.logic.commands.EditCommand.EditPersonDescriptor;
import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.tag.Tag;
+import seedu.address.model.person.Allergy;
/**
* Parses input arguments and creates a new EditCommand object
@@ -25,14 +27,17 @@
public class EditCommandParser implements Parser {
/**
- * Parses the given {@code String} of arguments in the context of the EditCommand
+ * Parses the given {@code String} of arguments in the context of the
+ * EditCommand
* and returns an EditCommand object for execution.
- * @throws ParseException if the user input does not conform the expected format
+ *
+ * @throws ParseException
+ * if the user input does not conform the expected format
*/
public EditCommand parse(String args) throws ParseException {
requireNonNull(args);
- ArgumentMultimap argMultimap =
- ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG);
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_EMAIL, PREFIX_PHONE,
+ PREFIX_GENDER, PREFIX_AGE, PREFIX_BLOODTYPE, PREFIX_ALLERGIES);
Index index;
@@ -42,23 +47,30 @@ public EditCommand parse(String args) throws ParseException {
throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE), pe);
}
- argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS);
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_EMAIL, PREFIX_PHONE,
+ PREFIX_GENDER, PREFIX_AGE, PREFIX_BLOODTYPE);
EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor();
if (argMultimap.getValue(PREFIX_NAME).isPresent()) {
editPersonDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()));
}
+ if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) {
+ editPersonDescriptor.setEmail(ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get()));
+ }
if (argMultimap.getValue(PREFIX_PHONE).isPresent()) {
editPersonDescriptor.setPhone(ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get()));
}
- if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) {
- editPersonDescriptor.setEmail(ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get()));
+ if (argMultimap.getValue(PREFIX_GENDER).isPresent()) {
+ editPersonDescriptor.setGender(ParserUtil.parseGender(argMultimap.getValue(PREFIX_GENDER).get()));
+ }
+ if (argMultimap.getValue(PREFIX_AGE).isPresent()) {
+ editPersonDescriptor.setAge(ParserUtil.parseAge(argMultimap.getValue(PREFIX_AGE).get()));
}
- if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) {
- editPersonDescriptor.setAddress(ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get()));
+ if (argMultimap.getValue(PREFIX_BLOODTYPE).isPresent()) {
+ editPersonDescriptor.setBloodType(ParserUtil.parseBloodType(argMultimap.getValue(PREFIX_BLOODTYPE).get()));
}
- parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editPersonDescriptor::setTags);
+ parseAllergiesForEdit(argMultimap.getAllValues(PREFIX_ALLERGIES)).ifPresent(editPersonDescriptor::setAllergies);
if (!editPersonDescriptor.isAnyFieldEdited()) {
throw new ParseException(EditCommand.MESSAGE_NOT_EDITED);
@@ -68,18 +80,21 @@ public EditCommand parse(String args) throws ParseException {
}
/**
- * Parses {@code Collection tags} into a {@code Set} if {@code tags} is non-empty.
- * If {@code tags} contain only one element which is an empty string, it will be parsed into a
- * {@code Set} containing zero tags.
+ * Parses {@code Collection allergies} into a {@code Set} if
+ * {@code allergies} is non-empty.
+ * If {@code allergies} contain only one element which is an empty string, it
+ * will be parsed into a
+ * {@code Set} containing zero allergies.
*/
- private Optional> parseTagsForEdit(Collection tags) throws ParseException {
- assert tags != null;
+ private Optional> parseAllergiesForEdit(Collection allergies) throws ParseException {
+ assert allergies != null;
- if (tags.isEmpty()) {
+ if (allergies.isEmpty()) {
return Optional.empty();
}
- Collection tagSet = tags.size() == 1 && tags.contains("") ? Collections.emptySet() : tags;
- return Optional.of(ParserUtil.parseTags(tagSet));
+ Collection allergySet = allergies.size() == 1
+ && allergies.contains("") ? Collections.emptySet() : allergies;
+ return Optional.of(ParserUtil.parseAllergies(allergySet));
}
}
diff --git a/src/main/java/seedu/address/logic/parser/EditRecordCommandParser.java b/src/main/java/seedu/address/logic/parser/EditRecordCommandParser.java
new file mode 100644
index 00000000000..e239143774f
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/EditRecordCommandParser.java
@@ -0,0 +1,97 @@
+package seedu.address.logic.parser;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_CONDITION;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_MEDICATION;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.EditCommand;
+import seedu.address.logic.commands.EditRecordCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.record.Condition;
+import seedu.address.model.record.Medication;
+
+/**
+ * Parses input arguments and creates a new EditRecordCommand object
+ */
+public class EditRecordCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the EditCommand
+ * and returns an EditCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public EditRecordCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ ArgumentMultimap argMultimap =
+ ArgumentTokenizer.tokenize(args, PREFIX_DATE, PREFIX_CONDITION, PREFIX_MEDICATION);
+
+ Index patientIndex;
+ Index recordIndex;
+
+ try {
+ String preamble = argMultimap.getPreamble();
+ patientIndex = ParserUtil.parsePatientIndex(preamble);
+ recordIndex = ParserUtil.parseRecordIndex(preamble);
+ } catch (ParseException pe) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ EditRecordCommand.MESSAGE_USAGE), pe);
+ }
+
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_DATE);
+
+ EditRecordCommand.EditRecordDescriptor editRecordDescriptor = new EditRecordCommand.EditRecordDescriptor();
+
+ if (argMultimap.getValue(PREFIX_DATE).isPresent()) {
+ editRecordDescriptor.setDateTime(ParserUtil.parseDateTime(argMultimap.getValue(PREFIX_DATE).get()));
+ }
+ parseConditionsForEdit(argMultimap.getAllValues(PREFIX_CONDITION))
+ .ifPresent(editRecordDescriptor::setConditions);
+
+ parseMedicationForEdit(argMultimap.getAllValues(PREFIX_MEDICATION))
+ .ifPresent(editRecordDescriptor::setMedications);
+
+ if (!editRecordDescriptor.isAnyFieldEdited()) {
+ throw new ParseException(EditCommand.MESSAGE_NOT_EDITED);
+ }
+
+ return new EditRecordCommand(patientIndex, recordIndex, editRecordDescriptor);
+ }
+
+ /**
+ * Parses {@code Collection condtions} into a {@code Set} if {@code condition} is non-empty.
+ * If {@code contion} contain only one element which is an empty string, it will be parsed into a
+ * {@code Set} containing zero allergies.
+ */
+ private Optional> parseConditionsForEdit(Collection conditions) throws ParseException {
+ assert conditions != null;
+
+ if (conditions.isEmpty()) {
+ return Optional.empty();
+ }
+ Collection conditionSet = conditions.size() == 1
+ && conditions.contains("") ? Collections.emptySet() : conditions;
+ return Optional.of(ParserUtil.parseConditions(conditionSet));
+ }
+ /**
+ * Parses {@code Collection medications} into a {@code Set} if {@code medications} is non-empty.
+ * If {@code medications} contain only one element which is an empty string, it will be parsed into a
+ * {@code Set} containing zero allergies.
+ */
+ private Optional> parseMedicationForEdit(Collection medications) throws ParseException {
+ assert medications != null;
+ if (medications.isEmpty()) {
+ return Optional.empty();
+ }
+ Collection medicationSet = medications.size() == 1
+ && medications.contains("") ? Collections.emptySet() : medications;
+ return Optional.of(ParserUtil.parseMedications(medicationSet));
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/FindRecordCommandParser.java b/src/main/java/seedu/address/logic/parser/FindRecordCommandParser.java
new file mode 100644
index 00000000000..4a167eee5a9
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/FindRecordCommandParser.java
@@ -0,0 +1,26 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import java.util.Arrays;
+
+import seedu.address.logic.commands.FindRecordCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.record.RecordContainsKeywordsPredicate;
+
+
+/**
+ * Parses input arguments and creates a new FindRecordCommand object
+ */
+public class FindRecordCommandParser implements Parser {
+ @Override
+ public FindRecordCommand parse(String args) throws ParseException {
+ String trimmedArgs = args.trim();
+ if (trimmedArgs.isEmpty()) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindRecordCommand.MESSAGE_USAGE));
+ }
+ String[] keywords = trimmedArgs.split("\\s+");
+ return new FindRecordCommand(new RecordContainsKeywordsPredicate(Arrays.asList(keywords)));
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/ParserUtil.java b/src/main/java/seedu/address/logic/parser/ParserUtil.java
index b117acb9c55..82b6716941c 100644
--- a/src/main/java/seedu/address/logic/parser/ParserUtil.java
+++ b/src/main/java/seedu/address/logic/parser/ParserUtil.java
@@ -2,30 +2,45 @@
import static java.util.Objects.requireNonNull;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import seedu.address.commons.core.index.Index;
import seedu.address.commons.util.StringUtil;
import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.person.Address;
+import seedu.address.model.person.Age;
+import seedu.address.model.person.Allergy;
+import seedu.address.model.person.BloodType;
import seedu.address.model.person.Email;
-import seedu.address.model.person.Name;
+import seedu.address.model.person.Gender;
import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
+import seedu.address.model.record.Condition;
+import seedu.address.model.record.Medication;
+import seedu.address.model.shared.DateTime;
+import seedu.address.model.shared.Name;
+import seedu.address.model.shared.Nric;
/**
- * Contains utility methods used for parsing strings in the various *Parser classes.
+ * Contains utility methods used for parsing strings in the various *Parser
+ * classes.
*/
public class ParserUtil {
+ public static final String MESSAGE_INVALID_INPUT = "Index input is not in the format of number/number";
public static final String MESSAGE_INVALID_INDEX = "Index is not a non-zero unsigned integer.";
+ public static final String MESSAGE_INVALID_PATIENT_INDEX = "Patient index is not a non-zero unsigned integer.";
+ public static final String MESSAGE_INVALID_RECORD_INDEX = "Record index is not a non-zero unsigned integer.";
/**
- * Parses {@code oneBasedIndex} into an {@code Index} and returns it. Leading and trailing whitespaces will be
+ * Parses {@code oneBasedIndex} into an {@code Index} and returns it. Leading
+ * and trailing whitespaces will be
* trimmed.
- * @throws ParseException if the specified index is invalid (not non-zero unsigned integer).
+ *
+ * @throws ParseException if the specified index is invalid (not non-zero
+ * unsigned integer).
*/
public static Index parseIndex(String oneBasedIndex) throws ParseException {
String trimmedIndex = oneBasedIndex.trim();
@@ -35,6 +50,42 @@ public static Index parseIndex(String oneBasedIndex) throws ParseException {
return Index.fromOneBased(Integer.parseInt(trimmedIndex));
}
+ /**
+ * Parses {@code oneBasedIndexes} for edit record command into a patient's
+ * {@code Index}
+ * and returns it. Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the specified patient index is invalid (not
+ * non-zero unsigned integer).
+ */
+ public static Index parsePatientIndex(String oneBasedIndexes) throws ParseException {
+ // Check if input matches the format of two numbers separated by a slash
+ if (!oneBasedIndexes.matches("\\d+/\\d+")) {
+ throw new ParseException(MESSAGE_INVALID_INPUT);
+ }
+ String trimmedIndex = oneBasedIndexes.split("/")[0].trim();
+ if (!StringUtil.isNonZeroUnsignedInteger(trimmedIndex)) {
+ throw new ParseException(MESSAGE_INVALID_PATIENT_INDEX);
+ }
+ return Index.fromOneBased(Integer.parseInt(trimmedIndex));
+ }
+
+ /**
+ * Parses {@code oneBasedIndexes} for edit record command into a record's
+ * {@code Index}
+ * and returns it. Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the specified patient index is invalid (not
+ * non-zero unsigned integer).
+ */
+ public static Index parseRecordIndex(String oneBasedIndexes) throws ParseException {
+ String trimmedIndex = oneBasedIndexes.split("/")[1].trim();
+ if (!StringUtil.isNonZeroUnsignedInteger(trimmedIndex)) {
+ throw new ParseException(MESSAGE_INVALID_RECORD_INDEX);
+ }
+ return Index.fromOneBased(Integer.parseInt(trimmedIndex));
+ }
+
/**
* Parses a {@code String name} into a {@code Name}.
* Leading and trailing whitespaces will be trimmed.
@@ -50,6 +101,21 @@ public static Name parseName(String name) throws ParseException {
return new Name(trimmedName);
}
+ /**
+ * Parses a {@code String nric} into a {@code Nric}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given {@code nric} is invalid.
+ */
+ public static Nric parseNric(String nric) throws ParseException {
+ requireNonNull(nric);
+ String trimmedNric = nric.trim();
+ if (!Nric.isValidNric(trimmedNric)) {
+ throw new ParseException(Nric.MESSAGE_CONSTRAINTS);
+ }
+ return new Nric(trimmedNric.toUpperCase());
+ }
+
/**
* Parses a {@code String phone} into a {@code Phone}.
* Leading and trailing whitespaces will be trimmed.
@@ -66,18 +132,85 @@ public static Phone parsePhone(String phone) throws ParseException {
}
/**
- * Parses a {@code String address} into an {@code Address}.
+ * Parses a {@code String gender} into an {@code Gender}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given {@code gender} is invalid.
+ */
+ public static Gender parseGender(String gender) throws ParseException {
+ requireNonNull(gender);
+ String trimmedGender = gender.trim();
+ if (!Gender.isValidGender(trimmedGender)) {
+ throw new ParseException(Gender.MESSAGE_CONSTRAINTS);
+ }
+ return new Gender(trimmedGender);
+ }
+
+ /**
+ * Parses a {@code String age} into an {@code Age}.
* Leading and trailing whitespaces will be trimmed.
*
- * @throws ParseException if the given {@code address} is invalid.
+ * @throws ParseException if the given {@code age} is invalid.
*/
- public static Address parseAddress(String address) throws ParseException {
- requireNonNull(address);
- String trimmedAddress = address.trim();
- if (!Address.isValidAddress(trimmedAddress)) {
- throw new ParseException(Address.MESSAGE_CONSTRAINTS);
+ public static Age parseAge(String age) throws ParseException {
+ requireNonNull(age);
+ int trimmedAge = 0;
+ try {
+ trimmedAge = Integer.parseInt(age.trim());
+
+ } catch (NumberFormatException e) {
+ throw new ParseException(Age.MESSAGE_CONSTRAINTS);
}
- return new Address(trimmedAddress);
+ if (!Age.isValidAge(trimmedAge)) {
+ throw new ParseException(Age.MESSAGE_CONSTRAINTS);
+ }
+
+ return new Age(trimmedAge);
+ }
+
+ /**
+ * Parses a {@code String bloodType} into an {@code BloodType}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given {@code allergy} is invalid.
+ */
+ public static BloodType parseBloodType(String bloodType) throws ParseException {
+ requireNonNull(bloodType);
+ String trimmedBloodType = bloodType.trim();
+ if (!BloodType.isValidBloodType(trimmedBloodType)) {
+ throw new ParseException(BloodType.MESSAGE_CONSTRAINTS);
+ }
+ return new BloodType(trimmedBloodType);
+ }
+
+ /**
+ * Parses a {@code String allergy} into an {@code Allergy}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given {@code allergy} is invalid.
+ */
+ public static Allergy parseAllergy(String allergy) throws ParseException {
+ requireNonNull(allergy);
+ String trimmedAllergy = allergy.trim();
+ if (!Allergy.isValidAllergy(trimmedAllergy)) {
+ throw new ParseException(Allergy.MESSAGE_CONSTRAINTS);
+ }
+ return new Allergy(trimmedAllergy);
+ }
+
+ /**
+ * Parses a {@code Collection allergies} into an {@code Set}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given {@code allergy} is invalid.
+ */
+ public static Set parseAllergies(Collection allergies) throws ParseException {
+ requireNonNull(allergies);
+ final Set allergiesSet = new HashSet<>();
+ for (String allergy : allergies) {
+ allergiesSet.add(parseAllergy(allergy));
+ }
+ return allergiesSet;
}
/**
@@ -96,29 +229,79 @@ public static Email parseEmail(String email) throws ParseException {
}
/**
- * Parses a {@code String tag} into a {@code Tag}.
+ * Parses a {@code String dateTime} into an {@code DateTime}.
* Leading and trailing whitespaces will be trimmed.
*
- * @throws ParseException if the given {@code tag} is invalid.
+ * @throws ParseException if the given {@code dateTime} is invalid.
*/
- public static Tag parseTag(String tag) throws ParseException {
- requireNonNull(tag);
- String trimmedTag = tag.trim();
- if (!Tag.isValidTagName(trimmedTag)) {
- throw new ParseException(Tag.MESSAGE_CONSTRAINTS);
+ public static DateTime parseDateTime(String dateTime) throws ParseException {
+ requireNonNull(dateTime);
+ String trimmedDateTime = dateTime.trim();
+ if (!DateTime.isValidDateTime(trimmedDateTime)) {
+ throw new ParseException(DateTime.MESSAGE_CONSTRAINTS);
}
- return new Tag(trimmedTag);
+ return new DateTime(trimmedDateTime);
}
/**
- * Parses {@code Collection tags} into a {@code Set}.
+ * Parses a {@code String condition} into an {@code Condition}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given {@code condition} is invalid.
+ */
+ public static Condition parseCondition(String condition) throws ParseException {
+ requireNonNull(condition);
+ String trimmedCondition = condition.trim();
+ if (!Condition.isValidCondition(trimmedCondition)) {
+ throw new ParseException(Condition.MESSAGE_CONSTRAINTS);
+ }
+ return new Condition(trimmedCondition);
+ }
+
+ /**
+ * Parses a {@code Collection condtions} into an
+ * {@code List}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given {@code condition} is invalid.
+ */
+ public static List parseConditions(Collection conditions) throws ParseException {
+ requireNonNull(conditions);
+ final List conditionsList = new ArrayList<>();
+ for (String condition : conditions) {
+ conditionsList.add(parseCondition(condition));
+ }
+ return conditionsList;
+ }
+
+ /**
+ * Parses a {@code String medication} into an {@code Medication}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given {@code medication} is invalid.
+ */
+ public static Medication parseMedication(String medication) throws ParseException {
+ requireNonNull(medication);
+ String trimmedMedication = medication.trim();
+ if (!Medication.isValidMedication(trimmedMedication)) {
+ throw new ParseException(Medication.MESSAGE_CONSTRAINTS);
+ }
+ return new Medication(trimmedMedication);
+ }
+
+ /**
+ * Parses a {@code Collection medications} into an
+ * {@code List}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given {@code medication} is invalid.
*/
- public static Set parseTags(Collection tags) throws ParseException {
- requireNonNull(tags);
- final Set tagSet = new HashSet<>();
- for (String tagName : tags) {
- tagSet.add(parseTag(tagName));
+ public static List parseMedications(Collection medications) throws ParseException {
+ requireNonNull(medications);
+ final List medicationList = new ArrayList<>();
+ for (String medication : medications) {
+ medicationList.add(parseMedication(medication));
}
- return tagSet;
+ return medicationList;
}
}
diff --git a/src/main/java/seedu/address/logic/parser/PinCommandParser.java b/src/main/java/seedu/address/logic/parser/PinCommandParser.java
new file mode 100644
index 00000000000..8ed7846388a
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/PinCommandParser.java
@@ -0,0 +1,29 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.PinCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new PinCommand object
+ */
+public class PinCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the PinCommand
+ * and returns a PinCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public PinCommand parse(String args) throws ParseException {
+ try {
+ Index index = ParserUtil.parseIndex(args);
+ return new PinCommand(index);
+ } catch (ParseException pe) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, PinCommand.MESSAGE_USAGE), pe);
+ }
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/parser/UnpinCommandParser.java b/src/main/java/seedu/address/logic/parser/UnpinCommandParser.java
new file mode 100644
index 00000000000..462f994f6f6
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/UnpinCommandParser.java
@@ -0,0 +1,29 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.UnpinCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new UnpinCommand object
+ */
+public class UnpinCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the UnpinCommand
+ * and returns a UnpinCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public UnpinCommand parse(String args) throws ParseException {
+ try {
+ Index index = ParserUtil.parseIndex(args);
+ return new UnpinCommand(index);
+ } catch (ParseException pe) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, UnpinCommand.MESSAGE_USAGE), pe);
+ }
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/parser/ViewCommandParser.java b/src/main/java/seedu/address/logic/parser/ViewCommandParser.java
new file mode 100644
index 00000000000..5e925cc01b0
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/ViewCommandParser.java
@@ -0,0 +1,27 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.ViewCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parse input and create a View Command.
+ */
+
+public class ViewCommandParser implements Parser {
+
+ @Override
+ public ViewCommand parse(String args) throws ParseException {
+
+ Index index;
+ try {
+ index = ParserUtil.parseIndex(args);
+ return new ViewCommand(index);
+ } catch (ParseException pe) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ ViewCommand.MESSAGE_USAGE), pe);
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java
index 73397161e84..bfd3ac50715 100644
--- a/src/main/java/seedu/address/model/AddressBook.java
+++ b/src/main/java/seedu/address/model/AddressBook.java
@@ -1,13 +1,20 @@
package seedu.address.model;
import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+import java.util.ArrayList;
import java.util.List;
import javafx.collections.ObservableList;
+import seedu.address.commons.core.index.Index;
import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.model.appointment.Appointment;
+import seedu.address.model.appointment.UniqueAppointmentList;
import seedu.address.model.person.Person;
import seedu.address.model.person.UniquePersonList;
+import seedu.address.model.record.Record;
+import seedu.address.model.record.UniqueRecordList;
/**
* Wraps all data at the address-book level
@@ -16,19 +23,29 @@
public class AddressBook implements ReadOnlyAddressBook {
private final UniquePersonList persons;
+ private final UniquePersonList personBeingViewed;
+ private final UniqueRecordList records;
+ private final UniqueAppointmentList appointments;
/*
- * The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication
- * between constructors. See https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html
+ * The 'unusual' code block below is a non-static initialization block,
+ * sometimes used to avoid duplication
+ * between constructors. See
+ * https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html
*
- * Note that non-static init blocks are not recommended to use. There are other ways to avoid duplication
- * among constructors.
+ * Note that non-static init blocks are not recommended to use. There are other
+ * ways to avoid duplication
+ * among constructors.
*/
{
persons = new UniquePersonList();
+ records = new UniqueRecordList();
+ appointments = new UniqueAppointmentList();
+ personBeingViewed = new UniquePersonList();
}
- public AddressBook() {}
+ public AddressBook() {
+ }
/**
* Creates an AddressBook using the Persons in the {@code toBeCopied}
@@ -48,25 +65,42 @@ public void setPersons(List persons) {
this.persons.setPersons(persons);
}
+ public void setRecords(Person person) {
+ ArrayList beingViewed = new ArrayList<>();
+ beingViewed.add(person);
+ this.personBeingViewed.setPersons(beingViewed);
+ this.records.setRecords(person.getRecords());
+ }
+
/**
* Resets the existing data of this {@code AddressBook} with {@code newData}.
*/
public void resetData(ReadOnlyAddressBook newData) {
requireNonNull(newData);
- setPersons(newData.getPersonList());
+ List fullList = newData.getPersonList();
+ setPersons(fullList);
}
//// person-level operations
/**
- * Returns true if a person with the same identity as {@code person} exists in the address book.
+ * Returns true if a person with the same identity as {@code person} exists in
+ * the address book.
*/
public boolean hasPerson(Person person) {
requireNonNull(person);
return persons.contains(person);
}
+ /**
+ * Returns true if the same record under the patient at {@code index} exists in the Medbook.
+ */
+ public boolean hasRecord(Record record, Index index) {
+ requireAllNonNull(record, index);
+ return persons.asUnmodifiableObservableList().get(index.getZeroBased()).getRecords().contains(record);
+ }
+
/**
* Adds a person to the address book.
* The person must not already exist in the address book.
@@ -76,9 +110,11 @@ public void addPerson(Person p) {
}
/**
- * Replaces the given person {@code target} in the list with {@code editedPerson}.
+ * Replaces the given person {@code target} in the list with
+ * {@code editedPerson}.
* {@code target} must exist in the address book.
- * The person identity of {@code editedPerson} must not be the same as another existing person in the address book.
+ * The person identity of {@code editedPerson} must not be the same as another
+ * existing person in the address book.
*/
public void setPerson(Person target, Person editedPerson) {
requireNonNull(editedPerson);
@@ -108,6 +144,32 @@ public ObservableList getPersonList() {
return persons.asUnmodifiableObservableList();
}
+ public ObservableList getRecordList() {
+ return records.asUnmodifiableObservableList();
+ }
+
+ public ObservableList getAppointmentList() {
+ resetAppointmentList();
+ return appointments.asUnmodifiableObservableList();
+ }
+
+ /**
+ * Resets the appointment list to include all appointments from all persons.
+ */
+ public void resetAppointmentList() {
+ UniqueAppointmentList newList = new UniqueAppointmentList();
+ for (Person person : persons) {
+ for (Appointment appointment : person.getAppointments()) {
+ newList.add(appointment);
+ }
+ }
+ appointments.setAppointments(newList);
+ }
+
+ public ObservableList getPersonBeingViewed() {
+ return personBeingViewed.asUnmodifiableObservableList();
+ }
+
@Override
public boolean equals(Object other) {
if (other == this) {
diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java
index d54df471c1f..eecccf52f84 100644
--- a/src/main/java/seedu/address/model/Model.java
+++ b/src/main/java/seedu/address/model/Model.java
@@ -5,7 +5,10 @@
import javafx.collections.ObservableList;
import seedu.address.commons.core.GuiSettings;
+import seedu.address.commons.core.index.Index;
+import seedu.address.model.appointment.Appointment;
import seedu.address.model.person.Person;
+import seedu.address.model.record.Record;
/**
* The API of the Model component.
@@ -13,6 +16,10 @@
public interface Model {
/** {@code Predicate} that always evaluate to true */
Predicate PREDICATE_SHOW_ALL_PERSONS = unused -> true;
+ Predicate PREDICATE_SHOW_ALL_RECORDS = unused -> true;
+
+ /** {@code Predicate} that always evaluate to true */
+ Predicate PREDICATE_SHOW_ALL_APPOINTMENTS = unused -> true;
/**
* Replaces user prefs data with the data in {@code userPrefs}.
@@ -53,10 +60,16 @@ public interface Model {
ReadOnlyAddressBook getAddressBook();
/**
- * Returns true if a person with the same identity as {@code person} exists in the address book.
+ * Returns true if a person with the same identity as {@code person} exists in
+ * the address book.
*/
boolean hasPerson(Person person);
+ /**
+ * Returns true if the same record exists under the patient in the Medbook
+ */
+ boolean hasRecord(Record record, Index index);
+
/**
* Deletes the given person.
* The person must exist in the address book.
@@ -72,7 +85,8 @@ public interface Model {
/**
* Replaces the given person {@code target} with {@code editedPerson}.
* {@code target} must exist in the address book.
- * The person identity of {@code editedPerson} must not be the same as another existing person in the address book.
+ * The person identity of {@code editedPerson} must not be the same as another
+ * existing person in the address book.
*/
void setPerson(Person target, Person editedPerson);
@@ -80,8 +94,38 @@ public interface Model {
ObservableList getFilteredPersonList();
/**
- * Updates the filter of the filtered person list to filter by the given {@code predicate}.
- * @throws NullPointerException if {@code predicate} is null.
+ * Updates the filter of the filtered person list to filter by the given
+ * {@code predicate}.
+ *
+ * @throws NullPointerException
+ * if {@code predicate} is null.
*/
void updateFilteredPersonList(Predicate predicate);
+
+ /** Returns an unmodifiable view of the pinned person list */
+ ObservableList getPinnedPersonList();
+
+ /** Returns an unmodifiable view of the filtered appointment list */
+ ObservableList getAppointmentList();
+
+ void resetAppointmentList();
+
+ ObservableList getRecordList();
+
+ /** Returns an unmodifiable view of the filtered record list */
+ ObservableList getFilteredRecordList();
+
+ void updateRecordList(Person person);
+
+ /**
+ * Updates the filter of the filtered record list to filter by the given
+ * {@code predicate}.
+ *
+ * @throws NullPointerException
+ * if {@code predicate} is null.
+ */
+ void updateFilteredRecordList(Predicate predicate);
+
+ ObservableList getPersonBeingViewed();
+
}
diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java
index 57bc563fde6..a9b108ff39c 100644
--- a/src/main/java/seedu/address/model/ModelManager.java
+++ b/src/main/java/seedu/address/model/ModelManager.java
@@ -11,17 +11,21 @@
import javafx.collections.transformation.FilteredList;
import seedu.address.commons.core.GuiSettings;
import seedu.address.commons.core.LogsCenter;
+import seedu.address.commons.core.index.Index;
+import seedu.address.model.appointment.Appointment;
import seedu.address.model.person.Person;
+import seedu.address.model.record.Record;
/**
* Represents the in-memory model of the address book data.
*/
public class ModelManager implements Model {
private static final Logger logger = LogsCenter.getLogger(ModelManager.class);
-
+ private static ModelManager instance;
private final AddressBook addressBook;
- private final UserPrefs userPrefs;
private final FilteredList filteredPersons;
+ private final FilteredList filteredRecords;
+ private final UserPrefs userPrefs;
/**
* Initializes a ModelManager with the given addressBook and userPrefs.
@@ -34,13 +38,23 @@ public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs
this.addressBook = new AddressBook(addressBook);
this.userPrefs = new UserPrefs(userPrefs);
filteredPersons = new FilteredList<>(this.addressBook.getPersonList());
+ filteredRecords = new FilteredList<>(this.addressBook.getRecordList());
+ instance = this;
}
public ModelManager() {
this(new AddressBook(), new UserPrefs());
}
- //=========== UserPrefs ==================================================================================
+ public static Model getInstance() {
+ if (instance == null) {
+ instance = new ModelManager(/* necessary initializations */);
+ }
+ return instance;
+ }
+
+ // =========== UserPrefs
+ // ==================================================================================
@Override
public void setUserPrefs(ReadOnlyUserPrefs userPrefs) {
@@ -75,7 +89,8 @@ public void setAddressBookFilePath(Path addressBookFilePath) {
userPrefs.setAddressBookFilePath(addressBookFilePath);
}
- //=========== AddressBook ================================================================================
+ // =========== AddressBook
+ // ================================================================================
@Override
public void setAddressBook(ReadOnlyAddressBook addressBook) {
@@ -93,6 +108,12 @@ public boolean hasPerson(Person person) {
return addressBook.hasPerson(person);
}
+ @Override
+ public boolean hasRecord(Record record, Index index) {
+ requireAllNonNull(record, index);
+ return addressBook.hasRecord(record, index);
+ }
+
@Override
public void deletePerson(Person target) {
addressBook.removePerson(target);
@@ -107,14 +128,15 @@ public void addPerson(Person person) {
@Override
public void setPerson(Person target, Person editedPerson) {
requireAllNonNull(target, editedPerson);
-
addressBook.setPerson(target, editedPerson);
}
- //=========== Filtered Person List Accessors =============================================================
+ // =========== Filtered Person List Accessors
+ // =============================================================
/**
- * Returns an unmodifiable view of the list of {@code Person} backed by the internal list of
+ * Returns an unmodifiable view of the list of {@code Person} backed by the
+ * internal list of
* {@code versionedAddressBook}
*/
@Override
@@ -128,6 +150,56 @@ public void updateFilteredPersonList(Predicate predicate) {
filteredPersons.setPredicate(predicate);
}
+ @Override
+ public ObservableList getPinnedPersonList() {
+ FilteredList pinnedPersons = new FilteredList<>(this.addressBook.getPersonList());
+ pinnedPersons.setPredicate(person -> person.isPinned());
+ return pinnedPersons;
+ }
+
+ /**
+ * Returns an unmodifiable view of the list of {@code Person} backed by the
+ * internal list of
+ * {@code versionedAddressBook}
+ */
+ @Override
+ public ObservableList getAppointmentList() {
+ return this.addressBook.getAppointmentList();
+ }
+
+ @Override
+ public void resetAppointmentList() {
+ this.addressBook.resetAppointmentList();
+ }
+
+ @Override
+ public ObservableList getRecordList() {
+ return this.addressBook.getRecordList();
+ }
+
+ @Override
+ public ObservableList getFilteredRecordList() {
+ return filteredRecords;
+ }
+
+ @Override
+ public void updateRecordList(Person person) {
+ requireNonNull(person);
+ this.addressBook.setRecords(person);
+ updateFilteredRecordList(PREDICATE_SHOW_ALL_RECORDS);
+ }
+
+ @Override
+ public void updateFilteredRecordList(Predicate predicate) {
+ requireNonNull(predicate);
+ filteredRecords.setPredicate(predicate);
+ }
+
+ @Override
+ public ObservableList getPersonBeingViewed() {
+ return this.addressBook.getPersonBeingViewed();
+ }
+
@Override
public boolean equals(Object other) {
if (other == this) {
@@ -142,7 +214,7 @@ 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)
+ && filteredRecords.equals(otherModelManager.filteredRecords);
}
-
}
diff --git a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java
index 6ddc2cd9a29..6f862be2eca 100644
--- a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java
+++ b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java
@@ -13,5 +13,4 @@ public interface ReadOnlyAddressBook {
* This list will not contain any duplicate persons.
*/
ObservableList getPersonList();
-
}
diff --git a/src/main/java/seedu/address/model/UserPrefs.java b/src/main/java/seedu/address/model/UserPrefs.java
index 6be655fb4c7..47ab6f836a1 100644
--- a/src/main/java/seedu/address/model/UserPrefs.java
+++ b/src/main/java/seedu/address/model/UserPrefs.java
@@ -14,7 +14,7 @@
public class UserPrefs implements ReadOnlyUserPrefs {
private GuiSettings guiSettings = new GuiSettings();
- private Path addressBookFilePath = Paths.get("data" , "addressbook.json");
+ private Path addressBookFilePath = Paths.get("data" , "medbook.json");
/**
* Creates a {@code UserPrefs} with default values.
diff --git a/src/main/java/seedu/address/model/appointment/Appointment.java b/src/main/java/seedu/address/model/appointment/Appointment.java
new file mode 100644
index 00000000000..da2af244f3f
--- /dev/null
+++ b/src/main/java/seedu/address/model/appointment/Appointment.java
@@ -0,0 +1,77 @@
+package seedu.address.model.appointment;
+
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+
+import java.util.Objects;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.model.shared.DateTime;
+import seedu.address.model.shared.Name;
+import seedu.address.model.shared.Nric;
+
+/**
+ * Represents an Appointment in the address book.
+ * Guarantees: details are present and not null, field values are validated,
+ * immutable.
+ */
+public class Appointment {
+
+ // Identity fields
+ private final Name name;
+ private final DateTime dateTime;
+ private final Nric nric;
+
+ /**
+ * Every field must be present and not null.
+ */
+ public Appointment(Name name, DateTime dateTime, Nric nric) {
+ requireAllNonNull(name, dateTime);
+ this.name = name;
+ this.dateTime = dateTime;
+ this.nric = nric;
+ }
+
+ public Name getName() {
+ return name;
+ }
+
+ public DateTime getDateTime() {
+ return dateTime;
+ }
+
+ public Nric getNric() {
+ return nric;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof Appointment)) {
+ return false;
+ }
+
+ Appointment otherAppointment = (Appointment) other;
+ return name.equals(otherAppointment.name)
+ && dateTime.equals(otherAppointment.dateTime)
+ && ((nric == null && otherAppointment.nric == null) || nric.equals(otherAppointment.nric));
+ }
+
+ @Override
+ public int hashCode() {
+ // use this method for custom fields hashing instead of implementing your own
+ return Objects.hash(name, dateTime);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("name", name)
+ .add("dateTime", dateTime)
+ .add("nric", nric)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/model/appointment/UniqueAppointmentList.java b/src/main/java/seedu/address/model/appointment/UniqueAppointmentList.java
new file mode 100644
index 00000000000..16f09078353
--- /dev/null
+++ b/src/main/java/seedu/address/model/appointment/UniqueAppointmentList.java
@@ -0,0 +1,148 @@
+package seedu.address.model.appointment;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+
+import java.util.Iterator;
+import java.util.List;
+
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import seedu.address.model.appointment.exceptions.AppointmentNotFoundException;
+import seedu.address.model.appointment.exceptions.DuplicateAppointmentException;
+
+/**
+ * A list of appointments that enforces uniqueness between its elements and does not allow nulls.
+ * An appointment is considered unique by comparing using {@code Appointment#equals(Appointment)}.
+ *
+ * Supports a minimal set of list operations.
+ *
+ * @see Appointment#equals(Appointment)
+ */
+public class UniqueAppointmentList implements Iterable {
+
+ private final ObservableList internalList = FXCollections.observableArrayList();
+ private final ObservableList internalUnmodifiableList =
+ FXCollections.unmodifiableObservableList(internalList);
+
+ /**
+ * Returns true if the list contains an equivalent appointment as the given argument.
+ */
+ public boolean contains(Appointment toCheck) {
+ requireNonNull(toCheck);
+ return internalList.stream().anyMatch(toCheck::equals);
+ }
+
+ /**
+ * Adds a appointment to the list.
+ * The appointment must not already exist in the list.
+ */
+ public void add(Appointment toAdd) {
+ requireNonNull(toAdd);
+ if (contains(toAdd)) {
+ throw new DuplicateAppointmentException();
+ }
+ internalList.add(toAdd);
+ }
+
+ /**
+ * Replaces the appointment {@code target} in the list with {@code editedappointment}.
+ * {@code target} must exist in the list.
+ * The appointment identity of {@code editedappointment} must not be the same as
+ * another existing appointment in the list.
+ */
+ public void setAppointment(Appointment target, Appointment editedappointment) {
+ requireAllNonNull(target, editedappointment);
+
+ int index = internalList.indexOf(target);
+ if (index == -1) {
+ throw new AppointmentNotFoundException();
+ }
+
+ if (!target.equals(editedappointment) && contains(editedappointment)) {
+ throw new DuplicateAppointmentException();
+ }
+
+ internalList.set(index, editedappointment);
+ }
+
+ /**
+ * Removes the equivalent appointment from the list.
+ * The appointment must exist in the list.
+ */
+ public void remove(Appointment toRemove) {
+ requireNonNull(toRemove);
+ if (!internalList.remove(toRemove)) {
+ throw new AppointmentNotFoundException();
+ }
+ }
+
+ public void setAppointments(UniqueAppointmentList replacement) {
+ requireNonNull(replacement);
+ internalList.setAll(replacement.internalList);
+ }
+
+ /**
+ * Replaces the contents of this list with {@code appointments}.
+ * {@code appointments} must not contain duplicate appointments.
+ */
+ public void setAppointments(List appointments) {
+ requireAllNonNull(appointments);
+ if (!appointmentsAreUnique(appointments)) {
+ throw new DuplicateAppointmentException();
+ }
+
+ internalList.setAll(appointments);
+ }
+
+ /**
+ * Returns the backing list as an unmodifiable {@code ObservableList}.
+ */
+ public ObservableList asUnmodifiableObservableList() {
+ return internalUnmodifiableList;
+ }
+
+ @Override
+ public Iterator iterator() {
+ return internalList.iterator();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof UniqueAppointmentList)) {
+ return false;
+ }
+
+ UniqueAppointmentList otherUniqueAppointmentList = (UniqueAppointmentList) other;
+ return internalList.equals(otherUniqueAppointmentList.internalList);
+ }
+
+ @Override
+ public int hashCode() {
+ return internalList.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return internalList.toString();
+ }
+
+ /**
+ * Returns true if {@code appointments} contains only unique appointments.
+ */
+ private boolean appointmentsAreUnique(List appointments) {
+ for (int i = 0; i < appointments.size() - 1; i++) {
+ for (int j = i + 1; j < appointments.size(); j++) {
+ if (appointments.get(i).equals(appointments.get(j))) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/seedu/address/model/appointment/exceptions/AppointmentNotFoundException.java b/src/main/java/seedu/address/model/appointment/exceptions/AppointmentNotFoundException.java
new file mode 100644
index 00000000000..5dc5c48d3d8
--- /dev/null
+++ b/src/main/java/seedu/address/model/appointment/exceptions/AppointmentNotFoundException.java
@@ -0,0 +1,6 @@
+package seedu.address.model.appointment.exceptions;
+
+/**
+ * Signals that the operation is unable to find the specified appointment.
+ */
+public class AppointmentNotFoundException extends RuntimeException {}
diff --git a/src/main/java/seedu/address/model/appointment/exceptions/DuplicateAppointmentException.java b/src/main/java/seedu/address/model/appointment/exceptions/DuplicateAppointmentException.java
new file mode 100644
index 00000000000..bbbaa46ea89
--- /dev/null
+++ b/src/main/java/seedu/address/model/appointment/exceptions/DuplicateAppointmentException.java
@@ -0,0 +1,11 @@
+package seedu.address.model.appointment.exceptions;
+
+/**
+ * Signals that the operation will result in duplicate Appointments (Appointments are considered
+ * duplicates if they have the same identity).
+ */
+public class DuplicateAppointmentException extends RuntimeException {
+ public DuplicateAppointmentException() {
+ super("Operation would result in duplicate Appointments");
+ }
+}
diff --git a/src/main/java/seedu/address/model/person/Address.java b/src/main/java/seedu/address/model/person/Address.java
deleted file mode 100644
index 469a2cc9a1e..00000000000
--- a/src/main/java/seedu/address/model/person/Address.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package seedu.address.model.person;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.commons.util.AppUtil.checkArgument;
-
-/**
- * Represents a Person's address in the address book.
- * Guarantees: immutable; is valid as declared in {@link #isValidAddress(String)}
- */
-public class Address {
-
- public static final String MESSAGE_CONSTRAINTS = "Addresses can take any values, and it should not be blank";
-
- /*
- * The first character of the address must not be a whitespace,
- * otherwise " " (a blank string) becomes a valid input.
- */
- public static final String VALIDATION_REGEX = "[^\\s].*";
-
- public final String value;
-
- /**
- * Constructs an {@code Address}.
- *
- * @param address A valid address.
- */
- public Address(String address) {
- requireNonNull(address);
- checkArgument(isValidAddress(address), MESSAGE_CONSTRAINTS);
- value = address;
- }
-
- /**
- * Returns true if a given string is a valid email.
- */
- public static boolean isValidAddress(String test) {
- return test.matches(VALIDATION_REGEX);
- }
-
- @Override
- public String toString() {
- return value;
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof Address)) {
- return false;
- }
-
- Address otherAddress = (Address) other;
- return value.equals(otherAddress.value);
- }
-
- @Override
- public int hashCode() {
- return value.hashCode();
- }
-
-}
diff --git a/src/main/java/seedu/address/model/person/Age.java b/src/main/java/seedu/address/model/person/Age.java
new file mode 100644
index 00000000000..8bee93180f3
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/Age.java
@@ -0,0 +1,59 @@
+package seedu.address.model.person;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.AppUtil.checkArgument;
+
+/**
+ * Represents a Patient's age in the MedBook.
+ * Guarantees: immutable; is valid as declared in {@link #isValidAge(Integer)}
+ */
+public class Age {
+ public static final String MESSAGE_CONSTRAINTS =
+ "Age should only be a non-negative integer";
+
+ public final Integer age;
+
+ /**
+ * Constructs a {@code Age}.
+ *
+ * @param age A valid age.
+ */
+ public Age(Integer age) {
+ requireNonNull(age);
+ checkArgument(isValidAge(age), MESSAGE_CONSTRAINTS);
+ this.age = age;
+ }
+
+ /**
+ * Returns true if a given integer is a valid age.
+ */
+ public static boolean isValidAge(Integer test) {
+ return test >= 0;
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(this.age);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof Age)) {
+ return false;
+ }
+
+ Age otherAge = (Age) other;
+ return this.age == otherAge.age;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.age.hashCode();
+ }
+
+}
diff --git a/src/main/java/seedu/address/model/person/Allergy.java b/src/main/java/seedu/address/model/person/Allergy.java
new file mode 100644
index 00000000000..708d6ab1387
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/Allergy.java
@@ -0,0 +1,64 @@
+package seedu.address.model.person;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.AppUtil.checkArgument;
+
+/**
+ * Represents an allergy in the address book.
+ * Guarantees: immutable; name is valid as declared in {@link #isValidAllergy(String)}
+ */
+public class Allergy {
+
+ public static final String MESSAGE_CONSTRAINTS = "Allergy names should be alphanumeric";
+
+ public static final String VALIDATION_REGEX = "\\p{Alnum}+";
+
+ public final String allergy;
+
+ /**
+ * Constructs a {@code Allergy}.
+ *
+ * @param allergy A valid allergy name.
+ */
+
+ public Allergy(String allergy) {
+ requireNonNull(allergy);
+ checkArgument(isValidAllergy(allergy), MESSAGE_CONSTRAINTS);
+ this.allergy = allergy;
+ }
+
+ /**
+ * Returns true if a given string is a valid tag name.
+ */
+ public static boolean isValidAllergy(String test) {
+ return test.matches(VALIDATION_REGEX);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof Allergy)) {
+ return false;
+ }
+
+ Allergy otherAllergy = (Allergy) other;
+ return allergy.equals(otherAllergy.allergy);
+ }
+
+ @Override
+ public int hashCode() {
+ return allergy.hashCode();
+ }
+
+ /**
+ * Format state as text for viewing.
+ */
+ public String toString() {
+ return '[' + allergy + ']';
+ }
+
+}
diff --git a/src/main/java/seedu/address/model/person/BloodType.java b/src/main/java/seedu/address/model/person/BloodType.java
new file mode 100644
index 00000000000..eee84072c0e
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/BloodType.java
@@ -0,0 +1,69 @@
+package seedu.address.model.person;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.AppUtil.checkArgument;
+
+import java.util.Objects;
+
+/**
+ * Represents a Patient's blood type in the MedBook.
+ * Guarantees: immutable; is valid as declared in
+ * {@link #isValidBloodType(String)}
+ */
+public class BloodType {
+ public static final String
+ MESSAGE_CONSTRAINTS = "Blood Type should only be one of the following: A+, A-, B+, B-, AB+, AB-, O+, O-";
+
+ public static final String[] POSSIBLE_BLOOD_TYPES = { "A+", "A-", "B+", "B-", "AB+", "AB-", "O+", "O-" };
+
+ public final String bloodType;
+
+ /**
+ * Constructs a {@code Gender}.
+ *
+ * @param bloodType A valid blood type.
+ */
+ public BloodType(String bloodType) {
+ requireNonNull(bloodType);
+ checkArgument(isValidBloodType(bloodType), MESSAGE_CONSTRAINTS);
+ this.bloodType = bloodType;
+ }
+
+ /**
+ * Returns true if a given string is a valid gender.
+ */
+ public static boolean isValidBloodType(String test) {
+ for (int i = 0; i < POSSIBLE_BLOOD_TYPES.length; i++) {
+ if (test.equals(POSSIBLE_BLOOD_TYPES[i])) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return this.bloodType;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof BloodType)) {
+ return false;
+ }
+
+ BloodType otherBloodType = (BloodType) other;
+ return Objects.equals(this.bloodType, otherBloodType.bloodType);
+ }
+
+ @Override
+ public int hashCode() {
+ return this.bloodType.hashCode();
+ }
+}
diff --git a/src/main/java/seedu/address/model/person/Gender.java b/src/main/java/seedu/address/model/person/Gender.java
new file mode 100644
index 00000000000..813a4cb79af
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/Gender.java
@@ -0,0 +1,62 @@
+package seedu.address.model.person;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.AppUtil.checkArgument;
+
+/**
+ * Represents a Patient's gender in the MedBook.
+ * Guarantees: immutable; is valid as declared in {@link #isValidGender(String)}
+ */
+public class Gender {
+
+ public static final String MESSAGE_CONSTRAINTS =
+ "Gender should only be either M or F";
+
+ public final String gender;
+
+ /**
+ * Constructs a {@code Gender}.
+ *
+ * @param gender A valid gender.
+ */
+ public Gender(String gender) {
+ requireNonNull(gender);
+ checkArgument(isValidGender(gender), MESSAGE_CONSTRAINTS);
+ this.gender = gender;
+ }
+
+ /**
+ * Returns true if a given string is a valid gender.
+ */
+ public static boolean isValidGender(String test) {
+ return test.equals("M") || test.equals("F");
+ }
+
+ @Override
+ public String toString() {
+ return this.gender;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof Gender)) {
+ return false;
+ }
+
+ Gender otherGender = (Gender) other;
+ return this.gender.equals(otherGender.gender);
+ }
+
+ @Override
+ public int hashCode() {
+ return this.gender.hashCode();
+ }
+
+
+
+}
diff --git a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java
index 62d19be2977..3642a62b06e 100644
--- a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java
+++ b/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java
@@ -19,7 +19,22 @@ public NameContainsKeywordsPredicate(List keywords) {
@Override
public boolean test(Person person) {
return keywords.stream()
- .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getName().fullName, keyword));
+ .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getName().fullName, keyword)
+ ||
+ StringUtil.containsWordIgnoreCase(person.getNric().nric, keyword)
+ ||
+ StringUtil.containsWordIgnoreCase(person.getPhone().value, keyword)
+ ||
+ person.getAllergies().stream()
+ .anyMatch(hobby -> StringUtil.containsWordIgnoreCase(hobby.allergy, keyword))
+ ||
+ StringUtil.containsWordIgnoreCase(person.getGender().gender, keyword)
+ ||
+ StringUtil.containsWordIgnoreCase(person.getBloodType().bloodType, keyword)
+ ||
+ StringUtil.containsWordIgnoreCase(person.getAge().age.toString(), keyword)
+ ||
+ StringUtil.containsWordIgnoreCase(person.getEmail().value, keyword));
}
@Override
diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/address/model/person/Person.java
index abe8c46b535..2557d5dbb2a 100644
--- a/src/main/java/seedu/address/model/person/Person.java
+++ b/src/main/java/seedu/address/model/person/Person.java
@@ -1,5 +1,6 @@
package seedu.address.model.person;
+import static java.util.Objects.requireNonNull;
import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
import java.util.Collections;
@@ -8,39 +9,61 @@
import java.util.Set;
import seedu.address.commons.util.ToStringBuilder;
-import seedu.address.model.tag.Tag;
+import seedu.address.model.appointment.Appointment;
+import seedu.address.model.appointment.UniqueAppointmentList;
+import seedu.address.model.record.Record;
+import seedu.address.model.record.UniqueRecordList;
+import seedu.address.model.shared.Name;
+import seedu.address.model.shared.Nric;
/**
* Represents a Person in the address book.
- * Guarantees: details are present and not null, field values are validated, immutable.
+ * Guarantees: details are present and not null, field values are validated,
+ * immutable.
*/
public class Person {
// Identity fields
private final Name name;
+ private final Nric nric;
private final Phone phone;
private final Email email;
-
- // Data fields
- private final Address address;
- private final Set tags = new HashSet<>();
+ private final Gender gender;
+ private final Age age;
+ private final BloodType bloodType;
+ private final Set allergies = new HashSet<>();
+ private final UniqueRecordList records = new UniqueRecordList();
+ private final UniqueAppointmentList appointments = new UniqueAppointmentList();
+ private boolean isPinned;
/**
- * Every field must be present and not null.
+ * Constructs a Person
*/
- public Person(Name name, Phone phone, Email email, Address address, Set tags) {
- requireAllNonNull(name, phone, email, address, tags);
+ public Person(Name name, Nric nric, Email email, Phone phone, Gender gender, Age age,
+ BloodType bloodType, Set allergies, UniqueRecordList records, UniqueAppointmentList appointments,
+ boolean isPinned) {
+ requireAllNonNull(name, phone, email, gender, age, allergies, appointments, isPinned);
this.name = name;
- this.phone = phone;
+ this.nric = nric;
this.email = email;
- this.address = address;
- this.tags.addAll(tags);
+ this.phone = phone;
+ this.gender = gender;
+ this.age = age;
+ this.bloodType = bloodType;
+ this.allergies.addAll(allergies);
+ this.records.setRecords(records);
+ this.appointments.setAppointments(appointments);
+ this.isPinned = isPinned;
}
public Name getName() {
return name;
}
+ public Nric getNric() {
+ return nric;
+ }
+
public Phone getPhone() {
return phone;
}
@@ -49,20 +72,45 @@ public Email getEmail() {
return email;
}
- public Address getAddress() {
- return address;
+ public Gender getGender() {
+ return gender;
+ }
+
+ public boolean isPinned() {
+ return isPinned;
+ }
+
+ public Age getAge() {
+ return age;
+ }
+
+ public BloodType getBloodType() {
+ return bloodType;
+ }
+
+ public Set getAllergies() {
+ return Collections.unmodifiableSet(allergies);
+ }
+
+ public UniqueRecordList getRecords() {
+ return this.records;
+ }
+
+ public UniqueAppointmentList getAppointments() {
+ return this.appointments;
}
/**
- * Returns an immutable tag set, which throws {@code UnsupportedOperationException}
- * if modification is attempted.
+ * Returns true if the same appointment as {@code appointment} exists in the
+ * person.
*/
- public Set getTags() {
- return Collections.unmodifiableSet(tags);
+ public boolean hasAppointment(Appointment appointment) {
+ requireNonNull(appointment);
+ return appointments.contains(appointment);
}
/**
- * Returns true if both persons have the same name.
+ * Returns true if both persons have the same nric.
* This defines a weaker notion of equality between two persons.
*/
public boolean isSamePerson(Person otherPerson) {
@@ -71,7 +119,15 @@ public boolean isSamePerson(Person otherPerson) {
}
return otherPerson != null
- && otherPerson.getName().equals(getName());
+ && otherPerson.getNric().equals(getNric());
+ }
+
+ /**
+ * Adds a record to the patient
+ */
+ public void addRecord(Record record) {
+ requireNonNull(record);
+ this.records.add(record);
}
/**
@@ -91,27 +147,38 @@ public boolean equals(Object other) {
Person otherPerson = (Person) other;
return name.equals(otherPerson.name)
+ && nric.equals(otherPerson.nric)
&& phone.equals(otherPerson.phone)
&& email.equals(otherPerson.email)
- && address.equals(otherPerson.address)
- && tags.equals(otherPerson.tags);
+ && gender.equals(otherPerson.gender)
+ && age.equals(otherPerson.age)
+ && bloodType.equals(otherPerson.bloodType)
+ && allergies.equals(otherPerson.allergies)
+ && records.equals(otherPerson.records)
+ && appointments.equals(otherPerson.appointments)
+ && isPinned == otherPerson.isPinned;
}
@Override
public int hashCode() {
// use this method for custom fields hashing instead of implementing your own
- return Objects.hash(name, phone, email, address, tags);
+ return Objects.hash(name, email, phone, gender, age, bloodType, allergies);
}
@Override
public String toString() {
return new ToStringBuilder(this)
.add("name", name)
- .add("phone", phone)
+ .add("nric", nric)
.add("email", email)
- .add("address", address)
- .add("tags", tags)
+ .add("phone", phone)
+ .add("gender", gender)
+ .add("age", age)
+ .add("bloodType", bloodType)
+ .add("allergies", allergies)
+ .add("records", records)
+ .add("appointments", appointments)
+ .add("isPinned", isPinned)
.toString();
}
-
}
diff --git a/src/main/java/seedu/address/model/record/Condition.java b/src/main/java/seedu/address/model/record/Condition.java
new file mode 100644
index 00000000000..350d4ceebba
--- /dev/null
+++ b/src/main/java/seedu/address/model/record/Condition.java
@@ -0,0 +1,57 @@
+package seedu.address.model.record;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.AppUtil.checkArgument;
+
+/**
+ * Represents a condition of a patient. Recorded inside a Record object.
+ */
+public class Condition {
+
+ public static final String MESSAGE_CONSTRAINTS = "Conditions should only contain alphanumeric characters, "
+ + "dashes and spaces";
+
+ public static final String VALIDATION_REGEX = "[a-zA-Z0-9- ]+";
+
+ public final String condition;
+
+ /**
+ * Constructs a {@code Condition}.
+ *
+ * @param condition A valid condition name.
+ */
+ public Condition(String condition) {
+ requireNonNull(condition);
+ checkArgument(isValidCondition(condition), MESSAGE_CONSTRAINTS);
+ this.condition = condition;
+ }
+
+ public static boolean isValidCondition(String test) {
+ return test.matches(VALIDATION_REGEX);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof Condition)) {
+ return false;
+ }
+
+ Condition otherCondition = (Condition) other;
+ return condition.equals(otherCondition.condition);
+ }
+
+ @Override
+ public int hashCode() {
+ return condition.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return condition;
+ }
+}
diff --git a/src/main/java/seedu/address/model/record/Medication.java b/src/main/java/seedu/address/model/record/Medication.java
new file mode 100644
index 00000000000..49e4be27115
--- /dev/null
+++ b/src/main/java/seedu/address/model/record/Medication.java
@@ -0,0 +1,55 @@
+package seedu.address.model.record;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.AppUtil.checkArgument;
+
+/**
+ * Represents a medication that a patient receives. Recorded inside a Record
+ * object.
+ */
+public class Medication {
+ public static final String MESSAGE_CONSTRAINTS = "Medications should only contain alphanumeric characters, "
+ + "dashes and spaces";
+
+ public static final String VALIDATION_REGEX = "[a-zA-Z0-9- ]+";
+ public final String medication;
+
+ /**
+ * Constructs a {@code Medication}
+ *
+ * @param medication A valid medication name.
+ */
+ public Medication(String medication) {
+ requireNonNull(medication);
+ checkArgument(isValidMedication(medication), MESSAGE_CONSTRAINTS);
+ this.medication = medication;
+ }
+
+ public static boolean isValidMedication(String test) {
+ return test.matches(VALIDATION_REGEX);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+
+ if (!(other instanceof Medication)) {
+ return false;
+ }
+
+ Medication otherMedication = (Medication) other;
+ return this.medication.equals(otherMedication.medication);
+ }
+
+ @Override
+ public int hashCode() {
+ return medication.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return medication;
+ }
+}
diff --git a/src/main/java/seedu/address/model/record/Record.java b/src/main/java/seedu/address/model/record/Record.java
new file mode 100644
index 00000000000..f4ec214ed5a
--- /dev/null
+++ b/src/main/java/seedu/address/model/record/Record.java
@@ -0,0 +1,115 @@
+package seedu.address.model.record;
+
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.commands.EditRecordCommand;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.shared.DateTime;
+import seedu.address.storage.Storage;
+import seedu.address.storage.StorageManager;
+
+/**
+ * Record of condition of a patient and date and time in which a patient visits
+ * the doctor
+ */
+public class Record {
+
+ private final List conditions = new ArrayList<>();
+ private final List medications = new ArrayList<>();
+ private final DateTime dateTime;
+ private Path filePath;
+ private final Integer personIndex;
+
+ /**
+ * Constructs a record object
+ */
+ public Record(DateTime dateTime, List conditions, List medications, Path filePath,
+ Integer personIndex) {
+ requireAllNonNull(dateTime, conditions);
+ this.dateTime = dateTime;
+ this.conditions.addAll(conditions);
+ this.medications.addAll(medications);
+ this.filePath = filePath;
+ this.personIndex = personIndex;
+ }
+
+ public DateTime getDateTime() {
+ return dateTime;
+ }
+
+ public int getPersonIndex() {
+ return personIndex;
+ }
+
+ public List getConditions() {
+ return Collections.unmodifiableList(conditions);
+ }
+
+ public Path getFilePath() {
+ return filePath; // Getter for the file path
+ }
+
+ // Set the filePath. This is the new setter method
+ public void setFilePath(Path filePath, int displayedIndex) {
+ requireAllNonNull(filePath); // Ensure the provided filePath is not null
+ this.filePath = filePath;
+ Model model = ModelManager.getInstance();
+ EditRecordCommand.EditRecordDescriptor editRecordDescriptor = new EditRecordCommand.EditRecordDescriptor();
+ editRecordDescriptor.setFilePath(filePath);
+ try {
+ Storage storage = StorageManager.getInstance();
+ storage.saveAddressBook(model.getAddressBook());
+ } catch (Exception c) {
+ c.printStackTrace();
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ // use this method for custom fields hashing instead of implementing your own
+ return Objects.hash(dateTime, conditions, filePath, personIndex);
+ }
+
+ public List getMedications() {
+ return Collections.unmodifiableList(medications);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof Record)) {
+ return false;
+ }
+
+ Record otherRecord = (Record) other;
+ return dateTime.equals(otherRecord.dateTime)
+ && conditions.equals(otherRecord.conditions)
+ && Objects.equals(filePath, otherRecord.filePath)
+ && personIndex.equals(otherRecord.personIndex)
+ && medications.equals(otherRecord.medications);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("dateTime", dateTime)
+ .add("conditions", conditions)
+ .add("medications", medications)
+ .add("filePath", filePath)
+ .add("personIndex", personIndex)
+ .toString();
+ }
+
+}
diff --git a/src/main/java/seedu/address/model/record/RecordContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/record/RecordContainsKeywordsPredicate.java
new file mode 100644
index 00000000000..f92e1cd57e2
--- /dev/null
+++ b/src/main/java/seedu/address/model/record/RecordContainsKeywordsPredicate.java
@@ -0,0 +1,46 @@
+package seedu.address.model.record;
+
+import java.util.List;
+import java.util.function.Predicate;
+
+import seedu.address.commons.util.StringUtil;
+import seedu.address.commons.util.ToStringBuilder;
+
+/**
+ * Tests that a {@code Record} matches any of the keywords given.
+ */
+public class RecordContainsKeywordsPredicate implements Predicate {
+ private final List keywords;
+ public RecordContainsKeywordsPredicate(List keywords) {
+ this.keywords = keywords;
+ }
+ @Override
+ public boolean test(Record record) {
+ return keywords.stream()
+ .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(record.getDateTime().toString(), keyword)
+ ||
+ record.getConditions().stream()
+ .anyMatch(condition -> StringUtil.containsWordIgnoreCase(condition.condition, keyword))
+ ||
+ record.getMedications().stream()
+ .anyMatch(medication -> StringUtil.containsWordIgnoreCase(medication.medication,
+ keyword)));
+ }
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+ if (!(other instanceof RecordContainsKeywordsPredicate)) {
+ return false;
+ }
+ RecordContainsKeywordsPredicate otherRecordContainsKeywordsPredicate = (RecordContainsKeywordsPredicate) other;
+ return keywords.equals(otherRecordContainsKeywordsPredicate.keywords);
+ }
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("keywords", keywords)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/model/record/UniqueRecordList.java b/src/main/java/seedu/address/model/record/UniqueRecordList.java
new file mode 100644
index 00000000000..84d9879c254
--- /dev/null
+++ b/src/main/java/seedu/address/model/record/UniqueRecordList.java
@@ -0,0 +1,163 @@
+package seedu.address.model.record;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+
+import java.util.Iterator;
+import java.util.List;
+
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import seedu.address.model.record.exceptions.DuplicateRecordException;
+import seedu.address.model.record.exceptions.RecordNotFoundException;
+
+/**
+ * A list of records that enforces uniqueness between its elements and does not
+ * allow nulls.
+ * A record is considered unique by comparing using
+ * {@code Record#equals(Object)}. As such, adding, updating, removal of
+ * records uses Record#equals(Object) for equality so as to ensure that the
+ * record being added, updated, removed is
+ * unique in terms of identity in the UniqueRecordList.
+ *
+ * Supports a minimal set of list operations.
+ *
+ * @see Record#equals(Object)
+ */
+public class UniqueRecordList implements Iterable {
+ private final ObservableList internalList = FXCollections.observableArrayList();
+ private final ObservableList internalUnmodifiableList = FXCollections
+ .unmodifiableObservableList(internalList);
+
+ /**
+ * Returns true if the list contains an equivalent record as the given argument.
+ */
+ public boolean contains(Record toCheck) {
+ requireNonNull(toCheck);
+ return internalList.stream().anyMatch(toCheck::equals);
+ }
+
+ /**
+ * Returns a record at a specified index.
+ */
+ public Record get(int index) {
+ return internalList.get(index);
+ }
+
+ /**
+ * Returns the size of the internalList
+ */
+ public int size() {
+ return internalList.size();
+ }
+
+ /**
+ * Adds a record to the list
+ */
+ public void add(Record recordToAdd) {
+ requireNonNull(recordToAdd);
+ if (contains(recordToAdd)) {
+ throw new DuplicateRecordException();
+ }
+ internalList.add(recordToAdd);
+ }
+
+ /**
+ * Replaces the record {@code target} in the list with {@code editedRecord}.
+ * {@code target} must exist in the list.
+ * The record identity of {@code editedRecord} must not be the same as another
+ * existing record in the list.
+ */
+
+ public void setRecord(Record target, Record editedRecord) {
+ requireAllNonNull(target, editedRecord);
+
+ int index = internalList.indexOf(target);
+ if (index == -1) {
+ throw new RecordNotFoundException();
+ }
+
+ if (!target.equals(editedRecord) && contains(editedRecord)) {
+ throw new DuplicateRecordException();
+ }
+
+ internalList.set(index, editedRecord);
+ }
+
+ /**
+ * Removes the equivalent record from the list.
+ * The record must exist in the list.
+ */
+ public void remove(Record recordToRemove) {
+ requireNonNull(recordToRemove);
+ if (!internalList.remove(recordToRemove)) {
+ throw new RecordNotFoundException();
+ }
+ }
+
+ public void setRecords(UniqueRecordList replacement) {
+ requireNonNull(replacement);
+ internalList.setAll(replacement.internalList);
+ }
+
+ /**
+ * Replaces the contents of this list with {@code persons}.
+ * {@code persons} must not contain duplicate persons.
+ */
+ public void setRecords(List records) {
+ requireAllNonNull(records);
+ if (!recordsAreUnique(records)) {
+ throw new DuplicateRecordException();
+ }
+
+ internalList.setAll(records);
+ }
+
+ public ObservableList asUnmodifiableObservableList() {
+ return internalUnmodifiableList;
+ }
+
+ @Override
+ public Iterator iterator() {
+ return internalList.iterator();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof UniqueRecordList)) {
+ return false;
+ }
+
+ UniqueRecordList otherUniqueRecordList = (UniqueRecordList) other;
+ return internalList.equals(otherUniqueRecordList.internalList);
+ }
+
+ @Override
+ public int hashCode() {
+ return internalList.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return internalList.toString();
+ }
+
+ /**
+ * Returns true if {@code records} contains only unique records.
+ */
+ private boolean recordsAreUnique(List records) {
+ for (int i = 0; i < records.size() - 1; i++) {
+ for (int j = i + 1; j < records.size(); j++) {
+ if (records.get(i).equals(records.get(j))) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/seedu/address/model/record/exceptions/DuplicateRecordException.java b/src/main/java/seedu/address/model/record/exceptions/DuplicateRecordException.java
new file mode 100644
index 00000000000..87357dc3441
--- /dev/null
+++ b/src/main/java/seedu/address/model/record/exceptions/DuplicateRecordException.java
@@ -0,0 +1,13 @@
+package seedu.address.model.record.exceptions;
+
+/**
+ * Signals that the operation will result in duplicate Records (Records are considered duplicates if they have the same
+ * DateTime and Conditions).
+ */
+
+public class DuplicateRecordException extends RuntimeException {
+ public DuplicateRecordException() {
+ super("Operation would result in duplicate records");
+ }
+
+}
diff --git a/src/main/java/seedu/address/model/record/exceptions/RecordNotFoundException.java b/src/main/java/seedu/address/model/record/exceptions/RecordNotFoundException.java
new file mode 100644
index 00000000000..12a4a74b67d
--- /dev/null
+++ b/src/main/java/seedu/address/model/record/exceptions/RecordNotFoundException.java
@@ -0,0 +1,7 @@
+package seedu.address.model.record.exceptions;
+
+/**
+ * Signals that the operation is unable to find the specified record.
+ */
+public class RecordNotFoundException extends RuntimeException{
+}
diff --git a/src/main/java/seedu/address/model/shared/DateTime.java b/src/main/java/seedu/address/model/shared/DateTime.java
new file mode 100644
index 00000000000..d72159643b8
--- /dev/null
+++ b/src/main/java/seedu/address/model/shared/DateTime.java
@@ -0,0 +1,68 @@
+package seedu.address.model.shared;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.AppUtil.checkArgument;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.time.format.ResolverStyle;
+
+/**
+ * Represents the date and time in which patient visits the doctor
+ */
+public class DateTime {
+ public static final String MESSAGE_CONSTRAINTS = "DateTime should be in the format of 'dd-MM-yyyy HHmm'";
+
+ public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("dd-MM-uuuu HHmm")
+ .withResolverStyle(ResolverStyle.STRICT);
+ public final LocalDateTime dateTime;
+
+ /**
+ * Constructs a {@code DateTime}.
+ *
+ * @param dateTime A valid date and time.
+ */
+ public DateTime(String dateTime) {
+ requireNonNull(dateTime);
+ checkArgument(isValidDateTime(dateTime), MESSAGE_CONSTRAINTS);
+ this.dateTime = LocalDateTime.parse(dateTime, FORMATTER);
+ }
+
+ /**
+ * Returns true if a given string is a valid date-time.
+ */
+ public static boolean isValidDateTime(String test) {
+ try {
+ LocalDateTime parsedDateTime = LocalDateTime.parse(test, FORMATTER);
+ return parsedDateTime.getYear() > 0;
+ } catch (DateTimeParseException e) {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof DateTime)) {
+ return false;
+ }
+
+ DateTime otherDateTime = (DateTime) other;
+ return dateTime.equals(otherDateTime.dateTime);
+ }
+
+ @Override
+ public int hashCode() {
+ return dateTime.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return dateTime.format(FORMATTER);
+ }
+}
diff --git a/src/main/java/seedu/address/model/person/Name.java b/src/main/java/seedu/address/model/shared/Name.java
similarity index 85%
rename from src/main/java/seedu/address/model/person/Name.java
rename to src/main/java/seedu/address/model/shared/Name.java
index 173f15b9b00..3381e507859 100644
--- a/src/main/java/seedu/address/model/person/Name.java
+++ b/src/main/java/seedu/address/model/shared/Name.java
@@ -1,4 +1,4 @@
-package seedu.address.model.person;
+package seedu.address.model.shared;
import static java.util.Objects.requireNonNull;
import static seedu.address.commons.util.AppUtil.checkArgument;
@@ -10,13 +10,13 @@
public class Name {
public static final String MESSAGE_CONSTRAINTS =
- "Names should only contain alphanumeric characters and spaces, and it should not be blank";
+ "Names should only contain alphanumeric characters, spaces, dots, and dashes, and it should not be blank";
/*
- * The first character of the address must not be a whitespace,
+ * The first character of the name must not be a whitespace,
* otherwise " " (a blank string) becomes a valid input.
*/
- public static final String VALIDATION_REGEX = "[\\p{Alnum}][\\p{Alnum} ]*";
+ public static final String VALIDATION_REGEX = "[\\p{Alnum}][\\p{Alnum} .-]*";
public final String fullName;
@@ -38,7 +38,6 @@ public static boolean isValidName(String test) {
return test.matches(VALIDATION_REGEX);
}
-
@Override
public String toString() {
return fullName;
diff --git a/src/main/java/seedu/address/model/shared/Nric.java b/src/main/java/seedu/address/model/shared/Nric.java
new file mode 100644
index 00000000000..fa78a794cbf
--- /dev/null
+++ b/src/main/java/seedu/address/model/shared/Nric.java
@@ -0,0 +1,67 @@
+package seedu.address.model.shared;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.AppUtil.checkArgument;
+
+/**
+ * Represents a Person's Nric in the address book.
+ * Guarantees: immutable; is valid as declared in {@link #isValidNric(String)}
+ */
+public class Nric {
+
+ public static final String MESSAGE_CONSTRAINTS = "NRIC should follow the format: a letter, "
+ + "followed by seven digits, and then another letter";
+
+ /*
+ * The Nric starts with a letter (either uppercase or lowercase),
+ * followed by seven digits, and ends with another letter (either uppercase or
+ * lowercase).
+ */
+ public static final String VALIDATION_REGEX = "[A-Za-z]\\d{7}[A-Za-z]";
+
+ public final String nric;
+
+ /**
+ * Constructs a {@code Nric}.
+ *
+ * @param nric A valid Nric.
+ */
+ public Nric(String nric) {
+ requireNonNull(nric);
+ checkArgument(isValidNric(nric), MESSAGE_CONSTRAINTS);
+ this.nric = nric;
+ }
+
+ /**
+ * Returns true if a given string is a valid Nric.
+ */
+ public static boolean isValidNric(String test) {
+ return test.matches(VALIDATION_REGEX);
+ }
+
+ @Override
+ public String toString() {
+ return nric;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof Nric)) {
+ return false;
+ }
+
+ Nric otherNric = (Nric) other;
+ return nric.equals(otherNric.nric);
+ }
+
+ @Override
+ public int hashCode() {
+ return nric.hashCode();
+ }
+
+}
diff --git a/src/main/java/seedu/address/model/tag/Tag.java b/src/main/java/seedu/address/model/tag/Tag.java
deleted file mode 100644
index f1a0d4e233b..00000000000
--- a/src/main/java/seedu/address/model/tag/Tag.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package seedu.address.model.tag;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.commons.util.AppUtil.checkArgument;
-
-/**
- * Represents a Tag in the address book.
- * Guarantees: immutable; name is valid as declared in {@link #isValidTagName(String)}
- */
-public class Tag {
-
- public static final String MESSAGE_CONSTRAINTS = "Tags names should be alphanumeric";
- public static final String VALIDATION_REGEX = "\\p{Alnum}+";
-
- public final String tagName;
-
- /**
- * Constructs a {@code Tag}.
- *
- * @param tagName A valid tag name.
- */
- public Tag(String tagName) {
- requireNonNull(tagName);
- checkArgument(isValidTagName(tagName), MESSAGE_CONSTRAINTS);
- this.tagName = tagName;
- }
-
- /**
- * Returns true if a given string is a valid tag name.
- */
- public static boolean isValidTagName(String test) {
- return test.matches(VALIDATION_REGEX);
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof Tag)) {
- return false;
- }
-
- Tag otherTag = (Tag) other;
- return tagName.equals(otherTag.tagName);
- }
-
- @Override
- public int hashCode() {
- return tagName.hashCode();
- }
-
- /**
- * Format state as text for viewing.
- */
- public String toString() {
- return '[' + tagName + ']';
- }
-
-}
diff --git a/src/main/java/seedu/address/model/util/SampleDataUtil.java b/src/main/java/seedu/address/model/util/SampleDataUtil.java
index 1806da4facf..912dfe6b08f 100644
--- a/src/main/java/seedu/address/model/util/SampleDataUtil.java
+++ b/src/main/java/seedu/address/model/util/SampleDataUtil.java
@@ -1,17 +1,28 @@
package seedu.address.model.util;
import java.util.Arrays;
+import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import seedu.address.model.AddressBook;
import seedu.address.model.ReadOnlyAddressBook;
-import seedu.address.model.person.Address;
+import seedu.address.model.appointment.Appointment;
+import seedu.address.model.appointment.UniqueAppointmentList;
+import seedu.address.model.person.Age;
+import seedu.address.model.person.Allergy;
+import seedu.address.model.person.BloodType;
import seedu.address.model.person.Email;
-import seedu.address.model.person.Name;
+import seedu.address.model.person.Gender;
import seedu.address.model.person.Person;
import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
+import seedu.address.model.record.Condition;
+import seedu.address.model.record.Medication;
+import seedu.address.model.record.Record;
+import seedu.address.model.record.UniqueRecordList;
+import seedu.address.model.shared.DateTime;
+import seedu.address.model.shared.Name;
+import seedu.address.model.shared.Nric;
/**
* Contains utility methods for populating {@code AddressBook} with sample data.
@@ -19,24 +30,42 @@
public class SampleDataUtil {
public static Person[] getSamplePersons() {
return new Person[] {
- new Person(new Name("Alex Yeoh"), new Phone("87438807"), new Email("alexyeoh@example.com"),
- new Address("Blk 30 Geylang Street 29, #06-40"),
- getTagSet("friends")),
- new Person(new Name("Bernice Yu"), new Phone("99272758"), new Email("berniceyu@example.com"),
- new Address("Blk 30 Lorong 3 Serangoon Gardens, #07-18"),
- getTagSet("colleagues", "friends")),
- new Person(new Name("Charlotte Oliveiro"), new Phone("93210283"), new Email("charlotte@example.com"),
- new Address("Blk 11 Ang Mo Kio Street 74, #11-04"),
- getTagSet("neighbours")),
- new Person(new Name("David Li"), new Phone("91031282"), new Email("lidavid@example.com"),
- new Address("Blk 436 Serangoon Gardens Street 26, #16-43"),
- getTagSet("family")),
- new Person(new Name("Irfan Ibrahim"), new Phone("92492021"), new Email("irfan@example.com"),
- new Address("Blk 47 Tampines Street 20, #17-35"),
- getTagSet("classmates")),
- new Person(new Name("Roy Balakrishnan"), new Phone("92624417"), new Email("royb@example.com"),
- new Address("Blk 45 Aljunied Street 85, #11-31"),
- getTagSet("colleagues"))
+ new Person(new Name("Alex Yeoh"), new Nric("S1234569A"),
+ new Email("alexyeoh@example.com"), new Phone("87438807"),
+ new Gender("M"), new Age(12), new BloodType("A+"),
+ getAllergySet("Peanuts"),
+ getRecordList(new Record(new DateTime("01-01-2001 1200"),
+ getConditionList("Fever"),
+ getMedicationList("Tylenol"), null, 0)),
+ getAppointmentList(new Appointment(new Name("Eye Exam"),
+ new DateTime("01-01-2001 1200"),
+ new Nric("S1234569A"))),
+ true),
+ new Person(new Name("Bernice Yu"), new Nric("T1234567A"),
+ new Email("berniceyu@example.com"), new Phone("99272758"),
+ new Gender("F"), new Age(31), new BloodType("B+"),
+ getAllergySet("Dust", "Peanuts"),
+ new UniqueRecordList(), new UniqueAppointmentList(), false),
+ new Person(new Name("Charlotte Oliveiro"), new Nric("S7654321A"),
+ new Email("charlotte@example.com"), new Phone("93210283"),
+ new Gender("F"), new Age(12), new BloodType("AB+"),
+ getAllergySet("Dust"),
+ new UniqueRecordList(), new UniqueAppointmentList(), false),
+ new Person(new Name("David Li"), new Nric("T1234567C"),
+ new Email("lidavid@example.com"), new Phone("91031282"),
+ new Gender("M"), new Age(33), new BloodType("O-"),
+ getAllergySet("Pollen"), new UniqueRecordList(),
+ new UniqueAppointmentList(), false),
+ new Person(new Name("Irfan Ibrahim"), new Nric("S5671234A"),
+ new Email("irfan@example.com"), new Phone("92492021"),
+ new Gender("M"), new Age(21), new BloodType("B-"),
+ getAllergySet("Fur"), new UniqueRecordList(),
+ new UniqueAppointmentList(), false),
+ new Person(new Name("Roy Balakrishnan"), new Nric("T1234567Z"),
+ new Email("royb@example.com"), new Phone("92624417"),
+ new Gender("M"), new Age(24), new BloodType("B+"),
+ getAllergySet("Grass"), new UniqueRecordList(),
+ new UniqueAppointmentList(), false)
};
}
@@ -45,16 +74,48 @@ public static ReadOnlyAddressBook getSampleAddressBook() {
for (Person samplePerson : getSamplePersons()) {
sampleAb.addPerson(samplePerson);
}
+ // for (Appointment sampleAppointment : getSampleAppointments()) {
+ // sampleAb.addAppointment(sampleAppointment);
+ // }
return sampleAb;
}
/**
- * Returns a tag set containing the list of strings given.
+ * Returns an allergy set containing the list of strings given.
*/
- public static Set getTagSet(String... strings) {
+ public static Set getAllergySet(String... strings) {
return Arrays.stream(strings)
- .map(Tag::new)
+ .map(Allergy::new)
.collect(Collectors.toSet());
}
+ /**
+ * Returns a list of conditions containing the list of strings given.
+ */
+ public static List getConditionList(String... strings) {
+ return Arrays.stream(strings)
+ .map(Condition::new)
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Returns a list of medications containing the list of strings given.
+ */
+ public static List getMedicationList(String... strings) {
+ return Arrays.stream(strings)
+ .map(Medication::new)
+ .collect(Collectors.toList());
+ }
+
+ public static UniqueRecordList getRecordList(Record... records) {
+ UniqueRecordList recordList = new UniqueRecordList();
+ recordList.setRecords(Arrays.asList(records));
+ return recordList;
+ }
+
+ public static UniqueAppointmentList getAppointmentList(Appointment... appointments) {
+ UniqueAppointmentList appointmentList = new UniqueAppointmentList();
+ appointmentList.setAppointments(Arrays.asList(appointments));
+ return appointmentList;
+ }
}
diff --git a/src/main/java/seedu/address/storage/JsonAdaptedAllergy.java b/src/main/java/seedu/address/storage/JsonAdaptedAllergy.java
new file mode 100644
index 00000000000..d55530973e4
--- /dev/null
+++ b/src/main/java/seedu/address/storage/JsonAdaptedAllergy.java
@@ -0,0 +1,51 @@
+package seedu.address.storage;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+import seedu.address.commons.exceptions.IllegalValueException;
+import seedu.address.model.person.Allergy;
+
+/**
+ * Jackson-friendly version of {@link Allergy}.
+ */
+public class JsonAdaptedAllergy {
+ private final String allergy;
+
+ /**
+ * Constructs a {@code JsonAdaptedAllergy} with the given {@code allergy}.
+ */
+
+ @JsonCreator
+ public JsonAdaptedAllergy(String allergy) {
+ this.allergy = allergy;
+ }
+
+ /**
+ * Converts a given {@code Tag} into this class for Jackson use.
+ */
+ public JsonAdaptedAllergy(Allergy source) {
+ this.allergy = source.allergy;
+ }
+
+ @JsonValue
+ public String getAllergy() {
+ return allergy;
+ }
+
+ /**
+ * Converts this Jackson-friendly adapted tag object into the model's {@code Allergy} object.
+ *
+ * @throws IllegalValueException if there were any data constraints violated in the adapted allergy.
+ */
+
+ public Allergy toModelType() throws IllegalValueException {
+ if (allergy == null) {
+ throw new IllegalValueException(Allergy.MESSAGE_CONSTRAINTS);
+ }
+ if (!Allergy.isValidAllergy(allergy)) {
+ throw new IllegalValueException(Allergy.MESSAGE_CONSTRAINTS);
+ }
+ return new Allergy(allergy);
+ }
+}
diff --git a/src/main/java/seedu/address/storage/JsonAdaptedAppointment.java b/src/main/java/seedu/address/storage/JsonAdaptedAppointment.java
new file mode 100644
index 00000000000..8491d2f0f4e
--- /dev/null
+++ b/src/main/java/seedu/address/storage/JsonAdaptedAppointment.java
@@ -0,0 +1,79 @@
+package seedu.address.storage;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import seedu.address.commons.exceptions.IllegalValueException;
+import seedu.address.model.appointment.Appointment;
+import seedu.address.model.person.Person;
+import seedu.address.model.shared.DateTime;
+import seedu.address.model.shared.Name;
+import seedu.address.model.shared.Nric;
+
+/**
+ * Jackson-friendly version of {@link Person}.
+ */
+class JsonAdaptedAppointment {
+
+ public static final String MISSING_FIELD_MESSAGE_FORMAT = "Appointment's %s field is missing!";
+
+ private final String name;
+ private final String dateTime;
+ private final String nric;
+
+ /**
+ * Constructs a {@code JsonAdaptedAppointment} with the given person details.
+ */
+ @JsonCreator
+ public JsonAdaptedAppointment(@JsonProperty("name") String name,
+ @JsonProperty("dateTime") String dateTime, @JsonProperty("nric") String nric) {
+ this.name = name;
+ this.dateTime = dateTime;
+ this.nric = nric;
+ }
+
+ /**
+ * Converts a given {@code Appointment} into this class for Jackson use.
+ */
+ public JsonAdaptedAppointment(Appointment source) {
+ name = source.getName().fullName;
+ dateTime = source.getDateTime().toString();
+ nric = source.getNric().toString();
+ }
+
+ /**
+ * Converts this Jackson-friendly adapted appointment object into the model's
+ * {@code Appointment} object.
+ *
+ * @throws IllegalValueException if there were any data constraints violated in
+ * the adapted appointment.
+ */
+ public Appointment toModelType() throws IllegalValueException {
+ if (name == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName()));
+ }
+ if (!Name.isValidName(name)) {
+ throw new IllegalValueException(Name.MESSAGE_CONSTRAINTS);
+ }
+ final Name modelname = new Name(name);
+
+ if (dateTime == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT,
+ DateTime.class.getSimpleName()));
+ }
+ if (!DateTime.isValidDateTime(dateTime)) {
+ throw new IllegalValueException(DateTime.MESSAGE_CONSTRAINTS);
+ }
+ final DateTime modelDateTime = new DateTime(dateTime);
+
+ if (nric == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Nric.class.getSimpleName()));
+ }
+ if (!Nric.isValidNric(nric)) {
+ throw new IllegalValueException(Nric.MESSAGE_CONSTRAINTS);
+ }
+ final Nric modelNric = new Nric(nric);
+
+ return new Appointment(modelname, modelDateTime, modelNric);
+ }
+}
diff --git a/src/main/java/seedu/address/storage/JsonAdaptedCondition.java b/src/main/java/seedu/address/storage/JsonAdaptedCondition.java
new file mode 100644
index 00000000000..34ea7c156b2
--- /dev/null
+++ b/src/main/java/seedu/address/storage/JsonAdaptedCondition.java
@@ -0,0 +1,48 @@
+package seedu.address.storage;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+import seedu.address.commons.exceptions.IllegalValueException;
+import seedu.address.model.record.Condition;
+
+/**
+ * Jackson-friendly version of {@link Condition}.
+ */
+public class JsonAdaptedCondition {
+
+ private final String condition;
+
+ /**
+ * Constructs a {@code JsonAdaptedCondition} with the given {@code condition}.
+ */
+ @JsonCreator
+ public JsonAdaptedCondition(String condition) {
+ this.condition = condition;
+ }
+ /**
+ * Converts a given {@code Condition} into this class for Jackson use.
+ */
+ public JsonAdaptedCondition(Condition source) {
+ this.condition = source.condition;
+ }
+
+ @JsonValue
+ public String getCondition() {
+ return condition;
+ }
+ /**
+ * Converts this Jackson-friendly adapted tag object into the model's {@code Condition} object.
+ *
+ * @throws IllegalValueException if there were any data constraints violated in the adapted condition.
+ */
+ public Condition toModelType() throws IllegalValueException {
+ if (condition == null) {
+ throw new IllegalValueException(Condition.MESSAGE_CONSTRAINTS);
+ }
+ if (!Condition.isValidCondition(condition)) {
+ throw new IllegalValueException(Condition.MESSAGE_CONSTRAINTS);
+ }
+ return new Condition(condition);
+ }
+}
diff --git a/src/main/java/seedu/address/storage/JsonAdaptedMedication.java b/src/main/java/seedu/address/storage/JsonAdaptedMedication.java
new file mode 100644
index 00000000000..584eb2061b0
--- /dev/null
+++ b/src/main/java/seedu/address/storage/JsonAdaptedMedication.java
@@ -0,0 +1,46 @@
+package seedu.address.storage;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+import seedu.address.commons.exceptions.IllegalValueException;
+import seedu.address.model.record.Medication;
+
+/**
+ * Jackson-friendly version of {@link Medication}
+ */
+public class JsonAdaptedMedication {
+ private final String medication;
+
+ /**
+ * Constructs a {@code JsonAdaptedMedication} with the given {@code medication}.
+ */
+ @JsonCreator
+ public JsonAdaptedMedication(String medication) {
+ this.medication = medication;
+ }
+ /**
+ * Converts a given {@code Medication} into this class for Jackson use.
+ */
+ public JsonAdaptedMedication(Medication source) {
+ this.medication = source.medication;
+ }
+ @JsonValue
+ public String getMedication() {
+ return medication;
+ }
+
+ /**
+ * Converts this Jackson-friendly adapted medication object into the model's {@code Medication} object.
+ * @throws IllegalValueException if there were any data constraints violated in the adapted condition.
+ */
+ public Medication toModelType() throws IllegalValueException {
+ if (medication == null) {
+ throw new IllegalValueException(Medication.MESSAGE_CONSTRAINTS);
+ }
+ if (!Medication.isValidMedication(medication)) {
+ throw new IllegalValueException(Medication.MESSAGE_CONSTRAINTS);
+ }
+ return new Medication(medication);
+ }
+}
diff --git a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java b/src/main/java/seedu/address/storage/JsonAdaptedPerson.java
index bd1ca0f56c8..b0ac690d30b 100644
--- a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java
+++ b/src/main/java/seedu/address/storage/JsonAdaptedPerson.java
@@ -10,12 +10,18 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import seedu.address.commons.exceptions.IllegalValueException;
-import seedu.address.model.person.Address;
+import seedu.address.model.appointment.Appointment;
+import seedu.address.model.appointment.UniqueAppointmentList;
+import seedu.address.model.person.Age;
+import seedu.address.model.person.Allergy;
+import seedu.address.model.person.BloodType;
import seedu.address.model.person.Email;
-import seedu.address.model.person.Name;
+import seedu.address.model.person.Gender;
import seedu.address.model.person.Person;
import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
+import seedu.address.model.record.UniqueRecordList;
+import seedu.address.model.shared.Name;
+import seedu.address.model.shared.Nric;
/**
* Jackson-friendly version of {@link Person}.
@@ -25,24 +31,46 @@ class JsonAdaptedPerson {
public static final String MISSING_FIELD_MESSAGE_FORMAT = "Person's %s field is missing!";
private final String name;
+ private final String nric;
private final String phone;
private final String email;
- private final String address;
- private final List tags = new ArrayList<>();
+ private final String gender;
+ private final Integer age;
+ private final String bloodType;
+ private final List allergies = new ArrayList<>();
+ private final List records = new ArrayList<>();
+ private final Boolean isPinned;
+ private final List appointments = new ArrayList<>();
/**
* Constructs a {@code JsonAdaptedPerson} with the given person details.
*/
@JsonCreator
- public JsonAdaptedPerson(@JsonProperty("name") String name, @JsonProperty("phone") String phone,
- @JsonProperty("email") String email, @JsonProperty("address") String address,
- @JsonProperty("tags") List tags) {
+ public JsonAdaptedPerson(@JsonProperty("name") String name, @JsonProperty("nric") String nric,
+ @JsonProperty("email") String email, @JsonProperty("phone") String phone,
+ @JsonProperty("gender") String gender,
+ @JsonProperty("age") Integer age, @JsonProperty("bloodType") String bloodType,
+ @JsonProperty("allergies") List allergies,
+ @JsonProperty("records") List records,
+ @JsonProperty("appointments") List appointments,
+ @JsonProperty("isPinned") Boolean isPinned) {
+
this.name = name;
+ this.nric = nric;
this.phone = phone;
this.email = email;
- this.address = address;
- if (tags != null) {
- this.tags.addAll(tags);
+ this.gender = gender;
+ this.age = age;
+ this.bloodType = bloodType;
+ if (allergies != null) {
+ this.allergies.addAll(allergies);
+ }
+ if (records != null) {
+ this.records.addAll(records);
+ }
+ this.isPinned = isPinned;
+ if (appointments != null) {
+ this.appointments.addAll(appointments);
}
}
@@ -51,23 +79,40 @@ public JsonAdaptedPerson(@JsonProperty("name") String name, @JsonProperty("phone
*/
public JsonAdaptedPerson(Person source) {
name = source.getName().fullName;
+ nric = source.getNric().nric;
phone = source.getPhone().value;
email = source.getEmail().value;
- address = source.getAddress().value;
- tags.addAll(source.getTags().stream()
- .map(JsonAdaptedTag::new)
+ gender = source.getGender().gender;
+ age = source.getAge().age;
+ bloodType = source.getBloodType().bloodType;
+ allergies.addAll(source.getAllergies().stream()
+ .map(JsonAdaptedAllergy::new)
.collect(Collectors.toList()));
+ records.addAll(source.getRecords().asUnmodifiableObservableList()
+ .stream()
+ .map(JsonAdaptedRecord::new)
+ .collect(Collectors.toList()));
+ appointments.addAll(source.getAppointments().asUnmodifiableObservableList().stream()
+ .map(JsonAdaptedAppointment::new).collect(Collectors.toList()));
+ isPinned = source.isPinned();
}
/**
- * Converts this Jackson-friendly adapted person object into the model's {@code Person} object.
+ * Converts this Jackson-friendly adapted person object into the model's
+ * {@code Person} object.
*
- * @throws IllegalValueException if there were any data constraints violated in the adapted person.
+ * @throws IllegalValueException if there were any data constraints violated in
+ * the adapted person.
*/
public Person toModelType() throws IllegalValueException {
- final List personTags = new ArrayList<>();
- for (JsonAdaptedTag tag : tags) {
- personTags.add(tag.toModelType());
+ final List allergiesList = new ArrayList<>();
+ for (JsonAdaptedAllergy allergy : allergies) {
+ allergiesList.add(allergy.toModelType());
+ }
+
+ final UniqueRecordList modelRecords = new UniqueRecordList();
+ for (JsonAdaptedRecord record : records) {
+ modelRecords.add(record.toModelType());
}
if (name == null) {
@@ -78,6 +123,15 @@ public Person toModelType() throws IllegalValueException {
}
final Name modelName = new Name(name);
+ if (nric == null) {
+ throw new IllegalValueException(
+ String.format(MISSING_FIELD_MESSAGE_FORMAT, seedu.address.model.shared.Nric.class.getSimpleName()));
+ }
+ if (!Nric.isValidNric(nric)) {
+ throw new IllegalValueException(seedu.address.model.shared.Nric.MESSAGE_CONSTRAINTS);
+ }
+ final Nric modelNric = new Nric(nric);
+
if (phone == null) {
throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName()));
}
@@ -94,16 +148,40 @@ public Person toModelType() throws IllegalValueException {
}
final Email modelEmail = new Email(email);
- if (address == null) {
- throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName()));
+ if (gender == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Gender.class.getSimpleName()));
}
- if (!Address.isValidAddress(address)) {
- throw new IllegalValueException(Address.MESSAGE_CONSTRAINTS);
+ if (!Gender.isValidGender(gender)) {
+ throw new IllegalValueException(Gender.MESSAGE_CONSTRAINTS);
}
- final Address modelAddress = new Address(address);
+ final Gender modelGender = new Gender(gender);
- final Set modelTags = new HashSet<>(personTags);
- return new Person(modelName, modelPhone, modelEmail, modelAddress, modelTags);
- }
+ if (age == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Age.class.getSimpleName()));
+ }
+ if (!Age.isValidAge(age)) {
+ throw new IllegalValueException(Age.MESSAGE_CONSTRAINTS);
+ }
+ final Age modelAge = new Age(age);
+
+ if (bloodType == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT,
+ BloodType.class.getSimpleName()));
+ }
+ if (!BloodType.isValidBloodType(bloodType)) {
+ throw new IllegalValueException(BloodType.MESSAGE_CONSTRAINTS);
+ }
+ final BloodType modelBloodType = new BloodType(bloodType);
+ final Set modelAllergies = new HashSet<>(allergiesList);
+
+ final UniqueAppointmentList modelAppointments = new UniqueAppointmentList();
+ for (JsonAdaptedAppointment jsonAdaptedAppointment : appointments) {
+ Appointment appointment = jsonAdaptedAppointment.toModelType();
+ modelAppointments.add(appointment);
+ }
+
+ return new Person(modelName, modelNric, modelEmail, modelPhone, modelGender,
+ modelAge, modelBloodType, modelAllergies, modelRecords, modelAppointments, isPinned);
+ }
}
diff --git a/src/main/java/seedu/address/storage/JsonAdaptedRecord.java b/src/main/java/seedu/address/storage/JsonAdaptedRecord.java
new file mode 100644
index 00000000000..148863f02d3
--- /dev/null
+++ b/src/main/java/seedu/address/storage/JsonAdaptedRecord.java
@@ -0,0 +1,108 @@
+package seedu.address.storage;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import seedu.address.commons.exceptions.IllegalValueException;
+import seedu.address.model.record.Condition;
+import seedu.address.model.record.Medication;
+import seedu.address.model.record.Record;
+import seedu.address.model.shared.DateTime;
+
+/**
+ * Jackson-friendly version of {@link Record}.
+ */
+public class JsonAdaptedRecord {
+
+ public static final String MISSING_FIELD_MESSAGE_FORMAT = "Record's %s field is missing!";
+ private final String dateTime;
+ private final List conditions = new ArrayList<>();
+ private final String filePath;
+ private final Integer personIndex;
+ private final List medications = new ArrayList<>();
+
+
+ /**
+ * Constructs a {@code JsonAdoptedRecord} with the given record details.
+ */
+ @JsonCreator
+ public JsonAdaptedRecord(@JsonProperty("dateTime") String dateTime,
+ @JsonProperty("condition") List conditions,
+ @JsonProperty("medication") List medications,
+ @JsonProperty("filePath") String filePath,
+ @JsonProperty("personIndex") Integer personIndex) {
+ this.dateTime = dateTime;
+ if (conditions != null) {
+ this.conditions.addAll(conditions);
+ }
+ this.filePath = filePath;
+ this.personIndex = personIndex;
+ if (medications != null) {
+ this.medications.addAll(medications);
+ }
+ }
+
+ /**
+ * Converts a given {@code Record} into this class for Jackson use.
+ */
+ public JsonAdaptedRecord(Record source) {
+ this.dateTime = source.getDateTime().toString();
+ this.personIndex = source.getPersonIndex();
+ this.filePath = source.getFilePath() == null ? null : source.getFilePath().toString();
+ this.conditions.addAll(source.getConditions().stream()
+ .map(JsonAdaptedCondition::new)
+ .collect(Collectors.toList()));
+ this.medications.addAll(source.getMedications().stream()
+ .map(JsonAdaptedMedication::new)
+ .collect(Collectors.toList()));
+ }
+
+ /**
+ * Converts this Jackson-friendly adapted person object into the model's
+ * {@code Person} object.
+ *
+ * @throws IllegalValueException if there were any data constraints violated in
+ * the adapted person.
+ */
+ public Record toModelType() throws IllegalValueException {
+ final List conditionsList = new ArrayList<>();
+ final List medicationsList = new ArrayList<>();
+
+ for (JsonAdaptedCondition condition : conditions) {
+ if (condition == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT,
+ Condition.class.getSimpleName()));
+ }
+ conditionsList.add(condition.toModelType());
+ }
+
+ for (JsonAdaptedMedication medication : medications) {
+ if (medication == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT,
+ Medication.class.getSimpleName()));
+ }
+ medicationsList.add(medication.toModelType());
+ }
+
+ if (dateTime == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT,
+ DateTime.class.getSimpleName()));
+ }
+
+ final DateTime modelDateTime = new DateTime(dateTime);
+ final List modelMedications = new ArrayList<>(medicationsList);
+ final List modelConditions = new ArrayList<>(conditionsList);
+ if (filePath == null) {
+ return new Record(modelDateTime, modelConditions, modelMedications, null, personIndex);
+ } else {
+ Path modelFilePath = Paths.get(filePath);
+ return new Record(modelDateTime, modelConditions, modelMedications, modelFilePath, personIndex);
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/storage/JsonAdaptedTag.java b/src/main/java/seedu/address/storage/JsonAdaptedTag.java
deleted file mode 100644
index 0df22bdb754..00000000000
--- a/src/main/java/seedu/address/storage/JsonAdaptedTag.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package seedu.address.storage;
-
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonValue;
-
-import seedu.address.commons.exceptions.IllegalValueException;
-import seedu.address.model.tag.Tag;
-
-/**
- * Jackson-friendly version of {@link Tag}.
- */
-class JsonAdaptedTag {
-
- private final String tagName;
-
- /**
- * Constructs a {@code JsonAdaptedTag} with the given {@code tagName}.
- */
- @JsonCreator
- public JsonAdaptedTag(String tagName) {
- this.tagName = tagName;
- }
-
- /**
- * Converts a given {@code Tag} into this class for Jackson use.
- */
- public JsonAdaptedTag(Tag source) {
- tagName = source.tagName;
- }
-
- @JsonValue
- public String getTagName() {
- return tagName;
- }
-
- /**
- * Converts this Jackson-friendly adapted tag object into the model's {@code Tag} object.
- *
- * @throws IllegalValueException if there were any data constraints violated in the adapted tag.
- */
- public Tag toModelType() throws IllegalValueException {
- if (!Tag.isValidTagName(tagName)) {
- throw new IllegalValueException(Tag.MESSAGE_CONSTRAINTS);
- }
- return new Tag(tagName);
- }
-
-}
diff --git a/src/main/java/seedu/address/storage/JsonAddressBookStorage.java b/src/main/java/seedu/address/storage/JsonAddressBookStorage.java
index 41e06f264e1..573f3d961cb 100644
--- a/src/main/java/seedu/address/storage/JsonAddressBookStorage.java
+++ b/src/main/java/seedu/address/storage/JsonAddressBookStorage.java
@@ -3,13 +3,14 @@
import static java.util.Objects.requireNonNull;
import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Optional;
import java.util.logging.Logger;
import seedu.address.commons.core.LogsCenter;
import seedu.address.commons.exceptions.DataLoadingException;
-import seedu.address.commons.exceptions.IllegalValueException;
import seedu.address.commons.util.FileUtil;
import seedu.address.commons.util.JsonUtil;
import seedu.address.model.ReadOnlyAddressBook;
@@ -23,58 +24,94 @@ public class JsonAddressBookStorage implements AddressBookStorage {
private Path filePath;
+ /**
+ * Constructs a new JsonAddressBookStorage instance with a given filePath.
+ *
+ * @param filePath The path to the file.
+ */
public JsonAddressBookStorage(Path filePath) {
this.filePath = filePath;
}
+ /**
+ * Returns the file path where the AddressBook is stored.
+ *
+ * @return The file path.
+ */
public Path getAddressBookFilePath() {
return filePath;
}
+ /**
+ * Reads the AddressBook from the default file path.
+ *
+ * @return An Optional containing the AddressBook if it exists, or empty
+ * otherwise.
+ * @throws DataLoadingException if there was an error reading the AddressBook.
+ */
@Override
public Optional readAddressBook() throws DataLoadingException {
return readAddressBook(filePath);
}
/**
- * Similar to {@link #readAddressBook()}.
+ * Reads the AddressBook from the specified file path.
*
- * @param filePath location of the data. Cannot be null.
- * @throws DataLoadingException if loading the data from storage failed.
+ * @param filePath The path to the file.
+ * @return An Optional containing the AddressBook if it exists, or empty
+ * otherwise.
+ * @throws DataLoadingException if there was an error reading the AddressBook.
*/
public Optional readAddressBook(Path filePath) throws DataLoadingException {
requireNonNull(filePath);
- Optional jsonAddressBook = JsonUtil.readJsonFile(
- filePath, JsonSerializableAddressBook.class);
- if (!jsonAddressBook.isPresent()) {
+ if (!FileUtil.isFileExists(filePath)) {
return Optional.empty();
}
try {
- return Optional.of(jsonAddressBook.get().toModelType());
- } catch (IllegalValueException ive) {
- logger.info("Illegal values found in " + filePath + ": " + ive.getMessage());
- throw new DataLoadingException(ive);
+ String data = new String(Files.readAllBytes(filePath), StandardCharsets.UTF_8);
+
+ JsonSerializableAddressBook jsonAddressBook = JsonUtil.fromJsonString(
+ data, JsonSerializableAddressBook.class);
+
+ if (jsonAddressBook == null) {
+ return Optional.empty();
+ }
+
+ return Optional.of(jsonAddressBook.toModelType());
+
+ } catch (Exception e) {
+ throw new DataLoadingException(e);
}
}
+ /**
+ * Saves the AddressBook to the default file path.
+ *
+ * @param addressBook The AddressBook to be saved.
+ * @throws IOException if there was an error writing to the file.
+ */
@Override
public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException {
saveAddressBook(addressBook, filePath);
}
/**
- * Similar to {@link #saveAddressBook(ReadOnlyAddressBook)}.
+ * Saves the AddressBook to the specified file path.
*
- * @param filePath location of the data. Cannot be null.
+ * @param addressBook The AddressBook to be saved.
+ * @param filePath The path to the file.
+ * @throws IOException if there was an error writing to the file.
*/
public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException {
requireNonNull(addressBook);
requireNonNull(filePath);
FileUtil.createIfMissing(filePath);
- JsonUtil.saveJsonFile(new JsonSerializableAddressBook(addressBook), filePath);
+ String jsonData = JsonUtil.toJsonString(new JsonSerializableAddressBook(addressBook));
+
+ Files.write(filePath, jsonData.getBytes(StandardCharsets.UTF_8));
}
}
diff --git a/src/main/java/seedu/address/storage/StorageManager.java b/src/main/java/seedu/address/storage/StorageManager.java
index 8b84a9024d5..b907e53f93f 100644
--- a/src/main/java/seedu/address/storage/StorageManager.java
+++ b/src/main/java/seedu/address/storage/StorageManager.java
@@ -17,15 +17,18 @@
public class StorageManager implements Storage {
private static final Logger logger = LogsCenter.getLogger(StorageManager.class);
+ private static StorageManager instance;
private AddressBookStorage addressBookStorage;
private UserPrefsStorage userPrefsStorage;
/**
- * Creates a {@code StorageManager} with the given {@code AddressBookStorage} and {@code UserPrefStorage}.
+ * Creates a {@code StorageManager} with the given {@code AddressBookStorage}
+ * and {@code UserPrefStorage}.
*/
public StorageManager(AddressBookStorage addressBookStorage, UserPrefsStorage userPrefsStorage) {
this.addressBookStorage = addressBookStorage;
this.userPrefsStorage = userPrefsStorage;
+ this.instance = this;
}
// ================ UserPrefs methods ==============================
@@ -45,7 +48,6 @@ public void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException {
userPrefsStorage.saveUserPrefs(userPrefs);
}
-
// ================ AddressBook methods ==============================
@Override
@@ -75,4 +77,8 @@ public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) thro
addressBookStorage.saveAddressBook(addressBook, filePath);
}
+ public static StorageManager getInstance() {
+ return instance;
+ }
+
}
diff --git a/src/main/java/seedu/address/ui/AppointmentCalendarPanel.java b/src/main/java/seedu/address/ui/AppointmentCalendarPanel.java
new file mode 100644
index 00000000000..18d35c87d92
--- /dev/null
+++ b/src/main/java/seedu/address/ui/AppointmentCalendarPanel.java
@@ -0,0 +1,135 @@
+package seedu.address.ui;
+
+import java.time.DayOfWeek;
+import java.time.LocalDate;
+import java.util.logging.Logger;
+
+import javafx.collections.ObservableList;
+import javafx.fxml.FXML;
+import javafx.geometry.Pos;
+import javafx.scene.Node;
+import javafx.scene.control.Label;
+import javafx.scene.control.ListCell;
+import javafx.scene.layout.GridPane;
+import javafx.scene.layout.Region;
+import javafx.scene.layout.VBox;
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.model.appointment.Appointment;
+
+/**
+ * Panel containing the list of appointments.
+ */
+public class AppointmentCalendarPanel extends UiPart {
+ private static final String FXML = "AppointmentCalenderPanel.fxml";
+ private final Logger logger = LogsCenter.getLogger(AppointmentCalendarPanel.class);
+
+ @FXML
+ private ObservableList appointmentList;
+
+ @FXML
+ private GridPane calendarGrid;
+ @FXML
+ private Label currentMonthLabel;
+
+ private LocalDate currentDate;
+
+ /**
+ * Creates a {@code AppointmentCalender
+ * Panel} with the given {@code ObservableList}.
+ */
+ public AppointmentCalendarPanel(ObservableList appointmentList) {
+ super(FXML);
+ this.appointmentList = appointmentList;
+ currentDate = LocalDate.now();
+ populateMonth(currentDate);
+ this.appointmentList.addListener((javafx.collections.ListChangeListener.Change extends Appointment> c) -> {
+ populateMonth(currentDate);
+ });
+ }
+
+ /**
+ * Custom {@code ListCell} that displays the graphics of a {@code Appointment}
+ * using
+ * a {@code AppointmentCard}.
+ */
+ class AppointmentListViewCell extends ListCell {
+ @Override
+ protected void updateItem(Appointment appointment, boolean empty) {
+ super.updateItem(appointment, empty);
+
+ if (empty || appointment == null) {
+ setGraphic(null);
+ setText(null);
+ } else {
+ setGraphic(new AppointmentCard(appointment, getIndex() + 1).getRoot());
+ }
+ }
+ }
+
+ @FXML
+ private void previousMonth() {
+ currentDate = currentDate.minusMonths(1);
+ populateMonth(currentDate);
+ }
+
+ @FXML
+ private void nextMonth() {
+ currentDate = currentDate.plusMonths(1);
+ populateMonth(currentDate);
+ }
+
+ private void populateMonth(LocalDate date) {
+ calendarGrid.getChildren().clear(); // Clear the previous month's data
+ currentMonthLabel.setText(date.getMonth() + " " + date.getYear());
+
+ // Print headers (days of the week)
+ DayOfWeek[] daysOfWeek = { DayOfWeek.SUNDAY, DayOfWeek.MONDAY, DayOfWeek.TUESDAY, DayOfWeek.WEDNESDAY,
+ DayOfWeek.THURSDAY, DayOfWeek.FRIDAY, DayOfWeek.SATURDAY };
+ for (int i = 0; i < daysOfWeek.length; i++) {
+ Label label = new Label(daysOfWeek[i].toString().substring(0, 3));
+ label.setAlignment(Pos.CENTER);
+ label.getStyleClass().add("label-bright");
+ calendarGrid.add(label, i, 0);
+ }
+
+ // Determine the first day of the month
+ LocalDate firstOfMonth = date.withDayOfMonth(1);
+ int dayOfWeek = firstOfMonth.getDayOfWeek().getValue() % 7;
+
+ int rowOffset = dayOfWeek;
+
+ // Populate days
+ for (int day = 1; day <= date.lengthOfMonth(); day++) {
+ VBox dateBox = new VBox();
+ dateBox.setMinHeight(50);
+ Label dateLabel = new Label(Integer.toString(day));
+ dateLabel.getStyleClass().add("label-bright");
+ dateBox.getChildren().add(dateLabel);
+ int currentRow = 1 + (day + rowOffset - 1) / 7;
+ calendarGrid.add(dateBox, dayOfWeek, currentRow);
+ dayOfWeek = (dayOfWeek + 1) % 7;
+ }
+ for (Appointment appointment : appointmentList) {
+ LocalDate appointmentDate = appointment.getDateTime().dateTime.toLocalDate();
+ if (appointmentDate.getMonth() == date.getMonth() && appointmentDate.getYear() == date.getYear()) {
+ // This appointment belongs to the currently displayed month
+
+ int day = appointmentDate.getDayOfMonth();
+ int cellDayOfWeek = (firstOfMonth.getDayOfWeek().getValue() + day - 1) % 7;
+ // Find the label that corresponds to this day
+ for (Node node : calendarGrid.getChildren()) {
+ if (GridPane.getRowIndex(node) == 1 + (day + rowOffset - 1) / 7
+ && GridPane.getColumnIndex(node) == cellDayOfWeek) {
+ VBox dateBox = (VBox) node;
+ if (dateBox.getChildren().size() < 3) { // Assuming 'n' is 3 for illustration
+ Label appointmentLabel = new Label(appointment.getName().fullName);
+ appointmentLabel.getStyleClass().add("label-bright");
+ dateBox.getChildren().add(appointmentLabel);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/ui/AppointmentCard.java b/src/main/java/seedu/address/ui/AppointmentCard.java
new file mode 100644
index 00000000000..c39608fd59d
--- /dev/null
+++ b/src/main/java/seedu/address/ui/AppointmentCard.java
@@ -0,0 +1,51 @@
+package seedu.address.ui;
+
+import javafx.fxml.FXML;
+import javafx.scene.control.Label;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.Region;
+import seedu.address.model.appointment.Appointment;
+
+/**
+ * An UI component that displays information of a {@code Appointment}.
+ */
+public class AppointmentCard extends UiPart {
+
+ private static final String FXML = "AppointmentListCard.fxml";
+
+ /**
+ * Note: Certain keywords such as "location" and "resources" are reserved
+ * keywords in JavaFX.
+ * As a consequence, UI elements' variable names cannot be set to such keywords
+ * or an exception will be thrown by JavaFX during runtime.
+ *
+ * @see The
+ * issue on AddressBook level 4
+ */
+
+ public final Appointment appointment;
+
+ @FXML
+ private HBox cardPane;
+ @FXML
+ private Label name;
+ @FXML
+ private Label dateTime;
+ @FXML
+ private Label nric;
+ @FXML
+ private Label id;
+
+ /**
+ * Creates a {@code AppointmentCode} with the given {@code Appointment} and
+ * index to display.
+ */
+ public AppointmentCard(Appointment appointment, int displayedIndex) {
+ super(FXML);
+ this.appointment = appointment;
+ id.setText(displayedIndex + ". ");
+ name.setText(appointment.getName().fullName);
+ dateTime.setText(appointment.getDateTime().toString());
+ nric.setText(appointment.getNric().nric);
+ }
+}
diff --git a/src/main/java/seedu/address/ui/AppointmentListPanel.java b/src/main/java/seedu/address/ui/AppointmentListPanel.java
new file mode 100644
index 00000000000..19c56e2231b
--- /dev/null
+++ b/src/main/java/seedu/address/ui/AppointmentListPanel.java
@@ -0,0 +1,50 @@
+package seedu.address.ui;
+
+import java.util.logging.Logger;
+
+import javafx.collections.ObservableList;
+import javafx.fxml.FXML;
+import javafx.scene.control.ListCell;
+import javafx.scene.control.ListView;
+import javafx.scene.layout.Region;
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.model.appointment.Appointment;
+
+/**
+ * Panel containing the list of appointments.
+ */
+public class AppointmentListPanel extends UiPart {
+ private static final String FXML = "AppointmentListPanel.fxml";
+ private final Logger logger = LogsCenter.getLogger(AppointmentListPanel.class);
+
+ @FXML
+ private ListView appointmentListView;
+
+ /**
+ * Creates a {@code AppointmentListPanel} with the given {@code ObservableList}.
+ */
+ public AppointmentListPanel(ObservableList appointmentList) {
+ super(FXML);
+ appointmentListView.setItems(appointmentList);
+ appointmentListView.setCellFactory(listView -> new AppointmentListViewCell());
+ }
+
+ /**
+ * Custom {@code ListCell} that displays the graphics of a {@code Appointment}
+ * using
+ * a {@code AppointmentCard}.
+ */
+ class AppointmentListViewCell extends ListCell {
+ @Override
+ protected void updateItem(Appointment appointment, boolean empty) {
+ super.updateItem(appointment, empty);
+
+ if (empty || appointment == null) {
+ setGraphic(null);
+ setText(null);
+ } else {
+ setGraphic(new AppointmentCard(appointment, getIndex() + 1).getRoot());
+ }
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/ui/AppointmentsWindow.java b/src/main/java/seedu/address/ui/AppointmentsWindow.java
new file mode 100644
index 00000000000..dc71a7c2896
--- /dev/null
+++ b/src/main/java/seedu/address/ui/AppointmentsWindow.java
@@ -0,0 +1,103 @@
+package seedu.address.ui;
+
+import java.util.logging.Logger;
+
+import javafx.collections.ObservableList;
+import javafx.fxml.FXML;
+import javafx.scene.layout.StackPane;
+import javafx.stage.Stage;
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.model.appointment.Appointment;
+
+/**
+ * Controller for an Appointments page
+ */
+public class AppointmentsWindow extends UiPart {
+
+ private static final String FXML = "AppointmentsWindow.fxml";
+
+ private final Logger logger = LogsCenter.getLogger(AppointmentsWindow.class);
+
+ private ObservableList appointmentList;
+
+ // Independent Ui parts residing in this Ui container
+ private AppointmentListPanel appointmentListPanel;
+ private AppointmentCalendarPanel appointmentCalenderPanel;
+
+ @FXML
+ private StackPane appointmentListPanelPlaceholder;
+ @FXML
+ private StackPane appointmentCalenderPanelPlaceholder;
+
+ /**
+ * Creates a new AppointmentsWindow.
+ *
+ * @param root Stage to use as the root of the AppointmentsWindow.
+ */
+ public AppointmentsWindow(Stage root, ObservableList appointmentList) {
+ super(FXML, root);
+ this.appointmentList = appointmentList;
+ }
+
+ /**
+ * Fills up all the placeholders of this window.
+ */
+ void fillInnerParts() {
+ appointmentCalenderPanelPlaceholder.getChildren().clear();
+
+ appointmentListPanel = new AppointmentListPanel(appointmentList);
+ appointmentListPanelPlaceholder.getChildren().add(appointmentListPanel.getRoot());
+
+ appointmentCalenderPanel = new AppointmentCalendarPanel(appointmentList);
+ appointmentCalenderPanelPlaceholder.getChildren().add(appointmentCalenderPanel.getRoot());
+ }
+
+ /**
+ * Shows the appointment window.
+ *
+ * @throws IllegalStateException
+ *
+ *
+ * if this method is called on a thread other than
+ * the JavaFX Application Thread.
+ *
+ *
+ * if this method is called during animation or
+ * layout processing.
+ *
+ *
+ * if this method is called on the primary stage.
+ *
+ *
+ * if {@code dialogStage} is already showing.
+ *
+ *
+ */
+ public void show() {
+ fillInnerParts();
+ logger.fine("Showing appointments window.");
+ getRoot().show();
+ getRoot().centerOnScreen();
+ }
+
+ /**
+ * Returns true if the appointments window is currently being shown.
+ */
+ public boolean isShowing() {
+ return getRoot().isShowing();
+ }
+
+ /**
+ * Hides the appointments window.
+ */
+ public void hide() {
+ getRoot().hide();
+ }
+
+ /**
+ * Focuses on the appointments window.
+ */
+ public void focus() {
+ getRoot().requestFocus();
+ }
+}
diff --git a/src/main/java/seedu/address/ui/HelpWindow.java b/src/main/java/seedu/address/ui/HelpWindow.java
index 3f16b2fcf26..08759f0f4de 100644
--- a/src/main/java/seedu/address/ui/HelpWindow.java
+++ b/src/main/java/seedu/address/ui/HelpWindow.java
@@ -9,23 +9,73 @@
import javafx.scene.input.ClipboardContent;
import javafx.stage.Stage;
import seedu.address.commons.core.LogsCenter;
+import seedu.address.logic.commands.AddAppointmentCommand;
+import seedu.address.logic.commands.AddCommand;
+import seedu.address.logic.commands.AddRecordCommand;
+import seedu.address.logic.commands.DeleteAppointmentCommand;
+import seedu.address.logic.commands.DeleteCommand;
+import seedu.address.logic.commands.DeleteRecordCommand;
+import seedu.address.logic.commands.EditCommand;
+import seedu.address.logic.commands.EditRecordCommand;
+import seedu.address.logic.commands.ExitCommand;
+import seedu.address.logic.commands.FindCommand;
+import seedu.address.logic.commands.FindRecordCommand;
+import seedu.address.logic.commands.ListCommand;
+import seedu.address.logic.commands.PinCommand;
+import seedu.address.logic.commands.UnpinCommand;
+import seedu.address.logic.commands.ViewAppointmentCommand;
+import seedu.address.logic.commands.ViewCommand;
/**
* Controller for a help page
*/
public class HelpWindow extends UiPart {
- public static final String USERGUIDE_URL = "https://se-education.org/addressbook-level3/UserGuide.html";
- public static final String HELP_MESSAGE = "Refer to the user guide: " + USERGUIDE_URL;
-
+ public static final String USERGUIDE_URL = "https://ay2324s1-cs2103t-t12-4.github.io/tp/UserGuide.html";
+ public static final String HELP_MESSAGE = "A more detailed User "
+ + "Guide can be accessed " + "from this url: ";
+ public static final String COMMAND_SUMMARY = "COMMAND SUMMARY";
private static final Logger logger = LogsCenter.getLogger(HelpWindow.class);
private static final String FXML = "HelpWindow.fxml";
@FXML
private Button copyButton;
-
+ @FXML
+ private Label commandSummary;
@FXML
private Label helpMessage;
+ @FXML
+ private Label addPatient;
+ @FXML
+ private Label editPatient;
+ @FXML
+ private Label addRecord;
+ @FXML
+ private Label editRecord;
+ @FXML
+ private Label addAppointment;
+ @FXML
+ private Label viewAppointment;
+ @FXML
+ private Label deleteAppointment;
+ @FXML
+ private Label findRecords;
+ @FXML
+ private Label deleteRecord;
+ @FXML
+ private Label list;
+ @FXML
+ private Label find;
+ @FXML
+ private Label pin;
+ @FXML
+ private Label unpin;
+ @FXML
+ private Label view;
+ @FXML
+ private Label delete;
+ @FXML
+ private Label exit;
/**
* Creates a new HelpWindow.
@@ -34,7 +84,24 @@ public class HelpWindow extends UiPart {
*/
public HelpWindow(Stage root) {
super(FXML, root);
- helpMessage.setText(HELP_MESSAGE);
+ commandSummary.setText(COMMAND_SUMMARY);
+ helpMessage.setText(HELP_MESSAGE + USERGUIDE_URL);
+ addPatient.setText(AddCommand.MESSAGE_USAGE);
+ editPatient.setText(EditCommand.MESSAGE_USAGE);
+ addRecord.setText(AddRecordCommand.MESSAGE_USAGE);
+ editRecord.setText(EditRecordCommand.MESSAGE_USAGE);
+ addAppointment.setText(AddAppointmentCommand.MESSAGE_USAGE);
+ viewAppointment.setText(ViewAppointmentCommand.MESSAGE_USAGE);
+ deleteAppointment.setText(DeleteAppointmentCommand.MESSAGE_USAGE);
+ pin.setText(PinCommand.MESSAGE_USAGE);
+ unpin.setText(UnpinCommand.MESSAGE_USAGE);
+ view.setText(ViewCommand.MESSAGE_USAGE);
+ delete.setText(DeleteCommand.MESSAGE_USAGE);
+ deleteRecord.setText(DeleteRecordCommand.MESSAGE_USAGE);
+ find.setText(FindCommand.MESSAGE_USAGE);
+ findRecords.setText(FindRecordCommand.MESSAGE_USAGE);
+ list.setText(ListCommand.COMMAND_WORD + ": Lists all patients in MedBook.");
+ exit.setText(ExitCommand.COMMAND_WORD + ": Exits the application.");
}
/**
@@ -46,21 +113,24 @@ public HelpWindow() {
/**
* Shows the help window.
+ *
* @throws IllegalStateException
- *
- *
- * if this method is called on a thread other than the JavaFX Application Thread.
- *
- *
- * if this method is called during animation or layout processing.
- *
- *
- * if this method is called on the primary stage.
- *
- *
- * if {@code dialogStage} is already showing.
- *
- *
+ *
+ *
+ * if this method is called on a thread other than
+ * the JavaFX Application Thread.
+ *
+ *
+ * if this method is called during animation or
+ * layout processing.
+ *
+ *
+ * if this method is called on the primary stage.
+ *
+ *
+ * if {@code dialogStage} is already showing.
+ *
+ *
*/
public void show() {
logger.fine("Showing help page about the application.");
diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/address/ui/MainWindow.java
index 79e74ef37c0..77d93a5e2a0 100644
--- a/src/main/java/seedu/address/ui/MainWindow.java
+++ b/src/main/java/seedu/address/ui/MainWindow.java
@@ -24,6 +24,7 @@
public class MainWindow extends UiPart {
private static final String FXML = "MainWindow.fxml";
+ private static MainWindow mainWindow;
private final Logger logger = LogsCenter.getLogger(getClass());
@@ -34,19 +35,24 @@ public class MainWindow extends UiPart {
private PersonListPanel personListPanel;
private ResultDisplay resultDisplay;
private HelpWindow helpWindow;
-
+ private AppointmentsWindow appointmentsWindow;
+ private PinnedPersonListPanel pinnedPersonListPanel;
+ private RecordListPanel recordListPanel;
+ private PersonListPanel personBeingViewedPanel;
@FXML
private StackPane commandBoxPlaceholder;
-
@FXML
private MenuItem helpMenuItem;
-
@FXML
private StackPane personListPanelPlaceholder;
-
+ @FXML
+ private StackPane pinnedPersonListPanelPlaceholder;
+ @FXML
+ private StackPane personBeingViewedPanelPlaceholder;
+ @FXML
+ private StackPane recordListPanelPlaceholder;
@FXML
private StackPane resultDisplayPlaceholder;
-
@FXML
private StackPane statusbarPlaceholder;
@@ -66,6 +72,8 @@ public MainWindow(Stage primaryStage, Logic logic) {
setAccelerators();
helpWindow = new HelpWindow();
+ appointmentsWindow = new AppointmentsWindow(new Stage(), logic.getFilteredAppointmentList());
+ mainWindow = this;
}
public Stage getPrimaryStage() {
@@ -76,8 +84,17 @@ private void setAccelerators() {
setAccelerator(helpMenuItem, KeyCombination.valueOf("F1"));
}
+ public static MainWindow getInstance() {
+ return mainWindow;
+ }
+
+ public void setResultDisplay(String s) {
+ resultDisplay.setFeedbackToUser(s);
+ }
+
/**
* Sets the accelerator of a MenuItem.
+ *
* @param keyCombination the KeyCombination value of the accelerator
*/
private void setAccelerator(MenuItem menuItem, KeyCombination keyCombination) {
@@ -113,6 +130,15 @@ void fillInnerParts() {
personListPanel = new PersonListPanel(logic.getFilteredPersonList());
personListPanelPlaceholder.getChildren().add(personListPanel.getRoot());
+ pinnedPersonListPanel = new PinnedPersonListPanel(logic.getPinnedPersonList());
+ pinnedPersonListPanelPlaceholder.getChildren().add(pinnedPersonListPanel.getRoot());
+
+ recordListPanel = new RecordListPanel(logic.getFilteredRecordList());
+ recordListPanelPlaceholder.getChildren().add(recordListPanel.getRoot());
+
+ personBeingViewedPanel = new PersonListPanel(logic.getPersonBeingViewed());
+ personBeingViewedPanelPlaceholder.getChildren().add(personBeingViewedPanel.getRoot());
+
resultDisplay = new ResultDisplay();
resultDisplayPlaceholder.getChildren().add(resultDisplay.getRoot());
@@ -147,6 +173,18 @@ public void handleHelp() {
}
}
+ /**
+ * Opens the appointments window or focuses on it if it's already opened.
+ */
+ @FXML
+ public void handleAppointments() {
+ if (!appointmentsWindow.isShowing()) {
+ appointmentsWindow.show();
+ } else {
+ appointmentsWindow.focus();
+ }
+ }
+
void show() {
primaryStage.show();
}
@@ -182,6 +220,10 @@ private CommandResult executeCommand(String commandText) throws CommandException
handleHelp();
}
+ if (commandResult.isShowAppointments()) {
+ handleAppointments();
+ }
+
if (commandResult.isExit()) {
handleExit();
}
diff --git a/src/main/java/seedu/address/ui/PersonCard.java b/src/main/java/seedu/address/ui/PersonCard.java
index 094c42cda82..9d43fb33f37 100644
--- a/src/main/java/seedu/address/ui/PersonCard.java
+++ b/src/main/java/seedu/address/ui/PersonCard.java
@@ -31,15 +31,21 @@ public class PersonCard extends UiPart {
@FXML
private Label name;
@FXML
+ private Label nric;
+ @FXML
private Label id;
@FXML
private Label phone;
@FXML
- private Label address;
- @FXML
private Label email;
@FXML
- private FlowPane tags;
+ private Label age;
+ @FXML
+ private FlowPane allergies;
+ @FXML
+ private Label gender;
+ @FXML
+ private Label bloodType;
/**
* Creates a {@code PersonCode} with the given {@code Person} and index to display.
@@ -49,11 +55,21 @@ public PersonCard(Person person, int displayedIndex) {
this.person = person;
id.setText(displayedIndex + ". ");
name.setText(person.getName().fullName);
+ nric.setText(person.getNric().nric);
phone.setText(person.getPhone().value);
- address.setText(person.getAddress().value);
+ age.setText(String.valueOf(person.getAge().age));
+ gender.setText(person.getGender().gender);
email.setText(person.getEmail().value);
- person.getTags().stream()
- .sorted(Comparator.comparing(tag -> tag.tagName))
- .forEach(tag -> tags.getChildren().add(new Label(tag.tagName)));
+ bloodType.setText(person.getBloodType().bloodType);
+
+ person.getAllergies().stream()
+ .sorted(Comparator.comparing(allergy -> allergy.allergy))
+ .forEach(allergy ->
+ {
+ Label label = new Label(allergy.allergy + " ");
+ label.setWrapText(true);
+ label.setMaxWidth(100);
+ allergies.getChildren().add(label);
+ });
}
}
diff --git a/src/main/java/seedu/address/ui/PinnedPersonListPanel.java b/src/main/java/seedu/address/ui/PinnedPersonListPanel.java
new file mode 100644
index 00000000000..112f1dec360
--- /dev/null
+++ b/src/main/java/seedu/address/ui/PinnedPersonListPanel.java
@@ -0,0 +1,49 @@
+package seedu.address.ui;
+
+import java.util.logging.Logger;
+
+import javafx.collections.ObservableList;
+import javafx.fxml.FXML;
+import javafx.scene.control.ListCell;
+import javafx.scene.control.ListView;
+import javafx.scene.layout.Region;
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.model.person.Person;
+
+/**
+ * Panel containing the list of pinned persons.
+ */
+public class PinnedPersonListPanel extends UiPart {
+ private static final String FXML = "PinnedPersonListPanel.fxml";
+ private final Logger logger = LogsCenter.getLogger(PinnedPersonListPanel.class);
+
+ @FXML
+ private ListView pinnedPersonListView;
+
+ /**
+ * Creates a {@code PinnedPersonListPanel} with the given {@code ObservableList}.
+ */
+ public PinnedPersonListPanel(ObservableList pinnedPersonList) {
+ super(FXML);
+ pinnedPersonListView.setItems(pinnedPersonList);
+ pinnedPersonListView.setCellFactory(listView -> new PinnedPersonListViewCell());
+ }
+
+ /**
+ * Custom {@code ListCell} that displays the graphics of a {@code Person} using a {@code PersonCard}.
+ */
+ class PinnedPersonListViewCell extends ListCell {
+ @Override
+ protected void updateItem(Person person, boolean empty) {
+ super.updateItem(person, empty);
+
+ if (empty || person == null) {
+ setGraphic(null);
+ setText(null);
+ } else {
+ setGraphic(new PersonCard(person, getIndex() + 1).getRoot());
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/seedu/address/ui/RecordCard.java b/src/main/java/seedu/address/ui/RecordCard.java
new file mode 100644
index 00000000000..909c02f35d7
--- /dev/null
+++ b/src/main/java/seedu/address/ui/RecordCard.java
@@ -0,0 +1,107 @@
+package seedu.address.ui;
+
+import java.awt.Desktop;
+import java.io.File;
+import java.io.IOException;
+
+import javafx.event.ActionEvent;
+import javafx.fxml.FXML;
+import javafx.scene.control.Hyperlink;
+import javafx.scene.control.Label;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.Region;
+import javafx.stage.FileChooser;
+import seedu.address.model.record.Record;
+
+/**
+ * An UI component that displays information of a {@code Record}.
+ */
+public class RecordCard extends UiPart {
+
+ private static final String FXML = "RecordListCard.fxml";
+
+ public final Record record;
+ @FXML
+ private HBox cardPane;
+ @FXML
+ private Label id;
+ @FXML
+ private Label dateTime;
+ @FXML
+ private Label condition;
+ @FXML
+ private Hyperlink filePathLabel;
+ @FXML
+ private Label medication;
+
+ private int displayedIndex;
+ private MainWindow mainWindow;
+
+ /**
+ * Creates a {@code RecordCard} with the given {@code Record} and index to
+ * display.
+ *
+ * @param record The record object to be displayed.
+ * @param displayedIndex The index at which the record will be displayed.
+ */
+ public RecordCard(Record record, int displayedIndex) {
+ super(FXML);
+ this.record = record;
+ id.setText(displayedIndex + ". ");
+ dateTime.setText(record.getDateTime().toString());
+ condition.setText(record.getConditions().toString());
+ if (record.getFilePath() != null) {
+ filePathLabel.setText(record.getFilePath().toString());
+ } else {
+ filePathLabel.setText("No file attached");
+ filePathLabel.setDisable(true); // Disable the hyperlink if no file is attached
+ }
+ this.displayedIndex = displayedIndex;
+ medication.setText(record.getMedications().toString());
+ mainWindow = MainWindow.getInstance();
+ }
+
+ /**
+ * Opens a file chooser dialog to attach a file to the record.
+ *
+ * @param event The event triggering this method call.
+ */
+ @FXML
+ public void handleAttachFile(ActionEvent event) {
+ FileChooser fileChooser = new FileChooser();
+ fileChooser.setTitle("Select File to Attach");
+ File file = fileChooser.showOpenDialog(cardPane.getScene().getWindow());
+
+ if (file != null) {
+ this.record.setFilePath(file.toPath(), displayedIndex);
+ filePathLabel.setText(file.toPath().toString());
+ mainWindow.setResultDisplay("File successfully attached!");
+ } else {
+ mainWindow.setResultDisplay("No file chosen");
+ }
+
+ }
+
+ /**
+ * Opens the file associated with the record if available.
+ *
+ * @param event The event triggering this method call.
+ */
+ @FXML
+ public void handleOpenFile(ActionEvent event) {
+ if (record.getFilePath() != null && Desktop.isDesktopSupported()) {
+ try {
+ Desktop.getDesktop().open(record.getFilePath().toFile());
+ mainWindow.setResultDisplay("File successfully opened");
+ } catch (IllegalArgumentException e) {
+ // Handle the case where the file does not exist or is not valid.
+
+ mainWindow.setResultDisplay("There was an issue with the file: " + e.getMessage());
+ } catch (IOException e) {
+ // Handle other I/O errors.
+ e.printStackTrace();
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/seedu/address/ui/RecordListPanel.java b/src/main/java/seedu/address/ui/RecordListPanel.java
new file mode 100644
index 00000000000..c0eaa00811c
--- /dev/null
+++ b/src/main/java/seedu/address/ui/RecordListPanel.java
@@ -0,0 +1,52 @@
+package seedu.address.ui;
+
+import java.util.logging.Logger;
+
+import javafx.collections.ObservableList;
+import javafx.fxml.FXML;
+import javafx.scene.control.ListCell;
+import javafx.scene.control.ListView;
+import javafx.scene.layout.Region;
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.model.record.Record;
+
+/**
+ * Panel contains a list of records.
+ */
+
+public class RecordListPanel extends UiPart {
+
+ private static final String FXML = "RecordListPanel.fxml";
+
+ private final Logger logger = LogsCenter.getLogger(RecordListPanel.class);
+
+ @FXML
+ private ListView recordListView;
+
+ /**
+ * Creates a {@code RecordListPanel} with the given {@code ObservableList}.
+ */
+ public RecordListPanel(ObservableList recordList) {
+ super(FXML);
+ recordListView.setItems(recordList);
+ recordListView.setCellFactory(listView -> new RecordListPanel.RecordListViewCell());
+ }
+
+ /**
+ * Custom {@code ListCell} that displays the graphics of a {@code Record} using
+ * a {@code RecordCard}.
+ */
+ class RecordListViewCell extends ListCell {
+ @Override
+ protected void updateItem(Record record, boolean empty) {
+ super.updateItem(record, empty);
+
+ if (empty || record == null) {
+ setGraphic(null);
+ setText(null);
+ } else {
+ setGraphic(new RecordCard(record, getIndex() + 1).getRoot());
+ }
+ }
+ }
+}
diff --git a/src/main/resources/view/AppointmentCalenderPanel.fxml b/src/main/resources/view/AppointmentCalenderPanel.fxml
new file mode 100644
index 00000000000..7d9937c4cd6
--- /dev/null
+++ b/src/main/resources/view/AppointmentCalenderPanel.fxml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/AppointmentListCard.fxml b/src/main/resources/view/AppointmentListCard.fxml
new file mode 100644
index 00000000000..bb7bd565245
--- /dev/null
+++ b/src/main/resources/view/AppointmentListCard.fxml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/AppointmentListPanel.fxml b/src/main/resources/view/AppointmentListPanel.fxml
new file mode 100644
index 00000000000..cb897960409
--- /dev/null
+++ b/src/main/resources/view/AppointmentListPanel.fxml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/AppointmentsWindow.fxml b/src/main/resources/view/AppointmentsWindow.fxml
new file mode 100644
index 00000000000..9dadda0d797
--- /dev/null
+++ b/src/main/resources/view/AppointmentsWindow.fxml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/DarkTheme.css b/src/main/resources/view/DarkTheme.css
index 36e6b001cd8..316a0cac8ba 100644
--- a/src/main/resources/view/DarkTheme.css
+++ b/src/main/resources/view/DarkTheme.css
@@ -1,210 +1,221 @@
.background {
- -fx-background-color: derive(#1d1d1d, 20%);
- background-color: #383838; /* Used in the default.html file */
+ -fx-background-color: derive(#1d1d1d, 20%);
+ background-color: #383838; /* Used in the default.html file */
+}
+
+.calendar-label {
+ -fx-font-size: 16px;
+}
+
+.label-appointment {
+ -fx-font-size: 7pt;
+ -fx-font-family: "Segoe UI Semibold";
+ -fx-text-fill: black;
+ -fx-opacity: 1;
+ -fx-text-overrun: ellipsis;
+ -fx-white-space: nowrap;
+ -fx-overflow: hidden;
}
.label {
- -fx-font-size: 11pt;
- -fx-font-family: "Segoe UI Semibold";
- -fx-text-fill: #555555;
- -fx-opacity: 0.9;
+ -fx-font-size: 11pt;
+ -fx-font-family: "Segoe UI Semibold";
+ -fx-text-fill: #555555;
+ -fx-opacity: 0.9;
}
.label-bright {
- -fx-font-size: 11pt;
- -fx-font-family: "Segoe UI Semibold";
- -fx-text-fill: white;
- -fx-opacity: 1;
+ -fx-font-size: 11pt;
+ -fx-font-family: "Segoe UI Semibold";
+ -fx-text-fill: white;
+ -fx-opacity: 1;
}
.label-header {
- -fx-font-size: 32pt;
- -fx-font-family: "Segoe UI Light";
- -fx-text-fill: white;
- -fx-opacity: 1;
+ -fx-font-size: 32pt;
+ -fx-font-family: "Segoe UI Light";
+ -fx-text-fill: white;
+ -fx-opacity: 1;
}
.text-field {
- -fx-font-size: 12pt;
- -fx-font-family: "Segoe UI Semibold";
+ -fx-font-size: 12pt;
+ -fx-font-family: "Segoe UI Semibold";
}
.tab-pane {
- -fx-padding: 0 0 0 1;
+ -fx-padding: 0 0 0 1;
}
.tab-pane .tab-header-area {
- -fx-padding: 0 0 0 0;
- -fx-min-height: 0;
- -fx-max-height: 0;
+ -fx-padding: 0 0 0 0;
+ -fx-min-height: 0;
+ -fx-max-height: 0;
}
.table-view {
- -fx-base: #1d1d1d;
- -fx-control-inner-background: #1d1d1d;
- -fx-background-color: #1d1d1d;
- -fx-table-cell-border-color: transparent;
- -fx-table-header-border-color: transparent;
- -fx-padding: 5;
+ -fx-base: #1d1d1d;
+ -fx-control-inner-background: #1d1d1d;
+ -fx-background-color: #1d1d1d;
+ -fx-table-cell-border-color: transparent;
+ -fx-table-header-border-color: transparent;
+ -fx-padding: 5;
}
.table-view .column-header-background {
- -fx-background-color: transparent;
+ -fx-background-color: transparent;
}
-.table-view .column-header, .table-view .filler {
- -fx-size: 35;
- -fx-border-width: 0 0 1 0;
- -fx-background-color: transparent;
- -fx-border-color:
- transparent
- transparent
- derive(-fx-base, 80%)
- transparent;
- -fx-border-insets: 0 10 1 0;
+.table-view .column-header,
+.table-view .filler {
+ -fx-size: 35;
+ -fx-border-width: 0 0 1 0;
+ -fx-background-color: transparent;
+ -fx-border-color: transparent transparent derive(-fx-base, 80%) transparent;
+ -fx-border-insets: 0 10 1 0;
}
.table-view .column-header .label {
- -fx-font-size: 20pt;
- -fx-font-family: "Segoe UI Light";
- -fx-text-fill: white;
- -fx-alignment: center-left;
- -fx-opacity: 1;
+ -fx-font-size: 20pt;
+ -fx-font-family: "Segoe UI Light";
+ -fx-text-fill: white;
+ -fx-alignment: center-left;
+ -fx-opacity: 1;
}
.table-view:focused .table-row-cell:filled:focused:selected {
- -fx-background-color: -fx-focus-color;
+ -fx-background-color: -fx-focus-color;
}
.split-pane:horizontal .split-pane-divider {
- -fx-background-color: derive(#1d1d1d, 20%);
- -fx-border-color: transparent transparent transparent #4d4d4d;
+ -fx-background-color: derive(#1d1d1d, 20%);
+ -fx-border-color: transparent transparent transparent #4d4d4d;
}
.split-pane {
- -fx-border-radius: 1;
- -fx-border-width: 1;
- -fx-background-color: derive(#1d1d1d, 20%);
+ -fx-border-radius: 1;
+ -fx-border-width: 1;
+ -fx-background-color: derive(#1d1d1d, 20%);
}
.list-view {
- -fx-background-insets: 0;
- -fx-padding: 0;
- -fx-background-color: derive(#1d1d1d, 20%);
+ -fx-background-insets: 0;
+ -fx-padding: 0;
+ -fx-background-color: derive(#1d1d1d, 20%);
}
.list-cell {
- -fx-label-padding: 0 0 0 0;
- -fx-graphic-text-gap : 0;
- -fx-padding: 0 0 0 0;
+ -fx-label-padding: 0 0 0 0;
+ -fx-graphic-text-gap: 0;
+ -fx-padding: 0 0 0 0;
}
.list-cell:filled:even {
- -fx-background-color: #3c3e3f;
+ -fx-background-color: #3c3e3f;
}
.list-cell:filled:odd {
- -fx-background-color: #515658;
+ -fx-background-color: #515658;
}
.list-cell:filled:selected {
- -fx-background-color: #424d5f;
+ -fx-background-color: #424d5f;
}
.list-cell:filled:selected #cardPane {
- -fx-border-color: #3e7b91;
- -fx-border-width: 1;
+ -fx-border-color: #3e7b91;
+ -fx-border-width: 1;
}
.list-cell .label {
- -fx-text-fill: white;
+ -fx-text-fill: white;
}
.cell_big_label {
- -fx-font-family: "Segoe UI Semibold";
- -fx-font-size: 16px;
- -fx-text-fill: #010504;
+ -fx-font-family: "Segoe UI Semibold";
+ -fx-font-size: 16px;
+ -fx-text-fill: #010504;
}
.cell_small_label {
- -fx-font-family: "Segoe UI";
- -fx-font-size: 13px;
- -fx-text-fill: #010504;
+ -fx-font-family: "Segoe UI";
+ -fx-font-size: 13px;
+ -fx-text-fill: #010504;
}
.stack-pane {
- -fx-background-color: derive(#1d1d1d, 20%);
+ -fx-background-color: derive(#1d1d1d, 20%);
}
.pane-with-border {
- -fx-background-color: derive(#1d1d1d, 20%);
- -fx-border-color: derive(#1d1d1d, 10%);
- -fx-border-top-width: 1px;
+ -fx-background-color: derive(#1d1d1d, 20%);
+ -fx-border-color: derive(#1d1d1d, 10%);
+ -fx-border-top-width: 1px;
}
.status-bar {
- -fx-background-color: derive(#1d1d1d, 30%);
+ -fx-background-color: derive(#1d1d1d, 30%);
}
.result-display {
- -fx-background-color: transparent;
- -fx-font-family: "Segoe UI Light";
- -fx-font-size: 13pt;
- -fx-text-fill: white;
+ -fx-background-color: transparent;
+ -fx-font-family: "Segoe UI Light";
+ -fx-font-size: 13pt;
+ -fx-text-fill: white;
}
.result-display .label {
- -fx-text-fill: black !important;
+ -fx-text-fill: black !important;
}
.status-bar .label {
- -fx-font-family: "Segoe UI Light";
- -fx-text-fill: white;
- -fx-padding: 4px;
- -fx-pref-height: 30px;
+ -fx-font-family: "Segoe UI Light";
+ -fx-text-fill: white;
+ -fx-padding: 4px;
+ -fx-pref-height: 30px;
}
.status-bar-with-border {
- -fx-background-color: derive(#1d1d1d, 30%);
- -fx-border-color: derive(#1d1d1d, 25%);
- -fx-border-width: 1px;
+ -fx-background-color: derive(#1d1d1d, 30%);
+ -fx-border-color: derive(#1d1d1d, 25%);
+ -fx-border-width: 1px;
}
.status-bar-with-border .label {
- -fx-text-fill: white;
+ -fx-text-fill: white;
}
.grid-pane {
- -fx-background-color: derive(#1d1d1d, 30%);
- -fx-border-color: derive(#1d1d1d, 30%);
- -fx-border-width: 1px;
+ -fx-background-color: derive(#1d1d1d, 30%);
+ -fx-border-color: derive(#1d1d1d, 30%);
+ -fx-border-width: 1px;
}
.grid-pane .stack-pane {
- -fx-background-color: derive(#1d1d1d, 30%);
+ -fx-background-color: derive(#1d1d1d, 30%);
}
.context-menu {
- -fx-background-color: derive(#1d1d1d, 50%);
+ -fx-background-color: derive(#1d1d1d, 50%);
}
.context-menu .label {
- -fx-text-fill: white;
+ -fx-text-fill: white;
}
.menu-bar {
- -fx-background-color: derive(#1d1d1d, 20%);
+ -fx-background-color: derive(#1d1d1d, 20%);
}
.menu-bar .label {
- -fx-font-size: 14pt;
- -fx-font-family: "Segoe UI Light";
- -fx-text-fill: white;
- -fx-opacity: 0.9;
+ -fx-font-size: 14pt;
+ -fx-font-family: "Segoe UI Light";
+ -fx-text-fill: white;
+ -fx-opacity: 0.9;
}
.menu .left-container {
- -fx-background-color: black;
+ -fx-background-color: black;
}
/*
@@ -213,140 +224,148 @@
* http://pixelduke.wordpress.com/2012/10/23/jmetro-windows-8-controls-on-java/
*/
.button {
- -fx-padding: 5 22 5 22;
- -fx-border-color: #e2e2e2;
- -fx-border-width: 2;
- -fx-background-radius: 0;
- -fx-background-color: #1d1d1d;
- -fx-font-family: "Segoe UI", Helvetica, Arial, sans-serif;
- -fx-font-size: 11pt;
- -fx-text-fill: #d8d8d8;
- -fx-background-insets: 0 0 0 0, 0, 1, 2;
+ -fx-padding: 5 22 5 22;
+ -fx-border-color: #e2e2e2;
+ -fx-border-width: 2;
+ -fx-background-radius: 0;
+ -fx-background-color: #1d1d1d;
+ -fx-font-family: "Segoe UI", Helvetica, Arial, sans-serif;
+ -fx-font-size: 11pt;
+ -fx-text-fill: #d8d8d8;
+ -fx-background-insets: 0 0 0 0, 0, 1, 2;
}
.button:hover {
- -fx-background-color: #3a3a3a;
+ -fx-background-color: #3a3a3a;
}
-.button:pressed, .button:default:hover:pressed {
+.button:pressed,
+.button:default:hover:pressed {
-fx-background-color: white;
-fx-text-fill: #1d1d1d;
}
.button:focused {
- -fx-border-color: white, white;
- -fx-border-width: 1, 1;
- -fx-border-style: solid, segments(1, 1);
- -fx-border-radius: 0, 0;
- -fx-border-insets: 1 1 1 1, 0;
+ -fx-border-color: white, white;
+ -fx-border-width: 1, 1;
+ -fx-border-style: solid, segments(1, 1);
+ -fx-border-radius: 0, 0;
+ -fx-border-insets: 1 1 1 1, 0;
}
-.button:disabled, .button:default:disabled {
- -fx-opacity: 0.4;
- -fx-background-color: #1d1d1d;
- -fx-text-fill: white;
+.button:disabled,
+.button:default:disabled {
+ -fx-opacity: 0.4;
+ -fx-background-color: #1d1d1d;
+ -fx-text-fill: white;
}
.button:default {
- -fx-background-color: -fx-focus-color;
- -fx-text-fill: #ffffff;
+ -fx-background-color: -fx-focus-color;
+ -fx-text-fill: #ffffff;
}
.button:default:hover {
- -fx-background-color: derive(-fx-focus-color, 30%);
+ -fx-background-color: derive(-fx-focus-color, 30%);
}
.dialog-pane {
- -fx-background-color: #1d1d1d;
+ -fx-background-color: #1d1d1d;
}
.dialog-pane > *.button-bar > *.container {
- -fx-background-color: #1d1d1d;
+ -fx-background-color: #1d1d1d;
}
.dialog-pane > *.label.content {
- -fx-font-size: 14px;
- -fx-font-weight: bold;
- -fx-text-fill: white;
+ -fx-font-size: 14px;
+ -fx-font-weight: bold;
+ -fx-text-fill: white;
}
.dialog-pane:header *.header-panel {
- -fx-background-color: derive(#1d1d1d, 25%);
+ -fx-background-color: derive(#1d1d1d, 25%);
}
.dialog-pane:header *.header-panel *.label {
- -fx-font-size: 18px;
- -fx-font-style: italic;
- -fx-fill: white;
- -fx-text-fill: white;
+ -fx-font-size: 18px;
+ -fx-font-style: italic;
+ -fx-fill: white;
+ -fx-text-fill: white;
}
.scroll-bar {
- -fx-background-color: derive(#1d1d1d, 20%);
+ -fx-background-color: derive(#1d1d1d, 20%);
}
.scroll-bar .thumb {
- -fx-background-color: derive(#1d1d1d, 50%);
- -fx-background-insets: 3;
+ -fx-background-color: derive(#1d1d1d, 50%);
+ -fx-background-insets: 3;
}
-.scroll-bar .increment-button, .scroll-bar .decrement-button {
- -fx-background-color: transparent;
- -fx-padding: 0 0 0 0;
+.scroll-bar .increment-button,
+.scroll-bar .decrement-button {
+ -fx-background-color: transparent;
+ -fx-padding: 0 0 0 0;
}
-.scroll-bar .increment-arrow, .scroll-bar .decrement-arrow {
- -fx-shape: " ";
+.scroll-bar .increment-arrow,
+.scroll-bar .decrement-arrow {
+ -fx-shape: " ";
}
-.scroll-bar:vertical .increment-arrow, .scroll-bar:vertical .decrement-arrow {
- -fx-padding: 1 8 1 8;
+.scroll-bar:vertical .increment-arrow,
+.scroll-bar:vertical .decrement-arrow {
+ -fx-padding: 1 8 1 8;
}
-.scroll-bar:horizontal .increment-arrow, .scroll-bar:horizontal .decrement-arrow {
- -fx-padding: 8 1 8 1;
+.scroll-bar:horizontal .increment-arrow,
+.scroll-bar:horizontal .decrement-arrow {
+ -fx-padding: 8 1 8 1;
}
#cardPane {
- -fx-background-color: transparent;
- -fx-border-width: 0;
+ -fx-background-color: transparent;
+ -fx-border-width: 0;
}
#commandTypeLabel {
- -fx-font-size: 11px;
- -fx-text-fill: #F70D1A;
+ -fx-font-size: 11px;
+ -fx-text-fill: #f70d1a;
}
#commandTextField {
- -fx-background-color: transparent #383838 transparent #383838;
- -fx-background-insets: 0;
- -fx-border-color: #383838 #383838 #ffffff #383838;
- -fx-border-insets: 0;
- -fx-border-width: 1;
- -fx-font-family: "Segoe UI Light";
- -fx-font-size: 13pt;
- -fx-text-fill: white;
+ -fx-background-color: transparent #383838 transparent #383838;
+ -fx-background-insets: 0;
+ -fx-border-color: #383838 #383838 #ffffff #383838;
+ -fx-border-insets: 0;
+ -fx-border-width: 1;
+ -fx-font-family: "Segoe UI Light";
+ -fx-font-size: 13pt;
+ -fx-text-fill: white;
}
-#filterField, #personListPanel, #personWebpage {
- -fx-effect: innershadow(gaussian, black, 10, 0, 0, 0);
+#filterField,
+#personListPanel,
+#personWebpage {
+ -fx-effect: innershadow(gaussian, black, 10, 0, 0, 0);
}
#resultDisplay .content {
- -fx-background-color: transparent, #383838, transparent, #383838;
- -fx-background-radius: 0;
+ -fx-background-color: transparent, #383838, transparent, #383838;
+ -fx-background-radius: 0;
}
#tags {
- -fx-hgap: 7;
- -fx-vgap: 3;
+ -fx-hgap: 7;
+ -fx-vgap: 3;
}
#tags .label {
- -fx-text-fill: white;
- -fx-background-color: #3e7b91;
- -fx-padding: 1 3 1 3;
- -fx-border-radius: 2;
- -fx-background-radius: 2;
- -fx-font-size: 11;
+ -fx-text-fill: white;
+ -fx-background-color: #3e7b91;
+ -fx-padding: 1 3 1 3;
+ -fx-border-radius: 2;
+ -fx-background-radius: 2;
+ -fx-font-size: 11;
}
diff --git a/src/main/resources/view/HelpWindow.fxml b/src/main/resources/view/HelpWindow.fxml
index e01f330de33..01ee058524e 100644
--- a/src/main/resources/view/HelpWindow.fxml
+++ b/src/main/resources/view/HelpWindow.fxml
@@ -7,9 +7,11 @@
+
-
+
+
@@ -18,27 +20,129 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml
index 7778f666a0a..4fd5260044b 100644
--- a/src/main/resources/view/MainWindow.fxml
+++ b/src/main/resources/view/MainWindow.fxml
@@ -11,50 +11,81 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/PersonListCard.fxml b/src/main/resources/view/PersonListCard.fxml
index f5e812e25e6..5e5e56d7b47 100644
--- a/src/main/resources/view/PersonListCard.fxml
+++ b/src/main/resources/view/PersonListCard.fxml
@@ -7,30 +7,58 @@
+
-
+
-
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
diff --git a/src/main/resources/view/PinnedPersonListPanel.fxml b/src/main/resources/view/PinnedPersonListPanel.fxml
new file mode 100644
index 00000000000..e29159ec918
--- /dev/null
+++ b/src/main/resources/view/PinnedPersonListPanel.fxml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/RecordListCard.fxml b/src/main/resources/view/RecordListCard.fxml
new file mode 100644
index 00000000000..8a15aabef9f
--- /dev/null
+++ b/src/main/resources/view/RecordListCard.fxml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/RecordListPanel.fxml b/src/main/resources/view/RecordListPanel.fxml
new file mode 100644
index 00000000000..bc60d33b1f2
--- /dev/null
+++ b/src/main/resources/view/RecordListPanel.fxml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/ViewedPersonPanel.fxml b/src/main/resources/view/ViewedPersonPanel.fxml
new file mode 100644
index 00000000000..1de0b081e5e
--- /dev/null
+++ b/src/main/resources/view/ViewedPersonPanel.fxml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/src/test/data/JsonAddressBookStorageTest/invalidAndValidPersonAddressBook.json b/src/test/data/JsonAddressBookStorageTest/invalidAndValidPersonAddressBook.json
index 6a4d2b7181c..7acf1ff3108 100644
--- a/src/test/data/JsonAddressBookStorageTest/invalidAndValidPersonAddressBook.json
+++ b/src/test/data/JsonAddressBookStorageTest/invalidAndValidPersonAddressBook.json
@@ -1,13 +1,56 @@
{
- "persons": [ {
- "name": "Valid Person",
- "phone": "9482424",
- "email": "hans@example.com",
- "address": "4th street"
- }, {
- "name": "Person With Invalid Phone Field",
- "phone": "948asdf2424",
- "email": "hans@example.com",
- "address": "4th street"
- } ]
+ "persons": [
+ {
+ "name": "Valid Person",
+ "nric": "S6789123A",
+ "email": "hans@example.com",
+ "phone": "9482424",
+ "gender": "M",
+ "age": "18",
+ "bloodType": "A+",
+ "allergies": ["Peanuts"],
+ "records": [
+ {
+ "dateTime": "09-10-2023 1800",
+ "conditions": ["Fever"],
+ "medications": ["Tylenol"],
+ "filename": null,
+ "personIndex": 0
+ }
+ ],
+ "appointments": [
+ {
+ "name": "Eye Examination",
+ "dateTime": "18-09-2023 1800"
+ }
+ ],
+ "isPinned": true
+ },
+ {
+ "name": "Person With Invalid Phone Field",
+ "nric": "S6789125A",
+ "email": "hans@example.com",
+ "phone": "948asdf2424",
+ "gender": "M",
+ "age": "18",
+ "bloodType": "A+",
+ "allergies": ["Peanuts"],
+ "records": [
+ {
+ "dateTime": "09-10-2023 1800",
+ "conditions": ["Fever"],
+ "medications": ["Tylenol"],
+ "filename": null,
+ "personIndex": 0
+ }
+ ],
+ "appointments": [
+ {
+ "name": "Eye Examination",
+ "dateTime": "18-09-2023 1800"
+ }
+ ],
+ "isPinned": true
+ }
+ ]
}
diff --git a/src/test/data/JsonAddressBookStorageTest/invalidJson.json b/src/test/data/JsonAddressBookStorageTest/invalidJson.json
new file mode 100644
index 00000000000..e7bbfc189c6
--- /dev/null
+++ b/src/test/data/JsonAddressBookStorageTest/invalidJson.json
@@ -0,0 +1 @@
+XrrVH/X9yzSVh1Ub7NKv5Ek+WBWnitTecLYC/v4/ZKUtqrnzVqj5swA7m7mn5jC2WcZ98B1RDyROMML+NFVojTbB3K2gkLbOHyCYqxIwroYxGwpUb695unkZSCVQNi+R
diff --git a/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json b/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json
index ccd21f7d1a9..fbcde079bc0 100644
--- a/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json
+++ b/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json
@@ -1,8 +1,30 @@
{
- "persons": [ {
- "name": "Person with invalid name field: Ha!ns Mu@ster",
- "phone": "9482424",
- "email": "hans@example.com",
- "address": "4th street"
- } ]
+ "persons": [
+ {
+ "name": "Person with invalid name field: Ha!ns Mu@ster",
+ "nric": "L1234567A",
+ "email": "hans@example.com",
+ "phone": "9482424",
+ "gender": "M",
+ "age": "18",
+ "bloodType": "A+",
+ "allergies": ["Peanuts"],
+ "records": [
+ {
+ "dateTime": "09-10-2023 1800",
+ "conditions": ["Fever"],
+ "medications": ["Tylenol"],
+ "filename": null,
+ "personIndex": 0
+ }
+ ],
+ "appointments": [
+ {
+ "name": "Eye Examination",
+ "dateTime": "18-09-2023 1800"
+ }
+ ],
+ "isPinned": false
+ }
+ ]
}
diff --git a/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json
index a7427fe7aa2..39b59c74d49 100644
--- a/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json
+++ b/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json
@@ -1,14 +1,59 @@
{
- "persons": [ {
- "name": "Alice Pauline",
- "phone": "94351253",
- "email": "alice@example.com",
- "address": "123, Jurong West Ave 6, #08-111",
- "tags": [ "friends" ]
- }, {
- "name": "Alice Pauline",
- "phone": "94351253",
- "email": "pauline@example.com",
- "address": "4th street"
- } ]
+ "_comment": "AddressBook save file which contains the same Person values as in TypicalPersons#getTypicalAddressBook()",
+ "persons": [
+ {
+ "name": "Alice Pauline",
+ "nric": "S1234567B",
+ "email": "alice@example.com",
+ "phone": "94351253",
+ "bloodType": "AB+",
+ "gender": "F",
+ "age": 20,
+ "allergies": ["Chocolate"],
+ "records": [
+ {
+ "dateTime": "09-10-2023 1800",
+ "conditions": ["Fever"],
+ "medications": ["Tylenol"],
+ "filename": null,
+ "personIndex": 0
+ }
+ ],
+ "appointments": [
+ {
+ "name": "Eye Exam",
+ "dateTime": "01-01-2001 1200",
+ "nric": "S1234567B"
+ }
+ ],
+ "isPinned": true
+ },
+ {
+ "name": "Alice Pauline",
+ "nric": "S1234567B",
+ "email": "alice@example.com",
+ "phone": "94351253",
+ "bloodType": "AB+",
+ "gender": "F",
+ "age": 20,
+ "allergies": ["Chocolate"],
+ "records": [
+ {
+ "dateTime": "09-10-2023 1800",
+ "conditions": ["Fever"],
+ "medications": ["Tylenol"],
+ "filename": null,
+ "personIndex": 0
+ }
+ ],
+ "appointments": [
+ {
+ "name": "Eye Exam",
+ "dateTime": "01-01-2001 1200",
+ "nric": "S1234567B"
+ }
+ ],
+ "isPinned": true
+ }
+ ]
}
diff --git a/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json
index ad3f135ae42..ed0fa6d6b49 100644
--- a/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json
+++ b/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json
@@ -1,8 +1,21 @@
{
- "persons": [ {
- "name": "Hans Muster",
- "phone": "9482424",
- "email": "invalid@email!3e",
- "address": "4th street"
- } ]
+ "persons": [
+ {
+ "name": "Hans Muster",
+ "nric": "S9999999A",
+ "email": "invalid@email!3e",
+ "phone": "9482424",
+ "gender": "M",
+ "age": "18",
+ "bloodType": "A+",
+ "allergies": ["Peanuts"],
+ "isPinned": true,
+ "appointments": [
+ {
+ "name": "Eye Examination",
+ "dateTime": "18-09-2023 1800"
+ }
+ ]
+ }
+ ]
}
diff --git a/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json
index 72262099d35..511a97bbf88 100644
--- a/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json
+++ b/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json
@@ -1,46 +1,180 @@
{
"_comment": "AddressBook save file which contains the same Person values as in TypicalPersons#getTypicalAddressBook()",
- "persons" : [ {
- "name" : "Alice Pauline",
- "phone" : "94351253",
- "email" : "alice@example.com",
- "address" : "123, Jurong West Ave 6, #08-111",
- "tags" : [ "friends" ]
- }, {
- "name" : "Benson Meier",
- "phone" : "98765432",
- "email" : "johnd@example.com",
- "address" : "311, Clementi Ave 2, #02-25",
- "tags" : [ "owesMoney", "friends" ]
- }, {
- "name" : "Carl Kurz",
- "phone" : "95352563",
- "email" : "heinz@example.com",
- "address" : "wall street",
- "tags" : [ ]
- }, {
- "name" : "Daniel Meier",
- "phone" : "87652533",
- "email" : "cornelia@example.com",
- "address" : "10th street",
- "tags" : [ "friends" ]
- }, {
- "name" : "Elle Meyer",
- "phone" : "9482224",
- "email" : "werner@example.com",
- "address" : "michegan ave",
- "tags" : [ ]
- }, {
- "name" : "Fiona Kunz",
- "phone" : "9482427",
- "email" : "lydia@example.com",
- "address" : "little tokyo",
- "tags" : [ ]
- }, {
- "name" : "George Best",
- "phone" : "9482442",
- "email" : "anna@example.com",
- "address" : "4th street",
- "tags" : [ ]
- } ]
+ "persons": [
+ {
+ "name": "Alice Pauline",
+ "nric": "S1234567B",
+ "email": "alice@example.com",
+ "phone": "94351253",
+ "bloodType": "AB+",
+ "gender": "F",
+ "age": 20,
+ "allergies": ["Chocolate"],
+ "records": [
+ {
+ "dateTime": "09-10-2023 1800",
+ "conditions": ["Fever"],
+ "medications": ["Tylenol"],
+ "filename": null,
+ "personIndex": 0
+ }
+ ],
+ "appointments": [
+ {
+ "name": "Eye Exam",
+ "dateTime": "01-01-2001 1200",
+ "nric": "S1234567B"
+ }
+ ],
+ "isPinned": true
+ },
+ {
+ "name": "Benson Meier",
+ "nric": "S1234567A",
+ "email": "johnd@example.com",
+ "phone": "98765432",
+ "bloodType": "B-",
+ "gender": "M",
+ "age": 15,
+ "allergies": ["Pollen", "Soil"],
+ "records": [
+ {
+ "dateTime": "09-09-2023 1800",
+ "conditions": ["Fever", "Cold"],
+ "medications": ["Tylenol"],
+ "filename": null,
+ "personIndex": 1
+ }
+ ],
+ "appointments": [
+ {
+ "name": "Vaccination",
+ "dateTime": "11-09-2001 1200",
+ "nric": "S1234567A"
+ }
+ ],
+ "isPinned": false
+ },
+ {
+ "name": "Carl Kurz",
+ "nric": "T1234567A",
+ "email": "heinz@example.com",
+ "phone": "95352563",
+ "bloodType": "AB-",
+ "gender": "M",
+ "age": 24,
+ "allergies": ["Dogs"],
+ "records": [
+ {
+ "dateTime": "23-10-2022 1130",
+ "conditions": ["Allergic Reaction"],
+ "medications": ["Pepto-Bismol"],
+ "filename": null,
+ "personIndex": 2
+ }
+ ],
+ "appointments": [
+ {
+ "name": "Colonoscopy",
+ "dateTime": "21-12-2001 1200",
+ "nric": "T1234567A"
+ }
+ ],
+ "isPinned": false
+ },
+ {
+ "name": "Ben Kim",
+ "nric": "T9876543A",
+ "email": "bkim@example.com",
+ "phone": "95352563",
+ "bloodType": "AB+",
+ "gender": "M",
+ "age": 25,
+ "allergies": ["Dogs"],
+ "records": [
+ {
+ "dateTime": "23-10-2022 1130",
+ "conditions": ["Allergic Reaction"],
+ "medications": ["Pepto-Bismol"],
+ "filename": null,
+ "personIndex": 2
+ },
+ {
+ "dateTime": "09-10-2023 1800",
+ "conditions": ["Fever"],
+ "medications": ["Tylenol"],
+ "filename": null,
+ "personIndex": 0
+ },
+ {
+ "dateTime": "09-09-2023 1800",
+ "conditions": ["Fever", "Cold"],
+ "medications": ["Tylenol"],
+ "filename": null,
+ "personIndex": 0
+ },
+ {
+ "dateTime": "09-09-2023 1800",
+ "conditions": ["Fever", "Cold"],
+ "medications": ["Tylenol"],
+ "filename": null,
+ "personIndex": 1
+ }
+ ],
+ "appointments": [],
+ "isPinned": false
+ },
+ {
+ "name": "Daniel Meier",
+ "nric": "S5678123A",
+ "email": "cornelia@example.com",
+ "phone": "87652533",
+ "bloodType": "AB+",
+ "gender": "M",
+ "age": 26,
+ "allergies": ["Cats"],
+ "records": [],
+ "appointments": [],
+ "isPinned": false
+ },
+ {
+ "name": "Elle Meyer",
+ "nric": "Z1234567A",
+ "email": "werner@example.com",
+ "phone": "9482224",
+ "bloodType": "A-",
+ "gender": "F",
+ "age": 27,
+ "allergies": ["Light"],
+ "records": [],
+ "appointments": [],
+ "isPinned": false
+ },
+ {
+ "name": "Fiona Kunz",
+ "nric": "X1234567A",
+ "email": "lydia@example.com",
+ "phone": "9482427",
+ "bloodType": "B+",
+ "gender": "F",
+ "age": 29,
+ "allergies": [],
+ "records": [],
+ "appointments": [],
+ "isPinned": false
+ },
+ {
+ "name": "George Best",
+ "nric": "S1234897A",
+ "email": "anna@example.com",
+ "phone": "9482442",
+ "bloodType": "O+",
+ "gender": "M",
+ "age": 30,
+ "allergies": [],
+ "records": [],
+ "appointments": [],
+ "isPinned": false
+ }
+ ]
}
diff --git a/src/test/java/seedu/address/logic/LogicManagerTest.java b/src/test/java/seedu/address/logic/LogicManagerTest.java
index baf8ce336a2..eba081d4480 100644
--- a/src/test/java/seedu/address/logic/LogicManagerTest.java
+++ b/src/test/java/seedu/address/logic/LogicManagerTest.java
@@ -3,9 +3,13 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static seedu.address.logic.Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX;
import static seedu.address.logic.Messages.MESSAGE_UNKNOWN_COMMAND;
-import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.AGE_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.ALLERGY_DESC_DUST;
+import static seedu.address.logic.commands.CommandTestUtil.BLOODTYPE_DESC_AMY;
import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.GENDER_DESC_AMY;
import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.NRIC_DESC_AMY;
import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY;
import static seedu.address.testutil.Assert.assertThrows;
import static seedu.address.testutil.TypicalPersons.AMY;
@@ -45,8 +49,7 @@ public class LogicManagerTest {
@BeforeEach
public void setUp() {
- JsonAddressBookStorage addressBookStorage =
- new JsonAddressBookStorage(temporaryFolder.resolve("addressBook.json"));
+ JsonAddressBookStorage addressBookStorage = new JsonAddressBookStorage(temporaryFolder.resolve("medbook.json"));
JsonUserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(temporaryFolder.resolve("userPrefs.json"));
StorageManager storage = new StorageManager(addressBookStorage, userPrefsStorage);
logic = new LogicManager(model, storage);
@@ -87,11 +90,29 @@ public void getFilteredPersonList_modifyList_throwsUnsupportedOperationException
assertThrows(UnsupportedOperationException.class, () -> logic.getFilteredPersonList().remove(0));
}
+ @Test
+ public void getFilteredAppointmentList_modifyList_throwsUnsupportedOperationException() {
+ assertThrows(UnsupportedOperationException.class, () -> logic.getFilteredAppointmentList().remove(0));
+ }
+
+ @Test
+ public void getRecordList_modifyList_throwsUnsupportedOperationException() {
+ assertThrows(UnsupportedOperationException.class, () -> logic.getRecordList().remove(0));
+ }
+
+ @Test
+ public void getPersonBeingViewed_modifyList_throwsUnsupportedOperationException() {
+ assertThrows(UnsupportedOperationException.class, () -> logic.getPersonBeingViewed().remove(0));
+ }
+
+
/**
* Executes the command and confirms that
* - no exceptions are thrown
* - the feedback message is equal to {@code expectedMessage}
- * - the internal model manager state is the same as that in {@code expectedModel}
+ * - the internal model manager state is the same as that in
+ * {@code expectedModel}
+ *
* @see #assertCommandFailure(String, Class, String, Model)
*/
private void assertCommandSuccess(String inputCommand, String expectedMessage,
@@ -102,7 +123,9 @@ private void assertCommandSuccess(String inputCommand, String expectedMessage,
}
/**
- * Executes the command, confirms that a ParseException is thrown and that the result message is correct.
+ * Executes the command, confirms that a ParseException is thrown and that the
+ * result message is correct.
+ *
* @see #assertCommandFailure(String, Class, String, Model)
*/
private void assertParseException(String inputCommand, String expectedMessage) {
@@ -110,7 +133,9 @@ private void assertParseException(String inputCommand, String expectedMessage) {
}
/**
- * Executes the command, confirms that a CommandException is thrown and that the result message is correct.
+ * Executes the command, confirms that a CommandException is thrown and that the
+ * result message is correct.
+ *
* @see #assertCommandFailure(String, Class, String, Model)
*/
private void assertCommandException(String inputCommand, String expectedMessage) {
@@ -118,7 +143,9 @@ private void assertCommandException(String inputCommand, String expectedMessage)
}
/**
- * Executes the command, confirms that the exception is thrown and that the result message is correct.
+ * Executes the command, confirms that the exception is thrown and that the
+ * result message is correct.
+ *
* @see #assertCommandFailure(String, Class, String, Model)
*/
private void assertCommandFailure(String inputCommand, Class extends Throwable> expectedException,
@@ -131,7 +158,9 @@ private void assertCommandFailure(String inputCommand, Class extends Throwable
* Executes the command and confirms that
* - the {@code expectedException} is thrown
* - the resulting error message is equal to {@code expectedMessage}
- * - the internal model manager state is the same as that in {@code expectedModel}
+ * - the internal model manager state is the same as that in
+ * {@code expectedModel}
+ *
* @see #assertCommandSuccess(String, String, Model)
*/
private void assertCommandFailure(String inputCommand, Class extends Throwable> expectedException,
@@ -141,15 +170,20 @@ private void assertCommandFailure(String inputCommand, Class extends Throwable
}
/**
- * Tests the Logic component's handling of an {@code IOException} thrown by the Storage component.
+ * Tests the Logic component's handling of an {@code IOException} thrown by the
+ * Storage component.
*
- * @param e the exception to be thrown by the Storage component
- * @param expectedMessage the message expected inside exception thrown by the Logic component
+ * @param e
+ * the exception to be thrown by the Storage component
+ * @param expectedMessage
+ * the message expected inside exception thrown by the
+ * Logic component
*/
private void assertCommandFailureForExceptionFromStorage(IOException e, String expectedMessage) {
Path prefPath = temporaryFolder.resolve("ExceptionUserPrefs.json");
- // Inject LogicManager with an AddressBookStorage that throws the IOException e when saving
+ // Inject LogicManager with an AddressBookStorage that throws the IOException e
+ // when saving
JsonAddressBookStorage addressBookStorage = new JsonAddressBookStorage(prefPath) {
@Override
public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath)
@@ -158,16 +192,16 @@ public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath)
}
};
- JsonUserPrefsStorage userPrefsStorage =
- new JsonUserPrefsStorage(temporaryFolder.resolve("ExceptionUserPrefs.json"));
+ JsonUserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(
+ temporaryFolder.resolve("ExceptionUserPrefs.json"));
StorageManager storage = new StorageManager(addressBookStorage, userPrefsStorage);
logic = new LogicManager(model, storage);
// Triggers the saveAddressBook method by executing an add command
- String addCommand = AddCommand.COMMAND_WORD + NAME_DESC_AMY + PHONE_DESC_AMY
- + EMAIL_DESC_AMY + ADDRESS_DESC_AMY;
- Person expectedPerson = new PersonBuilder(AMY).withTags().build();
+ String addCommand = AddCommand.COMMAND_WORD + NAME_DESC_AMY + NRIC_DESC_AMY + EMAIL_DESC_AMY + PHONE_DESC_AMY
+ + GENDER_DESC_AMY + AGE_DESC_AMY + BLOODTYPE_DESC_AMY + ALLERGY_DESC_DUST;
+ Person expectedPerson = new PersonBuilder(AMY).build();
ModelManager expectedModel = new ModelManager();
expectedModel.addPerson(expectedPerson);
assertCommandFailure(addCommand, CommandException.class, expectedMessage, expectedModel);
diff --git a/src/test/java/seedu/address/logic/commands/AddAppointmentCommandTest.java b/src/test/java/seedu/address/logic/commands/AddAppointmentCommandTest.java
new file mode 100644
index 00000000000..5e3f51b2734
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/AddAppointmentCommandTest.java
@@ -0,0 +1,154 @@
+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.Assert.assertThrows;
+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.commons.core.index.Index;
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.model.AddressBook;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.appointment.Appointment;
+import seedu.address.model.appointment.UniqueAppointmentList;
+import seedu.address.model.person.Person;
+import seedu.address.testutil.AppointmentBuilder;
+import seedu.address.testutil.PersonBuilder;
+
+public class AddAppointmentCommandTest {
+
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ @Test
+ public void constructor_nullPerson_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> new AddAppointmentCommand(INDEX_FIRST_PERSON, null));
+ }
+
+ @Test
+ public void execute_validIndexUnfilteredList_success() {
+
+ Person personToAddAppointment = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ Appointment validAppointment = new AppointmentBuilder().withNric(personToAddAppointment.getNric().toString())
+ .build();
+
+ AddAppointmentCommand addAppointmentCommand = new AddAppointmentCommand(INDEX_FIRST_PERSON, validAppointment);
+
+ UniqueAppointmentList newAppointmentList = new UniqueAppointmentList();
+ newAppointmentList.setAppointments(personToAddAppointment.getAppointments());
+ newAppointmentList.add(validAppointment);
+
+ Person editedPerson = new PersonBuilder(personToAddAppointment).withAppointments(newAppointmentList).build();
+
+ String expectedMessage = String.format(AddAppointmentCommand.MESSAGE_SUCCESS,
+ Messages.format(validAppointment, editedPerson));
+
+ ModelManager expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
+ expectedModel.setPerson(model.getFilteredPersonList().get(0), editedPerson);
+ expectedModel.resetAppointmentList();
+
+ assertCommandSuccess(addAppointmentCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void execute_invalidIndexUnfilteredList_throwsCommandException() {
+ Appointment validAppointment = new AppointmentBuilder().build();
+ Index outOfBoundIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1);
+ AddAppointmentCommand addAppointmentCommand = new AddAppointmentCommand(outOfBoundIndex, validAppointment);
+
+ assertCommandFailure(addAppointmentCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+
+ @Test
+ public void execute_validIndexFilteredList_success() {
+ showPersonAtIndex(model, INDEX_FIRST_PERSON);
+ Person personToAddAppointment = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ Appointment validAppointment = new AppointmentBuilder().withNric(personToAddAppointment.getNric().toString())
+ .build();
+
+ AddAppointmentCommand addAppointmentCommand = new AddAppointmentCommand(INDEX_FIRST_PERSON, validAppointment);
+
+ UniqueAppointmentList newAppointmentList = new UniqueAppointmentList();
+ newAppointmentList.setAppointments(personToAddAppointment.getAppointments());
+ newAppointmentList.add(validAppointment);
+
+ Person editedPerson = new PersonBuilder(personToAddAppointment).withAppointments(newAppointmentList).build();
+
+ String expectedMessage = String.format(AddAppointmentCommand.MESSAGE_SUCCESS,
+ Messages.format(validAppointment, editedPerson));
+
+ ModelManager expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
+ showPersonAtIndex(expectedModel, INDEX_FIRST_PERSON);
+ expectedModel.setPerson(model.getFilteredPersonList().get(0), editedPerson);
+ expectedModel.resetAppointmentList();
+
+ assertCommandSuccess(addAppointmentCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void execute_invalidIndexFilteredList_throwsCommandException() {
+ showPersonAtIndex(model, INDEX_FIRST_PERSON);
+
+ Appointment validAppointment = new AppointmentBuilder().build();
+ Index outOfBoundIndex = INDEX_SECOND_PERSON;
+ // ensures that outOfBoundIndex is still in bounds of address book list
+ assertTrue(outOfBoundIndex.getZeroBased() < model.getAddressBook().getPersonList().size());
+
+ AddAppointmentCommand addAppointmentCommand = new AddAppointmentCommand(outOfBoundIndex, validAppointment);
+
+ assertCommandFailure(addAppointmentCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+
+ @Test
+ public void execute_duplicateAppointmentUnfilteredList_failure() {
+ Appointment appointment = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased())
+ .getAppointments().asUnmodifiableObservableList().get(0);
+ AddAppointmentCommand addAppointmentCommand = new AddAppointmentCommand(INDEX_FIRST_PERSON, appointment);
+
+ assertCommandFailure(addAppointmentCommand, model, AddAppointmentCommand.MESSAGE_DUPLICATE_APPOINTMENT);
+ }
+
+ @Test
+ public void equals() {
+ Appointment eyeExam = new AppointmentBuilder().withName("Eye Exam").build();
+ Appointment earExam = new AppointmentBuilder().withName("Ear Exam").build();
+
+ final AddAppointmentCommand standardCommand = new AddAppointmentCommand(INDEX_FIRST_PERSON, eyeExam);
+
+ // same object -> returns true
+ assertTrue(standardCommand.equals(standardCommand));
+
+ // null -> returns false
+ assertFalse(standardCommand.equals(null));
+
+ // different types -> returns false
+ assertFalse(standardCommand.equals(new HelpCommand()));
+
+ // different index -> returns false
+ assertFalse(standardCommand.equals(new AddAppointmentCommand(INDEX_SECOND_PERSON, eyeExam)));
+
+ // different descriptor -> returns false
+ assertFalse(standardCommand.equals(new AddAppointmentCommand(INDEX_FIRST_PERSON, earExam)));
+ }
+
+ @Test
+ public void toStringMethod() {
+ Index targetIndex = Index.fromOneBased(1);
+ Appointment appointment = new AppointmentBuilder().build();
+ AddAppointmentCommand addAppointmentCommand = new AddAppointmentCommand(targetIndex, appointment);
+ String expected = new ToStringBuilder(addAppointmentCommand)
+ .add("toAdd", appointment)
+ .toString();
+ assertEquals(expected, addAppointmentCommand.toString());
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java b/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java
index 162a0c86031..d42f460c132 100644
--- a/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java
+++ b/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java
@@ -8,6 +8,7 @@
import org.junit.jupiter.api.Test;
import seedu.address.logic.Messages;
+import seedu.address.model.AddressBook;
import seedu.address.model.Model;
import seedu.address.model.ModelManager;
import seedu.address.model.UserPrefs;
@@ -30,7 +31,7 @@ public void setUp() {
public void execute_newPerson_success() {
Person validPerson = new PersonBuilder().build();
- Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs());
+ Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
expectedModel.addPerson(validPerson);
assertCommandSuccess(new AddCommand(validPerson), model,
diff --git a/src/test/java/seedu/address/logic/commands/AddCommandTest.java b/src/test/java/seedu/address/logic/commands/AddCommandTest.java
index 90e8253f48e..39b5dff02ee 100644
--- a/src/test/java/seedu/address/logic/commands/AddCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/AddCommandTest.java
@@ -16,13 +16,16 @@
import javafx.collections.ObservableList;
import seedu.address.commons.core.GuiSettings;
+import seedu.address.commons.core.index.Index;
import seedu.address.logic.Messages;
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.model.AddressBook;
import seedu.address.model.Model;
import seedu.address.model.ReadOnlyAddressBook;
import seedu.address.model.ReadOnlyUserPrefs;
+import seedu.address.model.appointment.Appointment;
import seedu.address.model.person.Person;
+import seedu.address.model.record.Record;
import seedu.address.testutil.PersonBuilder;
public class AddCommandTest {
@@ -138,6 +141,11 @@ public boolean hasPerson(Person person) {
throw new AssertionError("This method should not be called.");
}
+ @Override
+ public boolean hasRecord(Record record, Index index) {
+ throw new AssertionError("This method should not be called");
+ }
+
@Override
public void deletePerson(Person target) {
throw new AssertionError("This method should not be called.");
@@ -157,6 +165,47 @@ public ObservableList getFilteredPersonList() {
public void updateFilteredPersonList(Predicate predicate) {
throw new AssertionError("This method should not be called.");
}
+
+ @Override
+ public ObservableList getAppointmentList() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void resetAppointmentList() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public ObservableList getPinnedPersonList() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public ObservableList getRecordList() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void updateRecordList(Person person) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public ObservableList getPersonBeingViewed() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public ObservableList getFilteredRecordList() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void updateFilteredRecordList(Predicate predicate) {
+ throw new AssertionError("This method should not be called.");
+ }
+
}
/**
diff --git a/src/test/java/seedu/address/logic/commands/AddRecordCommandTest.java b/src/test/java/seedu/address/logic/commands/AddRecordCommandTest.java
new file mode 100644
index 00000000000..c8cfae15afd
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/AddRecordCommandTest.java
@@ -0,0 +1,127 @@
+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.TypicalIndexes.INDEX_THIRD_PERSON;
+import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+import static seedu.address.testutil.TypicalRecords.ALLERGIC_REACTION2;
+import static seedu.address.testutil.TypicalRecords.FEVER0;
+import static seedu.address.testutil.TypicalRecords.FEVER_AND_COLD0;
+import static seedu.address.testutil.TypicalRecords.FEVER_AND_COLD2;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.Messages;
+import seedu.address.model.AddressBook;
+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.record.UniqueRecordList;
+import seedu.address.testutil.PersonBuilder;
+
+public class AddRecordCommandTest {
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ @Test
+ public void execute_validIndexUnfilteredList_success() {
+ Person personToAddRecord = model.getFilteredPersonList().get(INDEX_THIRD_PERSON.getZeroBased());
+ UniqueRecordList records = new UniqueRecordList();
+ records.setRecords(personToAddRecord.getRecords());
+ records.add(FEVER_AND_COLD2);
+
+ Person personWithAddedRecord = new PersonBuilder(personToAddRecord).withRecords(records).build();
+ AddRecordCommand addRecordCommand = new AddRecordCommand(INDEX_THIRD_PERSON, FEVER_AND_COLD2);
+
+ String expectedMessage = String.format(AddRecordCommand.MESSAGE_SUCCESS,
+ Messages.format(FEVER_AND_COLD2, personWithAddedRecord));
+
+ Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
+ expectedModel.setPerson(model.getFilteredPersonList().get(2), personWithAddedRecord);
+ expectedModel.updateRecordList(personWithAddedRecord);
+ assertCommandSuccess(addRecordCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void execute_invalidIndexUnfilteredList_throwsCommandException() {
+ Index outOfBoundIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1);
+ AddRecordCommand addRecordCommand = new AddRecordCommand(outOfBoundIndex, ALLERGIC_REACTION2);
+
+ assertCommandFailure(addRecordCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+
+ @Test
+ public void execute_validIndexFilteredList_success() {
+ showPersonAtIndex(model, INDEX_FIRST_PERSON);
+
+ Person personToAddRecord = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ UniqueRecordList records = new UniqueRecordList();
+ records.setRecords(personToAddRecord.getRecords());
+ records.add(FEVER_AND_COLD0);
+
+ Person personWithAddedRecord = new PersonBuilder(personToAddRecord).withRecords(records).build();
+ AddRecordCommand addRecordCommand = new AddRecordCommand(INDEX_FIRST_PERSON, FEVER_AND_COLD0);
+
+ String expectedMessage = String.format(AddRecordCommand.MESSAGE_SUCCESS,
+ Messages.format(FEVER_AND_COLD0, personWithAddedRecord));
+
+ Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
+ showPersonAtIndex(expectedModel, INDEX_FIRST_PERSON);
+
+ expectedModel.setPerson(model.getFilteredPersonList().get(0), personWithAddedRecord);
+ expectedModel.updateRecordList(personWithAddedRecord);
+ assertCommandSuccess(addRecordCommand, model, expectedMessage, expectedModel);
+
+ }
+
+ @Test
+ public void execute_invalidIndexFilteredList_throwsCommandException() {
+ showPersonAtIndex(model, INDEX_FIRST_PERSON);
+
+ Index outOfBoundIndex = INDEX_SECOND_PERSON;
+ // ensures that outOfBoundIndex is still in bounds of address book list
+ assertTrue(outOfBoundIndex.getZeroBased() < model.getAddressBook().getPersonList().size());
+
+ AddRecordCommand addRecordCommand = new AddRecordCommand(outOfBoundIndex, FEVER0);
+
+ assertCommandFailure(addRecordCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+
+ @Test
+ public void execute_duplicateRecord_throwsCommandException() {
+ Index validIndex = INDEX_FIRST_PERSON;
+ AddRecordCommand addrecordCommand = new AddRecordCommand(validIndex, FEVER0);
+ assertCommandFailure(addrecordCommand, model, AddRecordCommand.MESSAGE_DUPLICATE_RECORDS);
+ }
+
+ @Test
+ public void equals() {
+ AddRecordCommand addRecordCommand1 = new AddRecordCommand(INDEX_FIRST_PERSON, FEVER0);
+ AddRecordCommand addRecordCommand2 = new AddRecordCommand(INDEX_FIRST_PERSON, FEVER0);
+ AddRecordCommand addRecordCommand3 = new AddRecordCommand(INDEX_THIRD_PERSON, ALLERGIC_REACTION2);
+
+ assertTrue(addRecordCommand1.equals(addRecordCommand1));
+ assertTrue(addRecordCommand1.equals(addRecordCommand2));
+ assertFalse(addRecordCommand1.equals(1));
+ assertFalse(addRecordCommand1.equals(null));
+ assertFalse(addRecordCommand1.equals(addRecordCommand3));
+
+ }
+
+ @Test
+ public void toStringMethod() {
+ AddRecordCommand addRecordCommand = new AddRecordCommand(INDEX_FIRST_PERSON, FEVER0);
+ String expected = AddRecordCommand.class.getCanonicalName() + "{index=" + INDEX_FIRST_PERSON + ", "
+ + "record=" + FEVER0 + "}";
+
+ assertEquals(expected, addRecordCommand.toString());
+
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/ClearCommandTest.java b/src/test/java/seedu/address/logic/commands/ClearCommandTest.java
deleted file mode 100644
index 80d9110c03a..00000000000
--- a/src/test/java/seedu/address/logic/commands/ClearCommandTest.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package seedu.address.logic.commands;
-
-import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
-import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
-
-import org.junit.jupiter.api.Test;
-
-import seedu.address.model.AddressBook;
-import seedu.address.model.Model;
-import seedu.address.model.ModelManager;
-import seedu.address.model.UserPrefs;
-
-public class ClearCommandTest {
-
- @Test
- public void execute_emptyAddressBook_success() {
- Model model = new ModelManager();
- Model expectedModel = new ModelManager();
-
- assertCommandSuccess(new ClearCommand(), model, ClearCommand.MESSAGE_SUCCESS, expectedModel);
- }
-
- @Test
- public void execute_nonEmptyAddressBook_success() {
- Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
- Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs());
- expectedModel.setAddressBook(new AddressBook());
-
- assertCommandSuccess(new ClearCommand(), model, ClearCommand.MESSAGE_SUCCESS, expectedModel);
- }
-
-}
diff --git a/src/test/java/seedu/address/logic/commands/CommandResultTest.java b/src/test/java/seedu/address/logic/commands/CommandResultTest.java
index 7b8c7cd4546..92c5bb86f7f 100644
--- a/src/test/java/seedu/address/logic/commands/CommandResultTest.java
+++ b/src/test/java/seedu/address/logic/commands/CommandResultTest.java
@@ -14,7 +14,7 @@ public void equals() {
// same values -> returns true
assertTrue(commandResult.equals(new CommandResult("feedback")));
- assertTrue(commandResult.equals(new CommandResult("feedback", false, false)));
+ assertTrue(commandResult.equals(new CommandResult("feedback", false, false, false)));
// same object -> returns true
assertTrue(commandResult.equals(commandResult));
@@ -29,10 +29,13 @@ public void equals() {
assertFalse(commandResult.equals(new CommandResult("different")));
// different showHelp value -> returns false
- assertFalse(commandResult.equals(new CommandResult("feedback", true, false)));
+ assertFalse(commandResult.equals(new CommandResult("feedback", true, false, false)));
+
+ // different showAppointment value -> returns false
+ assertFalse(commandResult.equals(new CommandResult("feedback", false, true, false)));
// different exit value -> returns false
- assertFalse(commandResult.equals(new CommandResult("feedback", false, true)));
+ assertFalse(commandResult.equals(new CommandResult("feedback", false, false, true)));
}
@Test
@@ -46,17 +49,21 @@ public void hashcode() {
assertNotEquals(commandResult.hashCode(), new CommandResult("different").hashCode());
// different showHelp value -> returns different hashcode
- assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", true, false).hashCode());
+ assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", true, false, false).hashCode());
+
+ // different showAppointments value -> returns different hashcode
+ assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", false, true, false).hashCode());
// different exit value -> returns different hashcode
- assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", false, true).hashCode());
+ assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", false, false, true).hashCode());
}
@Test
public void toStringMethod() {
CommandResult commandResult = new CommandResult("feedback");
String expected = CommandResult.class.getCanonicalName() + "{feedbackToUser="
- + commandResult.getFeedbackToUser() + ", showHelp=" + commandResult.isShowHelp()
+ + commandResult.getFeedbackToUser() + ", showHelp=" + commandResult.isShowHelp() + ", showAppointments="
+ + commandResult.isShowAppointments()
+ ", exit=" + commandResult.isExit() + "}";
assertEquals(expected, commandResult.toString());
}
diff --git a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java
index 643a1d08069..581d4aed6f8 100644
--- a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java
+++ b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java
@@ -2,11 +2,17 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_AGE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ALLERGIES;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_BLOODTYPE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_CONDITION;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE;
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GENDER;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_MEDICATION;
import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NRIC;
import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
import static seedu.address.testutil.Assert.assertThrows;
import java.util.ArrayList;
@@ -17,9 +23,14 @@
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.model.AddressBook;
import seedu.address.model.Model;
+import seedu.address.model.appointment.Appointment;
import seedu.address.model.person.NameContainsKeywordsPredicate;
import seedu.address.model.person.Person;
+import seedu.address.model.shared.DateTime;
+import seedu.address.model.shared.Name;
+import seedu.address.model.shared.Nric;
import seedu.address.testutil.EditPersonDescriptorBuilder;
+import seedu.address.testutil.EditRecordDescriptorBuilder;
/**
* Contains helper methods for testing commands.
@@ -28,31 +39,77 @@ public class CommandTestUtil {
public static final String VALID_NAME_AMY = "Amy Bee";
public static final String VALID_NAME_BOB = "Bob Choo";
- public static final String VALID_PHONE_AMY = "11111111";
- public static final String VALID_PHONE_BOB = "22222222";
+ public static final String VALID_NRIC_AMY = "T0000000A";
+ public static final String VALID_NRIC_BOB = "S0000000A";
public static final String VALID_EMAIL_AMY = "amy@example.com";
public static final String VALID_EMAIL_BOB = "bob@example.com";
- public static final String VALID_ADDRESS_AMY = "Block 312, Amy Street 1";
- public static final String VALID_ADDRESS_BOB = "Block 123, Bobby Street 3";
- public static final String VALID_TAG_HUSBAND = "husband";
- public static final String VALID_TAG_FRIEND = "friend";
+ public static final String VALID_PHONE_AMY = "11111111";
+ public static final String VALID_PHONE_BOB = "22222222";
+ public static final String VALID_GENDER_AMY = "F";
+ public static final String VALID_GENDER_BOB = "M";
+ public static final int VALID_AGE_AMY = 20;
+ public static final int VALID_AGE_BOB = 31;
+ public static final String VALID_BLOODTYPE_AMY = "B+";
+ public static final String VALID_BLOODTYPE_BOB = "A-";
+ public static final String VALID_ALLERGY_DUST = "Dust";
+ public static final String VALID_ALLERGY_PEANUTS = "Peanuts";
+
+ public static final String VALID_NAME_THYROID_CHECK = "Thyroid Check";
+ public static final String VALID_NAME_SLEEP_STUDY = "Sleep Study";
+ public static final String VALID_DATETIME_THYROID_CHECK = "01-01-2001 1200";
+ public static final String VALID_DATETIME_SLEEP_STUDY = "11-09-2001 1200";
+ public static final String VALID_CONDITION_DIARRHEA = "Diarrhea";
+ public static final String VALID_CONDITION_HEAT_STROKE = "Heat stroke";
+ public static final String VALID_MEDICATION_DIARRHEA = "Loperamide";
+ public static final String VALID_MEDICATION_HEAT_STROKE = "Phenobarbital";
+ public static final Appointment VALID_APPOINTMENT_THYROID_CHECK = new Appointment(
+ new Name(VALID_NAME_THYROID_CHECK), new DateTime(VALID_DATETIME_THYROID_CHECK), new Nric(VALID_NRIC_AMY));
+ public static final Appointment VALID_APPOINTMENT_SLEEP_STUDY = new Appointment(
+ new Name(VALID_NAME_SLEEP_STUDY),
+ new DateTime(VALID_DATETIME_SLEEP_STUDY), new Nric(VALID_NRIC_BOB));
public static final String NAME_DESC_AMY = " " + PREFIX_NAME + VALID_NAME_AMY;
public static final String NAME_DESC_BOB = " " + PREFIX_NAME + VALID_NAME_BOB;
- public static final String PHONE_DESC_AMY = " " + PREFIX_PHONE + VALID_PHONE_AMY;
- public static final String PHONE_DESC_BOB = " " + PREFIX_PHONE + VALID_PHONE_BOB;
+ public static final String NRIC_DESC_AMY = " " + PREFIX_NRIC + VALID_NRIC_AMY;
+ public static final String NRIC_DESC_BOB = " " + PREFIX_NRIC + VALID_NRIC_BOB;
public static final String EMAIL_DESC_AMY = " " + PREFIX_EMAIL + VALID_EMAIL_AMY;
public static final String EMAIL_DESC_BOB = " " + PREFIX_EMAIL + VALID_EMAIL_BOB;
- public static final String ADDRESS_DESC_AMY = " " + PREFIX_ADDRESS + VALID_ADDRESS_AMY;
- public static final String ADDRESS_DESC_BOB = " " + PREFIX_ADDRESS + VALID_ADDRESS_BOB;
- public static final String TAG_DESC_FRIEND = " " + PREFIX_TAG + VALID_TAG_FRIEND;
- public static final String TAG_DESC_HUSBAND = " " + PREFIX_TAG + VALID_TAG_HUSBAND;
+ public static final String PHONE_DESC_AMY = " " + PREFIX_PHONE + VALID_PHONE_AMY;
+ public static final String PHONE_DESC_BOB = " " + PREFIX_PHONE + VALID_PHONE_BOB;
+ public static final String GENDER_DESC_AMY = " " + PREFIX_GENDER + VALID_GENDER_AMY;
+ public static final String GENDER_DESC_BOB = " " + PREFIX_GENDER + VALID_GENDER_BOB;
+ public static final String AGE_DESC_AMY = " " + PREFIX_AGE + VALID_AGE_AMY;
+ public static final String AGE_DESC_BOB = " " + PREFIX_AGE + VALID_AGE_BOB;
+ public static final String BLOODTYPE_DESC_AMY = " " + PREFIX_BLOODTYPE + VALID_BLOODTYPE_AMY;
+ public static final String BLOODTYPE_DESC_BOB = " " + PREFIX_BLOODTYPE + VALID_BLOODTYPE_BOB;
+ public static final String ALLERGY_DESC_DUST = " " + PREFIX_ALLERGIES + VALID_ALLERGY_DUST;
+ public static final String ALLERGY_DESC_PEANUTS = " " + PREFIX_ALLERGIES + VALID_ALLERGY_PEANUTS;
+
+ public static final String NAME_DESC_SLEEP_STUDY = " " + PREFIX_NAME + VALID_NAME_SLEEP_STUDY;
+ public static final String NAME_DESC_THYROID_CHECK = " " + PREFIX_NAME + VALID_NAME_THYROID_CHECK;
+ public static final String DATETIME_DESC_SLEEP_STUDY = " " + PREFIX_DATE + VALID_DATETIME_SLEEP_STUDY;
+ public static final String DATETIME_DESC_THYROID_CHECK = " " + PREFIX_DATE + VALID_DATETIME_THYROID_CHECK;
+ public static final String CONDITION_DESC_HEAT_STROKE = " " + PREFIX_CONDITION + VALID_CONDITION_HEAT_STROKE;
+ public static final String CONDITION_DESC_DIARRHEA = " " + PREFIX_CONDITION + VALID_CONDITION_DIARRHEA;
+ public static final String MEDICATION_DESC_HEAT_STROKE = " " + PREFIX_MEDICATION + VALID_MEDICATION_HEAT_STROKE;
+ public static final String MEDICATION_DESC_DIARRHEA = " " + PREFIX_MEDICATION + VALID_MEDICATION_DIARRHEA;
public static final String INVALID_NAME_DESC = " " + PREFIX_NAME + "James&"; // '&' not allowed in names
- public static final String INVALID_PHONE_DESC = " " + PREFIX_PHONE + "911a"; // 'a' not allowed in phones
+ public static final String INVALID_NRIC_DESC = " " + PREFIX_NRIC + "A123456*A"; // '*' not allowed in NRIC
public static final String INVALID_EMAIL_DESC = " " + PREFIX_EMAIL + "bob!yahoo"; // missing '@' symbol
- public static final String INVALID_ADDRESS_DESC = " " + PREFIX_ADDRESS; // empty string not allowed for addresses
- public static final String INVALID_TAG_DESC = " " + PREFIX_TAG + "hubby*"; // '*' not allowed in tags
+ public static final String INVALID_PHONE_DESC = " " + PREFIX_PHONE + "911a"; // 'a' not allowed in phones
+ public static final String INVALID_GENDER_DESC = " " + PREFIX_GENDER + "A"; // 'A' not allowed in gender
+ public static final String INVALID_AGE_DESC = " " + PREFIX_AGE + "-1"; // age must be more than or equal to 0
+ public static final String INVALID_BLOODTYPE_DESC = " " + PREFIX_BLOODTYPE + "G"; // 'G' not allowed in bloodtype
+ public static final String INVALID_ALLERGY_DESC = " " + PREFIX_ALLERGIES + "Peanuts*";
+
+ public static final String INVALID_DATETIME_DESC = " " + PREFIX_DATE
+ + "11/1/01 1200"; // date must be in the format dd-mm-yyyy
+ public static final String INVALID_CONDITION_DESC = " " + PREFIX_CONDITION
+ + "Fever*"; // '*' not allowed in conditions
+
+ public static final String INVALID_MEDICATION_DESC = " " + PREFIX_MEDICATION
+ + "Tylenol+";
public static final String PREAMBLE_WHITESPACE = "\t \r \n";
public static final String PREAMBLE_NON_EMPTY = "NonEmptyPreamble";
@@ -60,18 +117,33 @@ public class CommandTestUtil {
public static final EditCommand.EditPersonDescriptor DESC_AMY;
public static final EditCommand.EditPersonDescriptor DESC_BOB;
+ public static final EditRecordCommand.EditRecordDescriptor DESC_FIRST_REC;
+ public static final EditRecordCommand.EditRecordDescriptor DESC_SECOND_REC;
+
static {
- DESC_AMY = new EditPersonDescriptorBuilder().withName(VALID_NAME_AMY)
- .withPhone(VALID_PHONE_AMY).withEmail(VALID_EMAIL_AMY).withAddress(VALID_ADDRESS_AMY)
- .withTags(VALID_TAG_FRIEND).build();
- DESC_BOB = new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB)
- .withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_BOB).withAddress(VALID_ADDRESS_BOB)
- .withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND).build();
+ DESC_AMY = new EditPersonDescriptorBuilder().withName(VALID_NAME_AMY).withNric(VALID_NRIC_AMY)
+ .withEmail(VALID_EMAIL_AMY).withPhone(VALID_PHONE_AMY).withGender(VALID_GENDER_AMY)
+ .withAge(VALID_AGE_AMY).withBloodType(VALID_BLOODTYPE_AMY)
+ .withAllergies(VALID_ALLERGY_DUST).build();
+ DESC_BOB = new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB).withNric(VALID_NRIC_BOB)
+ .withEmail(VALID_EMAIL_BOB).withPhone(VALID_PHONE_BOB).withGender(VALID_GENDER_BOB)
+ .withAge(VALID_AGE_BOB).withBloodType(VALID_BLOODTYPE_BOB)
+ .withAllergies(VALID_ALLERGY_DUST, VALID_ALLERGY_PEANUTS).build();
+ DESC_FIRST_REC = new EditRecordDescriptorBuilder()
+ .withDateTime(VALID_DATETIME_SLEEP_STUDY)
+ .withConditions(VALID_CONDITION_DIARRHEA)
+ .withMedications(VALID_MEDICATION_DIARRHEA).build();
+
+ DESC_SECOND_REC = new EditRecordDescriptorBuilder()
+ .withDateTime(VALID_DATETIME_THYROID_CHECK)
+ .withConditions(VALID_CONDITION_HEAT_STROKE)
+ .withMedications(VALID_MEDICATION_HEAT_STROKE).build();
}
/**
* Executes the given {@code command}, confirms that
- * - the returned {@link CommandResult} matches {@code expectedCommandResult}
+ * - the returned {@link CommandResult} matches {@code expectedCommandResult}
+ *
* - the {@code actualModel} matches {@code expectedModel}
*/
public static void assertCommandSuccess(Command command, Model actualModel, CommandResult expectedCommandResult,
@@ -86,7 +158,8 @@ public static void assertCommandSuccess(Command command, Model actualModel, Comm
}
/**
- * Convenience wrapper to {@link #assertCommandSuccess(Command, Model, CommandResult, Model)}
+ * Convenience wrapper to
+ * {@link #assertCommandSuccess(Command, Model, CommandResult, Model)}
* that takes a string {@code expectedMessage}.
*/
public static void assertCommandSuccess(Command command, Model actualModel, String expectedMessage,
@@ -99,20 +172,22 @@ public static void assertCommandSuccess(Command command, Model actualModel, Stri
* Executes the given {@code command}, confirms that
* - a {@code CommandException} is thrown
* - the CommandException message matches {@code expectedMessage}
- * - the address book, filtered person list and selected person in {@code actualModel} remain unchanged
+ * - the address book, filtered person list and selected person in
+ * {@code actualModel} remain unchanged
*/
public static void assertCommandFailure(Command command, Model actualModel, String expectedMessage) {
// we are unable to defensively copy the model for comparison later, so we can
// only do so by copying its components.
AddressBook expectedAddressBook = new AddressBook(actualModel.getAddressBook());
List expectedFilteredList = new ArrayList<>(actualModel.getFilteredPersonList());
-
assertThrows(CommandException.class, expectedMessage, () -> command.execute(actualModel));
assertEquals(expectedAddressBook, actualModel.getAddressBook());
assertEquals(expectedFilteredList, actualModel.getFilteredPersonList());
}
+
/**
- * Updates {@code model}'s filtered list to show only the person at the given {@code targetIndex} in the
+ * Updates {@code model}'s filtered list to show only the person at the given
+ * {@code targetIndex} in the
* {@code model}'s address book.
*/
public static void showPersonAtIndex(Model model, Index targetIndex) {
diff --git a/src/test/java/seedu/address/logic/commands/DeleteAppointmentCommandTest.java b/src/test/java/seedu/address/logic/commands/DeleteAppointmentCommandTest.java
new file mode 100644
index 00000000000..c7d31cb6cd7
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/DeleteAppointmentCommandTest.java
@@ -0,0 +1,113 @@
+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.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.commons.core.index.Index;
+import seedu.address.logic.Messages;
+import seedu.address.model.AddressBook;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.appointment.Appointment;
+import seedu.address.model.appointment.UniqueAppointmentList;
+import seedu.address.model.person.Person;
+import seedu.address.model.shared.Nric;
+import seedu.address.testutil.PersonBuilder;
+
+/**
+ * Contains integration tests (interaction with the Model) and unit tests for
+ * {@code DeleteAppointmentCommand}.
+ */
+public class DeleteAppointmentCommandTest {
+
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ @Test
+ public void execute_validIndex_success() {
+ Appointment appointmentToDelete = model.getAppointmentList().get(INDEX_FIRST_PERSON.getZeroBased());
+ Nric patientNric = appointmentToDelete.getNric();
+ Person personWithAppointment = model.getFilteredPersonList().stream()
+ .filter(person -> person.getNric().equals(patientNric))
+ .findFirst()
+ .orElse(null);
+ DeleteAppointmentCommand deleteAppointmentCommand = new DeleteAppointmentCommand(INDEX_FIRST_PERSON);
+
+ String expectedMessage = String.format(DeleteAppointmentCommand.MESSAGE_DELETE_APPOINTMENT_SUCCESS,
+ Messages.format(appointmentToDelete, personWithAppointment));
+
+ UniqueAppointmentList newAppointmentList = new UniqueAppointmentList();
+ newAppointmentList.setAppointments(personWithAppointment.getAppointments());
+ newAppointmentList.remove(appointmentToDelete);
+
+ Person editedPerson = new PersonBuilder(personWithAppointment).withAppointments(newAppointmentList).build();
+
+ ModelManager expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
+ expectedModel.setPerson(personWithAppointment, editedPerson);
+ expectedModel.resetAppointmentList();
+
+ assertCommandSuccess(deleteAppointmentCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void execute_invalidIndex_throwsCommandException() {
+ Index outOfBoundIndex = Index.fromOneBased(model.getAppointmentList().size() + 1);
+ DeleteAppointmentCommand deleteAppointmentCommand = new DeleteAppointmentCommand(outOfBoundIndex);
+
+ assertCommandFailure(deleteAppointmentCommand, model, Messages.MESSAGE_INVALID_APPOINTMENT_DISPLAYED_INDEX);
+ }
+
+ @Test
+ public void execute_invalidNric_throwsCommandException() {
+ Appointment appointmentToDelete = model.getAppointmentList().get(INDEX_FIRST_PERSON.getZeroBased());
+ Nric patientNric = appointmentToDelete.getNric();
+ Person personWithAppointment = model.getFilteredPersonList().stream()
+ .filter(person -> person.getNric().equals(patientNric))
+ .findFirst()
+ .orElse(null);
+ DeleteAppointmentCommand deleteAppointmentCommand = new DeleteAppointmentCommand(INDEX_FIRST_PERSON);
+
+ Person newPerson = new PersonBuilder(personWithAppointment).withNric("S9999999A").build();
+ model.setPerson(personWithAppointment, newPerson);
+
+ assertCommandFailure(deleteAppointmentCommand, model, DeleteAppointmentCommand.MESSAGE_INVALID_NRIC);
+ }
+
+ @Test
+ public void equals() {
+ DeleteAppointmentCommand deleteFirstCommand = new DeleteAppointmentCommand(INDEX_FIRST_PERSON);
+ DeleteAppointmentCommand deleteSecondCommand = new DeleteAppointmentCommand(INDEX_SECOND_PERSON);
+
+ // same object -> returns true
+ assertTrue(deleteFirstCommand.equals(deleteFirstCommand));
+
+ // same values -> returns true
+ DeleteAppointmentCommand deleteFirstCommandCopy = new DeleteAppointmentCommand(INDEX_FIRST_PERSON);
+ assertTrue(deleteFirstCommand.equals(deleteFirstCommandCopy));
+
+ // different types -> returns false
+ assertFalse(deleteFirstCommand.equals(1));
+
+ // null -> returns false
+ assertFalse(deleteFirstCommand.equals(null));
+
+ // different person -> returns false
+ assertFalse(deleteFirstCommand.equals(deleteSecondCommand));
+ }
+
+ @Test
+ public void toStringMethod() {
+ Index targetIndex = Index.fromOneBased(1);
+ DeleteAppointmentCommand deleteAppointmentCommand = new DeleteAppointmentCommand(targetIndex);
+ String expected = DeleteAppointmentCommand.class.getCanonicalName() + "{targetIndex=" + targetIndex + "}";
+ assertEquals(expected, deleteAppointmentCommand.toString());
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java b/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java
index b6f332eabca..521817ef013 100644
--- a/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java
@@ -14,6 +14,7 @@
import seedu.address.commons.core.index.Index;
import seedu.address.logic.Messages;
+import seedu.address.model.AddressBook;
import seedu.address.model.Model;
import seedu.address.model.ModelManager;
import seedu.address.model.UserPrefs;
@@ -35,8 +36,9 @@ public void execute_validIndexUnfilteredList_success() {
String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS,
Messages.format(personToDelete));
- ModelManager expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs());
+ ModelManager expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
expectedModel.deletePerson(personToDelete);
+ expectedModel.resetAppointmentList();
assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel);
}
@@ -59,8 +61,9 @@ public void execute_validIndexFilteredList_success() {
String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS,
Messages.format(personToDelete));
- Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs());
+ Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
expectedModel.deletePerson(personToDelete);
+ expectedModel.resetAppointmentList();
showNoPerson(expectedModel);
assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel);
diff --git a/src/test/java/seedu/address/logic/commands/DeleteRecordCommandTest.java b/src/test/java/seedu/address/logic/commands/DeleteRecordCommandTest.java
new file mode 100644
index 00000000000..90b90f9bdf5
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/DeleteRecordCommandTest.java
@@ -0,0 +1,91 @@
+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.commons.core.index.Index.fromOneBased;
+import static seedu.address.commons.core.index.Index.fromZeroBased;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
+import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+import static seedu.address.testutil.TypicalRecords.FEVER_AND_COLD0;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.Messages;
+import seedu.address.model.AddressBook;
+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.record.UniqueRecordList;
+import seedu.address.testutil.PersonBuilder;
+
+public class DeleteRecordCommandTest {
+
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ @Test
+ public void execute_validIndices_success() {
+ Person personToDeleteRecord = model.getFilteredPersonList().get(3);
+ UniqueRecordList records = new UniqueRecordList();
+ records.setRecords(personToDeleteRecord.getRecords());
+ records.remove(FEVER_AND_COLD0);
+
+ Person personWithDeletedRecord = new PersonBuilder(personToDeleteRecord).withRecords(records).build();
+ DeleteRecordCommand deleteRecordCommand = new DeleteRecordCommand(fromZeroBased(3), fromZeroBased(2));
+
+ String expectedMessage = String.format(DeleteRecordCommand.MESSAGE_DELETE_RECORD_SUCCESS,
+ Messages.format(FEVER_AND_COLD0, personWithDeletedRecord));
+
+ Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
+ expectedModel.setPerson(model.getFilteredPersonList().get(3), personWithDeletedRecord);
+ expectedModel.updateRecordList(personWithDeletedRecord);
+ assertCommandSuccess(deleteRecordCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void execute_invalidPatientIndex_throwsCommandException() {
+ Index outOfBoundPatientIndex = fromOneBased(model.getFilteredPersonList().size() + 1);
+ Index validRecordIndex = fromOneBased(3);
+ DeleteRecordCommand deleteRecordCommand = new DeleteRecordCommand(outOfBoundPatientIndex, validRecordIndex);
+
+ assertCommandFailure(deleteRecordCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+
+ @Test
+ public void execute_invalidRecordIndex_throwsCommandException() {
+ Index outOfBoundPatientIndex = fromOneBased(4);
+ Index validRecordIndex = fromOneBased(model.getFilteredPersonList().get(3).getRecords().size() + 1);
+ DeleteRecordCommand deleteRecordCommand = new DeleteRecordCommand(outOfBoundPatientIndex, validRecordIndex);
+
+ assertCommandFailure(deleteRecordCommand, model, Messages.MESSAGE_INVALID_RECORD_DISPLAYED_INDEX);
+ }
+
+ @Test
+ public void equals() {
+ DeleteRecordCommand deleteRecordCommand1 = new DeleteRecordCommand(fromOneBased(4), fromOneBased(3));
+ DeleteRecordCommand deleteRecordCommand2 = new DeleteRecordCommand(fromOneBased(1), fromOneBased(1));
+ DeleteRecordCommand deleteRecordCommand1Copy = new DeleteRecordCommand(fromOneBased(4), fromOneBased(3));
+
+ assertTrue(deleteRecordCommand1.equals(deleteRecordCommand1));
+ assertTrue(deleteRecordCommand1.equals(deleteRecordCommand1Copy));
+ assertFalse(deleteRecordCommand1.equals(deleteRecordCommand2));
+ assertFalse(deleteRecordCommand1.equals(1));
+ }
+
+ @Test
+ public void toStringMethod() {
+ Index patientIndex = fromOneBased(4);
+ Index recordIndex = fromOneBased(3);
+ DeleteRecordCommand deleteRecordCommand = new DeleteRecordCommand(patientIndex, recordIndex);
+
+ String expected = DeleteRecordCommand.class.getCanonicalName()
+ + "{targetPatientIndex=" + patientIndex + ", "
+ + "targetRecordIndex=" + recordIndex + "}";
+
+ assertEquals(expected, deleteRecordCommand.toString());
+ }
+
+}
diff --git a/src/test/java/seedu/address/logic/commands/EditCommandTest.java b/src/test/java/seedu/address/logic/commands/EditCommandTest.java
index 469dd97daa7..2bafac1bd48 100644
--- a/src/test/java/seedu/address/logic/commands/EditCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/EditCommandTest.java
@@ -5,9 +5,9 @@
import static org.junit.jupiter.api.Assertions.assertTrue;
import static seedu.address.logic.commands.CommandTestUtil.DESC_AMY;
import static seedu.address.logic.commands.CommandTestUtil.DESC_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_ALLERGY_DUST;
import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB;
import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND;
import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure;
import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
import static seedu.address.logic.commands.CommandTestUtil.showPersonAtIndex;
@@ -29,7 +29,8 @@
import seedu.address.testutil.PersonBuilder;
/**
- * Contains integration tests (interaction with the Model) and unit tests for EditCommand.
+ * Contains integration tests (interaction with the Model) and unit tests for
+ * EditCommand.
*/
public class EditCommandTest {
@@ -37,7 +38,10 @@ public class EditCommandTest {
@Test
public void execute_allFieldsSpecifiedUnfilteredList_success() {
- Person editedPerson = new PersonBuilder().build();
+ Person personToEdit = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ Person editedPerson = new PersonBuilder().withRecords(personToEdit.getRecords())
+ .withAppointments(personToEdit.getAppointments()).withIsPinned(personToEdit.isPinned()).build();
+
EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder(editedPerson).build();
EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON, descriptor);
@@ -45,6 +49,7 @@ public void execute_allFieldsSpecifiedUnfilteredList_success() {
Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
expectedModel.setPerson(model.getFilteredPersonList().get(0), editedPerson);
+ expectedModel.updateRecordList(editedPerson);
assertCommandSuccess(editCommand, model, expectedMessage, expectedModel);
}
@@ -56,10 +61,11 @@ public void execute_someFieldsSpecifiedUnfilteredList_success() {
PersonBuilder personInList = new PersonBuilder(lastPerson);
Person editedPerson = personInList.withName(VALID_NAME_BOB).withPhone(VALID_PHONE_BOB)
- .withTags(VALID_TAG_HUSBAND).build();
+ .withAllergies(VALID_ALLERGY_DUST).withIsPinned(lastPerson.isPinned())
+ .withAppointments(lastPerson.getAppointments()).build();
EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB)
- .withPhone(VALID_PHONE_BOB).withTags(VALID_TAG_HUSBAND).build();
+ .withPhone(VALID_PHONE_BOB).withAllergies(VALID_ALLERGY_DUST).build();
EditCommand editCommand = new EditCommand(indexLastPerson, descriptor);
String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, Messages.format(editedPerson));
@@ -78,6 +84,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());
+ expectedModel.updateRecordList(editedPerson);
assertCommandSuccess(editCommand, model, expectedMessage, expectedModel);
}
@@ -87,14 +94,18 @@ public void execute_filteredList_success() {
showPersonAtIndex(model, INDEX_FIRST_PERSON);
Person personInFilteredList = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
- Person editedPerson = new PersonBuilder(personInFilteredList).withName(VALID_NAME_BOB).build();
+
+ Person editedPerson = new PersonBuilder(personInFilteredList).withName(VALID_NAME_BOB)
+ .withIsPinned(personInFilteredList.isPinned())
+ .withAppointments(personInFilteredList.getAppointments()).build();
+
EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON,
new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB).build());
-
String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, Messages.format(editedPerson));
Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
expectedModel.setPerson(model.getFilteredPersonList().get(0), editedPerson);
+ expectedModel.updateRecordList(editedPerson);
assertCommandSuccess(editCommand, model, expectedMessage, expectedModel);
}
@@ -162,7 +173,7 @@ public void equals() {
assertFalse(standardCommand.equals(null));
// different types -> returns false
- assertFalse(standardCommand.equals(new ClearCommand()));
+ assertFalse(standardCommand.equals(new HelpCommand()));
// different index -> returns false
assertFalse(standardCommand.equals(new EditCommand(INDEX_SECOND_PERSON, DESC_AMY)));
diff --git a/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java b/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java
index b17c1f3d5c2..7c32720f034 100644
--- a/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java
+++ b/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java
@@ -5,11 +5,13 @@
import static org.junit.jupiter.api.Assertions.assertTrue;
import static seedu.address.logic.commands.CommandTestUtil.DESC_AMY;
import static seedu.address.logic.commands.CommandTestUtil.DESC_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_AGE_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_ALLERGY_PEANUTS;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_BLOODTYPE_BOB;
import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_GENDER_BOB;
import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB;
import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND;
import org.junit.jupiter.api.Test;
@@ -40,20 +42,28 @@ public void equals() {
EditPersonDescriptor editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withName(VALID_NAME_BOB).build();
assertFalse(DESC_AMY.equals(editedAmy));
+ // different email -> returns false
+ editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withEmail(VALID_EMAIL_BOB).build();
+ assertFalse(DESC_AMY.equals(editedAmy));
+
// different phone -> returns false
editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withPhone(VALID_PHONE_BOB).build();
assertFalse(DESC_AMY.equals(editedAmy));
- // different email -> returns false
- editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withEmail(VALID_EMAIL_BOB).build();
+ // different gender -> returns false
+ editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withGender(VALID_GENDER_BOB).build();
+ assertFalse(DESC_AMY.equals(editedAmy));
+
+ // different age -> returns false
+ editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withAge(VALID_AGE_BOB).build();
assertFalse(DESC_AMY.equals(editedAmy));
- // different address -> returns false
- editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withAddress(VALID_ADDRESS_BOB).build();
+ // different bloodType -> returns false
+ editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withBloodType(VALID_BLOODTYPE_BOB).build();
assertFalse(DESC_AMY.equals(editedAmy));
- // different tags -> returns false
- editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withTags(VALID_TAG_HUSBAND).build();
+ // different allergies -> returns false
+ editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withAllergies(VALID_ALLERGY_PEANUTS).build();
assertFalse(DESC_AMY.equals(editedAmy));
}
@@ -61,11 +71,14 @@ public void equals() {
public void toStringMethod() {
EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor();
String expected = EditPersonDescriptor.class.getCanonicalName() + "{name="
- + editPersonDescriptor.getName().orElse(null) + ", phone="
- + editPersonDescriptor.getPhone().orElse(null) + ", email="
- + editPersonDescriptor.getEmail().orElse(null) + ", address="
- + editPersonDescriptor.getAddress().orElse(null) + ", tags="
- + editPersonDescriptor.getTags().orElse(null) + "}";
+ + editPersonDescriptor.getNric().orElse(null) + ", nric="
+ + editPersonDescriptor.getName().orElse(null) + ", email="
+ + editPersonDescriptor.getEmail().orElse(null) + ", phone="
+ + editPersonDescriptor.getPhone().orElse(null) + ", gender="
+ + editPersonDescriptor.getGender().orElse(null) + ", age="
+ + editPersonDescriptor.getAge().orElse(null) + ", bloodType="
+ + editPersonDescriptor.getBloodType().orElse(null) + ", allergies="
+ + editPersonDescriptor.getAllergies().orElse(null) + "}";
assertEquals(expected, editPersonDescriptor.toString());
}
}
diff --git a/src/test/java/seedu/address/logic/commands/EditRecordCommandTest.java b/src/test/java/seedu/address/logic/commands/EditRecordCommandTest.java
new file mode 100644
index 00000000000..945a139d1e7
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/EditRecordCommandTest.java
@@ -0,0 +1,194 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static seedu.address.logic.commands.CommandTestUtil.DESC_FIRST_REC;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_CONDITION_DIARRHEA;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_CONDITION_HEAT_STROKE;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_DATETIME_SLEEP_STUDY;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_DATETIME_THYROID_CHECK;
+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_FIRST_RECORD;
+import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.EditRecordCommand.EditRecordDescriptor;
+import seedu.address.model.AddressBook;
+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.record.Record;
+import seedu.address.model.record.UniqueRecordList;
+import seedu.address.testutil.EditRecordDescriptorBuilder;
+import seedu.address.testutil.PersonBuilder;
+import seedu.address.testutil.RecordBuilder;
+
+public class EditRecordCommandTest {
+
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ @Test
+ public void execute_allFieldsSpecifiedUnfilteredList_success() {
+ Person personToEdit = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ UniqueRecordList oldList = personToEdit.getRecords();
+ Record recordToEdit = oldList.asUnmodifiableObservableList().get(INDEX_FIRST_RECORD.getZeroBased());
+ Record editedRecord = new RecordBuilder(recordToEdit)
+ .withDateTime(VALID_DATETIME_THYROID_CHECK)
+ .withConditions(VALID_CONDITION_DIARRHEA).withPersonIndex(0).build();
+ UniqueRecordList newList = new UniqueRecordList();
+ newList.setRecords(oldList);
+ newList.setRecord(recordToEdit, editedRecord);
+
+ Person editedPerson = new PersonBuilder(personToEdit).withRecords(newList).build();
+ EditRecordDescriptor descriptor = new EditRecordDescriptorBuilder(recordToEdit)
+ .withDateTime(VALID_DATETIME_THYROID_CHECK)
+ .withConditions(VALID_CONDITION_DIARRHEA).withPatientIndex(0).build();
+ EditRecordCommand editRecordCommand = new EditRecordCommand(INDEX_FIRST_PERSON, INDEX_FIRST_RECORD,
+ descriptor);
+
+ String expectedMessage = String.format(EditRecordCommand.MESSAGE_EDIT_RECORD_SUCCESS,
+ Messages.format(editedRecord, editedPerson));
+ Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
+ expectedModel.setPerson(model.getFilteredPersonList().get(0), editedPerson);
+ expectedModel.updateRecordList(editedPerson);
+
+ assertCommandSuccess(editRecordCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void execute_someFieldsSpecifiedUnfilteredList_success() {
+ Person firstPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ UniqueRecordList oldList = firstPerson.getRecords();
+ Record recordToEdit = oldList.asUnmodifiableObservableList().get(INDEX_FIRST_RECORD.getZeroBased());
+ Record editedRecord = new RecordBuilder(recordToEdit)
+ .withDateTime(VALID_DATETIME_SLEEP_STUDY).build();
+ UniqueRecordList newList = new UniqueRecordList();
+ newList.setRecords(oldList);
+ newList.setRecord(recordToEdit, editedRecord);
+
+ PersonBuilder personInList = new PersonBuilder(firstPerson);
+ Person editedPerson = personInList.withRecords(newList).build();
+ EditRecordDescriptor descriptor = new EditRecordDescriptorBuilder()
+ .withDateTime(VALID_DATETIME_SLEEP_STUDY).build();
+ EditRecordCommand editRecordCommand = new EditRecordCommand(INDEX_FIRST_PERSON, INDEX_FIRST_RECORD,
+ descriptor);
+
+ String expectedMessage = String.format(EditRecordCommand.MESSAGE_EDIT_RECORD_SUCCESS, Messages.format(
+ editedRecord, editedPerson));
+ Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
+ expectedModel.setPerson(model.getFilteredPersonList().get(0), editedPerson);
+ expectedModel.updateRecordList(editedPerson);
+
+ assertCommandSuccess(editRecordCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void execute_noFieldSpecifiedUnfilteredList_failure() {
+ EditRecordCommand editRecordCommand = new EditRecordCommand(INDEX_FIRST_PERSON, INDEX_FIRST_RECORD,
+ new EditRecordCommand.EditRecordDescriptor());
+
+ assertCommandFailure(editRecordCommand, model, EditRecordCommand.MESSAGE_DUPLICATE_RECORD);
+ }
+
+ @Test
+ public void execute_filteredList_success() {
+ showPersonAtIndex(model, INDEX_FIRST_PERSON);
+
+ Person personInFilteredList = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ UniqueRecordList oldList = personInFilteredList.getRecords();
+ Record recordToEdit = oldList.asUnmodifiableObservableList().get(INDEX_FIRST_RECORD.getZeroBased());
+ Record editedRecord = new RecordBuilder(recordToEdit).withConditions(VALID_CONDITION_HEAT_STROKE)
+ .withDateTime(VALID_DATETIME_THYROID_CHECK)
+ .withPersonIndex(0).build();
+ UniqueRecordList newList = new UniqueRecordList();
+ newList.setRecords(oldList);
+ newList.setRecord(recordToEdit, editedRecord);
+
+ Person editedPerson = new PersonBuilder(personInFilteredList).withRecords(newList).build();
+ EditRecordCommand editRecordCommand = new EditRecordCommand(INDEX_FIRST_PERSON, INDEX_FIRST_RECORD,
+ new EditRecordDescriptorBuilder().withConditions(VALID_CONDITION_HEAT_STROKE)
+ .withDateTime(VALID_DATETIME_THYROID_CHECK)
+ .withPatientIndex(0).build());
+ String expectedMessage = String.format(EditRecordCommand.MESSAGE_EDIT_RECORD_SUCCESS,
+ Messages.format(editedRecord, editedPerson));
+
+ Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
+ showPersonAtIndex(expectedModel, INDEX_FIRST_PERSON);
+ expectedModel.setPerson(model.getFilteredPersonList().get(0), editedPerson);
+ expectedModel.updateRecordList(editedPerson);
+
+ assertCommandSuccess(editRecordCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void execute_duplicateRecordUnfilteredList_failure() {
+ Person firstPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ UniqueRecordList uniqueRecordList = firstPerson.getRecords();
+ Record firstRecord = uniqueRecordList.asUnmodifiableObservableList()
+ .get(INDEX_FIRST_RECORD.getZeroBased());
+ EditRecordDescriptor descriptor = new EditRecordDescriptorBuilder(firstRecord)
+ .build();
+ EditRecordCommand editRecordCommand = new EditRecordCommand(INDEX_FIRST_PERSON,
+ INDEX_FIRST_RECORD, descriptor);
+ assertCommandFailure(editRecordCommand, model, EditRecordCommand.MESSAGE_DUPLICATE_RECORD);
+ }
+
+ @Test
+ public void execute_duplicateRecordFilteredList_failure() {
+ showPersonAtIndex(model, INDEX_FIRST_PERSON);
+
+ Person personInList = model.getAddressBook().getPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ UniqueRecordList uniqueRecordList = personInList.getRecords();
+ Record firstRecord = uniqueRecordList.asUnmodifiableObservableList()
+ .get(INDEX_FIRST_RECORD.getZeroBased());
+ EditRecordCommand editRecordCommand = new EditRecordCommand(INDEX_FIRST_PERSON, INDEX_FIRST_RECORD,
+ new EditRecordDescriptorBuilder(firstRecord).build());
+
+ assertCommandFailure(editRecordCommand, model, EditRecordCommand.MESSAGE_DUPLICATE_RECORD);
+ }
+
+ @Test
+ public void execute_invalidRecordIndexUnfilteredList_failure() {
+ Index outOfBoundIndex = Index.fromZeroBased(model.getFilteredPersonList().get(
+ INDEX_FIRST_PERSON.getZeroBased()).getRecords().asUnmodifiableObservableList().size());
+ EditRecordDescriptor descriptor = new EditRecordDescriptorBuilder()
+ .withDateTime(VALID_DATETIME_THYROID_CHECK).build();
+ EditRecordCommand editRecordCommand = new EditRecordCommand(INDEX_FIRST_PERSON, outOfBoundIndex,
+ descriptor);
+
+ assertCommandFailure(editRecordCommand, model, Messages.MESSAGE_INVALID_RECORD_DISPLAYED_INDEX);
+ }
+
+ @Test
+ public void execute_invalidPersonIndexUnfilteredList_failure() {
+ Index outOfBoundIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1);
+ EditRecordDescriptor descriptor = new EditRecordDescriptorBuilder()
+ .withDateTime(VALID_DATETIME_THYROID_CHECK).build();
+ EditRecordCommand editRecordCommand = new EditRecordCommand(outOfBoundIndex, INDEX_FIRST_RECORD,
+ descriptor);
+
+ assertCommandFailure(editRecordCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+
+ @Test
+ public void toStringTest() {
+ EditRecordDescriptor editedFirstRecord = new EditRecordDescriptorBuilder(
+ DESC_FIRST_REC)
+ .withDateTime(VALID_DATETIME_THYROID_CHECK).build();
+ EditRecordCommand editRecordCommand = new EditRecordCommand(INDEX_FIRST_PERSON,
+ INDEX_FIRST_RECORD, editedFirstRecord);
+ String expected = EditRecordCommand.class.getCanonicalName() + "{patientIndex=" + INDEX_FIRST_PERSON
+ + ", "
+ + "recordIndex=" + INDEX_FIRST_RECORD + ", "
+ + "editRecordDescriptor=" + editedFirstRecord + "}";
+
+ assertEquals(expected, editRecordCommand.toString());
+ }
+
+}
diff --git a/src/test/java/seedu/address/logic/commands/EditRecordDescriptorTest.java b/src/test/java/seedu/address/logic/commands/EditRecordDescriptorTest.java
new file mode 100644
index 00000000000..d9b4bc58e05
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/EditRecordDescriptorTest.java
@@ -0,0 +1,59 @@
+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.DESC_FIRST_REC;
+import static seedu.address.logic.commands.CommandTestUtil.DESC_SECOND_REC;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_CONDITION_DIARRHEA;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_DATETIME_THYROID_CHECK;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.testutil.EditRecordDescriptorBuilder;
+
+public class EditRecordDescriptorTest {
+ @Test
+ public void equals() {
+ // same values -> returns true
+ EditRecordCommand.EditRecordDescriptor descriptorWithSameValues =
+ new EditRecordCommand.EditRecordDescriptor(DESC_FIRST_REC);
+ assertTrue(DESC_FIRST_REC.equals(descriptorWithSameValues));
+
+ // same object -> returns true
+ assertTrue(DESC_FIRST_REC.equals(DESC_FIRST_REC));
+
+ // null -> returns false
+ assertFalse(DESC_FIRST_REC.equals(null));
+
+ // different types -> returns false
+ assertFalse(DESC_FIRST_REC.equals(5));
+
+ // different values -> returns false
+ assertFalse(DESC_FIRST_REC.equals(DESC_SECOND_REC));
+
+ // different dateTime -> returns false
+ EditRecordCommand.EditRecordDescriptor editedFirstRecord =
+ new EditRecordDescriptorBuilder(DESC_FIRST_REC).withDateTime(VALID_DATETIME_THYROID_CHECK).build();
+ assertFalse(DESC_FIRST_REC.equals(editedFirstRecord));
+
+ // different condition -> returns false
+ EditRecordCommand.EditRecordDescriptor editedSecondRecord =
+ new EditRecordDescriptorBuilder(DESC_SECOND_REC).withConditions(VALID_CONDITION_DIARRHEA).build();
+ assertFalse(DESC_SECOND_REC.equals(editedSecondRecord));
+ }
+
+ @Test
+ public void toStringMethod() {
+ EditRecordCommand.EditRecordDescriptor editRecordDescriptor = new EditRecordCommand.EditRecordDescriptor();
+ String expected = EditRecordCommand.EditRecordDescriptor.class.getCanonicalName() + "{dateTime="
+ + editRecordDescriptor.getDateTime().orElse(null) + ", conditions="
+ + editRecordDescriptor.getConditions().orElse(null) + ", medications="
+ + editRecordDescriptor.getMedications().orElse(null) + ", filePath="
+ + editRecordDescriptor.getFilePath().orElse(null) + ", patientIndex="
+ + editRecordDescriptor.getPatientIndex().orElse(null) + "}";
+
+ assertEquals(expected, editRecordDescriptor.toString());
+ }
+
+}
diff --git a/src/test/java/seedu/address/logic/commands/ExitCommandTest.java b/src/test/java/seedu/address/logic/commands/ExitCommandTest.java
index 9533c473875..f5854e5dfa3 100644
--- a/src/test/java/seedu/address/logic/commands/ExitCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/ExitCommandTest.java
@@ -14,7 +14,7 @@ public class ExitCommandTest {
@Test
public void execute_exit_success() {
- CommandResult expectedCommandResult = new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true);
+ CommandResult expectedCommandResult = new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, false, true);
assertCommandSuccess(new ExitCommand(), model, expectedCommandResult, expectedModel);
}
}
diff --git a/src/test/java/seedu/address/logic/commands/FindRecordCommandTest.java b/src/test/java/seedu/address/logic/commands/FindRecordCommandTest.java
new file mode 100644
index 00000000000..fb855bf66aa
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/FindRecordCommandTest.java
@@ -0,0 +1,89 @@
+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.commons.core.index.Index.fromZeroBased;
+import static seedu.address.logic.Messages.MESSAGE_RECORDS_LISTED_OVERVIEW;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
+import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.AddressBook;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.record.RecordContainsKeywordsPredicate;
+
+public class FindRecordCommandTest {
+
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ // ViewCommand needs to be executed first for FindRecordCommand
+ private ViewCommand viewCommand = new ViewCommand(fromZeroBased(3));
+ private CommandResult cr = viewCommand.execute(model);
+
+ public FindRecordCommandTest() throws CommandException {
+ }
+
+ @Test
+ public void equals() {
+ RecordContainsKeywordsPredicate firstPredicate =
+ new RecordContainsKeywordsPredicate(Arrays.asList("Tylenol", "Pepto-Bismol"));
+ RecordContainsKeywordsPredicate secondPredicate =
+ new RecordContainsKeywordsPredicate(Arrays.asList("Tylenol"));
+
+ FindRecordCommand findRecordCommand1 = new FindRecordCommand(firstPredicate);
+ FindRecordCommand findRecordCommand2 = new FindRecordCommand(secondPredicate);
+ assertTrue(findRecordCommand1.equals(findRecordCommand1));
+
+ FindRecordCommand findRecordCommand1Copy = new FindRecordCommand(firstPredicate);
+ assertTrue(findRecordCommand1.equals(findRecordCommand1Copy));
+ assertFalse(findRecordCommand1.equals(findRecordCommand2));
+ assertFalse(findRecordCommand1.equals(firstPredicate));
+ }
+
+ @Test
+ public void execute_zeroKeywords_noPersonFound() throws CommandException {
+ String expectedMessage = String.format(MESSAGE_RECORDS_LISTED_OVERVIEW, 0);
+ RecordContainsKeywordsPredicate predicate = preparePredicate(" ");
+ FindRecordCommand findRecordCommand = new FindRecordCommand(predicate);
+ Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
+ viewCommand.execute(expectedModel);
+ findRecordCommand.execute(expectedModel);
+ expectedModel.updateFilteredRecordList(predicate);
+ assertCommandSuccess(findRecordCommand, model, expectedMessage, expectedModel);
+ assertEquals(Collections.emptyList(), model.getFilteredRecordList());
+ }
+
+ @Test
+ public void execute_multipleKeywords_singleRecordsFound() throws CommandException {
+ String expectedMessage = String.format(MESSAGE_RECORDS_LISTED_OVERVIEW, 3);
+ RecordContainsKeywordsPredicate predicate = preparePredicate("Tylenol");
+ FindRecordCommand findRecordCommand = new FindRecordCommand(predicate);
+ Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
+ viewCommand.execute(expectedModel);
+ findRecordCommand.execute(expectedModel);
+ expectedModel.updateFilteredRecordList(predicate);
+ assertCommandSuccess(findRecordCommand, model, expectedMessage, expectedModel);
+ }
+ @Test
+ public void toStringMethod() {
+ RecordContainsKeywordsPredicate predicate = preparePredicate("Tylenol");
+ FindRecordCommand command = new FindRecordCommand(predicate);
+ String expected = FindRecordCommand.class.getCanonicalName() + "{predicate=" + predicate + "}";
+ assertEquals(expected, command.toString());
+ }
+
+ /**
+ * Parses {@code userInput} into a {@code RecordContainsKeywordsPredicate}.
+ */
+ private RecordContainsKeywordsPredicate preparePredicate(String userInput) {
+ return new RecordContainsKeywordsPredicate(Arrays.asList(userInput.split("\\s+")));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/HelpCommandTest.java b/src/test/java/seedu/address/logic/commands/HelpCommandTest.java
index 4904fc4352e..3b35c387c4c 100644
--- a/src/test/java/seedu/address/logic/commands/HelpCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/HelpCommandTest.java
@@ -14,7 +14,7 @@ public class HelpCommandTest {
@Test
public void execute_help_success() {
- CommandResult expectedCommandResult = new CommandResult(SHOWING_HELP_MESSAGE, true, false);
+ CommandResult expectedCommandResult = new CommandResult(SHOWING_HELP_MESSAGE, true, false, false);
assertCommandSuccess(new HelpCommand(), model, expectedCommandResult, expectedModel);
}
}
diff --git a/src/test/java/seedu/address/logic/commands/PinCommandTest.java b/src/test/java/seedu/address/logic/commands/PinCommandTest.java
new file mode 100644
index 00000000000..2ca3866f0ee
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/PinCommandTest.java
@@ -0,0 +1,113 @@
+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.commons.core.index.Index;
+import seedu.address.logic.Messages;
+import seedu.address.model.AddressBook;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.person.Person;
+import seedu.address.testutil.PersonBuilder;
+
+/**
+ * Contains integration tests (interaction with the Model) and unit tests for
+ * PinCommand.
+ */
+public class PinCommandTest {
+
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ @Test
+ public void execute_validIndexUnfilteredList_success() {
+ Person personToPin = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ Person pinnedPerson = new PersonBuilder(personToPin).withIsPinned(true).build();
+ PinCommand pinCommand = new PinCommand(INDEX_FIRST_PERSON);
+
+ String expectedMessage = String.format(PinCommand.MESSAGE_PIN_PERSON_SUCCESS, Messages.format(personToPin));
+
+ Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
+ expectedModel.setPerson(model.getFilteredPersonList().get(0), pinnedPerson);
+
+ assertCommandSuccess(pinCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void execute_invalidIndexUnfilteredList_throwsCommandException() {
+ Index outOfBoundIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1);
+ PinCommand pinCommand = new PinCommand(outOfBoundIndex);
+
+ assertCommandFailure(pinCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+
+ @Test
+ public void execute_validIndexFilteredList_success() {
+ showPersonAtIndex(model, INDEX_FIRST_PERSON);
+
+ Person personToPin = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ Person pinnedPerson = new PersonBuilder(personToPin).withIsPinned(true).build();
+ PinCommand pinCommand = new PinCommand(INDEX_FIRST_PERSON);
+
+ String expectedMessage = String.format(PinCommand.MESSAGE_PIN_PERSON_SUCCESS, Messages.format(personToPin));
+
+ Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
+ showPersonAtIndex(expectedModel, INDEX_FIRST_PERSON);
+ expectedModel.setPerson(model.getFilteredPersonList().get(0), pinnedPerson);
+ assertCommandSuccess(pinCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void execute_invalidIndexFilteredList_throwsCommandException() {
+ showPersonAtIndex(model, INDEX_FIRST_PERSON);
+
+ Index outOfBoundIndex = INDEX_SECOND_PERSON;
+ // ensures that outOfBoundIndex is still in bounds of address book list
+ assertTrue(outOfBoundIndex.getZeroBased() < model.getAddressBook().getPersonList().size());
+
+ PinCommand pinCommand = new PinCommand(outOfBoundIndex);
+
+ assertCommandFailure(pinCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+
+ @Test
+ public void equals() {
+ PinCommand pinFirstCommand = new PinCommand(INDEX_FIRST_PERSON);
+ PinCommand pinSecondCommand = new PinCommand(INDEX_SECOND_PERSON);
+
+ // same object -> returns true
+ assertTrue(pinFirstCommand.equals(pinFirstCommand));
+
+ // same values -> returns true
+ PinCommand unpinFirstCommandCopy = new PinCommand(INDEX_FIRST_PERSON);
+ assertTrue(pinFirstCommand.equals(unpinFirstCommandCopy));
+
+ // different types -> returns false
+ assertFalse(pinFirstCommand.equals(1));
+
+ // null -> returns false
+ assertFalse(pinFirstCommand.equals(null));
+
+ // different person -> returns false
+ assertFalse(pinFirstCommand.equals(pinSecondCommand));
+ }
+
+ @Test
+ public void toStringMethod() {
+ Index targetIndex = Index.fromOneBased(1);
+ PinCommand pinCommand = new PinCommand(targetIndex);
+ String expected = PinCommand.class.getCanonicalName() + "{targetIndex=" + targetIndex + "}";
+ assertEquals(expected, pinCommand.toString());
+ }
+
+}
diff --git a/src/test/java/seedu/address/logic/commands/UnpinCommandTest.java b/src/test/java/seedu/address/logic/commands/UnpinCommandTest.java
new file mode 100644
index 00000000000..79bd28c45b6
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/UnpinCommandTest.java
@@ -0,0 +1,84 @@
+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.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.commons.core.index.Index;
+import seedu.address.logic.Messages;
+import seedu.address.model.AddressBook;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.person.Person;
+import seedu.address.testutil.PersonBuilder;
+
+/**
+ * Contains integration tests (interaction with the Model) and unit tests for
+ * UnpinCommand.
+ */
+public class UnpinCommandTest {
+
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ @Test
+ public void execute_validIndex_success() {
+ Person personToUnpin = model.getPinnedPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ Person unpinnedPerson = new PersonBuilder(personToUnpin).withIsPinned(false).build();
+ UnpinCommand unpinCommand = new UnpinCommand(INDEX_FIRST_PERSON);
+
+ String expectedMessage = String.format(UnpinCommand.MESSAGE_UNPIN_PERSON_SUCCESS,
+ Messages.format(personToUnpin));
+
+ Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
+ expectedModel.setPerson(model.getPinnedPersonList().get(0), unpinnedPerson);
+
+ assertCommandSuccess(unpinCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void execute_invalidIndexUnfilteredList_throwsCommandException() {
+ Index outOfBoundIndex = Index.fromOneBased(model.getPinnedPersonList().size() + 1);
+ UnpinCommand unpinCommand = new UnpinCommand(outOfBoundIndex);
+
+ assertCommandFailure(unpinCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+
+ @Test
+ public void equals() {
+ UnpinCommand unpinFirstCommand = new UnpinCommand(INDEX_FIRST_PERSON);
+ UnpinCommand unpinSecondCommand = new UnpinCommand(INDEX_SECOND_PERSON);
+
+ // same object -> returns true
+ assertTrue(unpinFirstCommand.equals(unpinFirstCommand));
+
+ // same values -> returns true
+ UnpinCommand unpinFirstCommandCopy = new UnpinCommand(INDEX_FIRST_PERSON);
+ assertTrue(unpinFirstCommand.equals(unpinFirstCommandCopy));
+
+ // different types -> returns false
+ assertFalse(unpinFirstCommand.equals(1));
+
+ // null -> returns false
+ assertFalse(unpinFirstCommand.equals(null));
+
+ // different person -> returns false
+ assertFalse(unpinFirstCommand.equals(unpinSecondCommand));
+ }
+
+ @Test
+ public void toStringMethod() {
+ Index targetIndex = Index.fromOneBased(1);
+ UnpinCommand unpinCommand = new UnpinCommand(targetIndex);
+ String expected = UnpinCommand.class.getCanonicalName() + "{targetIndex=" + targetIndex + "}";
+ assertEquals(expected, unpinCommand.toString());
+ }
+
+}
diff --git a/src/test/java/seedu/address/logic/commands/ViewAppointmentCommandTest.java b/src/test/java/seedu/address/logic/commands/ViewAppointmentCommandTest.java
new file mode 100644
index 00000000000..6238e2ff455
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/ViewAppointmentCommandTest.java
@@ -0,0 +1,20 @@
+package seedu.address.logic.commands;
+
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
+import static seedu.address.logic.commands.ViewAppointmentCommand.SHOWING_APPOINTMENTS_MESSAGE;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+
+public class ViewAppointmentCommandTest {
+ private Model model = new ModelManager();
+ private Model expectedModel = new ModelManager();
+
+ @Test
+ public void execute_help_success() {
+ CommandResult expectedCommandResult = new CommandResult(SHOWING_APPOINTMENTS_MESSAGE, false, true, false);
+ assertCommandSuccess(new ViewAppointmentCommand(), model, expectedCommandResult, expectedModel);
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/ViewCommandTest.java b/src/test/java/seedu/address/logic/commands/ViewCommandTest.java
new file mode 100644
index 00000000000..84590d95bd6
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/ViewCommandTest.java
@@ -0,0 +1,118 @@
+package seedu.address.logic.commands;
+
+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.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.commons.core.index.Index;
+import seedu.address.logic.Messages;
+import seedu.address.model.AddressBook;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.person.Person;
+
+public class ViewCommandTest {
+
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ @Test
+ public void executeValidIndex_success() {
+ Person personToView = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ ViewCommand viewCommand = new ViewCommand(INDEX_FIRST_PERSON);
+
+ String expectedMessage = String.format(ViewCommand.MESSAGE_VIEW_PERSON_SUCCESS, Messages.format(personToView));
+ Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
+
+ expectedModel.setPerson(model.getFilteredPersonList().get(0), personToView);
+ expectedModel.updateRecordList(personToView);
+ assertCommandSuccess(viewCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void executeInvalidIndex_throwsCommandException() {
+ Index indexOutOfBounds = Index.fromOneBased(model.getFilteredPersonList().size() + 1);
+ ViewCommand viewCommand = new ViewCommand(indexOutOfBounds);
+
+ assertCommandFailure(viewCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+
+ @Test
+ public void execute_validIndexRecordList_success() {
+
+ Person personToView = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ ViewCommand viewCommand = new ViewCommand(INDEX_FIRST_PERSON);
+
+ String expectedMessage = String.format(ViewCommand.MESSAGE_VIEW_PERSON_SUCCESS,
+ Messages.format(personToView));
+
+ Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
+ try {
+ viewCommand.execute(model);
+ viewCommand.execute(expectedModel);
+ } catch (Exception ignore) {
+ return;
+ }
+
+ sameRecordList(model, expectedModel);
+ assertCommandSuccess(viewCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void execute_validIndexSamePerson_success() {
+
+ Person personToView = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ ViewCommand viewCommand = new ViewCommand(INDEX_FIRST_PERSON);
+
+ String expectedMessage = String.format(ViewCommand.MESSAGE_VIEW_PERSON_SUCCESS,
+ Messages.format(personToView));
+
+ Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
+ try {
+ viewCommand.execute(model);
+ viewCommand.execute(expectedModel);
+ } catch (Exception ignore) {
+ return;
+ }
+
+ samePersonViewed(model, expectedModel);
+ assertCommandSuccess(viewCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void equals() {
+
+ ViewCommand viewCommand1 = new ViewCommand(INDEX_FIRST_PERSON);
+ ViewCommand viewCommand2 = new ViewCommand(INDEX_SECOND_PERSON);
+
+ // same object -> returns true
+ assertTrue(viewCommand1.equals(viewCommand1));
+
+ // same values -> returns true
+ ViewCommand viewFirstCommandCopy = new ViewCommand(INDEX_FIRST_PERSON);
+ assertTrue(viewCommand1.equals(viewFirstCommandCopy));
+
+ // different types -> returns false
+ assertFalse(viewCommand1.equals(1));
+
+ // null -> returns false
+ assertFalse(viewCommand1.equals(null));
+
+ // different person -> returns false
+ assertFalse(viewCommand1.equals(viewCommand2));
+ }
+
+ private void sameRecordList(Model model1, Model model2) {
+ assertTrue(model1.getRecordList().equals(model2.getRecordList()));
+ }
+
+ private void samePersonViewed(Model model1, Model model2) {
+ assertTrue(model1.getPersonBeingViewed().equals(model2.getPersonBeingViewed()));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/AddAppointmentCommandParserTest.java b/src/test/java/seedu/address/logic/parser/AddAppointmentCommandParserTest.java
new file mode 100644
index 00000000000..96b2c86edf4
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/AddAppointmentCommandParserTest.java
@@ -0,0 +1,81 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.commands.CommandTestUtil.DATETIME_DESC_SLEEP_STUDY;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_DATETIME_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_NAME_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_SLEEP_STUDY;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_DATETIME_SLEEP_STUDY;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_SLEEP_STUDY;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
+import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.AddAppointmentCommand;
+import seedu.address.model.appointment.Appointment;
+import seedu.address.model.shared.DateTime;
+import seedu.address.model.shared.Name;
+
+public class AddAppointmentCommandParserTest {
+
+ private static final String MESSAGE_INVALID_FORMAT = String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ AddAppointmentCommand.MESSAGE_USAGE);
+ private AddAppointmentCommandParser parser = new AddAppointmentCommandParser();
+
+ @Test
+ public void parse_missingParts_failure() {
+ // no index specified
+ assertParseFailure(parser, NAME_DESC_SLEEP_STUDY + DATETIME_DESC_SLEEP_STUDY, MESSAGE_INVALID_FORMAT);
+
+ // no field specified
+ assertParseFailure(parser, "1", MESSAGE_INVALID_FORMAT);
+
+ // no index and no field specified
+ assertParseFailure(parser, "", MESSAGE_INVALID_FORMAT);
+ }
+
+ @Test
+ public void parse_invalidPreamble_failure() {
+ // negative index
+ assertParseFailure(parser, "-5" + NAME_DESC_SLEEP_STUDY + DATETIME_DESC_SLEEP_STUDY,
+ MESSAGE_INVALID_FORMAT);
+
+ // zero index
+ assertParseFailure(parser, "0" + NAME_DESC_SLEEP_STUDY + DATETIME_DESC_SLEEP_STUDY,
+ MESSAGE_INVALID_FORMAT);
+
+ // invalid arguments being parsed as preamble
+ assertParseFailure(parser, "1 some random string", MESSAGE_INVALID_FORMAT);
+
+ // invalid prefix being parsed as preamble
+ assertParseFailure(parser, "1 i/ string", MESSAGE_INVALID_FORMAT);
+ }
+
+ @Test
+ public void parse_invalidValue_failure() {
+ assertParseFailure(parser, "1" + INVALID_NAME_DESC + DATETIME_DESC_SLEEP_STUDY,
+ Name.MESSAGE_CONSTRAINTS); // invalid name
+ assertParseFailure(parser, "1" + NAME_DESC_SLEEP_STUDY + INVALID_DATETIME_DESC,
+ DateTime.MESSAGE_CONSTRAINTS); // invalid datetime
+
+ // multiple invalid values, but only the first invalid value is captured
+ assertParseFailure(parser, "1" + INVALID_NAME_DESC + INVALID_DATETIME_DESC,
+ Name.MESSAGE_CONSTRAINTS);
+ }
+
+ @Test
+ public void parse_allFieldsSpecified_success() {
+ Index targetIndex = INDEX_SECOND_PERSON;
+ String userInput = targetIndex.getOneBased() + NAME_DESC_SLEEP_STUDY + DATETIME_DESC_SLEEP_STUDY;
+
+ Appointment appointment = new Appointment(new Name(VALID_NAME_SLEEP_STUDY),
+ new DateTime(VALID_DATETIME_SLEEP_STUDY), null);
+
+ AddAppointmentCommand expectedCommand = new AddAppointmentCommand(targetIndex, appointment);
+
+ assertParseSuccess(parser, userInput, expectedCommand);
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java b/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java
index 5bc11d3cdaa..5b43aca7e4a 100644
--- a/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java
+++ b/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java
@@ -1,32 +1,46 @@
package seedu.address.logic.parser;
import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
-import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY;
-import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.AGE_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.AGE_DESC_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.ALLERGY_DESC_DUST;
+import static seedu.address.logic.commands.CommandTestUtil.ALLERGY_DESC_PEANUTS;
+import static seedu.address.logic.commands.CommandTestUtil.BLOODTYPE_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.BLOODTYPE_DESC_BOB;
import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY;
import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.INVALID_ADDRESS_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.GENDER_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.GENDER_DESC_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_AGE_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_ALLERGY_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_BLOODTYPE_DESC;
import static seedu.address.logic.commands.CommandTestUtil.INVALID_EMAIL_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_GENDER_DESC;
import static seedu.address.logic.commands.CommandTestUtil.INVALID_NAME_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_NRIC_DESC;
import static seedu.address.logic.commands.CommandTestUtil.INVALID_PHONE_DESC;
-import static seedu.address.logic.commands.CommandTestUtil.INVALID_TAG_DESC;
import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY;
import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.NRIC_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.NRIC_DESC_BOB;
import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY;
import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_BOB;
import static seedu.address.logic.commands.CommandTestUtil.PREAMBLE_NON_EMPTY;
import static seedu.address.logic.commands.CommandTestUtil.PREAMBLE_WHITESPACE;
-import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_FRIEND;
-import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_HUSBAND;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_AGE_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_ALLERGY_DUST;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_ALLERGY_PEANUTS;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_BLOODTYPE_BOB;
import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_GENDER_BOB;
import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB;
import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_AGE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_BLOODTYPE;
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GENDER;
import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NRIC;
import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
@@ -37,12 +51,15 @@
import seedu.address.logic.Messages;
import seedu.address.logic.commands.AddCommand;
-import seedu.address.model.person.Address;
+import seedu.address.model.person.Age;
+import seedu.address.model.person.Allergy;
+import seedu.address.model.person.BloodType;
import seedu.address.model.person.Email;
-import seedu.address.model.person.Name;
+import seedu.address.model.person.Gender;
import seedu.address.model.person.Person;
import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
+import seedu.address.model.shared.Name;
+import seedu.address.model.shared.Nric;
import seedu.address.testutil.PersonBuilder;
public class AddCommandParserTest {
@@ -50,47 +67,63 @@ public class AddCommandParserTest {
@Test
public void parse_allFieldsPresent_success() {
- Person expectedPerson = new PersonBuilder(BOB).withTags(VALID_TAG_FRIEND).build();
+ Person expectedPerson = new PersonBuilder(BOB).withAllergies(VALID_ALLERGY_DUST).build();
// whitespace only preamble
- assertParseSuccess(parser, PREAMBLE_WHITESPACE + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB
- + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedPerson));
-
+ assertParseSuccess(parser, PREAMBLE_WHITESPACE + NAME_DESC_BOB + NRIC_DESC_BOB
+ + EMAIL_DESC_BOB + PHONE_DESC_BOB
+ + GENDER_DESC_BOB + AGE_DESC_BOB + BLOODTYPE_DESC_BOB
+ + ALLERGY_DESC_DUST,
+ new AddCommand(expectedPerson));
// multiple tags - all accepted
- Person expectedPersonMultipleTags = new PersonBuilder(BOB).withTags(VALID_TAG_FRIEND, VALID_TAG_HUSBAND)
+ Person expectedPersonMultipleTags = new PersonBuilder(BOB).withAllergies(VALID_ALLERGY_DUST,
+ VALID_ALLERGY_PEANUTS)
.build();
assertParseSuccess(parser,
- NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB + TAG_DESC_HUSBAND + TAG_DESC_FRIEND,
+ NAME_DESC_BOB + NRIC_DESC_BOB + EMAIL_DESC_BOB + PHONE_DESC_BOB + GENDER_DESC_BOB
+ + AGE_DESC_BOB + BLOODTYPE_DESC_BOB + ALLERGY_DESC_DUST
+ + ALLERGY_DESC_PEANUTS,
new AddCommand(expectedPersonMultipleTags));
}
@Test
public void parse_repeatedNonTagValue_failure() {
- String validExpectedPersonString = NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB
- + ADDRESS_DESC_BOB + TAG_DESC_FRIEND;
+ String validExpectedPersonString = NAME_DESC_BOB + NRIC_DESC_BOB + EMAIL_DESC_BOB + PHONE_DESC_BOB
+ + GENDER_DESC_BOB + AGE_DESC_BOB + BLOODTYPE_DESC_BOB + ALLERGY_DESC_DUST;
// multiple names
assertParseFailure(parser, NAME_DESC_AMY + validExpectedPersonString,
Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME));
+ // multiple emails
+ assertParseFailure(parser, EMAIL_DESC_AMY + validExpectedPersonString,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_EMAIL));
+
// multiple phones
assertParseFailure(parser, PHONE_DESC_AMY + validExpectedPersonString,
Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE));
- // multiple emails
- assertParseFailure(parser, EMAIL_DESC_AMY + validExpectedPersonString,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_EMAIL));
+ // multiple genders
+ assertParseFailure(parser, GENDER_DESC_AMY + validExpectedPersonString,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_GENDER));
+
+ // multiple ages
+ assertParseFailure(parser, AGE_DESC_AMY + validExpectedPersonString,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_AGE));
- // multiple addresses
- assertParseFailure(parser, ADDRESS_DESC_AMY + validExpectedPersonString,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_ADDRESS));
+ // multiple bloodTypes
+ assertParseFailure(parser, BLOODTYPE_DESC_AMY + validExpectedPersonString,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_BLOODTYPE));
// multiple fields repeated
assertParseFailure(parser,
- validExpectedPersonString + PHONE_DESC_AMY + EMAIL_DESC_AMY + NAME_DESC_AMY + ADDRESS_DESC_AMY
+ validExpectedPersonString + NRIC_DESC_AMY + PHONE_DESC_AMY + EMAIL_DESC_AMY
+ + NAME_DESC_AMY + PREFIX_GENDER + PREFIX_AGE + PREFIX_BLOODTYPE
+ validExpectedPersonString,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME, PREFIX_ADDRESS, PREFIX_EMAIL, PREFIX_PHONE));
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME, PREFIX_NRIC, PREFIX_EMAIL,
+ PREFIX_PHONE,
+ PREFIX_GENDER, PREFIX_AGE, PREFIX_BLOODTYPE));
// invalid value followed by valid value
@@ -106,9 +139,17 @@ public void parse_repeatedNonTagValue_failure() {
assertParseFailure(parser, INVALID_PHONE_DESC + validExpectedPersonString,
Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE));
- // invalid address
- assertParseFailure(parser, INVALID_ADDRESS_DESC + validExpectedPersonString,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_ADDRESS));
+ // invalid gender
+ assertParseFailure(parser, INVALID_GENDER_DESC + validExpectedPersonString,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_GENDER));
+
+ // invalid age
+ assertParseFailure(parser, INVALID_AGE_DESC + validExpectedPersonString,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_AGE));
+
+ // invalid bloodType
+ assertParseFailure(parser, INVALID_BLOODTYPE_DESC + validExpectedPersonString,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_BLOODTYPE));
// valid value followed by invalid value
@@ -124,16 +165,25 @@ public void parse_repeatedNonTagValue_failure() {
assertParseFailure(parser, validExpectedPersonString + INVALID_PHONE_DESC,
Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE));
- // invalid address
- assertParseFailure(parser, validExpectedPersonString + INVALID_ADDRESS_DESC,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_ADDRESS));
+ // invalid gender
+ assertParseFailure(parser, validExpectedPersonString + INVALID_GENDER_DESC,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_GENDER));
+
+ // invalid age
+ assertParseFailure(parser, validExpectedPersonString + INVALID_AGE_DESC,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_AGE));
+
+ // invalid bloodtype
+ assertParseFailure(parser, validExpectedPersonString + INVALID_BLOODTYPE_DESC,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_BLOODTYPE));
}
@Test
public void parse_optionalFieldsMissing_success() {
// zero tags
- Person expectedPerson = new PersonBuilder(AMY).withTags().build();
- assertParseSuccess(parser, NAME_DESC_AMY + PHONE_DESC_AMY + EMAIL_DESC_AMY + ADDRESS_DESC_AMY,
+ Person expectedPerson = new PersonBuilder(AMY).withAllergies().build();
+ assertParseSuccess(parser, NAME_DESC_AMY + NRIC_DESC_AMY + EMAIL_DESC_AMY + PHONE_DESC_AMY
+ + GENDER_DESC_AMY + AGE_DESC_AMY + BLOODTYPE_DESC_AMY,
new AddCommand(expectedPerson));
}
@@ -142,55 +192,100 @@ public void parse_compulsoryFieldMissing_failure() {
String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE);
// missing name prefix
- assertParseFailure(parser, VALID_NAME_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB,
+ assertParseFailure(parser, VALID_NAME_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB
+ + GENDER_DESC_BOB + AGE_DESC_BOB + BLOODTYPE_DESC_BOB,
expectedMessage);
// missing phone prefix
- assertParseFailure(parser, NAME_DESC_BOB + VALID_PHONE_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB,
+ assertParseFailure(parser, NAME_DESC_BOB + EMAIL_DESC_BOB + VALID_PHONE_BOB
+ + GENDER_DESC_BOB + AGE_DESC_BOB + BLOODTYPE_DESC_BOB,
expectedMessage);
// missing email prefix
- assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + VALID_EMAIL_BOB + ADDRESS_DESC_BOB,
+ assertParseFailure(parser, NAME_DESC_BOB + VALID_EMAIL_BOB + PHONE_DESC_BOB
+ + GENDER_DESC_BOB + AGE_DESC_BOB + BLOODTYPE_DESC_BOB,
+ expectedMessage);
+
+ // missing gender prefix
+ assertParseFailure(parser, NAME_DESC_BOB + EMAIL_DESC_BOB + PHONE_DESC_BOB
+ + VALID_GENDER_BOB + AGE_DESC_BOB + BLOODTYPE_DESC_BOB,
expectedMessage);
- // missing address prefix
- assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + VALID_ADDRESS_BOB,
+ // missing age prefix
+ assertParseFailure(parser, NAME_DESC_BOB + VALID_EMAIL_BOB + PHONE_DESC_BOB
+ + GENDER_DESC_BOB + VALID_AGE_BOB + BLOODTYPE_DESC_BOB,
+ expectedMessage);
+
+ // missing bloodType prefix
+ assertParseFailure(parser, NAME_DESC_BOB + VALID_EMAIL_BOB + PHONE_DESC_BOB
+ + GENDER_DESC_BOB + VALID_AGE_BOB + VALID_BLOODTYPE_BOB,
expectedMessage);
// all prefixes missing
- assertParseFailure(parser, VALID_NAME_BOB + VALID_PHONE_BOB + VALID_EMAIL_BOB + VALID_ADDRESS_BOB,
+ assertParseFailure(parser, VALID_NAME_BOB + VALID_EMAIL_BOB + VALID_PHONE_BOB
+ + VALID_GENDER_BOB + VALID_AGE_BOB + VALID_BLOODTYPE_BOB,
expectedMessage);
}
@Test
public void parse_invalidValue_failure() {
// invalid name
- assertParseFailure(parser, INVALID_NAME_DESC + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB
- + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Name.MESSAGE_CONSTRAINTS);
+ assertParseFailure(parser,
+ INVALID_NAME_DESC + NRIC_DESC_BOB + EMAIL_DESC_BOB + PHONE_DESC_BOB + GENDER_DESC_BOB
+ + AGE_DESC_BOB + BLOODTYPE_DESC_BOB,
+ Name.MESSAGE_CONSTRAINTS);
+
+ // invalid nric
+ assertParseFailure(parser,
+ NAME_DESC_BOB + INVALID_NRIC_DESC + EMAIL_DESC_BOB + PHONE_DESC_BOB + GENDER_DESC_BOB
+ + AGE_DESC_BOB + BLOODTYPE_DESC_BOB,
+ Nric.MESSAGE_CONSTRAINTS);
// invalid phone
- assertParseFailure(parser, NAME_DESC_BOB + INVALID_PHONE_DESC + EMAIL_DESC_BOB + ADDRESS_DESC_BOB
- + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Phone.MESSAGE_CONSTRAINTS);
+ assertParseFailure(parser,
+ NAME_DESC_BOB + NRIC_DESC_BOB + EMAIL_DESC_BOB + INVALID_PHONE_DESC + GENDER_DESC_BOB
+ + AGE_DESC_BOB + BLOODTYPE_DESC_BOB,
+ Phone.MESSAGE_CONSTRAINTS);
// invalid email
- assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + INVALID_EMAIL_DESC + ADDRESS_DESC_BOB
- + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Email.MESSAGE_CONSTRAINTS);
+ assertParseFailure(parser,
+ NAME_DESC_BOB + NRIC_DESC_BOB + INVALID_EMAIL_DESC + PHONE_DESC_BOB + GENDER_DESC_BOB
+ + AGE_DESC_BOB + BLOODTYPE_DESC_BOB,
+ Email.MESSAGE_CONSTRAINTS);
- // invalid address
- assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + INVALID_ADDRESS_DESC
- + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Address.MESSAGE_CONSTRAINTS);
+ // invalid gender
+ assertParseFailure(parser,
+ NAME_DESC_BOB + NRIC_DESC_BOB + EMAIL_DESC_BOB + PHONE_DESC_BOB + INVALID_GENDER_DESC
+ + AGE_DESC_BOB + BLOODTYPE_DESC_BOB,
+ Gender.MESSAGE_CONSTRAINTS);
+
+ // invalid age
+ assertParseFailure(parser,
+ NAME_DESC_BOB + NRIC_DESC_BOB + EMAIL_DESC_BOB + PHONE_DESC_BOB + GENDER_DESC_BOB
+ + INVALID_AGE_DESC + BLOODTYPE_DESC_BOB,
+ Age.MESSAGE_CONSTRAINTS);
- // invalid tag
- assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB
- + INVALID_TAG_DESC + VALID_TAG_FRIEND, Tag.MESSAGE_CONSTRAINTS);
+ // invalid bloodType
+ assertParseFailure(parser,
+ NAME_DESC_BOB + NRIC_DESC_BOB + EMAIL_DESC_BOB + PHONE_DESC_BOB + GENDER_DESC_BOB
+ + AGE_DESC_BOB + INVALID_BLOODTYPE_DESC,
+ BloodType.MESSAGE_CONSTRAINTS);
+
+ // invalid Allergy
+ assertParseFailure(parser,
+ NAME_DESC_BOB + NRIC_DESC_BOB + EMAIL_DESC_BOB + PHONE_DESC_BOB + GENDER_DESC_BOB
+ + AGE_DESC_BOB + BLOODTYPE_DESC_BOB + INVALID_ALLERGY_DESC,
+ Allergy.MESSAGE_CONSTRAINTS);
// two invalid values, only first invalid value reported
- assertParseFailure(parser, INVALID_NAME_DESC + PHONE_DESC_BOB + EMAIL_DESC_BOB + INVALID_ADDRESS_DESC,
+ assertParseFailure(parser, INVALID_NAME_DESC + NRIC_DESC_BOB + EMAIL_DESC_BOB + PHONE_DESC_BOB
+ + INVALID_GENDER_DESC + AGE_DESC_BOB + BLOODTYPE_DESC_BOB,
Name.MESSAGE_CONSTRAINTS);
// non-empty preamble
- assertParseFailure(parser, PREAMBLE_NON_EMPTY + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB
- + ADDRESS_DESC_BOB + TAG_DESC_HUSBAND + TAG_DESC_FRIEND,
+ assertParseFailure(parser,
+ PREAMBLE_NON_EMPTY + NAME_DESC_BOB + NRIC_DESC_BOB + EMAIL_DESC_BOB + PHONE_DESC_BOB
+ + GENDER_DESC_BOB + AGE_DESC_BOB + BLOODTYPE_DESC_BOB,
String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE));
}
}
diff --git a/src/test/java/seedu/address/logic/parser/AddRecordCommandParserTest.java b/src/test/java/seedu/address/logic/parser/AddRecordCommandParserTest.java
new file mode 100644
index 00000000000..bc0408f6dd0
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/AddRecordCommandParserTest.java
@@ -0,0 +1,26 @@
+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 static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
+import static seedu.address.testutil.TypicalRecords.FEVER0;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.AddRecordCommand;
+
+public class AddRecordCommandParserTest {
+ private AddRecordCommandParser parser = new AddRecordCommandParser();
+
+ @Test
+ public void parse_validArgs_returnsAddRecordCommand() {
+ assertParseSuccess(parser, "1 d/09-10-2023 1800 c/Fever m/Tylenol",
+ new AddRecordCommand(INDEX_FIRST_PERSON, FEVER0));
+ }
+
+ @Test
+ public void parse_invalidArgs_throwsParseException() {
+ assertParseFailure(parser, "a", String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddRecordCommand.MESSAGE_USAGE));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java b/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java
index 5a1ab3dbc0c..80b702b98a0 100644
--- a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java
+++ b/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java
@@ -13,8 +13,9 @@
import org.junit.jupiter.api.Test;
+import seedu.address.logic.commands.AddAppointmentCommand;
import seedu.address.logic.commands.AddCommand;
-import seedu.address.logic.commands.ClearCommand;
+import seedu.address.logic.commands.DeleteAppointmentCommand;
import seedu.address.logic.commands.DeleteCommand;
import seedu.address.logic.commands.EditCommand;
import seedu.address.logic.commands.EditCommand.EditPersonDescriptor;
@@ -22,9 +23,16 @@
import seedu.address.logic.commands.FindCommand;
import seedu.address.logic.commands.HelpCommand;
import seedu.address.logic.commands.ListCommand;
+import seedu.address.logic.commands.PinCommand;
+import seedu.address.logic.commands.UnpinCommand;
+import seedu.address.logic.commands.ViewAppointmentCommand;
+import seedu.address.logic.commands.ViewCommand;
import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.appointment.Appointment;
import seedu.address.model.person.NameContainsKeywordsPredicate;
import seedu.address.model.person.Person;
+import seedu.address.testutil.AppointmentBuilder;
+import seedu.address.testutil.AppointmentUtil;
import seedu.address.testutil.EditPersonDescriptorBuilder;
import seedu.address.testutil.PersonBuilder;
import seedu.address.testutil.PersonUtil;
@@ -40,12 +48,6 @@ public void parseCommand_add() throws Exception {
assertEquals(new AddCommand(person), command);
}
- @Test
- public void parseCommand_clear() throws Exception {
- assertTrue(parser.parseCommand(ClearCommand.COMMAND_WORD) instanceof ClearCommand);
- assertTrue(parser.parseCommand(ClearCommand.COMMAND_WORD + " 3") instanceof ClearCommand);
- }
-
@Test
public void parseCommand_delete() throws Exception {
DeleteCommand command = (DeleteCommand) parser.parseCommand(
@@ -56,7 +58,7 @@ public void parseCommand_delete() throws Exception {
@Test
public void parseCommand_edit() throws Exception {
Person person = new PersonBuilder().build();
- EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder(person).build();
+ EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder(person).withNullNric().build();
EditCommand command = (EditCommand) parser.parseCommand(EditCommand.COMMAND_WORD + " "
+ INDEX_FIRST_PERSON.getOneBased() + " " + PersonUtil.getEditPersonDescriptorDetails(descriptor));
assertEquals(new EditCommand(INDEX_FIRST_PERSON, descriptor), command);
@@ -88,10 +90,52 @@ public void parseCommand_list() throws Exception {
assertTrue(parser.parseCommand(ListCommand.COMMAND_WORD + " 3") instanceof ListCommand);
}
+ @Test
+ public void parseCommand_pin() throws Exception {
+ PinCommand command = (PinCommand) parser.parseCommand(
+ PinCommand.COMMAND_WORD + " " + INDEX_FIRST_PERSON.getOneBased());
+ assertEquals(new PinCommand(INDEX_FIRST_PERSON), command);
+ }
+
+ @Test
+ public void parseCommand_unpin() throws Exception {
+ UnpinCommand command = (UnpinCommand) parser.parseCommand(
+ UnpinCommand.COMMAND_WORD + " " + INDEX_FIRST_PERSON.getOneBased());
+ assertEquals(new UnpinCommand(INDEX_FIRST_PERSON), command);
+ }
+
+ @Test
+ public void parseCommand_addAppointment() throws Exception {
+ Appointment appointment = new AppointmentBuilder().withNric(null).build();
+ AddAppointmentCommand command = (AddAppointmentCommand) parser
+ .parseCommand(AppointmentUtil.getAddAppointmentCommand(appointment));
+ assertEquals(new AddAppointmentCommand(INDEX_FIRST_PERSON, appointment), command);
+ }
+
+ @Test
+ public void parseCommand_viewAppointment() throws Exception {
+ assertTrue(parser.parseCommand(ViewAppointmentCommand.COMMAND_WORD) instanceof ViewAppointmentCommand);
+ assertTrue(parser.parseCommand(ViewAppointmentCommand.COMMAND_WORD + " 3") instanceof ViewAppointmentCommand);
+ }
+
+ @Test
+ public void parseCommand_deleteAppointment() throws Exception {
+ DeleteAppointmentCommand command = (DeleteAppointmentCommand) parser.parseCommand(
+ DeleteAppointmentCommand.COMMAND_WORD + " " + INDEX_FIRST_PERSON.getOneBased());
+ assertEquals(new DeleteAppointmentCommand(INDEX_FIRST_PERSON), command);
+ }
+
+ @Test
+ public void parseCommand_view() throws Exception {
+ ViewCommand viewCommand = (ViewCommand) parser.parseCommand(
+ ViewCommand.COMMAND_WORD + " " + INDEX_FIRST_PERSON.getOneBased());
+ assertEquals(new ViewCommand(INDEX_FIRST_PERSON), viewCommand);
+ }
+
@Test
public void parseCommand_unrecognisedInput_throwsParseException() {
assertThrows(ParseException.class, String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE), ()
- -> parser.parseCommand(""));
+ -> parser.parseCommand(""));
}
@Test
diff --git a/src/test/java/seedu/address/logic/parser/DeleteAppointmentCommandParserTest.java b/src/test/java/seedu/address/logic/parser/DeleteAppointmentCommandParserTest.java
new file mode 100644
index 00000000000..0dc796bb009
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/DeleteAppointmentCommandParserTest.java
@@ -0,0 +1,36 @@
+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 static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.DeleteAppointmentCommand;
+
+/**
+ * As we are only doing white-box testing, our test cases do not cover path
+ * variations
+ * outside of the DeleteAppointmentCommand code. For example, inputs "1" and "1
+ * abc" take the
+ * same path through the DeleteAppointmentCommand, 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 DeleteAppointmentCommandParserTest {
+
+ private DeleteAppointmentCommandParser parser = new DeleteAppointmentCommandParser();
+
+ @Test
+ public void parse_validArgs_returnsDeleteAppointmentCommand() {
+ assertParseSuccess(parser, "1", new DeleteAppointmentCommand(INDEX_FIRST_PERSON));
+ }
+
+ @Test
+ public void parse_invalidArgs_throwsParseException() {
+ assertParseFailure(parser, "a",
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteAppointmentCommand.MESSAGE_USAGE));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/DeleteRecordCommandParserTest.java b/src/test/java/seedu/address/logic/parser/DeleteRecordCommandParserTest.java
new file mode 100644
index 00000000000..c35622f8aab
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/DeleteRecordCommandParserTest.java
@@ -0,0 +1,25 @@
+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 static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
+import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_RECORD;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.DeleteRecordCommand;
+
+public class DeleteRecordCommandParserTest {
+
+ private DeleteRecordCommandParser parser = new DeleteRecordCommandParser();
+ @Test
+ public void parse_invalidUserInput_failure() {
+ assertParseFailure(parser, "1",
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteRecordCommand.MESSAGE_USAGE));
+ }
+ @Test
+ public void parse_validUserInput_success() {
+ assertParseSuccess(parser, "1/1", new DeleteRecordCommand(INDEX_FIRST_PERSON, INDEX_FIRST_RECORD));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java b/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java
index cc7175172d4..3f1020bacb0 100644
--- a/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java
+++ b/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java
@@ -1,31 +1,35 @@
package seedu.address.logic.parser;
import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
-import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY;
-import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.AGE_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.ALLERGY_DESC_DUST;
+import static seedu.address.logic.commands.CommandTestUtil.ALLERGY_DESC_PEANUTS;
+import static seedu.address.logic.commands.CommandTestUtil.BLOODTYPE_DESC_AMY;
import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY;
import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.INVALID_ADDRESS_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.GENDER_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_AGE_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_ALLERGY_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_BLOODTYPE_DESC;
import static seedu.address.logic.commands.CommandTestUtil.INVALID_EMAIL_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_GENDER_DESC;
import static seedu.address.logic.commands.CommandTestUtil.INVALID_NAME_DESC;
import static seedu.address.logic.commands.CommandTestUtil.INVALID_PHONE_DESC;
-import static seedu.address.logic.commands.CommandTestUtil.INVALID_TAG_DESC;
import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY;
import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY;
import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_FRIEND;
-import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_HUSBAND;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_AGE_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_ALLERGY_DUST;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_ALLERGY_PEANUTS;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_BLOODTYPE_AMY;
import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_GENDER_AMY;
import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_AMY;
import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_AMY;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ALLERGIES;
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GENDER;
import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
@@ -38,19 +42,21 @@
import seedu.address.logic.Messages;
import seedu.address.logic.commands.EditCommand;
import seedu.address.logic.commands.EditCommand.EditPersonDescriptor;
-import seedu.address.model.person.Address;
+import seedu.address.model.person.Age;
+import seedu.address.model.person.Allergy;
+import seedu.address.model.person.BloodType;
import seedu.address.model.person.Email;
-import seedu.address.model.person.Name;
+import seedu.address.model.person.Gender;
import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
+import seedu.address.model.shared.Name;
import seedu.address.testutil.EditPersonDescriptorBuilder;
public class EditCommandParserTest {
- private static final String TAG_EMPTY = " " + PREFIX_TAG;
+ private static final String ALLERGY_EMPTY = " " + PREFIX_ALLERGIES;
- private static final String MESSAGE_INVALID_FORMAT =
- String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE);
+ private static final String MESSAGE_INVALID_FORMAT = String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ EditCommand.MESSAGE_USAGE);
private EditCommandParser parser = new EditCommandParser();
@@ -78,40 +84,49 @@ public void parse_invalidPreamble_failure() {
assertParseFailure(parser, "1 some random string", MESSAGE_INVALID_FORMAT);
// invalid prefix being parsed as preamble
- assertParseFailure(parser, "1 i/ string", MESSAGE_INVALID_FORMAT);
+ assertParseFailure(parser, "1 z/ string", MESSAGE_INVALID_FORMAT);
}
@Test
public void parse_invalidValue_failure() {
assertParseFailure(parser, "1" + INVALID_NAME_DESC, Name.MESSAGE_CONSTRAINTS); // invalid name
- assertParseFailure(parser, "1" + INVALID_PHONE_DESC, Phone.MESSAGE_CONSTRAINTS); // invalid phone
assertParseFailure(parser, "1" + INVALID_EMAIL_DESC, Email.MESSAGE_CONSTRAINTS); // invalid email
- assertParseFailure(parser, "1" + INVALID_ADDRESS_DESC, Address.MESSAGE_CONSTRAINTS); // invalid address
- assertParseFailure(parser, "1" + INVALID_TAG_DESC, Tag.MESSAGE_CONSTRAINTS); // invalid tag
+ assertParseFailure(parser, "1" + INVALID_PHONE_DESC, Phone.MESSAGE_CONSTRAINTS); // invalid phone
+ assertParseFailure(parser, "1" + INVALID_GENDER_DESC, Gender.MESSAGE_CONSTRAINTS); // invalid gender
+ assertParseFailure(parser, "1" + INVALID_AGE_DESC, Age.MESSAGE_CONSTRAINTS); // invalid age
+ assertParseFailure(parser, "1" + INVALID_BLOODTYPE_DESC, BloodType.MESSAGE_CONSTRAINTS); // invalid bloodType
+ assertParseFailure(parser, "1" + INVALID_ALLERGY_DESC, Allergy.MESSAGE_CONSTRAINTS); // invalid allergy
- // invalid phone followed by valid email
- assertParseFailure(parser, "1" + INVALID_PHONE_DESC + EMAIL_DESC_AMY, Phone.MESSAGE_CONSTRAINTS);
+ // invalid email followed by valid phone
+ assertParseFailure(parser, "1" + INVALID_EMAIL_DESC + VALID_PHONE_AMY, Email.MESSAGE_CONSTRAINTS);
- // while parsing {@code PREFIX_TAG} alone will reset the tags of the {@code Person} being edited,
+ // while parsing {@code PREFIX_TAG} alone will reset the tags of the {@code
+ // Person} being edited,
// parsing it together with a valid tag results in error
- assertParseFailure(parser, "1" + TAG_DESC_FRIEND + TAG_DESC_HUSBAND + TAG_EMPTY, Tag.MESSAGE_CONSTRAINTS);
- assertParseFailure(parser, "1" + TAG_DESC_FRIEND + TAG_EMPTY + TAG_DESC_HUSBAND, Tag.MESSAGE_CONSTRAINTS);
- assertParseFailure(parser, "1" + TAG_EMPTY + TAG_DESC_FRIEND + TAG_DESC_HUSBAND, Tag.MESSAGE_CONSTRAINTS);
+ assertParseFailure(parser, "1" + ALLERGY_DESC_DUST + ALLERGY_DESC_PEANUTS + ALLERGY_EMPTY,
+ Allergy.MESSAGE_CONSTRAINTS);
+ assertParseFailure(parser, "1" + ALLERGY_DESC_DUST + ALLERGY_EMPTY + ALLERGY_DESC_PEANUTS,
+ Allergy.MESSAGE_CONSTRAINTS);
+ assertParseFailure(parser, "1" + ALLERGY_EMPTY + ALLERGY_DESC_DUST + ALLERGY_DESC_PEANUTS,
+ Allergy.MESSAGE_CONSTRAINTS);
// multiple invalid values, but only the first invalid value is captured
- assertParseFailure(parser, "1" + INVALID_NAME_DESC + INVALID_EMAIL_DESC + VALID_ADDRESS_AMY + VALID_PHONE_AMY,
+ assertParseFailure(parser, "1" + INVALID_NAME_DESC + INVALID_EMAIL_DESC + VALID_PHONE_AMY
+ + VALID_GENDER_AMY + VALID_AGE_AMY + VALID_BLOODTYPE_AMY,
Name.MESSAGE_CONSTRAINTS);
}
@Test
public void parse_allFieldsSpecified_success() {
Index targetIndex = INDEX_SECOND_PERSON;
- String userInput = targetIndex.getOneBased() + PHONE_DESC_BOB + TAG_DESC_HUSBAND
- + EMAIL_DESC_AMY + ADDRESS_DESC_AMY + NAME_DESC_AMY + TAG_DESC_FRIEND;
+ String userInput = targetIndex.getOneBased() + PHONE_DESC_AMY + ALLERGY_DESC_DUST
+ + EMAIL_DESC_AMY + GENDER_DESC_AMY + NAME_DESC_AMY + ALLERGY_DESC_PEANUTS
+ + AGE_DESC_AMY + BLOODTYPE_DESC_AMY;
EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_AMY)
- .withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_AMY).withAddress(VALID_ADDRESS_AMY)
- .withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND).build();
+ .withEmail(VALID_EMAIL_AMY).withPhone(VALID_PHONE_AMY).withGender(VALID_GENDER_AMY)
+ .withAge(VALID_AGE_AMY).withBloodType(VALID_BLOODTYPE_AMY)
+ .withAllergies(VALID_ALLERGY_DUST, VALID_ALLERGY_PEANUTS).build();
EditCommand expectedCommand = new EditCommand(targetIndex, descriptor);
assertParseSuccess(parser, userInput, expectedCommand);
@@ -120,10 +135,10 @@ public void parse_allFieldsSpecified_success() {
@Test
public void parse_someFieldsSpecified_success() {
Index targetIndex = INDEX_FIRST_PERSON;
- String userInput = targetIndex.getOneBased() + PHONE_DESC_BOB + EMAIL_DESC_AMY;
+ String userInput = targetIndex.getOneBased() + EMAIL_DESC_AMY + PHONE_DESC_AMY;
- EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_BOB)
- .withEmail(VALID_EMAIL_AMY).build();
+ EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withEmail(VALID_EMAIL_AMY)
+ .withPhone(VALID_PHONE_AMY).build();
EditCommand expectedCommand = new EditCommand(targetIndex, descriptor);
assertParseSuccess(parser, userInput, expectedCommand);
@@ -138,27 +153,39 @@ public void parse_oneFieldSpecified_success() {
EditCommand expectedCommand = new EditCommand(targetIndex, descriptor);
assertParseSuccess(parser, userInput, expectedCommand);
+ // email
+ userInput = targetIndex.getOneBased() + EMAIL_DESC_AMY;
+ descriptor = new EditPersonDescriptorBuilder().withEmail(VALID_EMAIL_AMY).build();
+ expectedCommand = new EditCommand(targetIndex, descriptor);
+ assertParseSuccess(parser, userInput, expectedCommand);
+
// phone
userInput = targetIndex.getOneBased() + PHONE_DESC_AMY;
descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_AMY).build();
expectedCommand = new EditCommand(targetIndex, descriptor);
assertParseSuccess(parser, userInput, expectedCommand);
- // email
- userInput = targetIndex.getOneBased() + EMAIL_DESC_AMY;
- descriptor = new EditPersonDescriptorBuilder().withEmail(VALID_EMAIL_AMY).build();
+ // gender
+ userInput = targetIndex.getOneBased() + GENDER_DESC_AMY;
+ descriptor = new EditPersonDescriptorBuilder().withGender(VALID_GENDER_AMY).build();
+ expectedCommand = new EditCommand(targetIndex, descriptor);
+ assertParseSuccess(parser, userInput, expectedCommand);
+
+ // age
+ userInput = targetIndex.getOneBased() + AGE_DESC_AMY;
+ descriptor = new EditPersonDescriptorBuilder().withAge(VALID_AGE_AMY).build();
expectedCommand = new EditCommand(targetIndex, descriptor);
assertParseSuccess(parser, userInput, expectedCommand);
- // address
- userInput = targetIndex.getOneBased() + ADDRESS_DESC_AMY;
- descriptor = new EditPersonDescriptorBuilder().withAddress(VALID_ADDRESS_AMY).build();
+ // bloodType
+ userInput = targetIndex.getOneBased() + BLOODTYPE_DESC_AMY;
+ descriptor = new EditPersonDescriptorBuilder().withBloodType(VALID_BLOODTYPE_AMY).build();
expectedCommand = new EditCommand(targetIndex, descriptor);
assertParseSuccess(parser, userInput, expectedCommand);
// tags
- userInput = targetIndex.getOneBased() + TAG_DESC_FRIEND;
- descriptor = new EditPersonDescriptorBuilder().withTags(VALID_TAG_FRIEND).build();
+ userInput = targetIndex.getOneBased() + ALLERGY_DESC_DUST;
+ descriptor = new EditPersonDescriptorBuilder().withAllergies(VALID_ALLERGY_DUST).build();
expectedCommand = new EditCommand(targetIndex, descriptor);
assertParseSuccess(parser, userInput, expectedCommand);
}
@@ -179,28 +206,31 @@ public void parse_multipleRepeatedFields_failure() {
assertParseFailure(parser, userInput, Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE));
- // mulltiple valid fields repeated
- userInput = targetIndex.getOneBased() + PHONE_DESC_AMY + ADDRESS_DESC_AMY + EMAIL_DESC_AMY
- + TAG_DESC_FRIEND + PHONE_DESC_AMY + ADDRESS_DESC_AMY + EMAIL_DESC_AMY + TAG_DESC_FRIEND
- + PHONE_DESC_BOB + ADDRESS_DESC_BOB + EMAIL_DESC_BOB + TAG_DESC_HUSBAND;
+ // multiple valid fields repeated
+ userInput = targetIndex.getOneBased() + PHONE_DESC_AMY + GENDER_DESC_AMY + EMAIL_DESC_AMY
+ + ALLERGY_DESC_DUST + PHONE_DESC_AMY + GENDER_DESC_AMY + EMAIL_DESC_AMY
+ + ALLERGY_DESC_DUST
+ + PHONE_DESC_BOB + GENDER_DESC_AMY + EMAIL_DESC_BOB + ALLERGY_DESC_PEANUTS;
assertParseFailure(parser, userInput,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS));
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE, PREFIX_EMAIL,
+ PREFIX_GENDER));
// multiple invalid values
- userInput = targetIndex.getOneBased() + INVALID_PHONE_DESC + INVALID_ADDRESS_DESC + INVALID_EMAIL_DESC
- + INVALID_PHONE_DESC + INVALID_ADDRESS_DESC + INVALID_EMAIL_DESC;
+ userInput = targetIndex.getOneBased() + INVALID_PHONE_DESC + INVALID_GENDER_DESC + INVALID_EMAIL_DESC
+ + INVALID_PHONE_DESC + INVALID_GENDER_DESC + INVALID_EMAIL_DESC;
assertParseFailure(parser, userInput,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS));
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE, PREFIX_EMAIL,
+ PREFIX_GENDER));
}
@Test
public void parse_resetTags_success() {
Index targetIndex = INDEX_THIRD_PERSON;
- String userInput = targetIndex.getOneBased() + TAG_EMPTY;
+ String userInput = targetIndex.getOneBased() + ALLERGY_EMPTY;
- EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withTags().build();
+ EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withAllergies().build();
EditCommand expectedCommand = new EditCommand(targetIndex, descriptor);
assertParseSuccess(parser, userInput, expectedCommand);
diff --git a/src/test/java/seedu/address/logic/parser/EditRecordCommandParserTest.java b/src/test/java/seedu/address/logic/parser/EditRecordCommandParserTest.java
new file mode 100644
index 00000000000..b7abca88a93
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/EditRecordCommandParserTest.java
@@ -0,0 +1,184 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.commands.CommandTestUtil.CONDITION_DESC_DIARRHEA;
+import static seedu.address.logic.commands.CommandTestUtil.CONDITION_DESC_HEAT_STROKE;
+import static seedu.address.logic.commands.CommandTestUtil.DATETIME_DESC_SLEEP_STUDY;
+import static seedu.address.logic.commands.CommandTestUtil.DATETIME_DESC_THYROID_CHECK;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_CONDITION_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_DATETIME_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_MEDICATION_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.MEDICATION_DESC_DIARRHEA;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_CONDITION_DIARRHEA;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_CONDITION_HEAT_STROKE;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_DATETIME_SLEEP_STUDY;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_MEDICATION_DIARRHEA;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_MEDICATION_HEAT_STROKE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_CONDITION;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
+import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
+import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_RECORD;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.EditRecordCommand;
+import seedu.address.model.record.Condition;
+import seedu.address.model.shared.DateTime;
+import seedu.address.testutil.EditRecordDescriptorBuilder;
+
+
+public class EditRecordCommandParserTest {
+ private static final String CONDITION_EMPTY = " " + PREFIX_CONDITION;
+
+ private static final String MESSAGE_INVALID_FORMAT =
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditRecordCommand.MESSAGE_USAGE);
+
+ private EditRecordCommandParser parser = new EditRecordCommandParser();
+
+ @Test
+ public void parse_missingParts_failure() {
+ // no index specified
+ assertParseFailure(parser, VALID_DATETIME_SLEEP_STUDY, MESSAGE_INVALID_FORMAT);
+
+ // no field specified
+ assertParseFailure(parser, "1/1", EditRecordCommand.MESSAGE_NOT_EDITED);
+
+ // no index and no field specified
+ assertParseFailure(parser, "", MESSAGE_INVALID_FORMAT);
+ }
+
+ @Test
+ public void parse_invalidPreamble_failure() {
+ // negative index
+ assertParseFailure(parser, "-5/1" + DATETIME_DESC_SLEEP_STUDY, MESSAGE_INVALID_FORMAT);
+
+ // zero index
+ assertParseFailure(parser, "0/1" + DATETIME_DESC_THYROID_CHECK, MESSAGE_INVALID_FORMAT);
+
+ // invalid arguments being parsed as preamble
+ assertParseFailure(parser, "1 some random string", MESSAGE_INVALID_FORMAT);
+
+ // invalid prefix being parsed as preamble
+ assertParseFailure(parser, "1 i/ string", MESSAGE_INVALID_FORMAT);
+ }
+
+ @Test
+ public void parse_invalidValue_failure() {
+ assertParseFailure(parser, "1/1" + INVALID_DATETIME_DESC, DateTime.MESSAGE_CONSTRAINTS); // invalid dateTime
+ assertParseFailure(parser, "1/1" + INVALID_CONDITION_DESC, Condition.MESSAGE_CONSTRAINTS); // invalid condition
+
+ // invalid dateTime followed by valid condition
+ assertParseFailure(parser, "1/1" + INVALID_DATETIME_DESC
+ + VALID_CONDITION_HEAT_STROKE + VALID_MEDICATION_HEAT_STROKE, DateTime.MESSAGE_CONSTRAINTS);
+
+ // while parsing {@code PREFIX_TAG} alone will reset the tags of the {@code Person} being edited,
+ // parsing it together with a valid tag results in error
+ assertParseFailure(parser, "1/1" + CONDITION_DESC_HEAT_STROKE + CONDITION_DESC_DIARRHEA + CONDITION_EMPTY,
+ Condition.MESSAGE_CONSTRAINTS);
+ assertParseFailure(parser, "1/1" + CONDITION_DESC_HEAT_STROKE + CONDITION_EMPTY + CONDITION_DESC_DIARRHEA,
+ Condition.MESSAGE_CONSTRAINTS);
+ assertParseFailure(parser, "1/1" + CONDITION_EMPTY + CONDITION_DESC_HEAT_STROKE + CONDITION_DESC_DIARRHEA,
+ Condition.MESSAGE_CONSTRAINTS);
+
+ // multiple invalid values, but only the first invalid value is captured
+ assertParseFailure(parser, "1/1" + INVALID_DATETIME_DESC + INVALID_CONDITION_DESC + INVALID_MEDICATION_DESC,
+ DateTime.MESSAGE_CONSTRAINTS);
+ }
+
+ @Test
+ public void parse_allFieldsSpecified_success() {
+ Index patientIndex = INDEX_FIRST_PERSON;
+ Index recordIndex = INDEX_FIRST_RECORD;
+ String userInput = patientIndex.getOneBased() + "/" + recordIndex.getOneBased()
+ + DATETIME_DESC_SLEEP_STUDY + CONDITION_DESC_DIARRHEA + MEDICATION_DESC_DIARRHEA;
+
+ EditRecordCommand.EditRecordDescriptor descriptor = new EditRecordDescriptorBuilder()
+ .withDateTime(VALID_DATETIME_SLEEP_STUDY)
+ .withConditions(VALID_CONDITION_DIARRHEA)
+ .withMedications(VALID_MEDICATION_DIARRHEA).build();
+ EditRecordCommand expectedCommand = new EditRecordCommand(patientIndex, recordIndex, descriptor);
+
+ assertParseSuccess(parser, userInput, expectedCommand);
+ }
+
+ @Test
+ public void parse_someFieldsSpecified_success() {
+ Index patientIndex = INDEX_FIRST_PERSON;
+ Index recordIndex = INDEX_FIRST_RECORD;
+ String userInput = patientIndex.getOneBased() + "/" + recordIndex.getOneBased() + DATETIME_DESC_SLEEP_STUDY;
+
+ EditRecordCommand.EditRecordDescriptor descriptor = new EditRecordDescriptorBuilder()
+ .withDateTime(VALID_DATETIME_SLEEP_STUDY).build();
+ EditRecordCommand expectedCommand = new EditRecordCommand(patientIndex, recordIndex, descriptor);
+
+ assertParseSuccess(parser, userInput, expectedCommand);
+ }
+
+ @Test
+ public void parse_oneFieldSpecified_success() {
+ // dateTime
+ Index patientIndex = INDEX_FIRST_PERSON;
+ Index recordIndex = INDEX_FIRST_RECORD;
+ String userInput = patientIndex.getOneBased() + "/" + recordIndex.getOneBased() + DATETIME_DESC_SLEEP_STUDY;
+ EditRecordCommand.EditRecordDescriptor descriptor = new EditRecordDescriptorBuilder()
+ .withDateTime(VALID_DATETIME_SLEEP_STUDY).build();
+ EditRecordCommand expectedCommand = new EditRecordCommand(patientIndex, recordIndex, descriptor);
+ assertParseSuccess(parser, userInput, expectedCommand);
+
+ // condition
+ userInput = patientIndex.getOneBased() + "/" + recordIndex.getOneBased() + CONDITION_DESC_DIARRHEA;
+ descriptor = new EditRecordDescriptorBuilder().withConditions(VALID_CONDITION_DIARRHEA).build();
+ expectedCommand = new EditRecordCommand(patientIndex, recordIndex, descriptor);
+ assertParseSuccess(parser, userInput, expectedCommand);
+
+
+ }
+
+ @Test
+ public void parse_multipleRepeatedFields_failure() {
+ // valid followed by invalid
+ Index patientIndex = INDEX_FIRST_PERSON;
+ Index recordIndex = INDEX_FIRST_RECORD;
+ String userInput = patientIndex.getOneBased() + "/" + recordIndex.getOneBased()
+ + INVALID_DATETIME_DESC + DATETIME_DESC_THYROID_CHECK;
+
+ assertParseFailure(parser, userInput, Messages.getErrorMessageForDuplicatePrefixes(PREFIX_DATE));
+
+ // invalid followed by valid
+ userInput = patientIndex.getOneBased() + "/" + recordIndex.getOneBased()
+ + DATETIME_DESC_THYROID_CHECK + INVALID_DATETIME_DESC;
+
+ assertParseFailure(parser, userInput, Messages.getErrorMessageForDuplicatePrefixes(PREFIX_DATE));
+
+ // mulltiple valid fields repeated
+ userInput = patientIndex.getOneBased() + "/" + recordIndex.getOneBased()
+ + DATETIME_DESC_THYROID_CHECK + DATETIME_DESC_SLEEP_STUDY + CONDITION_DESC_DIARRHEA
+ + CONDITION_DESC_HEAT_STROKE;
+
+ assertParseFailure(parser, userInput,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_DATE));
+
+ // multiple invalid values
+ userInput = patientIndex.getOneBased() + "/" + recordIndex.getOneBased()
+ + INVALID_DATETIME_DESC + INVALID_DATETIME_DESC + INVALID_CONDITION_DESC;
+
+ assertParseFailure(parser, userInput,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_DATE));
+ }
+
+ @Test
+ public void parse_resetConditions_success() {
+ Index patientIndex = INDEX_FIRST_PERSON;
+ Index recordIndex = INDEX_FIRST_RECORD;
+ String userInput = patientIndex.getOneBased() + "/" + recordIndex.getOneBased() + CONDITION_EMPTY;
+
+ EditRecordCommand.EditRecordDescriptor descriptor = new EditRecordDescriptorBuilder().withConditions().build();
+ EditRecordCommand expectedCommand = new EditRecordCommand(patientIndex, recordIndex, descriptor);
+
+ assertParseSuccess(parser, userInput, expectedCommand);
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/FindRecordCommandParserTest.java b/src/test/java/seedu/address/logic/parser/FindRecordCommandParserTest.java
new file mode 100644
index 00000000000..b4f380977c3
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/FindRecordCommandParserTest.java
@@ -0,0 +1,33 @@
+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 java.util.Arrays;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.FindRecordCommand;
+import seedu.address.model.record.RecordContainsKeywordsPredicate;
+
+public class FindRecordCommandParserTest {
+ private FindRecordCommandParser parser = new FindRecordCommandParser();
+ @Test
+ public void parse_emptyArg_throwsParseException() {
+ assertParseFailure(parser, " ", String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ FindRecordCommand.MESSAGE_USAGE));
+ }
+
+ @Test
+ public void parse_validArgs_returnsFindRecordCommand() {
+ // no leading and trailing whitespaces
+ FindRecordCommand expectedFindRecordCommand =
+ new FindRecordCommand(new RecordContainsKeywordsPredicate(Arrays.asList("Tylenol")));
+ assertParseSuccess(parser, "Tylenol", expectedFindRecordCommand);
+
+ // multiple whitespaces between keywords
+ assertParseSuccess(parser, " \n Tylenol \n", expectedFindRecordCommand);
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/ParserUtilTest.java b/src/test/java/seedu/address/logic/parser/ParserUtilTest.java
index 4256788b1a7..7bac7ac0ba8 100644
--- a/src/test/java/seedu/address/logic/parser/ParserUtilTest.java
+++ b/src/test/java/seedu/address/logic/parser/ParserUtilTest.java
@@ -6,33 +6,50 @@
import static seedu.address.testutil.Assert.assertThrows;
import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import org.junit.jupiter.api.Test;
import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.person.Address;
+import seedu.address.model.person.Age;
+import seedu.address.model.person.Allergy;
+import seedu.address.model.person.BloodType;
import seedu.address.model.person.Email;
-import seedu.address.model.person.Name;
+import seedu.address.model.person.Gender;
import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
+import seedu.address.model.record.Condition;
+import seedu.address.model.record.Medication;
+import seedu.address.model.shared.Name;
+
public class ParserUtilTest {
private static final String INVALID_NAME = "R@chel";
- private static final String INVALID_PHONE = "+651234";
- private static final String INVALID_ADDRESS = " ";
private static final String INVALID_EMAIL = "example.com";
- private static final String INVALID_TAG = "#friend";
+ private static final String INVALID_PHONE = "+651234";
+ private static final String INVALID_GENDER = " ";
+ private static final String INVALID_AGE = " ";
+ private static final String INVALID_BLOODTYPE = "D";
+ private static final String INVALID_ALLERGY = "#dust";
+ private static final String INVALID_CONDITION = "Fever*";
+ private static final String INVALID_MEDICATION = "Tylenol+";
private static final String VALID_NAME = "Rachel Walker";
- private static final String VALID_PHONE = "123456";
- private static final String VALID_ADDRESS = "123 Main Street #0505";
private static final String VALID_EMAIL = "rachel@example.com";
- private static final String VALID_TAG_1 = "friend";
- private static final String VALID_TAG_2 = "neighbour";
+ private static final String VALID_PHONE = "123456";
+ private static final String VALID_GENDER = "M";
+ private static final int VALID_AGE = 10;
+ private static final String VALID_AGE_STRING = "10";
+ private static final String VALID_BLOODTYPE = "A+";
+ private static final String VALID_ALLERGY_1 = "Nuts";
+ private static final String VALID_ALLERGY_2 = "Coconut";
+ private static final String VALID_CONDITION = "Fever";
+ private static final String VALID_MEDICATION = "Tylenol";
+
private static final String WHITESPACE = " \t\r\n";
@@ -79,6 +96,29 @@ public void parseName_validValueWithWhitespace_returnsTrimmedName() throws Excep
assertEquals(expectedName, ParserUtil.parseName(nameWithWhitespace));
}
+ @Test
+ public void parseEmail_null_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> ParserUtil.parseEmail((String) null));
+ }
+
+ @Test
+ public void parseEmail_invalidValue_throwsParseException() {
+ assertThrows(ParseException.class, () -> ParserUtil.parseEmail(INVALID_EMAIL));
+ }
+
+ @Test
+ public void parseEmail_validValueWithoutWhitespace_returnsEmail() throws Exception {
+ Email expectedEmail = new Email(VALID_EMAIL);
+ assertEquals(expectedEmail, ParserUtil.parseEmail(VALID_EMAIL));
+ }
+
+ @Test
+ public void parseEmail_validValueWithWhitespace_returnsTrimmedEmail() throws Exception {
+ String emailWithWhitespace = WHITESPACE + VALID_EMAIL + WHITESPACE;
+ Email expectedEmail = new Email(VALID_EMAIL);
+ assertEquals(expectedEmail, ParserUtil.parseEmail(emailWithWhitespace));
+ }
+
@Test
public void parsePhone_null_throwsNullPointerException() {
assertThrows(NullPointerException.class, () -> ParserUtil.parsePhone((String) null));
@@ -103,94 +143,186 @@ public void parsePhone_validValueWithWhitespace_returnsTrimmedPhone() throws Exc
}
@Test
- public void parseAddress_null_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> ParserUtil.parseAddress((String) null));
+ public void parseGender_null_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> ParserUtil.parseGender((String) null));
}
@Test
- public void parseAddress_invalidValue_throwsParseException() {
- assertThrows(ParseException.class, () -> ParserUtil.parseAddress(INVALID_ADDRESS));
+ public void parseGender_invalidValue_throwsParseException() {
+ assertThrows(ParseException.class, () -> ParserUtil.parseGender(INVALID_GENDER));
}
@Test
- public void parseAddress_validValueWithoutWhitespace_returnsAddress() throws Exception {
- Address expectedAddress = new Address(VALID_ADDRESS);
- assertEquals(expectedAddress, ParserUtil.parseAddress(VALID_ADDRESS));
+ public void parseGender_validValueWithoutWhitespace_returnsGender() throws Exception {
+ Gender expectedGender = new Gender(VALID_GENDER);
+ assertEquals(expectedGender, ParserUtil.parseGender(VALID_GENDER));
}
@Test
- public void parseAddress_validValueWithWhitespace_returnsTrimmedAddress() throws Exception {
- String addressWithWhitespace = WHITESPACE + VALID_ADDRESS + WHITESPACE;
- Address expectedAddress = new Address(VALID_ADDRESS);
- assertEquals(expectedAddress, ParserUtil.parseAddress(addressWithWhitespace));
+ public void parseGender_validValueWithWhitespace_returnsTrimmedGender() throws Exception {
+ String genderWithWhitespace = WHITESPACE + VALID_GENDER + WHITESPACE;
+ Gender expectedGender = new Gender(VALID_GENDER);
+ assertEquals(expectedGender, ParserUtil.parseGender(genderWithWhitespace));
}
@Test
- public void parseEmail_null_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> ParserUtil.parseEmail((String) null));
+ public void parseAge_null_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> ParserUtil.parseAge((String) null));
}
@Test
- public void parseEmail_invalidValue_throwsParseException() {
- assertThrows(ParseException.class, () -> ParserUtil.parseEmail(INVALID_EMAIL));
+ public void parseAge_invalidValue_throwsParseException() {
+ assertThrows(ParseException.class, () -> ParserUtil.parseAge(INVALID_AGE));
}
@Test
- public void parseEmail_validValueWithoutWhitespace_returnsEmail() throws Exception {
- Email expectedEmail = new Email(VALID_EMAIL);
- assertEquals(expectedEmail, ParserUtil.parseEmail(VALID_EMAIL));
+ public void parseAge_validValueWithoutWhitespace_returnsAge() throws Exception {
+ Age expectedAge = new Age(VALID_AGE);
+ assertEquals(expectedAge, ParserUtil.parseAge(VALID_AGE_STRING));
}
@Test
- public void parseEmail_validValueWithWhitespace_returnsTrimmedEmail() throws Exception {
- String emailWithWhitespace = WHITESPACE + VALID_EMAIL + WHITESPACE;
- Email expectedEmail = new Email(VALID_EMAIL);
- assertEquals(expectedEmail, ParserUtil.parseEmail(emailWithWhitespace));
+ public void parseAge_validValueWithWhitespace_returnsTrimmedAge() throws Exception {
+ String ageWithWhitespace = WHITESPACE + VALID_AGE + WHITESPACE;
+ Age expectedAge = new Age(VALID_AGE);
+ assertEquals(expectedAge, ParserUtil.parseAge(ageWithWhitespace));
}
@Test
- public void parseTag_null_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> ParserUtil.parseTag(null));
+ public void parseBloodType_null_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> ParserUtil.parseBloodType((String) null));
}
@Test
- public void parseTag_invalidValue_throwsParseException() {
- assertThrows(ParseException.class, () -> ParserUtil.parseTag(INVALID_TAG));
+ public void parseBloodType_invalidValue_throwsParseException() {
+ assertThrows(ParseException.class, () -> ParserUtil.parseBloodType(INVALID_BLOODTYPE));
}
@Test
- public void parseTag_validValueWithoutWhitespace_returnsTag() throws Exception {
- Tag expectedTag = new Tag(VALID_TAG_1);
- assertEquals(expectedTag, ParserUtil.parseTag(VALID_TAG_1));
+ public void parseBloodType_validValueWithoutWhitespace_returnsBloodType() throws Exception {
+ BloodType expectedBloodType = new BloodType(VALID_BLOODTYPE);
+ assertEquals(expectedBloodType, ParserUtil.parseBloodType(VALID_BLOODTYPE));
}
@Test
- public void parseTag_validValueWithWhitespace_returnsTrimmedTag() throws Exception {
- String tagWithWhitespace = WHITESPACE + VALID_TAG_1 + WHITESPACE;
- Tag expectedTag = new Tag(VALID_TAG_1);
- assertEquals(expectedTag, ParserUtil.parseTag(tagWithWhitespace));
+ public void parseBloodType_validValueWithWhitespace_returnsTrimmedBloodType() throws Exception {
+ String bloodTypeWithWhitespace = WHITESPACE + VALID_BLOODTYPE + WHITESPACE;
+ BloodType expectedBloodType = new BloodType(VALID_BLOODTYPE);
+ assertEquals(expectedBloodType, ParserUtil.parseBloodType(bloodTypeWithWhitespace));
}
@Test
- public void parseTags_null_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> ParserUtil.parseTags(null));
+ public void parseAllergy_null_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> ParserUtil.parseAllergy(null));
}
@Test
- public void parseTags_collectionWithInvalidTags_throwsParseException() {
- assertThrows(ParseException.class, () -> ParserUtil.parseTags(Arrays.asList(VALID_TAG_1, INVALID_TAG)));
+ public void parseAllergy_invalidValue_throwsParseException() {
+ assertThrows(ParseException.class, () -> ParserUtil.parseAllergy(INVALID_ALLERGY));
}
@Test
- public void parseTags_emptyCollection_returnsEmptySet() throws Exception {
- assertTrue(ParserUtil.parseTags(Collections.emptyList()).isEmpty());
+ public void parseAllergy_validValueWithoutWhitespace_returnsAllergy() throws Exception {
+ Allergy expectedAllergy = new Allergy(VALID_ALLERGY_1);
+ assertEquals(expectedAllergy, ParserUtil.parseAllergy(VALID_ALLERGY_1));
}
@Test
- public void parseTags_collectionWithValidTags_returnsTagSet() throws Exception {
- Set actualTagSet = ParserUtil.parseTags(Arrays.asList(VALID_TAG_1, VALID_TAG_2));
- Set expectedTagSet = new HashSet(Arrays.asList(new Tag(VALID_TAG_1), new Tag(VALID_TAG_2)));
+ public void parseAllergy_validValueWithWhitespace_returnsTrimmedAllergy() throws Exception {
+ String allergyWithWhitespace = WHITESPACE + VALID_ALLERGY_1 + WHITESPACE;
+ Allergy expectedAllergy = new Allergy(VALID_ALLERGY_1);
+ assertEquals(expectedAllergy, ParserUtil.parseAllergy(allergyWithWhitespace));
+ }
- assertEquals(expectedTagSet, actualTagSet);
+ @Test
+ public void parseAllergies_null_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> ParserUtil.parseAllergies(null));
+ }
+
+ @Test
+ public void parseAllergies_collectionWithInvalidAllergies_throwsParseException() {
+ assertThrows(ParseException.class, () -> ParserUtil.parseAllergies(Arrays.asList(VALID_ALLERGY_1,
+ INVALID_ALLERGY)));
+ }
+
+ @Test
+ public void parseAllergies_emptyCollection_returnsEmptySet() throws Exception {
+ assertTrue(ParserUtil.parseAllergies(Collections.emptyList()).isEmpty());
+ }
+
+ @Test
+ public void parseAllergies_collectionWithValidAllergies_returnsAllergySet() throws Exception {
+ Set actualAllergySet = ParserUtil.parseAllergies(Arrays.asList(VALID_ALLERGY_1, VALID_ALLERGY_2));
+ Set expectedAllergySet = new HashSet(Arrays.asList(new Allergy(VALID_ALLERGY_1),
+ new Allergy(VALID_ALLERGY_2)));
+
+ assertEquals(expectedAllergySet, actualAllergySet);
+ }
+ @Test
+ public void parseCondition_validCondition_returnsCondition() throws Exception {
+ Condition expectedCondition = new Condition(VALID_CONDITION);
+ assertEquals(expectedCondition, ParserUtil.parseCondition(VALID_CONDITION));
+ }
+ @Test
+ public void parseCondition_validValueWithWhitespace_returnsTrimmedCondition() throws Exception {
+ String conditionWithWhitespace = WHITESPACE + VALID_CONDITION + WHITESPACE;
+ Condition expectedCondition = new Condition(VALID_CONDITION);
+ assertEquals(expectedCondition, ParserUtil.parseCondition(conditionWithWhitespace));
+ }
+ @Test
+ public void parseCondition_withInvalidCondition_throwsParseException() {
+ assertThrows(ParseException.class, () -> ParserUtil.parseCondition(INVALID_CONDITION));
+ }
+
+ @Test
+ public void parseMedication_validMedication_returnsMedication() throws Exception {
+ Medication expectedMedication = new Medication(VALID_MEDICATION);
+ assertEquals(expectedMedication, ParserUtil.parseMedication(VALID_MEDICATION));
+ }
+ @Test
+ public void parseCondition_withNullCondition_throwsParseException() {
+ assertThrows(NullPointerException.class, () -> ParserUtil.parseCondition(null));
+ }
+ @Test
+ public void parseConditions_listWithValidConditions_returnsConditionsList() throws Exception {
+ List conditionList = ParserUtil.parseConditions(Arrays.asList(VALID_CONDITION));
+ List expectedConditionList = new ArrayList<>(Arrays.asList(new Condition(VALID_CONDITION)));
+
+ assertEquals(expectedConditionList, conditionList);
+ }
+ @Test
+ public void parseConditions_listWithInvalidCondition_throwsParseException() {
+ assertThrows(ParseException.class, () -> ParserUtil.parseConditions(Arrays.asList(VALID_CONDITION,
+ INVALID_CONDITION)));
+ }
+ @Test
+ public void parseMedication_validValueWithWhitespace_returnsTrimmedMedication() throws Exception {
+ String medicationWithWhitespace = WHITESPACE + VALID_MEDICATION + WHITESPACE;
+ Medication expectedMedication = new Medication(VALID_MEDICATION);
+ assertEquals(expectedMedication, ParserUtil.parseMedication(medicationWithWhitespace));
+ }
+
+ @Test
+ public void parseMedication_withInvalidMedication_throwsParseException() {
+ assertThrows(ParseException.class, () -> ParserUtil.parseMedication(INVALID_MEDICATION));
+ }
+
+ @Test
+ public void parseMedication_withNullMedication_throwsParseException() {
+ assertThrows(NullPointerException.class, () -> ParserUtil.parseMedication(null));
+ }
+
+ @Test
+ public void parseMedications_listWithValidMedications_returnsMedicationsList() throws Exception {
+ List medicationList = ParserUtil.parseMedications(Arrays.asList(VALID_MEDICATION));
+ List