diff --git a/.gitignore b/.gitignore index 71c9194e8bd..6d4a9c4b45a 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,5 @@ src/test/data/sandbox/ # MacOS custom attributes files created by Finder .DS_Store docs/_site/ + +/bin diff --git a/LICENSE b/LICENSE index 39b3478982c..c42f359e055 100644 --- a/LICENSE +++ b/LICENSE @@ -2,11 +2,11 @@ MIT License Copyright (c) 2016 Software Engineering Education - FOSS Resources -Permission is hereby granted, free of charge, to any person obtaining a copy +Permission is hereby granted, free of charge, to any patient obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is +copies of the Software, and to permit patients to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all diff --git a/README.md b/README.md index 13f5c77403f..959cbf914d8 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,13 @@ -[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions) +[![CI Status](https://github.com/AY2223S2-CS2103T-T12-2/tp/actions/workflows/gradle.yml/badge.svg)](https://github.com/AY2223S2-CS2103T-T12-2/tp/actions) ![Ui](docs/images/Ui.png) -* This is **a sample project for Software Engineering (SE) students**.
+* This is **a hospital patient management system**.
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. + * Inpro of patients + * Access medical records of patients +* The patient management system (called `MedInfo`) can be used for managing patient records. + * It is to digitise hospital patient management data through a CLI app with a GUI to display patient information, medical records or necessary financial records. + * It is to computerise the Front Office Management of Hospital to develop software which is user friendly, simple, fast, and cost–effective. + * The main function of the system is to register and store patient details and doctor details, retrieve these details as and when required, and also to manipulate these details meaningfully. +* This project is based on the AddressBook-Level3 project created by the [SE-EDU initiative](https://se-education.org). diff --git a/build.gradle b/build.gradle index 108397716bd..373bd48fc73 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ plugins { id 'jacoco' } -mainClassName = 'seedu.address.Main' +mainClassName = 'seedu.medinfo.Main' sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 @@ -23,6 +23,7 @@ checkstyle { test { useJUnitPlatform() finalizedBy jacocoTestReport + enableAssertions = true } task coverage(type: JacocoReport) { @@ -66,7 +67,11 @@ dependencies { } shadowJar { - archiveFileName = 'addressbook.jar' + archiveFileName = 'medinfo.jar' +} + +run { + enableAssertions = true } defaultTasks 'clean', 'test' diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 1c9514e966a..9cb8b045ae8 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -2,58 +2,68 @@ layout: page title: 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` ## Project team -### John Doe +### Navaneeth Ramapurath - + -[[homepage](http://www.comp.nus.edu.sg/~damithch)] -[[github](https://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](https://github.com/nramapurath)] +[[portfolio](team/nramapurath.md)] -* Role: Project Advisor +- Role: Team Lead +- Code Focus Area: Logic, model +- Primary Role: Scheduling and tracking +- Secondary Role: Testing -### Jane Doe +### Jerald Kiew - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/jeraldkiew)] +[[portfolio](team/jeraldkiew.md)] -* Role: Team Lead -* Responsibilities: UI +- Role: Developer +- Code Focus Area: Model +- Primary Role: Documentation +- Secondary Role: Code Quality -### Johnny Doe +### K Sunil Avinash - + -[[github](http://github.com/johndoe)] [[portfolio](team/johndoe.md)] +[[github](http://github.com/ksunil2001)] +[[portfolio](team/ksunil2001.md)] -* Role: Developer -* Responsibilities: Data +- Role: Developer +- Code Focus Area: Storage +- Primary Role: Code Quality +- Secondary Role: Documentation -### Jean Doe +### Nicholas Arlin Halim - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/daytona65)] +[[portfolio](team/daytona65.md)] -* Role: Developer -* Responsibilities: Dev Ops + Threading +- Role: Developer +- Code Focus Area: UI +- Primary Role: Code Integration +- Secondary Role: Testing -### James Doe +### Sun Yitong - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/yitong241)] +[[portfolio](team/yitong241.md)] -* Role: Developer -* Responsibilities: UI +- Role: Developer +- Code Focus Area: Logic +- Primary Role: Deliverables and deadlines +- Secondary Role: Scheduling and tracking diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 46eae8ee565..a505382a352 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -2,64 +2,105 @@ layout: page title: Developer Guide --- -* Table of Contents -{:toc} --------------------------------------------------------------------------------------------------------------------- +**MedInfo** is a desktop application that empowers clinics and hopitals to manage their patients. + +This Developer Guide provides an in-depth documentation on how **MedInfo** is designed and implemented. +It covers the architecture of **MedInfo** and provides detailed descriptions regarding the implementation design. + +You can use this guide to maintain, upgrade, and evolve **MedInfo**. + +## Table of contents + +* [Getting Started](#setting-up-getting-started) +* [Design](#design) + * [Architecture](#architecture) + * [UI](#ui-component) + * [Logic](#logic-component) + * [Model](#model-component) + * [Storage](#storage-component) + * [Common Classes](#common-classes) +* [Implementation](#implementation) + * [Add Patients Feature](#adding-a-patient) + * [Add Wards Feature](#adding-a-ward) + * [Edit Patients Feature](#editing-a-patient) + * [Delete Patients Feature](#deleting-a-patient) + * [Find Patients Feature](#finding-patients) + * [List Patients Feature](#list-all-patients) + * [[Proposed] Undo Redo Feature](#proposed-undoredo-feature) + * [[Proposed] Data Archiving](#proposed-data-archiving) +* [Appendix: Requirements](#appendix-requirements) + * [Target User Profile](#target-user-profile) + * [Value Proposition](#value-proposition) + * [User Stories](#user-stories) + * [Use Cases](#use-cases) + * [Use case: **add a patient**](#use-case-uc01---add-a-patient) + * [Use case: **delete a patient**](#use-case-uc02---delete-a-patient) + * [Use case: **edit a patient**](#use-case-uc03---edit-a-patient) + * [Use case: **find a patient**](#use-case-uc04---find-a-patient) + * [Use case: **clear all patients**](#use-case-uc05---clear-all-patients) + * [Use case: **add a ward**](#use-case-uc06---add-a-new-ward) + * [Use case: **delete a ward**](#use-case-uc07---delete-a-ward) + * [Use case: **sort patients**](#use-case-uc08---sort-patients) + * [Non-Functional Requirement](#non-functional-requirements) + * [Glossary](#glossary) +* [Appendix: Instructions for Manual Testing ](#appendix-instructions-for-manual-testing) + * [**Launch and shutdown**](#launch-and-shutdown) + * [**Delete a patient**](#delete-a-patient) + * [**Save data**](#save-data) +* [Appendix: Planned Enhancements](#appendix-planned-enhancements) -## **Acknowledgements** -* {list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well} - --------------------------------------------------------------------------------------------------------------------- +--- -## **Setting up, getting started** +## Setting up, getting started Refer to the guide [_Setting up and getting started_](SettingUp.md). --------------------------------------------------------------------------------------------------------------------- +--- -## **Design** +## Design
:bulb: **Tip:** The `.puml` files used to create diagrams in this document can be found in the [diagrams](https://github.com/se-edu/addressbook-level3/tree/master/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*** given above explains the high-level design of the App. +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. **Main components of the architecture** **`Main`** has two classes called [`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). It is responsible for, -* At app launch: Initializes the components in the correct sequence, and connects them up with each other. -* At shut down: Shuts down the components and invokes cleanup methods where necessary. + +- At app launch: Initializes the components in the correct sequence, and connects them up with each other. +- At shut down: Shuts down the components and invokes cleanup methods where necessary. [**`Commons`**](#common-classes) represents a collection of classes used by multiple other components. The rest of the App consists of four components. -* [**`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. - +- [**`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. **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`. +The _Sequence Diagram_ below shows how the components interact with each other for the scenario where the user issues the command `delete 1`. Each of the four main components (also shown in the diagram above), -* 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. +- 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.) 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. @@ -79,10 +120,10 @@ The `UI` component uses the JavaFx UI framework. The layout of these UI parts ar The `UI` component, -* 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`. +- 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 @@ -93,9 +134,10 @@ Here's a (partial) class diagram of the `Logic` component: How the `Logic` component works: -1. When `Logic` is called upon to execute a command, it uses the `AddressBookParser` class to parse the user command. + +1. When `Logic` is called upon to execute a command, it uses the `MedInfoParser` class to parse the user command. 1. This results in a `Command` object (more precisely, an object of one of its subclasses e.g., `AddCommand`) which is executed by the `LogicManager`. -1. The command can communicate with the `Model` when it is executed (e.g. to add a person). +1. The command can communicate with the `Model` when it is executed (e.g. to add a patient). 1. The result of the command execution is encapsulated as a `CommandResult` object which is returned back from `Logic`. The Sequence Diagram below illustrates the interactions within the `Logic` component for the `execute("delete 1")` API call. @@ -110,27 +152,22 @@ Here are the other classes in `Logic` (omitted from the class diagram above) tha 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. + +- When called upon to parse a user command, the `MedInfoParser` 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 `MedInfoParser` 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) - The `Model` component, -* 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) - -
: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.
- - - -
+- 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) ### Storage component @@ -140,190 +177,486 @@ The `Model` component, 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`) + +- can save both address book data and user preference data in json format, and read them back into corresponding objects. +- inherits from both `MedInfoStorage` 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`) ### Common classes -Classes used by multiple components are in the `seedu.addressbook.commons` package. +Classes used by multiple components are in the `seedu.medinfo.commons` package. --------------------------------------------------------------------------------------------------------------------- +--- ## **Implementation** -This section describes some noteworthy details on how certain features are implemented. +This section describes some noteworthy details on how certain features are implemented in MedInfo. -### \[Proposed\] Undo/redo feature +### Adding a patient +In MedInfo, a user can add a patient using the `add` command. +#### Implementation +- The `add` command takes in 2 compulsory fields (name and NRIC) and 1 optional field (status) +- It is supported by the `AddCommandParser` that extracts the relevant fields from the entered command. -#### Proposed Implementation +The following activity diagram summarizes what happens when a user enters an `add` command: -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: +![AddActivityDiagram](images/AddActivityDiagram.png) -* `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. +Details: +- The user enters an `add` command with the name and NRIC specified. +- If the user entered a `Status` (prefixed by `s/`), the patient created will have that status. +- The created patient is added to the model. -These operations are exposed in the `Model` interface as `Model#commitAddressBook()`, `Model#undoAddressBook()` and `Model#redoAddressBook()` respectively. -Given below is an example usage scenario and how the undo/redo mechanism behaves at each step. +### Adding a ward +In MedInfo, a user can add a patient using the `addward` command. +#### Implementation +- The `add` command takes in 1 compulsory field (ward name) and 1 optional field (capacity) +- It is supported by the `AddWardCommandParser` that extracts the relevant fields from the entered command. -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. +The following activity diagram summarizes what happens when a user enters an `addward` command: -![UndoRedoState0](images/UndoRedoState0.png) +![AddWardActivityDiagram](images/AddWardActivityDiagram.png) -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. +Details: +- The user enters an `addward` command with the ward name specified. +- If the user entered a `Capacity` (prefixed by `c/`), the ward created will have that capacity. +- The created ward is added to the model. -![UndoRedoState1](images/UndoRedoState1.png) -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`. +### Editing a patient +In MedInfo, a user can edit a patient using the `edit` command. +#### Implementation +- The `edit` command takes in 1 compulsory argument (index) and up to 3 optional fields (status, ward and discharge date). +- It is supported by the `EditCommandParser` that extracts the relevant fields from the entered command. -![UndoRedoState2](images/UndoRedoState2.png) +The following activity diagram summarizes what happens when a user enters an `edit` command: -
: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`. +![EditActivityDiagram](images/EditActivityDiagram.png) -
+Details: +- The index is based on the last displayed list of patients. This design choice was made as: + - A user would most likely perform a `find` or `list` operation to confirm the patient to edit. + - Finding patient by NRIC would be too cumbersome for the user. +- Name and NRIC are not editable as these are identifying fields of a patient. -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. -![UndoRedoState3](images/UndoRedoState3.png) +### Deleting a patient +In MedInfo, a user can edit a patient using the `delete` command. +#### Implementation +- The `delete` command takes in 1 compulsory argument (index). +- It is supported by the `DeleteCommandParser` that extracts the index from the entered command. -
: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 activity diagram summarizes what happens when a user enters a `delete` command: -
+![DeleteActivityDiagram](images/DeleteActivityDiagram.png) -The following sequence diagram shows how the undo operation works: +Details: +- When a user executes this command, they are greeted with an alert window to confirm deletion. + - Clicking 'OK' at this point will let MedInfo proceed with the deletion. + - Clicking 'Cancel' or closing the window will abort the deletion and trigger a `list` operation. +- Aborting a deletion leads to a `list` operation so that the user can view all patients. +- The index is based on the last displayed list of patients. This design choice was made as: + - A user would most likely perform a `find` or `list` operation to confirm the patient to delete. + - Finding patient by NRIC would be too cumbersome for the user. -![UndoSequenceDiagram](images/UndoSequenceDiagram.png) -
: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. +### Finding patients +In MedInfo, a user can find patients matching certain conditions using the `find` command. +#### Implementation +- The `find` command takes in 1 compulsory field (one of either name, NRIC or status). +- It is supported by the `FindCommandParser` which extracts one of the possible fields: + - Name entered in the command (prefixed by `name/`) + - NRIC entered in the command (prefixed by `nric/`) + - Status entered in the command (prefixed by `s/`) +- If the user enters multiple fields, MedInfo highlights the error to the user. -
+The following activity diagram summarizes what happens when a user enters a `find` command: -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. +![FindActivityDiagram](images/FindActivityDiagram.png) -
: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. -
+### List all patients +In MedInfo, a user can list all patients using the `list` command. +#### Implementation +- The `list` command does not take in any arguments. -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. +The following activity diagram summarizes what happens when a user enters a `find` command: -![UndoRedoState4](images/UndoRedoState4.png) +![ListActivityDiagram](images/ListActivityDiagram.png) -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. +### \[Proposed\] Data archiving -![UndoRedoState5](images/UndoRedoState5.png) +_{Explain here how the data archiving feature will be implemented}_ -The following activity diagram summarizes what happens when a user executes a new command: +--- - +## **Documentation, logging, testing, configuration, dev-ops** -#### Design considerations: +- [Documentation guide](Documentation.md) +- [Testing guide](Testing.md) +- [Logging guide](Logging.md) +- [Configuration guide](Configuration.md) +- [DevOps guide](DevOps.md) -**Aspect: How undo & redo executes:** +--- -* **Alternative 1 (current choice):** Saves the entire address book. - * Pros: Easy to implement. - * Cons: May have performance issues in terms of memory usage. +## **Appendix: Requirements** -* **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. +### Target user profile -_{more aspects and alternatives to be added}_ +- has a need to manage a significant number of patients coming into an A&E in a private hospital +- can type fast +- prefers typing to execute commands and allocating patients by their status to their respective wards +- is reasonably comfortable using CLI apps -### \[Proposed\] Data archiving +### Value proposition -_{Explain here how the data archiving feature will be implemented}_ +- solve the problem of slow and multiple step process of allocating patients to their respective wards in an A&E +- provide faster access to a particular patient’s details for hospital admin staff +- provide a more efficient process of managing patients by their status to cater to patients who are in urgent need of care, which in turn provides a better quality of service +- provide a simple system to keep track and update a patient's discharge date +### User stories --------------------------------------------------------------------------------------------------------------------- +Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unlikely to have) - `*` -## **Documentation, logging, testing, configuration, dev-ops** +| Priority | As a …​ | I want to …​ | So that I can…​ | +|----------|--------------|----------------------------------------------------|---------------------------------------------------------------------------------------------| +| `* * *` | staff member | add a patient | record more information later | +| `* * *` | staff member | add a ward | assign patients to the ward later | +| `* * *` | staff member | add personal particulars to a patient | record their name and NRIC | +| `* * *` | staff member | add current status of patient | keep track of patient status | +| `* * *` | staff member | add current ward of patient | keep track of patient location | +| `* * *` | staff member | add a discharge date to hospitalised patients | keep track of discharge dates | +| `* * *` | staff member | view the list of commands | use the system proficiently | +| `* * *` | staff member | search for patients by personal particulars | do a basic search to find a certain patient | +| `* * *` | staff member | search for patients by discharge date | plan for future patient discharges | +| `* * *` | staff member | update personal particulars of a patient | keep the personal particulars of a patient up to date | +| `* * *` | staff member | update patient ward | move patients to different wards | +| `* * *` | staff member | update the status of a patient | keep the status of a patient up to date | +| `* * *` | staff member | update the discharge date of hospitalised patients | change the discharge date of a hospitalised patient due to unexpected medical complications | +| `* * *` | staff member | remove a patient | put the patient on hold or remove duplicate entries | +| `* * *` | staff member | remove the discharge date of a patient | put the patient's discharge date on hold while awaiting further assessment | +| `* *` | staff member | view the list of patients sorted by discharge date | plan for future patient discharges | +| `* *` | staff member | see patients with approaching discharge dates | be aware of approaching discharge patients | +| `* *` | staff member | view patients by ward | find patients in a ward and check the occupancy within certain wards | +| `* *` | staff member | view patients by status | address higher priority patients more quickly | +| `* *` | staff member | view list of wards | see current wards in system at a glance | +| `* *` | staff member | view list of patients | see current patients in system at a glance | +| `* *` | staff member | update the name of a ward | keep the name of a ward up to date | +| `* *` | staff member | update the capacity of a ward | keep the maximum number of patients in a ward up to date | +| `* *` | staff member | view the current total occupancy | see the total number of patients currently admitted | +| `* *` | staff member | view the total capacity of a ward | see the maximum number of patients I can admit to this ward | +| `* *` | staff member | view the current occupancy of a ward | see the number of patients currently admitted to a ward, and to know if a ward is full | +| `* *` | staff member | confirm a deletion of a patient | avoid accidentally deleting a patient, which cannot be undone | -* [Documentation guide](Documentation.md) -* [Testing guide](Testing.md) -* [Logging guide](Logging.md) -* [Configuration guide](Configuration.md) -* [DevOps guide](DevOps.md) --------------------------------------------------------------------------------------------------------------------- +### Use cases -## **Appendix: Requirements** +(For all use cases below, the **System** is the `MedInfo` and the **Actor** is the `user`, unless specified otherwise) -### Product scope +#### Use case: UC01 - Add a patient -**Target user profile**: +**MSS** -* 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 +1. User requests to add a new patient to MedInfo. +2. The user enters patient details. + 1. The following are required information: + - Name + - NRIC + 2. The following are non-required information: + - Status + - Ward +3. The system adds the user into the MedInfo system. +4. The system shows the new created user in the patient list. -**Value proposition**: manage contacts faster than a typical mouse/GUI driven app + Use case ends. +**Extensions** -### User stories +- 2a. If any of the required fields are not completed. -Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unlikely to have) - `*` + - 2a1. the user is informed of this and show the correct format for the command -| 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 | +Use case resumes at step 2. -*{More to be added}* +- 2b. If the entered NRIC is already present in another record in the system. + + - 2b1. the user is informed that the NRIC is already present in the system. + + Use case resumes at step 2. + +- 2c. If the input field is invalid. + + - 2c1. the user is informed of this, and correct format for the command is displayed. + + Use case resumes at step 2. -### Use cases -(For all use cases below, the **System** is the `AddressBook` and the **Actor** is the `user`, unless specified otherwise) +- 2d. If the entered ward is not present in the system. -**Use case: Delete a person** + - 2d1. the user is informed that the ward does not exist in the system. + + Use case resumes at step 2. + + +#### Use case: UC02 - Delete a patient + +**MSS** + +1. User requests to list filtered patients +2. MedInfo shows a list of filtered patients +3. User requests to delete a specific patient in the list by index number +4. MedInfo shows confirmation window +5. MedInfo deletes the patient + + Use case ends. + +**Extensions** + +- 2a. The list is empty. + + Use case ends. + +- 3a. The requested patient's index number is invalid. + + - 3a1. MedInfo shows an error message. + + Use case resumes at step 2. + +- 4a. The user cancels the deletion in the confirmation window + + - 4a1. MedInfo shows the patient list + + - 4b1. MedInfo shows an error message. + + Use case resumes at step 2. + +#### Use case: UC03 - Edit a patient **MSS** -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 +1. User requests to list filtered patients +2. MedInfo shows a list of filtered patients +3. User requests to edit a specific patient in the list by index number + 1. The following can be edited: + - Status + - Ward + - Discharge Date + 2. The following cannot be edited: + - Name + - NRIC +4. MedInfo edits the patient Use case ends. **Extensions** -* 2a. The list is empty. +- 2a. The list is empty. Use case ends. -* 3a. The given index is invalid. +- 3a. The requested patient's index number is invalid. + + - 3a1. MedInfo shows an error message. + + Use case resumes at step 2. - * 3a1. AddressBook shows an error message. +- 3b. The prefixes entered are invalid. + + - 3b1. MedInfo shows an error message. Use case resumes at step 2. -*{More to be added}* +- 3b. The Status entered is invalid. + + - 3b1. MedInfo shows an error message. + + Use case resumes at step 2. + +- 3b. The Ward entered is invalid. + + - 3b1. MedInfo shows an error message. + + Use case resumes at step 2. + +- 3b. The Discharge Date entered is invalid. + + - 3b1. MedInfo shows an error message. + + Use case resumes at step 2. + +- 3d. User tries to edit a non-editable field (Name/NRIC). + + - 3d1. MedInfo shows an error message. + + Use case resumes at step 2. + + +#### Use case: UC04 - Find a patient + +**MSS** + +1. User requests to find specific patients by either NRIC or Name or Status or Ward +2. MedInfo shows a list of patients + + Use case ends. + +**Extensions** + +- 1a. The requested patient's NRIC does not exist in the system. + + - 1a1. MedInfo does not list any patients. + + Use case ends. + +- 1b. The requested patient's Name does not exist in the system. + + - 1b1. MedInfo does not list any patients. + + Use case ends. + +- 1c. The requested patient's Status does not exist in the system. + + - 1c1. MedInfo does not list any patients. + + Use case ends. + +- 1d. The requested patient's Ward does not exist in the system. + +- 1d1. MedInfo does not list any patients. + + Use case ends. + +#### Use case: UC05 - Clear all patients + +**MSS** + +1. User requests to clear all the patient records in the system +2. MedInfo shows confirmation window +3. MedInfo deletes all patients in the system + + Use case ends. + +- 3a. The user cancels the deletion in the confirmation window + - 3a1. MedInfo shows the patient list + + +#### Use case: UC06 - Add a new ward + +**MSS** + +1. User requests to add a new ward to MedInfo. +2. The user enters ward details. + 1. The following are required information: + - Name + 2. The following are non-required information: + - Capacity +3. The system adds the ward into the MedInfo system. +4. The system shows the new created ward in the ward list. + + Use case ends. + +**Extensions** +* 2a. If any of the required fields are not completed. + + * 2a1. the user is informed of this and show the correct format for the command + +Use case resumes at step 2. + +* 2b. If the entered Name is already present in another record in the system. + + * 2b1. the user is informed that the Name is already present in the system. + + Use case resumes at step 2. + +* 2c. If the input field is invalid. + + * 2c1. the user is informed of this, and correct format for the command is displayed. + + Use case resumes at step 2. + + +#### Use case: UC07 - Delete a ward + +**MSS** + +1. User requests to list wards +2. MedInfo shows a list of wards +3. User requests to delete a specific ward in the list by index number +4. MedInfo shows confirmation window +5. MedInfo deletes the patient + + Use case ends. + +**Extensions** + +- 2a. The ward list is empty. + + Use case ends. + +- 3a. The requested ward's index number is invalid. + + - 3a1. MedInfo shows an error message. + + Use case resumes at step 2. + +- 3b. The requested ward has patients inside + + - 3b1. MedInfo shows an error message. + + Use case resumes at step 2. + +- 4a. The user cancels the deletion in the confirmation window + - 4a1. MedInfo shows the patient list + + Use case resumes at step 2 + +#### Use case: UC08 - Sort Patients + +**MSS** + +1. User requests to list patients +2. MedInfo shows a list of patients +3. User requests to sort all the patients by a specific order +4. MedInfo sorts all the patients +5. MedInfo displays the sorted list + + Use case ends + +**Extensions** + +- 2. The patient list is empty + + Use case ends. +- 3a. The requested sorting order is invalid + - 3a1. MedInfo shows an error message. + + Use case resumes at step 2. + +- 3b. The requested sorting field is invalid + - 3b1. MedInfo shows an error message. + + Use case resumes at step 2. ### 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. +2. Should be able to hold up to 1000 patients 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. - -*{More to be added}* +4. Should mask the NRIC of patients when listing (e.g. XXXXX264G). +5. The product is not required to validate the medical records. ### Glossary -* **Mainstream OS**: Windows, Linux, Unix, OS-X -* **Private contact detail**: A contact detail that is not meant to be shared with others +- **Mainstream OS**: Windows, Linux, Unix, OS-X +- **CLI**: Command Line Interface +- **Use case**: It describes an interaction between the user and the system for a specific functionality of the system. +- **Priority Level**: Level of seriousness of a patient's health condition (e.g. `Stable`, `Mild`, `Severe`) +- **Medical Record**: Blood type, allergies, medical cases and history of medical conditions --------------------------------------------------------------------------------------------------------------------- +--- ## **Appendix: Instructions for manual testing** @@ -340,38 +673,114 @@ testers are expected to do more *exploratory* testing. 1. Download the jar file and copy into an empty folder - 1. Double-click the jar file Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum. + 2. Double-click the jar file Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum. -1. Saving window preferences +2. Saving window preferences 1. Resize the window to an optimum size. Move the window to a different location. Close the window. - 1. Re-launch the app by double-clicking the jar file.
- Expected: The most recent window size and location is retained. + 2. Re-launch the app by double-clicking the jar file.
+ Expected: The most recent window size and location is retained. -1. _{ more test cases …​ }_ -### Deleting a person +### Delete a patient -1. Deleting a person while all persons are being shown +1. Deleting a patient while all patients are being shown - 1. Prerequisites: List all persons using the `list` command. Multiple persons in the list. + 1. Prerequisites: List all patients using the `list` command. Multiple patients in the list. - 1. Test case: `delete 1`
+ 2. 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. - 1. Test case: `delete 0`
- Expected: No person is deleted. Error details shown in the status message. Status bar remains the same. + 3. Test case: `delete 0`
+ Expected: No patient is deleted. Error details shown in the status message. Status bar remains the same. - 1. Other incorrect delete commands to try: `delete`, `delete x`, `...` (where x is larger than the list size)
+ 4. Other incorrect delete commands to try: `delete`, `delete x`, `...` (where x is larger than the list size)
Expected: Similar to previous. -1. _{ more test cases …​ }_ -### Saving data +### Save data -1. Dealing with missing/corrupted data files +1. Dealing with missing data files + 1. MedInfo will display sample data if data files cannot be found in the data directory. - 1. _{explain how to simulate a missing/corrupted file, and the expected behavior}_ +2. Dealing with corrupted data files + 1. MedInfo will clear the current data if the data files are corrupted or modified invalidly. + + +--- -1. _{ more test cases …​ }_ +## **Appendix: Planned Enhancements** + +Given below are a few enhancements we hope to bring into future iterations of MedInfo: + +### Date-time validation: +Currently, MedInfo allows the user to input dates in the past (e.g.`01/01/1000 1000`). While there may be reason to +input past date-times, such as if the user forgot to enter dates previously, MedInfo is meant to be a **current** +patient tracking system. As such, inputting dates from the past would not make sense as the patients would have been +discharged already (and hence have no reason to be recorded in MedInfo). +Also, MedInfo currently allows entering time in non HHmm format (e.g. '210.4'). This shall be looked into while +improving validation overall. + +Possible Implementation: +- Future validation could be implemented by adding a method in `Discharge.java` to check if a given date is a valid +future discharge date (by comparing to the current date) +- The method created above would then be called within `parseDischarge()` in `ParserUtil.java` to ensure that a +valid future discharge date-time was entered + +### Strict NRIC validation: +Currently, MedInfo checks whether a valid NRIC has been entered based on a validaton regex within +`Nric.java` (`^[STFG]\d{7}[A-Z]$`). This regex restricts the NRIC to be a capital letter (either +S/F/T/G) followed by 7 numbers and a capital letter. True valid NRICs make use of stronger validation +logic, involving check digits. Such validation would need to be added before using MedInfo in a +real-life scenario. However, due to the constraints it would place on testing during development, +it shall be implemented in the future instead. + +### UI component behaviour on window resizing +On resizing, the status bar maintains its centered location as from the full-size window. As a result, +the information in it gets truncated. As this information is critical, future enhancements should address this, and +other UI components that get cut off on window resizing. + +Possible implementations: +- adjusting padding +- setting position constraint +- setting a higher minimum window width + +### Multiple parameter search +Currently, MedInfo only allows finding patients by **onw** of four parameters: +1. name (`name/`) +2. NRIC (`nric/`) +3. status (`s/`) +4. ward name (`w/`) + +The `find` command does not allow multiple parameters to be used at once. For example, attempting to find critical +patients in ward 'ER1' with the command `find s/red w/ER1` would result in an error. However, as a hospital +staff, finding patients matching multiple criteria is a valid use-case. Hence, this is planned as a future enhancement. + +### Find by ward +Currently, the `find` command in MedInfo finds patients by wards as it does patients by name, i.e. by displaying +all patients that match any of the keywords supplied. As a result, the logic is slightly flawed. Consider the +scenario of trying to find patients in a ward named 'Class A', when the other wards in the system include 'Class B' +and 'Class C'. Entering `find w/Class A` would also display patients in the other 2 wards due to the common keyword +'Class'. This should be addressed in future iterations of MedInfo by making the `find` command match ward name +exactly. + + +### Handling long names +Currently, long names are truncated (with `...`) once they go past the maximum displayable length. This makes it +impossible to get long patient names after entry into the system. This would need to be addressed in the future. + +Possible implementation: +- limit name input to 40 characters: While simple to implement (change validation regex in `Name.java` to +`[\\p{Alnum}][\\p{Alnum} ]{0,39}`, it is not recommended, as it does not handle the case of patients with names +longer than 40 characters +- text wrapping to display the whole name within the patient card in the UI + +### Expand `help` command +Currently, entering the `help` command in MedInfo opens a small dialog box with a link to MedInfo's website. While +the user can read the user guide from the website to learn how to use the commands, perhaps a quick command summary +could be added to this window for returning users who do not wish to open the website but just need a quick refresher +on the commands. + +Possible implementation: +- add command summary in text to help window display diff --git a/docs/SettingUp.md b/docs/SettingUp.md index 275445bd551..787ab0c455c 100644 --- a/docs/SettingUp.md +++ b/docs/SettingUp.md @@ -23,7 +23,7 @@ If you plan to use Intellij IDEA (highly recommended): 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. **Verify the setup**: - 1. Run the `seedu.address.Main` and try a few commands. + 1. Run the `seedu.medinfo.Main` and try a few commands. 1. [Run the tests](Testing.md) to ensure they all pass. -------------------------------------------------------------------------------------------------------------------- diff --git a/docs/Testing.md b/docs/Testing.md index 8a99e82438a..d1d455a3b96 100644 --- a/docs/Testing.md +++ b/docs/Testing.md @@ -29,8 +29,8 @@ There are two ways to run tests. This project has three types of tests: 1. *Unit tests* targeting the lowest level methods/classes.
- e.g. `seedu.address.commons.StringUtilTest` + e.g. `seedu.medinfo.commons.StringUtilTest` 1. *Integration tests* that are checking the integration of multiple code units (those code units are assumed to be working).
- e.g. `seedu.address.storage.StorageManagerTest` + e.g. `seedu.medinfo.storage.StorageManagerTest` 1. Hybrids of unit and integration tests. These test are checking multiple code units as well as how the are connected together.
- e.g. `seedu.address.logic.LogicManagerTest` + e.g. `seedu.medinfo.logic.LogicManagerTest` diff --git a/docs/UserGuide.md b/docs/UserGuide.md index e7df68b01ea..8ac35365aea 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -3,191 +3,472 @@ layout: page title: User Guide --- -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. +## Introduction +Welcome to the MedInfo User Guide! +MedInfo is a desktop application for **private hospital administrative staff** to +help manage patients and wards. + +This User Guide will guide you through installing the app, getting familiarised with its features and using it. +If this is your first time using this Guide, you may refer to this segment +on [how to use our User Guide](#how-to-use-the-user-guide) + +## Table of Contents * Table of Contents {:toc} --------------------------------------------------------------------------------------------------------------------- +--- +## About MedInfo +MedInfo is a simple application that will help you manage your patients' **statuses, wards, +discharge dates**, and ward **occupancies**.
+
+Considering the time-critical nature of hospital services, patient and ward management are +of utmost priority, where the smallest delays or lapses in updating information can affect +how your hospital attends to your patients. **MedInfo** was designed with this in mind. +It has simplified and optimized patient and ward management, allowing you to in-process, update, and move patients +around while still being able to view overall stats of the hospital at a glance.
+
+[Back to Table of Contents](#table-of-contents) + +--- -## Quick start +## Getting Started +Below, you'll find everything you need to install and set up MedInfo. -1. Ensure you have Java `11` or above installed in your Computer. +### System Requirements +For the best possible experience, we recommend that you use MedInfo on one of the following operating systems: +- Windows +- macOS +- Linux -1. Download the latest `addressbook.jar` from [here](https://github.com/se-edu/addressbook-level3/releases). +To run MedInfo, you will need to have Java 11 or above installed on your system. If you don't, you can find the +appropriate version for your system [here](https://www.oracle.com/java/technologies/downloads/). -1. Copy the file to the folder you want to use as the _home folder_ for your AddressBook. +### Quick Start +1. Download the latest `medinfo.jar` from [here](https://github.com/AY2223S2-CS2103T-T12-2/tp/releases). -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.
- ![Ui](images/Ui.png) +2. Copy the file to the folder you want to use as the [_home folder_](#glossary) for your MedInfo. -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.
+3. Run the application by + 2. Double-clicking the [`.jar`](#glossary) file or + 3. Opening a [command terminal](#glossary), `cd` into the folder you put the `.jar` file in, and use the `java -jar medinfo.jar` +command to run the application.
+4. A [GUI](#glossary) similar to the one below should appear in a few seconds. If you are starting the app for the first time, the +app will contain some sample data for you to try commands.
+ ![Ui](images/Ui.png) + +5. Type the command in the command input 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: - * `list` : Lists all contacts. + - `list` : Lists all patients. + + - `add nric/S1234567A name/John Doe` : Adds a patient named `John Doe` to MedInfo. + + - `delete 1` : Deletes the first patient on the currently displayed list. + + - `clear` : Clears all patient and ward data. + + - `exit` : Exits the app. - * `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. +7. Refer to the [Features](#features) section below for details on each command. + +[Back to Table of Contents](#table-of-contents) + +--- +## User Interface Overview + +![UiOverview](images/UiOverview.png) + +The main GUI has 7 sections as highlighted above. +- **_Menu Bar_**: File and Help buttons to exit or get help. +- **_Command Input_**: Key in commands here and press Enter to execute. +- **_Result Display_**: Responses from the application including error and success messages appear here. +- **_Adjustable Panels_**: Click and drag on the purple highlighted areas to customize the panel sizes to your preference. +- **_Patient List_**: List of patients you have. +- **_Ward List_**: List of wards you have. +- **_Save Location and Stats_**: Status bar which displays the location of data storage and some statistics of your hospital. + +[Back to Table of Contents](#table-of-contents) + +--- +## How to use the User Guide - * `delete 3` : Deletes the 3rd contact shown in the current list. +### Navigation +This Guide contains detailed explanations on the **commands** available and what they do. If you wish to navigate to +any section within this Guide, the [**Table of Contents**](#table-of-contents) above provides a quick way to do so. +Each section in the User Guide comes with a [Back to Table of Contents](#table-of-contents) link in the footer to +take you back to the Table of Contents. - * `clear` : Deletes all contacts. +If you know what you're looking for, press Ctrl + F to search anywhere within this Guide +for a keyword. - * `exit` : Exits the app. +### Symbols used -1. Refer to the [Features](#features) below for details of each command. +| Symbol | Meaning | +|----------------------|-------------------------------------------------------------| +| :information_source: | General information/notes. | +| :bulb: | Tips that will optimize your usage of MedInfo. | +| :exclamation: | Information that is crucial to know before using a command. | --------------------------------------------------------------------------------------------------------------------- +--- ## Features +The section below describes the commands available in MedInfo. The commands fall under 3 categories: +- [Patient Features](#patient-features) +- [Ward Features](#ward-features) +- [Utility Features](#utility-features) + +Our commands follow a certain format for ease of use. If you are new to MedInfo, do take some time to familiarise +yourself with the command format from the notes below. +
**:information_source: 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`. +- Words in `UPPER_CASE` are the parameters to be supplied by you.
+ e.g. in `add nric/NRIC name/NAME`, `NRIC` and `NAME` are parameters which can be used as `add nric/S1234567A name/John Doe`. -* 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`. +- Items in square brackets are optional.
+ e.g `name/NAME [s/STATUS]` can be used as `name/John Doe s/GREEN` or as `name/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. +[//]: # 'Might be used in future features' +[//]: # '- 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.' -* 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. +- Parameters can be in any order.
+ e.g. if the command specifies `name/NAME s/STATUS`, `s/STATUS name/NAME` is also acceptable. -* If a parameter is expected only once in the command but you specified it multiple times, only the last occurrence of the parameter will be taken.
- e.g. if you specify `p/12341234 p/56785678`, only `p/56785678` will be taken. +- If a parameter is expected only once in the command, but you specified it multiple times, only the last occurrence of + the parameter will be taken.
+ e.g. if you specify `nric/S1234567X nric/S1234567A`, only `nric/S1234567A` will be taken. -* Extraneous parameters for commands that do not take in parameters (such as `help`, `list`, `exit` and `clear`) will be ignored.
+- Commands that do not take in parameters (such as `help`, `list`, and `exit`) will ignore any parameters supplied + along with it.
e.g. if the command specifies `help 123`, it will be interpreted as `help`.
-### Viewing help : `help` +## Patient Features +### Adding a patient to the system: `add` -Shows a message explaning how to access the help page. +Adds the patient (NRIC, name and status). -![help message](images/helpMessage.png) +Format: `add nric/NRIC name/NAME [s/STATUS]` -Format: `help` +
:bulb: **Tip:** +The first and last letters in NRIC must be capitalised. +
+
-### Adding a person: `add` +**:information_source: Notes about `add`:**
+- The default status is set to `GRAY`. +- The default ward is set to `Waiting Room`. +- No discharge date is added by default. +
-Adds a person to the address book. +Examples: -Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…​` +- `add nric/S1234567A name/John Doe s/RED` -
:bulb: **Tip:** -A person can have any number of tags (including 0) -
+[Back to Table of Contents](#table-of-contents) -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` +### Listing all patients in the system: `list` -Shows a list of all persons in the address book. +Shows a list of all patients with their details in the system. Format: `list` -### Editing a person : `edit` +[Back to Table of Contents](#table-of-contents) + + +### Editing a patient’s details in the system: `edit` + +Edit an existing patient’s status or ward or discharge date-time. -Edits an existing person in the address book. +Format: `edit INDEX [s/STATUS] [w/WARD] [d/DISCHARGE]` -Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…​` +- Edits the patient's details at the specified index as of the currently displayed list. +- The status of a patient is either `GRAY` or `GREEN` or `YELLOW` or `RED`. +- The ward allocated to a patient is represented as an alphanumeric string. E.g `A01`. +- The discharge date-time is of the `dd/MM/yyyy HHmm` format. E.g `12/03/2023 1200` is interpreted as 12th March 2023 1200hrs. -* 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. +Examples: + +- `edit 1 s/GREEN` Edits the status of the first currently displayed patient to be `GREEN`. +- `edit 5 w/A01` Edits the ward of the fifth currently displayed patient to be `A01`. +- `edit 4 d/27/07/2023 1600` Edits the discharge date-time of the fourth currently displayed patient to be `27/07/2023 1600` which is read as 27th July 2023 1600hrs. + +[Back to Table of Contents](#table-of-contents) + + +### Sorting all patients in the system: `sort` + +Sorts all the patients with the specified field and order in the system. + +The fields that you can sort by include: +- patient name (`name/`) +- status (`s/`) +- discharge date (`d/`) +- ward name (`w/`) + +You can sort in one of two orders: +- ascending (`asc`) +- descending (`desc`) + +Format: `sort FIELD/ORDER` + +
+ +**:information_source: Notes about `sort`:**
+- You can only sort by one field in a command. +- When sorting by status (in ascending order), the order is `GRAY`, `GREEN`, `YELLOW`, then `RED`. The `GRAY` status is given the lowest priority as the patient's condition is unknown, while the `RED` status is given the highest priority due to the patient's critical condition. +
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` +- `sort s/asc` -Finds persons whose names contain any of the given keywords. +[Back to Table of Contents](#table-of-contents) -Format: `find KEYWORD [MORE_KEYWORDS]` -* 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). +### Finding patients by name in the system: `find` + +Shows a list of all patients with their details that match input name or NRIC. + +Format: `find name/NAME`, `find nric/NRIC`, `find s/STATUS`, `find w/WARD` + +- 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` +- Either only the name or only the NRIC 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` + +
:bulb: **Tip:** +Using `find` with a status acts as a filter on that status. +
+ Examples: -* `find John` returns `john` and `John Doe` -* `find alex david` returns `Alex Yeoh`, `David Li`
- ![result for 'find alex david'](images/findAlexDavidResult.png) -### Deleting a person : `delete` +- `find name/John` returns `john` and `John Smith` +- `find name/john carlos` returns `John Smith`, `Carlos Lopez`
+ ![result for 'find alex david'](images/findJohnCarlosResult.png) + +[Back to Table of Contents](#table-of-contents) -Deletes the specified person from the address book. + +### Deleting a patient from the system: `delete` + +
:exclamation: **Caution:** +Once deleted, a patient cannot be recovered. +
+ +Deletes a patient by index. Format: `delete INDEX` -* 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, …​ +- Deletes the patient at the specified index as of the currently displayed list. + +
+ +**:information_source: Notes about `delete`:**
+On entering a `delete` command, a confirmation window will pop-up requesting for confirmation, regardless of the command's validity. This is to verify that you truly intend to perform that action and are fully aware of its consequences. +
+ +Examples: + +`delete 1` + + + + +[Back to Table of Contents](#table-of-contents) + +## Ward Features +### Adding a ward to the system: `addward` + +Adds the ward (name and capacity). + +Format: `addward w/WARD [c/CAPACITY]` + +- The ward name is represented as an alphanumeric string. E.g `A01`. +- The capacity is a positive integer. E.g `50`. + + +
:bulb: **Tip:** +
+ +
+ +**:information_source: Note about `add`:** +The default capacity is set to 10. +
+ +Examples: + +- `addward w/A01 c/25` Adds the ward with name `A01` and capacity `25` to the system. + +[Back to Table of Contents](#table-of-contents) + + +### Editing a ward's details in the system: `editward` + +Edit an existing ward's name or capacity. + +Format: `editward INDEX [w/WARD] [c/CAPACITY]` + +- Edits the ward's details at the specified index as of the currently displayed list. +- The given capacity has to be an integer. +- The given capacity has to be greater or equal to the ward's current occupancy + +Examples: + +- `editward 1 w/A02` Edits the name of the first currently displayed ward to be `A02`. +- `editward 5 c/35` Edits the capacity of the fifth currently displayed ward to be `35`. + +[Back to Table of Contents](#table-of-contents) + +### Deleting a ward from the system: `deleteward` + +
:exclamation: **Caution:** +Once deleted, a ward cannot be recovered. +
+ +Deletes a ward by index. + +Format: `deleteward INDEX` + +- Deletes the ward at the specified index as of the currently displayed list. + +
+ +**:information_source: Notes about `deleteward`:**
+- On entering a `deleteward` command, a confirmation window will pop-up requesting for confirmation, regardless of the command's validity. This is to verify that you truly intend to perform that action and are fully aware of its consequences. +- You will not be able to delete a ward that currently has patients assigned to it. +
+ 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` +`deleteward 1` + +[Back to Table of Contents](#table-of-contents) + +## Utility Features +### Clearing all data : `clear` + +
:exclamation: **Caution:** +Once cleared, MedInfo data cannot be recovered! +
-Clears all entries from the address book. +Clears all data (patients and wards) stored in MedInfo. Format: `clear` +[Back to Table of Contents](#table-of-contents) + +### Viewing help : `help` + +Shows a message explaining how to access the help page. + + +![help message](images/helpMessage.png) + +Format: `help` + +[Back to Table of Contents](#table-of-contents) + ### Exiting the program : `exit` Exits the program. Format: `exit` +[Back to Table of Contents](#table-of-contents) + + ### Saving the data +MedInfo data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually. + +[Back to Table of Contents](#table-of-contents) -AddressBook data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually. ### Editing the data file -AddressBook data are saved as a JSON file `[JAR file location]/data/addressbook.json`. Advanced users are welcome to update data directly by editing that data file. +MedInfo data are saved as a JSON file `[JAR file location]/data/medinfo.json`. Advanced users can update the data directly by editing that data file.
: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. +Edit the data file at your own risk! Any changes that make the data invalid will lead to MedInfo clearing +all data and starting anew.
-### Archiving data files `[coming in v2.0]` +[Back to Table of Contents](#table-of-contents) + +--- + +## FAQ :raising_hand: + +**Q**: I keep forgetting the commands, is there a quick way to get help?
+ +**A**: Yes! Entering the `help` command will show a message explaining how to access the help page.
+
-_Details coming soon ..._ +**Q**: Why can't I remove the waiting room?
--------------------------------------------------------------------------------------------------------------------- +**A**: As every hospital would have some pre-screening room for patients to wait in, and to make it easier +to start entering patients into the system, the waiting room is made un-deletable.
+
-## FAQ +**Q**: How do I transfer my data to another device/computer?
-**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 device/ computer and overwrite the empty data file it creates with +the file that contains the data of your previous MedInfo home folder.
+
--------------------------------------------------------------------------------------------------------------------- +[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` +| Action | Format, Examples | +| --------------- | --------------------------------------------------------------------------------------------------- | +| **Add** | `add nric/NRIC name/NAME [s/STATUS]`
e.g., `add nric/S1234567A name/John Doe s/GREEN` | +| **List** | `list` | +| **Edit** | `edit INDEX [s/STATUS] [w/WARD] [d/DISCHARGE]`
e.g.,`edit 1 s/GREEN` | +| **Sort** | `sort FIELD/ORDER`
e.g., `sort name/asc`, `sort d/desc` | +| **Find** | `find name/NAME` or `find nric/NRIC` or `find s/STATUS`or `find w/WARD`
e.g., `find name/John` | +| **Delete** | `delete INDEX`
e.g., `delete 1` | +| **Add Ward** | `addward w/WARD [c/CAPACITY]`
e.g., `addward w/S1234567A c/25` | +| **Edit Ward** | `editward INDEX [w/WARD] [c/CAPACITY]`
e.g., `editward 1 w/A02 c/35` | +| **Delete Ward** | `deleteward INDEX`
e.g., `deleteward 1` | +| **Clear** | `clear` | +| **Help** | `help` | +| **Exit** | `exit` | + +[Back to Table of Contents](#table-of-contents) + +--- + +## Glossary + +| Term | Definition | +|----------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **`.jar`** | A package file format that groups together Java program files and data files for ease of distribution. | +| **`cd`** | A command used to change the current working directory in various operating systems. To use it, type `cd` followed by a space and the folder you wish to work in. | +| **Command terminal** | A program that allows the user to enter commands that the computer processes. Examples of popular terminals include Terminal (for macOS) and PowerShell (for Windows). | +| **Discharge Date** | The date on which a patient can be discharged from the hospital. | +| **GUI** | Graphical User Interface. A form of user interface that allows users to interact primarily through graphics. | +| **Home folder** | The main folder you wish to run MedInfo in. On running MedInfo, this folder will become populated with data and preference files. | +| **In-process** | The process of checking in patients with their identifying information before they consult a medical professional. | +| **Occupancy** | The number of patients in a given ward at a point in time. | +| **Status** | A code indicating a patient's current condition. MedInfo works with 4 statuses: GRAY (unknown), GREEN (stable), YELLOW (serious), and RED (critical). | +| **Ward** | A separate room in a hospital, typically allocated to a particular type of patient. | + + +[Back to Table of Contents](#table-of-contents) diff --git a/docs/_config.yml b/docs/_config.yml index 6bd245d8f4e..3db08193a89 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,4 +1,4 @@ -title: "AB-3" +title: "MedInfo" theme: minima header_pages: @@ -8,7 +8,7 @@ header_pages: markdown: kramdown -repository: "se-edu/addressbook-level3" +repository: "AY2223S2-CS2103T-T12-2/tp" github_icon: "images/github-icon.png" plugins: diff --git a/docs/_sass/minima/_base.scss b/docs/_sass/minima/_base.scss index 0d3f6e80ced..6d4252a28f0 100644 --- a/docs/_sass/minima/_base.scss +++ b/docs/_sass/minima/_base.scss @@ -268,6 +268,7 @@ table { overflow-x: auto; -webkit-overflow-scrolling: touch; -ms-overflow-style: -ms-autohiding-scrollbar; + content: attr(href); } } @@ -288,8 +289,11 @@ table { text-align: center; } .site-header:before { - content: "AB-3"; + content: "MedInfo"; font-size: 32px; } + a::after { + content: " (link)"; + } } diff --git a/docs/diagrams/AddActivityDiagram.puml b/docs/diagrams/AddActivityDiagram.puml new file mode 100644 index 00000000000..84b7e20db4f --- /dev/null +++ b/docs/diagrams/AddActivityDiagram.puml @@ -0,0 +1,18 @@ +@startuml +skinparam activity { + ArrowColor #A80036 + BackgroundColor #FEFECE + BorderColor #A80036 + BorderThickness 1.5 +} +start +:User executes add command; +if () then ([Status present]) + :Create patient with Status; +else ([else]) + :Create guest with Status GRAY; +endif +:Add patient to model; +stop + +@enduml diff --git a/docs/diagrams/AddWardActivityDiagram.puml b/docs/diagrams/AddWardActivityDiagram.puml new file mode 100644 index 00000000000..bc14feb8060 --- /dev/null +++ b/docs/diagrams/AddWardActivityDiagram.puml @@ -0,0 +1,18 @@ +@startuml +skinparam activity { + ArrowColor #A80036 + BackgroundColor #FEFECE + BorderColor #A80036 + BorderThickness 1.5 +} +start +:User executes addward command; +if () then ([Capacity present]) + :Create ward with capacity; +else ([else]) + :Create ward with default capacity; +endif +:Add ward to model; +stop + +@enduml diff --git a/docs/diagrams/ArchitectureSequenceDiagram.puml b/docs/diagrams/ArchitectureSequenceDiagram.puml index ef81d18c337..37c76ffbe9c 100644 --- a/docs/diagrams/ArchitectureSequenceDiagram.puml +++ b/docs/diagrams/ArchitectureSequenceDiagram.puml @@ -13,13 +13,13 @@ activate ui UI_COLOR ui -[UI_COLOR]> logic : execute("delete 1") activate logic LOGIC_COLOR -logic -[LOGIC_COLOR]> model : deletePerson(p) +logic -[LOGIC_COLOR]> model : deletePatient(p) activate model MODEL_COLOR model -[MODEL_COLOR]-> logic deactivate model -logic -[LOGIC_COLOR]> storage : saveAddressBook(addressBook) +logic -[LOGIC_COLOR]> storage : saveMedInfo(medInfo) activate storage STORAGE_COLOR storage -[STORAGE_COLOR]> storage : Save to file diff --git a/docs/diagrams/BetterModelClassDiagram.puml b/docs/diagrams/BetterModelClassDiagram.puml deleted file mode 100644 index 598474a5c82..00000000000 --- a/docs/diagrams/BetterModelClassDiagram.puml +++ /dev/null @@ -1,21 +0,0 @@ -@startuml -!include style.puml -skinparam arrowThickness 1.1 -skinparam arrowColor MODEL_COLOR -skinparam classBackgroundColor MODEL_COLOR - -AddressBook *-right-> "1" UniquePersonList -AddressBook *-right-> "1" UniqueTagList -UniqueTagList -[hidden]down- UniquePersonList -UniqueTagList -[hidden]down- UniquePersonList - -UniqueTagList -right-> "*" Tag -UniquePersonList -right-> Person - -Person -up-> "*" Tag - -Person *--> Name -Person *--> Phone -Person *--> Email -Person *--> Address -@enduml diff --git a/docs/diagrams/CommitActivityDiagram.puml b/docs/diagrams/CommitActivityDiagram.puml deleted file mode 100644 index 6a6b23a006f..00000000000 --- a/docs/diagrams/CommitActivityDiagram.puml +++ /dev/null @@ -1,15 +0,0 @@ -@startuml -start -:User executes command; - -'Since the beta syntax does not support placing the condition outside the -'diamond we place it as the true branch instead. - -if () then ([command commits AddressBook]) - :Purge redundant states; - :Save AddressBook to - addressBookStateList; -else ([else]) -endif -stop -@enduml diff --git a/docs/diagrams/DeleteActivityDiagram.puml b/docs/diagrams/DeleteActivityDiagram.puml new file mode 100644 index 00000000000..39df2958735 --- /dev/null +++ b/docs/diagrams/DeleteActivityDiagram.puml @@ -0,0 +1,18 @@ +@startuml +skinparam activity { + ArrowColor #A80036 + BackgroundColor #FEFECE + BorderColor #A80036 + BorderThickness 1.5 +} +start +:User executes delete command; +if () then ([User confirms deletion]) + :Find patient to delete; + :Delete patient from the model; +else ([else]) + :List all patients; +endif +stop + +@enduml diff --git a/docs/diagrams/DeleteSequenceDiagram.puml b/docs/diagrams/DeleteSequenceDiagram.puml index 1dc2311b245..425cec6eff3 100644 --- a/docs/diagrams/DeleteSequenceDiagram.puml +++ b/docs/diagrams/DeleteSequenceDiagram.puml @@ -3,7 +3,7 @@ box Logic LOGIC_COLOR_T1 participant ":LogicManager" as LogicManager LOGIC_COLOR -participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":MedInfoParser" as MedInfoParser LOGIC_COLOR participant ":DeleteCommandParser" as DeleteCommandParser LOGIC_COLOR participant "d:DeleteCommand" as DeleteCommand LOGIC_COLOR participant ":CommandResult" as CommandResult LOGIC_COLOR @@ -16,17 +16,17 @@ end box [-> LogicManager : execute("delete 1") activate LogicManager -LogicManager -> AddressBookParser : parseCommand("delete 1") -activate AddressBookParser +LogicManager -> MedInfoParser : parseCommand("delete 1") +activate MedInfoParser create DeleteCommandParser -AddressBookParser -> DeleteCommandParser +MedInfoParser -> DeleteCommandParser activate DeleteCommandParser -DeleteCommandParser --> AddressBookParser +DeleteCommandParser --> MedInfoParser deactivate DeleteCommandParser -AddressBookParser -> DeleteCommandParser : parse("1") +MedInfoParser -> DeleteCommandParser : parse("1") activate DeleteCommandParser create DeleteCommand @@ -36,19 +36,19 @@ activate DeleteCommand DeleteCommand --> DeleteCommandParser : d deactivate DeleteCommand -DeleteCommandParser --> AddressBookParser : d +DeleteCommandParser --> MedInfoParser : d deactivate DeleteCommandParser 'Hidden arrow to position the destroy marker below the end of the activation bar. -DeleteCommandParser -[hidden]-> AddressBookParser +DeleteCommandParser -[hidden]-> MedInfoParser destroy DeleteCommandParser -AddressBookParser --> LogicManager : d -deactivate AddressBookParser +MedInfoParser --> LogicManager : d +deactivate MedInfoParser LogicManager -> DeleteCommand : execute() activate DeleteCommand -DeleteCommand -> Model : deletePerson(1) +DeleteCommand -> Model : deletePatient(1) activate Model Model --> DeleteCommand diff --git a/docs/diagrams/EditActivityDiagram.puml b/docs/diagrams/EditActivityDiagram.puml new file mode 100644 index 00000000000..b722317f77a --- /dev/null +++ b/docs/diagrams/EditActivityDiagram.puml @@ -0,0 +1,15 @@ +@startuml +skinparam activity { + ArrowColor #A80036 + BackgroundColor #FEFECE + BorderColor #A80036 + BorderThickness 1.5 +} +start +:User executes edit command; +:Find patient to edit; +:Create new patient using fields entered; +:Set new patient to the model; +stop + +@enduml diff --git a/docs/diagrams/FindActivityDiagram.puml b/docs/diagrams/FindActivityDiagram.puml new file mode 100644 index 00000000000..b95306b506c --- /dev/null +++ b/docs/diagrams/FindActivityDiagram.puml @@ -0,0 +1,14 @@ +@startuml +skinparam activity { + ArrowColor #A80036 + BackgroundColor #FEFECE + BorderColor #A80036 + BorderThickness 1.5 +} +start +:User executes find command with a search field; +:Find patient(s) whose corresponding field matches; +:Update model to display the patient(s); +stop + +@enduml diff --git a/docs/diagrams/ListActivityDiagram.puml b/docs/diagrams/ListActivityDiagram.puml new file mode 100644 index 00000000000..c9757fd9a5a --- /dev/null +++ b/docs/diagrams/ListActivityDiagram.puml @@ -0,0 +1,13 @@ +@startuml +skinparam activity { + ArrowColor #A80036 + BackgroundColor #FEFECE + BorderColor #A80036 + BorderThickness 1.5 +} +start +:User executes list command; +:Update model to display all patients; +stop + +@enduml diff --git a/docs/diagrams/LogicClassDiagram.puml b/docs/diagrams/LogicClassDiagram.puml index d4193173e18..3ccf6311356 100644 --- a/docs/diagrams/LogicClassDiagram.puml +++ b/docs/diagrams/LogicClassDiagram.puml @@ -6,7 +6,7 @@ skinparam classBackgroundColor LOGIC_COLOR package Logic { -Class AddressBookParser +Class MedInfoParser Class XYZCommand Class CommandResult Class "{abstract}\nCommand" as Command @@ -27,8 +27,8 @@ Class HiddenOutside #FFFFFF HiddenOutside ..> Logic LogicManager .right.|> Logic -LogicManager -right->"1" AddressBookParser -AddressBookParser ..> XYZCommand : creates > +LogicManager -right->"1" MedInfoParser +MedInfoParser ..> XYZCommand : creates > XYZCommand -up-|> Command LogicManager .left.> Command : executes > diff --git a/docs/diagrams/ModelClassDiagram.puml b/docs/diagrams/ModelClassDiagram.puml index 4439108973a..43bbfc19c8d 100644 --- a/docs/diagrams/ModelClassDiagram.puml +++ b/docs/diagrams/ModelClassDiagram.puml @@ -5,46 +5,51 @@ skinparam arrowColor MODEL_COLOR skinparam classBackgroundColor MODEL_COLOR Package Model <>{ -Class "<>\nReadOnlyAddressBook" as ReadOnlyAddressBook +Class "<>\nReadOnlyMedInfo" as ReadOnlyMedInfo Class "<>\nReadOnlyUserPrefs" as ReadOnlyUserPrefs Class "<>\nModel" as Model -Class AddressBook +Class MedInfo Class ModelManager Class UserPrefs -Class UniquePersonList -Class Person -Class Address -Class Email +Class UniquePatientList +Class Patient +Class UniqueWardList +Class Ward +Class Capacity +Class WardName +Class Nric Class Name -Class Phone -Class Tag +Class Status +Class Discharge } Class HiddenOutside #FFFFFF HiddenOutside ..> Model -AddressBook .up.|> ReadOnlyAddressBook +MedInfo .up.|> ReadOnlyMedInfo ModelManager .up.|> Model Model .right.> ReadOnlyUserPrefs -Model .left.> ReadOnlyAddressBook -ModelManager -left-> "1" AddressBook +Model .left.> ReadOnlyMedInfo +ModelManager -left-> "1" MedInfo ModelManager -right-> "1" UserPrefs UserPrefs .up.|> ReadOnlyUserPrefs -AddressBook *--> "1" UniquePersonList -UniquePersonList --> "~* all" Person -Person *--> Name -Person *--> Phone -Person *--> Email -Person *--> Address -Person *--> "*" Tag - -Name -[hidden]right-> Phone -Phone -[hidden]right-> Address -Address -[hidden]right-> Email - -ModelManager -->"~* filtered" Person +MedInfo *--> "1" UniquePatientList +MedInfo *--> "1" UniqueWardList +UniquePatientList --> "~* all" Patient +UniqueWardList --> "~* all" Ward +Patient *--> Name +Patient *--> Nric +Patient *--> Status +Patient *--> WardName +Patient *--> Discharge +Ward *--> Capacity +Ward *--> WardName +Ward *--> "1" UniquePatientList + + +ModelManager -->"~* filtered" Patient @enduml diff --git a/docs/diagrams/ParserClasses.puml b/docs/diagrams/ParserClasses.puml index 0c7424de6e0..e4c79b373ec 100644 --- a/docs/diagrams/ParserClasses.puml +++ b/docs/diagrams/ParserClasses.puml @@ -9,7 +9,7 @@ Class XYZCommand package "Parser classes"{ Class "<>\nParser" as Parser -Class AddressBookParser +Class MedInfoParser Class XYZCommandParser Class CliSyntax Class ParserUtil @@ -19,12 +19,12 @@ Class Prefix } Class HiddenOutside #FFFFFF -HiddenOutside ..> AddressBookParser +HiddenOutside ..> MedInfoParser -AddressBookParser .down.> XYZCommandParser: creates > +MedInfoParser .down.> XYZCommandParser: creates > XYZCommandParser ..> XYZCommand : creates > -AddressBookParser ..> Command : returns > +MedInfoParser ..> Command : returns > XYZCommandParser .up.|> Parser XYZCommandParser ..> ArgumentMultimap XYZCommandParser ..> ArgumentTokenizer diff --git a/docs/diagrams/StorageClassDiagram.puml b/docs/diagrams/StorageClassDiagram.puml index 760305e0e58..d22666cb14a 100644 --- a/docs/diagrams/StorageClassDiagram.puml +++ b/docs/diagrams/StorageClassDiagram.puml @@ -14,12 +14,11 @@ Class JsonUserPrefsStorage Class "<>\nStorage" as Storage Class StorageManager -package "AddressBook Storage" #F4F6F6{ -Class "<>\nAddressBookStorage" as AddressBookStorage -Class JsonAddressBookStorage -Class JsonSerializableAddressBook +package "MedInfo Storage" #F4F6F6{ +Class "<>\nMedInfoStorage" as MedInfoStorage +Class JsonMedInfoStorage +Class JsonSerializableMedInfo Class JsonAdaptedPerson -Class JsonAdaptedTag } } @@ -29,15 +28,14 @@ HiddenOutside ..> Storage StorageManager .up.|> Storage StorageManager -up-> "1" UserPrefsStorage -StorageManager -up-> "1" AddressBookStorage +StorageManager -up-> "1" MedInfoStorage Storage -left-|> UserPrefsStorage -Storage -right-|> AddressBookStorage +Storage -right-|> MedInfoStorage JsonUserPrefsStorage .up.|> UserPrefsStorage -JsonAddressBookStorage .up.|> AddressBookStorage -JsonAddressBookStorage ..> JsonSerializableAddressBook -JsonSerializableAddressBook --> "*" JsonAdaptedPerson -JsonAdaptedPerson --> "*" JsonAdaptedTag +JsonMedInfoStorage .up.|> MedInfoStorage +JsonMedInfoStorage ..> JsonSerializableMedInfo +JsonSerializableMedInfo --> "*" JsonAdaptedPerson @enduml diff --git a/docs/diagrams/UiClassDiagram.puml b/docs/diagrams/UiClassDiagram.puml index 95473d5aa19..5e019e425ac 100644 --- a/docs/diagrams/UiClassDiagram.puml +++ b/docs/diagrams/UiClassDiagram.puml @@ -11,8 +11,10 @@ Class UiManager Class MainWindow Class HelpWindow Class ResultDisplay -Class PersonListPanel -Class PersonCard +Class PatientListPanel +Class PatientCard +Class WardListPanel +Class WardCard Class StatusBarFooter Class CommandBox } @@ -32,26 +34,30 @@ UiManager .left.|> Ui UiManager -down-> "1" MainWindow MainWindow *-down-> "1" CommandBox MainWindow *-down-> "1" ResultDisplay -MainWindow *-down-> "1" PersonListPanel +MainWindow *-down-> "1" PatientListPanel +MainWindow *-down-> "1" WardListPanel MainWindow *-down-> "1" StatusBarFooter MainWindow --> "0..1" HelpWindow -PersonListPanel -down-> "*" PersonCard +PatientListPanel -down-> "*" PatientCard +WardListPanel -down-> "*" WardCard MainWindow -left-|> UiPart ResultDisplay --|> UiPart CommandBox --|> UiPart -PersonListPanel --|> UiPart -PersonCard --|> UiPart +PatientListPanel --|> UiPart +PatientCard --|> UiPart StatusBarFooter --|> UiPart HelpWindow --|> UiPart -PersonCard ..> Model +PatientCard ..> Model +WardCard ..> Model UiManager -right-> Logic MainWindow -left-> Logic -PersonListPanel -[hidden]left- HelpWindow +PatientListPanel -[hidden]left- HelpWindow +WardListPanel -[hidden]left- HelpWindow HelpWindow -[hidden]left- CommandBox CommandBox -[hidden]left- ResultDisplay ResultDisplay -[hidden]left- StatusBarFooter diff --git a/docs/diagrams/UndoRedoState0.puml b/docs/diagrams/UndoRedoState0.puml deleted file mode 100644 index 96e30744d24..00000000000 --- a/docs/diagrams/UndoRedoState0.puml +++ /dev/null @@ -1,20 +0,0 @@ -@startuml -!include style.puml -skinparam ClassFontColor #000000 -skinparam ClassBorderColor #000000 - -title Initial state - -package States { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab2:AddressBook__" -} -State1 -[hidden]right-> State2 -State2 -[hidden]right-> State3 -hide State2 -hide State3 - -class Pointer as "Current State" #FFFFF -Pointer -up-> State1 -@end diff --git a/docs/diagrams/UndoRedoState1.puml b/docs/diagrams/UndoRedoState1.puml deleted file mode 100644 index 01fcb9b2b96..00000000000 --- a/docs/diagrams/UndoRedoState1.puml +++ /dev/null @@ -1,22 +0,0 @@ -@startuml -!include style.puml -skinparam ClassFontColor #000000 -skinparam ClassBorderColor #000000 - -title After command "delete 5" - -package States <> { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab2:AddressBook__" -} - -State1 -[hidden]right-> State2 -State2 -[hidden]right-> State3 - -hide State3 - -class Pointer as "Current State" #FFFFF - -Pointer -up-> State2 -@end diff --git a/docs/diagrams/UndoRedoState2.puml b/docs/diagrams/UndoRedoState2.puml deleted file mode 100644 index bccc230a5d1..00000000000 --- a/docs/diagrams/UndoRedoState2.puml +++ /dev/null @@ -1,20 +0,0 @@ -@startuml -!include style.puml -skinparam ClassFontColor #000000 -skinparam ClassBorderColor #000000 - -title After command "add n/David" - -package States <> { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab2:AddressBook__" -} - -State1 -[hidden]right-> State2 -State2 -[hidden]right-> State3 - -class Pointer as "Current State" #FFFFF - -Pointer -up-> State3 -@end diff --git a/docs/diagrams/UndoRedoState3.puml b/docs/diagrams/UndoRedoState3.puml deleted file mode 100644 index ea29c9483e4..00000000000 --- a/docs/diagrams/UndoRedoState3.puml +++ /dev/null @@ -1,20 +0,0 @@ -@startuml -!include style.puml -skinparam ClassFontColor #000000 -skinparam ClassBorderColor #000000 - -title After command "undo" - -package States <> { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab2:AddressBook__" -} - -State1 -[hidden]right-> State2 -State2 -[hidden]right-> State3 - -class Pointer as "Current State" #FFFFF - -Pointer -up-> State2 -@end diff --git a/docs/diagrams/UndoRedoState4.puml b/docs/diagrams/UndoRedoState4.puml deleted file mode 100644 index 1b784cece80..00000000000 --- a/docs/diagrams/UndoRedoState4.puml +++ /dev/null @@ -1,20 +0,0 @@ -@startuml -!include style.puml -skinparam ClassFontColor #000000 -skinparam ClassBorderColor #000000 - -title After command "list" - -package States <> { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab2:AddressBook__" -} - -State1 -[hidden]right-> State2 -State2 -[hidden]right-> State3 - -class Pointer as "Current State" #FFFFF - -Pointer -up-> State2 -@end diff --git a/docs/diagrams/UndoRedoState5.puml b/docs/diagrams/UndoRedoState5.puml deleted file mode 100644 index 88927be32bc..00000000000 --- a/docs/diagrams/UndoRedoState5.puml +++ /dev/null @@ -1,21 +0,0 @@ -@startuml -!include style.puml -skinparam ClassFontColor #000000 -skinparam ClassBorderColor #000000 - -title After command "clear" - -package States <> { - class State1 as "__ab0:AddressBook__" - class State2 as "__ab1:AddressBook__" - class State3 as "__ab3:AddressBook__" -} - -State1 -[hidden]right-> State2 -State2 -[hidden]right-> State3 - -class Pointer as "Current State" #FFFFF - -Pointer -up-> State3 -note right on link: State ab2 deleted. -@end diff --git a/docs/diagrams/UndoSequenceDiagram.puml b/docs/diagrams/UndoSequenceDiagram.puml deleted file mode 100644 index 410aab4e412..00000000000 --- a/docs/diagrams/UndoSequenceDiagram.puml +++ /dev/null @@ -1,53 +0,0 @@ -@startuml -!include style.puml - -box Logic LOGIC_COLOR_T1 -participant ":LogicManager" as LogicManager LOGIC_COLOR -participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR -participant "u:UndoCommand" as UndoCommand LOGIC_COLOR -end box - -box Model MODEL_COLOR_T1 -participant ":Model" as Model MODEL_COLOR -participant ":VersionedAddressBook" as VersionedAddressBook MODEL_COLOR -end box -[-> LogicManager : execute(undo) -activate LogicManager - -LogicManager -> AddressBookParser : parseCommand(undo) -activate AddressBookParser - -create UndoCommand -AddressBookParser -> UndoCommand -activate UndoCommand - -UndoCommand --> AddressBookParser -deactivate UndoCommand - -AddressBookParser --> LogicManager : u -deactivate AddressBookParser - -LogicManager -> UndoCommand : execute() -activate UndoCommand - -UndoCommand -> Model : undoAddressBook() -activate Model - -Model -> VersionedAddressBook : undo() -activate VersionedAddressBook - -VersionedAddressBook -> VersionedAddressBook :resetData(ReadOnlyAddressBook) -VersionedAddressBook --> Model : -deactivate VersionedAddressBook - -Model --> UndoCommand -deactivate Model - -UndoCommand --> LogicManager : result -deactivate UndoCommand -UndoCommand -[hidden]-> LogicManager : result -destroy UndoCommand - -[<--LogicManager -deactivate LogicManager -@enduml diff --git a/docs/diagrams/tracing/LogicSequenceDiagram.puml b/docs/diagrams/tracing/LogicSequenceDiagram.puml index fdcbe1c0ccc..495e67f6405 100644 --- a/docs/diagrams/tracing/LogicSequenceDiagram.puml +++ b/docs/diagrams/tracing/LogicSequenceDiagram.puml @@ -2,7 +2,7 @@ !include ../style.puml Participant ":LogicManager" as logic LOGIC_COLOR -Participant ":AddressBookParser" as abp LOGIC_COLOR +Participant ":MedInfoParser" as abp LOGIC_COLOR Participant ":EditCommandParser" as ecp LOGIC_COLOR Participant "command:EditCommand" as ec LOGIC_COLOR @@ -13,7 +13,7 @@ create ecp abp -> ecp abp -> ecp ++: parse(arguments) create ec -ecp -> ec ++: index, editPersonDescriptor +ecp -> ec ++: index, editPatientDescriptor ec --> ecp -- ecp --> abp --: command abp --> logic --: command diff --git a/docs/images/AddActivityDiagram.png b/docs/images/AddActivityDiagram.png new file mode 100644 index 00000000000..d3b38ec58c1 Binary files /dev/null and b/docs/images/AddActivityDiagram.png differ diff --git a/docs/images/AddWardActivityDiagram.png b/docs/images/AddWardActivityDiagram.png new file mode 100644 index 00000000000..efe8474b546 Binary files /dev/null and b/docs/images/AddWardActivityDiagram.png differ diff --git a/docs/images/ArchitectureSequenceDiagram.png b/docs/images/ArchitectureSequenceDiagram.png index 2f1346869d0..b40184c42ed 100644 Binary files a/docs/images/ArchitectureSequenceDiagram.png and b/docs/images/ArchitectureSequenceDiagram.png differ diff --git a/docs/images/BetterModelClassDiagram.png b/docs/images/BetterModelClassDiagram.png deleted file mode 100644 index 94440f0ac4a..00000000000 Binary files a/docs/images/BetterModelClassDiagram.png and /dev/null differ diff --git a/docs/images/CommitActivityDiagram.png b/docs/images/CommitActivityDiagram.png deleted file mode 100644 index c08c13f5c8b..00000000000 Binary files a/docs/images/CommitActivityDiagram.png and /dev/null differ diff --git a/docs/images/DeleteActivityDiagram.png b/docs/images/DeleteActivityDiagram.png new file mode 100644 index 00000000000..c9788d56210 Binary files /dev/null and b/docs/images/DeleteActivityDiagram.png differ diff --git a/docs/images/DeleteSequenceDiagram.png b/docs/images/DeleteSequenceDiagram.png index fa327b39618..c1f68fa1fe3 100644 Binary files a/docs/images/DeleteSequenceDiagram.png and b/docs/images/DeleteSequenceDiagram.png differ diff --git a/docs/images/EditActivityDiagram.png b/docs/images/EditActivityDiagram.png new file mode 100644 index 00000000000..9c7e6b066ce Binary files /dev/null and b/docs/images/EditActivityDiagram.png differ diff --git a/docs/images/FindActivityDiagram.png b/docs/images/FindActivityDiagram.png new file mode 100644 index 00000000000..008d08ffe0f Binary files /dev/null and b/docs/images/FindActivityDiagram.png differ diff --git a/docs/images/ListActivityDiagram.png b/docs/images/ListActivityDiagram.png new file mode 100644 index 00000000000..ecaafcca88f Binary files /dev/null and b/docs/images/ListActivityDiagram.png differ diff --git a/docs/images/LogicClassDiagram.png b/docs/images/LogicClassDiagram.png index 9e9ba9f79e5..10efd4ea493 100644 Binary files a/docs/images/LogicClassDiagram.png and b/docs/images/LogicClassDiagram.png differ diff --git a/docs/images/ModelClassDiagram.png b/docs/images/ModelClassDiagram.png index 04070af60d8..0e6788365f2 100644 Binary files a/docs/images/ModelClassDiagram.png and b/docs/images/ModelClassDiagram.png differ diff --git a/docs/images/ParserClasses.png b/docs/images/ParserClasses.png index e7b4c8880cd..661eef23295 100644 Binary files a/docs/images/ParserClasses.png and b/docs/images/ParserClasses.png differ diff --git a/docs/images/StorageClassDiagram.png b/docs/images/StorageClassDiagram.png index 2533a5c1af0..1ccf5167fdd 100644 Binary files a/docs/images/StorageClassDiagram.png and b/docs/images/StorageClassDiagram.png differ diff --git a/docs/images/Ui.png b/docs/images/Ui.png index 5bd77847aa2..541b80650b7 100644 Binary files a/docs/images/Ui.png and b/docs/images/Ui.png differ diff --git a/docs/images/UiClassDiagram.png b/docs/images/UiClassDiagram.png index 785e04dbab4..a59a49b7edf 100644 Binary files a/docs/images/UiClassDiagram.png and b/docs/images/UiClassDiagram.png differ diff --git a/docs/images/UiOverview.png b/docs/images/UiOverview.png new file mode 100644 index 00000000000..26d29dbf45c Binary files /dev/null and b/docs/images/UiOverview.png differ diff --git a/docs/images/UndoRedoState0.png b/docs/images/UndoRedoState0.png deleted file mode 100644 index 8f7538cd884..00000000000 Binary files a/docs/images/UndoRedoState0.png and /dev/null differ diff --git a/docs/images/UndoRedoState1.png b/docs/images/UndoRedoState1.png deleted file mode 100644 index df9908d0948..00000000000 Binary files a/docs/images/UndoRedoState1.png and /dev/null differ diff --git a/docs/images/UndoRedoState2.png b/docs/images/UndoRedoState2.png deleted file mode 100644 index 36519c1015b..00000000000 Binary files a/docs/images/UndoRedoState2.png and /dev/null differ diff --git a/docs/images/UndoRedoState3.png b/docs/images/UndoRedoState3.png deleted file mode 100644 index 19959d01712..00000000000 Binary files a/docs/images/UndoRedoState3.png and /dev/null differ diff --git a/docs/images/UndoRedoState4.png b/docs/images/UndoRedoState4.png deleted file mode 100644 index 4c623e4f2c5..00000000000 Binary files a/docs/images/UndoRedoState4.png and /dev/null differ diff --git a/docs/images/UndoRedoState5.png b/docs/images/UndoRedoState5.png deleted file mode 100644 index 84ad2afa6bd..00000000000 Binary files a/docs/images/UndoRedoState5.png and /dev/null differ diff --git a/docs/images/UndoSequenceDiagram.png b/docs/images/UndoSequenceDiagram.png deleted file mode 100644 index 6addcd3a8d9..00000000000 Binary files a/docs/images/UndoSequenceDiagram.png and /dev/null differ diff --git a/docs/images/daytona65.png b/docs/images/daytona65.png new file mode 100644 index 00000000000..aa708c60d74 Binary files /dev/null and b/docs/images/daytona65.png differ diff --git a/docs/images/findAlexDavidResult.png b/docs/images/findAlexDavidResult.png deleted file mode 100644 index 235da1c273e..00000000000 Binary files a/docs/images/findAlexDavidResult.png and /dev/null differ diff --git a/docs/images/findJohnCarlosResult.png b/docs/images/findJohnCarlosResult.png new file mode 100644 index 00000000000..9c975591112 Binary files /dev/null and b/docs/images/findJohnCarlosResult.png differ diff --git a/docs/images/helpMessage.png b/docs/images/helpMessage.png index b1f70470137..83891b4097b 100644 Binary files a/docs/images/helpMessage.png and b/docs/images/helpMessage.png differ diff --git a/docs/images/jeraldkiew.png b/docs/images/jeraldkiew.png new file mode 100644 index 00000000000..2bee9784cee Binary files /dev/null and b/docs/images/jeraldkiew.png differ diff --git a/docs/images/ksunil2001.png b/docs/images/ksunil2001.png new file mode 100644 index 00000000000..4e727bce52b Binary files /dev/null and b/docs/images/ksunil2001.png differ diff --git a/docs/images/nramapurath.png b/docs/images/nramapurath.png new file mode 100644 index 00000000000..d333486b3a0 Binary files /dev/null and b/docs/images/nramapurath.png differ diff --git a/docs/images/tracing/LogicSequenceDiagram.png b/docs/images/tracing/LogicSequenceDiagram.png index c9b1f6cc232..04561d95143 100644 Binary files a/docs/images/tracing/LogicSequenceDiagram.png and b/docs/images/tracing/LogicSequenceDiagram.png differ diff --git a/docs/images/yitong241.png b/docs/images/yitong241.png new file mode 100644 index 00000000000..236c92ec961 Binary files /dev/null and b/docs/images/yitong241.png differ diff --git a/docs/index.md b/docs/index.md index 7601dbaad0d..0954fb12411 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,6 +1,6 @@ --- layout: page -title: AddressBook Level-3 +title: MedInfo --- [![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions) @@ -8,10 +8,11 @@ title: AddressBook Level-3 ![Ui](images/Ui.png) -**AddressBook is a desktop application for managing your contact details.** While it has a GUI, most of the user interactions happen using a CLI (Command Line Interface). +**MedInfo is a desktop application for managing patients and wards.** While it has a GUI, most of the user interactions happen using a CLI (Command Line Interface). -* If you are interested in using AddressBook, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start). -* If you are interested about developing AddressBook, the [**Developer Guide**](DeveloperGuide.html) is a good place to start. +* If you are interested in using MedInfo, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start). +* If you are interested about developing MedInfo, the [**Developer Guide**](DeveloperGuide.html) is a good place to start. +* Find out more about the team behind MedInfo [**here**](AboutUs.html). **Acknowledgements** diff --git a/docs/team/daytona65.md b/docs/team/daytona65.md new file mode 100644 index 00000000000..6ec58e16433 --- /dev/null +++ b/docs/team/daytona65.md @@ -0,0 +1,66 @@ +--- +layout: page +title: Nicholas Halim's Project Portfolio Page +--- + +### Project: MedInfo + +MedInfo is a desktop application for private hospital administrative staff. It helps manage patients, +their status, discharge dates and wards. MedInfo aims to solve the problem of slow, multiple step process of +documenting patient medical records during in-processing by zeroing in on the important details and provide simple, +fast access to a particular patient’s medical records for hospital admin staff. + +The user interacts with MedInfo using a CLI, and it has a GUI created with JavaFX. +MedInfo is written in Java, and has about 10 kLoC. + +Given below are my contributions to the project. + +* **New Feature**: + * Ward class and its associated classes + * Ward to Patient interactions + * Occupancy and capacity tracking for wards + * Patient coloured status bars + * Split screens + * Overall stats display in status bar footer + + +* Code contributed: [**RepoSense**](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=daytona65&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2023-02-17&tabOpen=false&zFR=false) + + +* View Pull Requests I have authored [**here**](https://github.com/AY2223S2-CS2103T-T12-2/tp/pulls?q=is%3Apr+author%3Adaytona65+) . + + +* **Project management and Contributions**: + * Led ideation and implementation of new features for my team + * Managed the review and merging of my team's PRs + * Helped assign and delegate some tasks to team members + * Ensured team vision was aligned + * Wrote Javadocs and helped to check code quality. + + +* **Enhancements to existing features**: + * Enhance the GUI and added split screens + * Mostly worked on implementing new features without breaking existing features + + +* **Documentation**: + * User Guide: + * Fixed Table of Contents links + * Added User Interface Overview + * Updated Introduction and About MedInfo + * Developer Guide: + * Helped review the Guide. + +* **Community**: + * View PRs I have reviewed [**here**](https://github.com/AY2223S2-CS2103T-T12-2/tp/pulls?q=is%3Apr+reviewed-by%3Adaytona65+). + * View my contributions to forum discussions [**here**](https://github.com/nus-cs2103-AY2223S2/forum/issues?q=is%3Aissue+author%3Adaytona65+). + * View my reported bugs and suggestions for other teams in the class [**here**](https://github.com/AY2223S2-CS2103-F11-1/tp/issues?q=is%3Aissue+is%3Aclosed) + , under Tester D. + + +* **Tools**: + * Figma used for initial UI draft + * Google Docs for miscellaneous documentation and changelog + * Zoom for weekly team meetings + * Telegram for team discussions + diff --git a/docs/team/jeraldkiew.md b/docs/team/jeraldkiew.md new file mode 100644 index 00000000000..55021c8d0f6 --- /dev/null +++ b/docs/team/jeraldkiew.md @@ -0,0 +1,75 @@ +--- +layout: page +title: Jerald Kiew's Project Portfolio Page +--- + +### Project: MedInfo + +MedInfo is a desktop application for private hospital administrative staff. It helps manage patients, +their status, discharge dates and wards. MedInfo aims to solve the problem of slow, multiple step process of +documenting patient medical records during in-processing by zeroing in on the important details and provide simple, +fast access to a particular patient’s medical records for hospital admin staff. + +The user interacts with MedInfo using a CLI, and it has a GUI created with JavaFX. +MedInfo is written in Java, and has about 10 kLoC. + +Given below are my contributions to the project. + +- **New Feature** + + - Ward implementation + - `editward` command is a core feature required to augment the name and capacities of existing wards, and ensure these changes reflect for the corresponding patients [\#163](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/163) + - `Capacity` class together with `Ward` helped check that every ward's occupancy does not exceed its allocated capacity [\#138](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/138) + - Status implementation + - Patient Class implementation + - Refactoring `Patient` class to accomodate a `Ward`, so that patients can be assigned to their respective wards [\#109](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/109) + +- **Code contributed**: [RepoSense link](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=jeraldkiew&breakdown=true&sort=groupTitle%20dsc&sortWithin=title&since=2023-02-17&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other) + +- **Project management**: + + - Reviewed and merged teammate's PRs + - Overviewed the design of classes under model + - Spearheaded documentation + - Ensured team vision was aligned + +- **Enhancements to existing features**: + + - Implemented total occupancy of MedInfo + - Tracks and displays total number of patients currently in MedInfo [\#146](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/146) + - Fixed response and error messages + - Standardised and corrected messages concerning optional parameters [\#265](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/265) + - Fixed `help` message linking user to User Guide [\#265](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/265) + - Refactored initial `Person` and `Person`-related classes to `Patient` + - Refactored existing classes as well as designed new classes for our new purposes in model [\#85](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/85) [\#86](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/86) [\#89](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/89) + +- **Documentation**: + + - User Guide + - Added documentation for `editward` and `deleteward` [\#171](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/171) + - Added informative sections for `delete` and `deleteward` confirmation and constraints [\#265](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/265) + - Added and corrected Ui screenshots [\#265](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/265) + - Developer Guide: + - Added various user stories [\#137](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/137) + - Added and updated PUML diagrams [\#152](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/152) [\#251](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/251) [\#265](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/265) + + + + + + + + + + +- **Contributions to Team-based tasks**: + - Refactored Person to Patient throughout the code + - Maintained issue tracker + - Updated user and developer docs + +- **Review/mentoring contributions**: + + - PRs reviewed (with non-trivial review comments): [\#136](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/136) [\#159](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/159) + +- **Contributions beyond the project team**: + - Reported bugs and suggestion for another team in the class - [Ultron](https://github.com/AY2223S2-CS2103T-F12-4/tp) [\#154](https://github.com/AY2223S2-CS2103T-F12-4/tp/issues/154) [\#160](https://github.com/AY2223S2-CS2103T-F12-4/tp/issues/160) [\#167](https://github.com/AY2223S2-CS2103T-F12-4/tp/issues/167) diff --git a/docs/team/johndoe.md b/docs/team/johndoe.md deleted file mode 100644 index 773a07794e2..00000000000 --- a/docs/team/johndoe.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -layout: page -title: John Doe's Project Portfolio Page ---- - -### Project: AddressBook Level 3 - -AddressBook - Level 3 is a desktop address book application used for teaching Software Engineering principles. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC. - -Given below are my contributions to the project. - -* **New Feature**: Added the ability to undo/redo previous commands. - * What it does: allows the user to undo all previous commands one at a time. Preceding undo commands can be reversed by using the redo command. - * Justification: This feature improves the product significantly because a user can make mistakes in commands and the app should provide a convenient way to rectify them. - * Highlights: This enhancement affects existing commands and commands to be added in future. It required an in-depth analysis of design alternatives. The implementation too was challenging as it required changes to existing commands. - * Credits: *{mention here if you reused any code/ideas from elsewhere or if a third-party library is heavily used in the feature so that a reader can make a more accurate judgement of how much effort went into the feature}* - -* **New Feature**: Added a history command that allows the user to navigate to previous commands using up/down keys. - -* **Code contributed**: [RepoSense link]() - -* **Project management**: - * Managed releases `v1.3` - `v1.5rc` (3 releases) on GitHub - -* **Enhancements to existing features**: - * Updated the GUI color scheme (Pull requests [\#33](), [\#34]()) - * Wrote additional tests for existing features to increase coverage from 88% to 92% (Pull requests [\#36](), [\#38]()) - -* **Documentation**: - * User Guide: - * Added documentation for the features `delete` and `find` [\#72]() - * Did cosmetic tweaks to existing documentation of features `clear`, `exit`: [\#74]() - * Developer Guide: - * Added implementation details of the `delete` feature. - -* **Community**: - * PRs reviewed (with non-trivial review comments): [\#12](), [\#32](), [\#19](), [\#42]() - * Contributed to forum discussions (examples: [1](), [2](), [3](), [4]()) - * Reported bugs and suggestions for other teams in the class (examples: [1](), [2](), [3]()) - * Some parts of the history feature I added was adopted by several other class mates ([1](), [2]()) - -* **Tools**: - * Integrated a third party library (Natty) to the project ([\#42]()) - * Integrated a new Github plugin (CircleCI) to the team repo - -* _{you can add/remove categories in the list above}_ diff --git a/docs/team/ksunil2001.md b/docs/team/ksunil2001.md new file mode 100644 index 00000000000..5bff13f82e4 --- /dev/null +++ b/docs/team/ksunil2001.md @@ -0,0 +1,56 @@ +--- +layout: page +title: K Sunil Avinash's Project Portfolio Page +--- + +### Project: MedInfo + +MedInfo is a desktop application for private hospital administrative staff. It helps manage patients, +their status, discharge dates and wards. MedInfo aims to solve the problem of slow, multiple step process of +documenting patient medical records during in-processing by zeroing in on the important details and provide simple, +fast access to a particular patient’s medical records for hospital admin staff. + +The user interacts with MedInfo using a CLI, and it has a GUI created with JavaFX. +MedInfo is written in Java, and has about 10 kLoC. + +Given below are my contributions to the project. + +- **New Feature** + - Added discharge date-time field to patient
+ + +- **Code contributed**: [RepoSense](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=ksunil2001&breakdown=true)
+ + +- **Enhancements to existing features**: + - Fixed majority of the failing testcases + - This was hard to fix for some testcases, as it required me to dig deep inside the code with the debugger to find the errors + - Even after finding the error, it took some time to understand how and why the error occurred before I could fix it + - Overall it was a time-consuming process + - Updated json files for test cases + - Fixed the display of ward name when editing a patient's details + - Created a help.txt file to summarise the list of commands and their syntax
+ + +- **Documentation**: + - Organised features by Patient, Ward and Utility in the User Guide + - Updated the editward and addward command features in the User Guide + - Added a use case for the find command in the Developer Guide + - Updated the product scope, target user profile, value proposition, user stories, use cases and glossary of the Developer Guide
+ + +- **Contributions to Team-based tasks**: + - Refactored Person to Patient throughout the code + - Fixed checkstyle issues
+ + +- **Review/mentoring contributions**: + - This link lists all the PRs I have reviewed: [Reviewed PRs](https://github.com/AY2223S2-CS2103T-T12-2/tp/pulls?q=is%3Apr+is%3Aclosed+reviewed-by%3Aksunil2001) + - Helped to verify the code style and clarify a doubt in this PR: [PR](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/121) + - Corrected the code to implement abstraction and improve the code style in this PR: [PR](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/159) + - Checked the grammar of the UG in this PR: [PR](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/249)
+ + +- **Contributions beyond the project team**: + - Reported a total of 11 bugs during the PE dry run + - I made sure to describe all the bugs as detailed and clear as possible with screenshots, to ensure the product's team would not have any doubts diff --git a/docs/team/nramapurath.md b/docs/team/nramapurath.md new file mode 100644 index 00000000000..bda8a5a1683 --- /dev/null +++ b/docs/team/nramapurath.md @@ -0,0 +1,66 @@ +--- +layout: page +title: Navaneeth Ramapurath's Project Portfolio Page +--- + +### Project: MedInfo + +MedInfo is a desktop application for private hospital administrative staff. It helps manage patients, +their status, discharge dates and wards. MedInfo aims to solve the problem of slow, multiple step process of +documenting patient medical records during in-processing by zeroing in on the important details and provide simple, +fast access to a particular patient’s medical records for hospital admin staff. + +The user interacts with MedInfo using a CLI, and it has a GUI created with JavaFX. +MedInfo is written in Java, and has about 10 kLoC. + +Given below are my contributions to the project. + +- **New Feature** + - Edit patient status + - Edit patient ward + - Confirmation window pop-up on delete operations + - This was crucial to prevent admin staff from accidentally performing potentially destructive operations + + +- **Code contributed**: [RepoSense link](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=nramapurath&breakdown=true) + - You can view the list of PRs that I have authored [here](https://github.com/AY2223S2-CS2103T-T12-2/tp/pulls?page=1&q=is%3Apr+author%3Anramapurath) + + +- **Enhancements to existing features**: + - Fixed logic to improve sorting by discharge date + - Made ward uniqueness case-insensitive to prevent duplicate wards + - Fixed app crash on invalid save data + + +- **Project management**: + - GitHub Issues to track bugs, features and user stories + - GitHub Projects to view progress at a glance + - Maintained changelog on a collaborative document + - Weekly team meetings held over Zoom to track progress + + +- **Documentation**: + - User Guide: + - Updated Introduction and About MedInfo + - Added Glossary for definitions and technical term descriptions + - Added Getting Started section + - Added FAQs + - Developer Guide: + - Added Planned Enhancements section + - Added activity diagrams for user interactions + - Edited user stories to match project scope + + +- **Review/mentoring contributions**: + - You can view the list of PRs that I have reviewed [here](https://github.com/AY2223S2-CS2103T-T12-2/tp/pulls?q=is%3Apr+reviewed-by%3Anramapurath) + - Facilitated discussions with team on design decisions + - Provided technical support when necessary over Telegram + + +- **Other Tools**: + - Figma used for initial UI draft + - PlantUML for class and activity diagrams + - Google Docs for miscellaneous documentation and changelog + - Microsoft Teams for communication with CS2103T Teaching Team + - Zoom for weekly team meetings + - Telegram for team discussions diff --git a/docs/team/yitong241.md b/docs/team/yitong241.md new file mode 100644 index 00000000000..7a7c1a72094 --- /dev/null +++ b/docs/team/yitong241.md @@ -0,0 +1,70 @@ +--- +layout: page +title: Sun Yitong's Project Portfolio Page +--- + +### Project: MedInfo + +MedInfo is a desktop application for private hospital administrative staff. It helps manage patients, +their status, discharge dates and wards. MedInfo aims to solve the problem of slow, multiple step process of +documenting patient medical records during in-processing by zeroing in on the important details and provide simple, +fast access to a particular patient’s medical records for hospital admin staff. + +The user interacts with MedInfo using a CLI, and it has a GUI created with JavaFX. +MedInfo is written in Java, and has about 10 kLoC. + +Given below are my contributions to the project. + +* **New Feature**: + * `status` attribute [#95](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/95) + * represent the medical status of the patients with `status` + * `find` command [#110](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/110) + * find the patient by searching for specific `name`, `nric`, `status` and `ward`. + * `addward` command [#143](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/131) + * add new wards into the MedInfo system + * `deleteward` command [#156](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/156) + * delete an existing ward in the system + * `sort` command [#159](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/159) + * sort the displayed list of patients by `name`, `status`, `ward name` and `discharge date` in `ascending` or `descending` order. + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=yitong241&breakdown=true) +* **PR contributed**: [PR](https://github.com/AY2223S2-CS2103T-T12-2/tp/pulls?page=1&q=is%3Apr+is%3Aclosed+author%3Ayitong241) + +* **Project management**: + - Active reviewer for code-related Pull Requests; + - Active contributor of issue tracker; + - Manage the deadlines and tasks for teammates; + +* **Enhancements to existing features**: + *Modify the logic to adapt to MedInfo [#93](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/93) + * `add` command + * add new patients with specific `name`, `nric`, `status` and `ward` [#115](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/115) + * `find` command + * modify FindCommand to find based on `name`, `nric`, or `status` [#110](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/110) + * add confirmation windows when clearing [#151](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/131) + +* **Documentation**: + * User Guide: + * `sort-by` command + * refine overall structure (table of contents, summary table) + * Developer Guide: + * Table of content [#165](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/165) + * `add` command and corresponding use cases [#131](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/131) + * `sort` command and corresponding explanations [#165](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/165) + +* **Community**: + * Review many code-related PRs by the whole group with detailed insights and comments + * [Reviewed PRs](https://github.com/AY2223S2-CS2103T-T12-2/tp/pulls?q=is%3Apr+is%3Aclosed+reviewed-by%3Ayitong241) + * Refactor the application from AB3 to MedInfo completely + * [Link to PR](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/177) + +* **Issues Fixed**: + * Fix message constraints bug in Storage [#94](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/94) + * Fix bugs caused by improper use of Enum [#95](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/95) + * Fix bugs caused by inputting multiple prefixes when finding [#116](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/116) + * Fix issue with sort command error message display [#167](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/167) + * Fix issue with date time validation [#234](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/234) + +* **Testing**: + * Add test cases for find command [#123](https://github.com/AY2223S2-CS2103T-T12-2/tp/pull/123) + diff --git a/docs/tutorials/AddRemark.md b/docs/tutorials/AddRemark.md index 880c701042f..4ea4578a334 100644 --- a/docs/tutorials/AddRemark.md +++ b/docs/tutorials/AddRemark.md @@ -23,12 +23,12 @@ For now, let’s keep `RemarkCommand` as simple as possible and print some outpu **`RemarkCommand.java`:** ``` java -package seedu.address.logic.commands; +package seedu.medinfo.logic.commands; -import seedu.address.model.Model; +import seedu.medinfo.model.Model; /** - * Changes the remark of an existing person in the address book. + * Changes the remark of an existing patient in the address book. */ public class RemarkCommand extends Command { @@ -65,8 +65,8 @@ Following the convention in other commands, we add relevant messages as constant ``` java public static final String MESSAGE_USAGE = COMMAND_WORD - + ": Edits the remark of the person identified " - + "by the index number used in the last person listing. " + + ": Edits the remark of the patient identified " + + "by the index number used in the last patient listing. " + "Existing remark will be overwritten by the input.\n" + "Parameters: INDEX (must be a positive integer) " + "r/ [REMARK]\n" @@ -91,7 +91,7 @@ Let’s change `RemarkCommand` to parse input from the user. We start by modifying the constructor of `RemarkCommand` to accept an `Index` and a `String`. While we are at it, let’s change the error message to echo the values. While this is not a replacement for tests, it is an obvious way to tell if our code is functioning as intended. ``` java -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.medinfo.commons.util.CollectionUtil.requireAllNonNull; //... public class RemarkCommand extends Command { //... @@ -101,8 +101,8 @@ public class RemarkCommand extends Command { private final String remark; /** - * @param index of the person in the filtered person list to edit the remark - * @param remark of the person to be updated to + * @param index of the patient in the filtered patient list to edit the remark + * @param remark of the patient to be updated to */ public RemarkCommand(Index index, String remark) { requireAllNonNull(index, remark); @@ -142,7 +142,7 @@ Your code should look something like [this](https://github.com/se-edu/addressboo Now let’s move on to writing a parser that will extract the index and remark from the input provided by the user. -Create a `RemarkCommandParser` class in the `seedu.address.logic.parser` package. The class must extend the `Parser` interface. +Create a `RemarkCommandParser` class in the `seedu.medinfo.logic.parser` package. The class must extend the `Parser` interface. ![The relationship between Parser and RemarkCommandParser](../images/add-remark/ParserInterface.png) @@ -225,11 +225,11 @@ If you are stuck, check out the sample ## Add `Remark` to the model -Now that we have all the information that we need, let’s lay the groundwork for propagating the remarks added into the in-memory storage of person data. We achieve that by working with the `Person` model. Each field in a Person is implemented as a separate class (e.g. a `Name` object represents the person’s name). That means we should add a `Remark` class so that we can use a `Remark` object to represent a remark given to a person. +Now that we have all the information that we need, let’s lay the groundwork for propagating the remarks added into the in-memory storage of patient data. We achieve that by working with the `Person` model. Each field in a Person is implemented as a separate class (e.g. a `Name` object represents the patient’s name). That means we should add a `Remark` class so that we can use a `Remark` object to represent a remark given to a patient. ### Add a new `Remark` class -Create a new `Remark` in `seedu.address.model.person`. Since a `Remark` is a field that is similar to `Address`, we can reuse a significant bit of code. +Create a new `Remark` in `seedu.medinfo.model.patient`. Since a `Remark` is a field that is similar to `Address`, we can reuse a significant bit of code. A copy-paste and search-replace later, you should have something like [this](https://github.com/se-edu/addressbook-level3/commit/4516e099699baa9e2d51801bd26f016d812dedcc#diff-41bb13c581e280c686198251ad6cc337cd5e27032772f06ed9bf7f1440995ece). Note how `Remark` has no constrains and thus does not require input validation. @@ -240,9 +240,9 @@ Let’s change `RemarkCommand` and `RemarkCommandParser` to use the new `Remark` ## Add a placeholder element for remark to the UI -Without getting too deep into `fxml`, let’s go on a 5 minute adventure to get some placeholder text to show up for each person. +Without getting too deep into `fxml`, let’s go on a 5 minute adventure to get some placeholder text to show up for each patient. -Simply add the following to [`seedu.address.ui.PersonCard`](https://github.com/se-edu/addressbook-level3/commit/850b78879582f38accb05dd20c245963c65ea599#diff-639834f1e05afe2276a86372adf0fe5f69314642c2d93cfa543d614ce5a76688). +Simply add the following to [`seedu.medinfo.ui.PatientCard`](https://github.com/se-edu/addressbook-level3/commit/850b78879582f38accb05dd20c245963c65ea599#diff-639834f1e05afe2276a86372adf0fe5f69314642c2d93cfa543d614ce5a76688). **`PersonCard.java`:** @@ -311,9 +311,9 @@ Just add [this one line of code!](https://github.com/se-edu/addressbook-level3/c **`PersonCard.java`:** ``` java -public PersonCard(Person person, int displayedIndex) { +public PersonCard(Person patient, int displayedIndex) { //... - remark.setText(person.getRemark().value); + remark.setText(patient.getRemark().value); } ``` @@ -343,25 +343,25 @@ save it with `Model#setPerson()`. throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); } - Person personToEdit = lastShownList.get(index.getZeroBased()); - Person editedPerson = new Person( - personToEdit.getName(), personToEdit.getPhone(), personToEdit.getEmail(), - personToEdit.getAddress(), remark, personToEdit.getTags()); + Person patientToEdit = lastShownList.get(index.getZeroBased()); + Person editedPatient = new Person( + patientToEdit.getName(), patientToEdit.getPhone(), patientToEdit.getEmail(), + patientToEdit.getAddress(), remark, patientToEdit.getTags()); - model.setPerson(personToEdit, editedPerson); + model.setPerson(patientToEdit, editedPatient); model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); - return new CommandResult(generateSuccessMessage(editedPerson)); + return new CommandResult(generateSuccessMessage(editedPatient)); } /** * Generates a command execution success message based on whether * the remark is added to or removed from - * {@code personToEdit}. + * {@code patientToEdit}. */ - private String generateSuccessMessage(Person personToEdit) { + private String generateSuccessMessage(Person patientToEdit) { String message = !remark.value.isEmpty() ? MESSAGE_ADD_REMARK_SUCCESS : MESSAGE_DELETE_REMARK_SUCCESS; - return String.format(message, personToEdit); + return String.format(message, patientToEdit); } ``` diff --git a/docs/tutorials/RemovingFields.md b/docs/tutorials/RemovingFields.md index f29169bc924..79bb6b0b14f 100644 --- a/docs/tutorials/RemovingFields.md +++ b/docs/tutorials/RemovingFields.md @@ -28,7 +28,7 @@ IntelliJ IDEA provides a refactoring tool that can identify *most* parts of a re ### Assisted refactoring -The `address` field in `Person` is actually an instance of the `seedu.address.model.person.Address` class. Since removing the `Address` class will break the application, we start by identifying `Address`'s usages. This allows us to see code that depends on `Address` to function properly and edit them on a case-by-case basis. Right-click the `Address` class and select `Refactor` \> `Safe Delete` through the menu. +The `address` field in `Person` is actually an instance of the `seedu.medinfo.model.patient.Address` class. Since removing the `Address` class will break the application, we start by identifying `Address`'s usages. This allows us to see code that depends on `Address` to function properly and edit them on a case-by-case basis. Right-click the `Address` class and select `Refactor` \> `Safe Delete` through the menu. * :bulb: To make things simpler, you can unselect the options `Search in comments and strings` and `Search for text occurrences` ![Usages detected](../images/remove/UnsafeDelete.png) @@ -100,7 +100,7 @@ In `src/test/data/`, data meant for testing purposes are stored. While keeping t ```json { - "persons": [ { + "patients": [ { "name": "Person with invalid name field: Ha!ns Mu@ster", "phone": "9482424", "email": "hans@example.com", diff --git a/docs/tutorials/TracingCode.md b/docs/tutorials/TracingCode.md index 4fb62a83ef6..230ede22b03 100644 --- a/docs/tutorials/TracingCode.md +++ b/docs/tutorials/TracingCode.md @@ -39,7 +39,7 @@ In our case, we would want to begin the tracing at the very point where the App -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`. +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.medinfo.logic.Logic`. @@ -48,7 +48,7 @@ According to the sequence diagram you saw earlier (and repeated above for refere :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`. -A quick look at the `seedu.address.logic.Logic` (an extract given below) confirms that this indeed might be what we’re looking for. +A quick look at the `seedu.medinfo.logic.Logic` (an extract given below) confirms that this indeed might be what we’re looking for. ```java public interface Logic { @@ -189,22 +189,22 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [ @Override public CommandResult execute(Model model) throws CommandException { ... - Person personToEdit = lastShownList.get(index.getZeroBased()); - Person editedPerson = createEditedPerson(personToEdit, editPersonDescriptor); - if (!personToEdit.isSamePerson(editedPerson) && model.hasPerson(editedPerson)) { + Person patientToEdit = lastShownList.get(index.getZeroBased()); + Person editedPatient = createEditedPerson(patientToEdit, editPatientDescriptor); + if (!patientToEdit.isSamePerson(editedPatient) && model.hasPerson(editedPatient)) { throw new CommandException(MESSAGE_DUPLICATE_PERSON); } - model.setPerson(personToEdit, editedPerson); + model.setPerson(patientToEdit, editedPatient); model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); - return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, editedPerson)); + return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, editedPatient)); } ``` 1. As suspected, `command#execute()` does indeed make changes to the `model` object. Specifically, - * it uses the `setPerson()` method (defined in the interface `Model` and implemented in `ModelManager` as per the usual pattern) to update the person data. - * 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. + * it uses the `setPerson()` method (defined in the interface `Model` and implemented in `ModelManager` as per the usual pattern) to update the patient data. + * it uses the `updateFilteredPersonList` method to ask the `Model` to populate the 'filtered list' with _all_ patients.
+ FYI, The 'filtered list' is the list of patients 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 patients so that the user can see the edited patient along with all other patients. 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 patients is being tracked.
* :bulb: This may be a good time to read through the [`Model` component section of the DG](../DeveloperGuide.html#model-component) @@ -231,7 +231,7 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [ * {@code JsonSerializableAddressBook}. */ public JsonSerializableAddressBook(ReadOnlyAddressBook source) { - persons.addAll( + patients.addAll( source.getPersonList() .stream() .map(JsonAdaptedPerson::new) diff --git a/src/main/java/seedu/address/commons/core/Messages.java b/src/main/java/seedu/address/commons/core/Messages.java deleted file mode 100644 index 1deb3a1e469..00000000000 --- a/src/main/java/seedu/address/commons/core/Messages.java +++ /dev/null @@ -1,13 +0,0 @@ -package seedu.address.commons.core; - -/** - * Container for user visible messages. - */ -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_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!"; - -} diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/address/logic/Logic.java deleted file mode 100644 index 92cd8fa605a..00000000000 --- a/src/main/java/seedu/address/logic/Logic.java +++ /dev/null @@ -1,50 +0,0 @@ -package seedu.address.logic; - -import java.nio.file.Path; - -import javafx.collections.ObservableList; -import seedu.address.commons.core.GuiSettings; -import seedu.address.logic.commands.CommandResult; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; - -/** - * API of the Logic component - */ -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. - */ - CommandResult execute(String commandText) throws CommandException, ParseException; - - /** - * Returns the AddressBook. - * - * @see seedu.address.model.Model#getAddressBook() - */ - ReadOnlyAddressBook getAddressBook(); - - /** Returns an unmodifiable view of the filtered list of persons */ - ObservableList getFilteredPersonList(); - - /** - * Returns the user prefs' address book file path. - */ - Path getAddressBookFilePath(); - - /** - * Returns the user prefs' GUI settings. - */ - GuiSettings getGuiSettings(); - - /** - * Set the user prefs' GUI settings. - */ - void setGuiSettings(GuiSettings guiSettings); -} diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/address/logic/commands/AddCommand.java deleted file mode 100644 index 71656d7c5c8..00000000000 --- a/src/main/java/seedu/address/logic/commands/AddCommand.java +++ /dev/null @@ -1,67 +0,0 @@ -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_EMAIL; -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 seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; -import seedu.address.model.person.Person; - -/** - * Adds a person to the address book. - */ -public class AddCommand extends Command { - - public static final String COMMAND_WORD = "add"; - - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a person to the address book. " - + "Parameters: " - + PREFIX_NAME + "NAME " - + PREFIX_PHONE + "PHONE " - + PREFIX_EMAIL + "EMAIL " - + PREFIX_ADDRESS + "ADDRESS " - + "[" + PREFIX_TAG + "TAG]...\n" - + "Example: " + COMMAND_WORD + " " - + PREFIX_NAME + "John Doe " - + PREFIX_PHONE + "98765432 " - + PREFIX_EMAIL + "johnd@example.com " - + PREFIX_ADDRESS + "311, Clementi Ave 2, #02-25 " - + PREFIX_TAG + "friends " - + PREFIX_TAG + "owesMoney"; - - 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"; - - private final Person toAdd; - - /** - * Creates an AddCommand to add the specified {@code Person} - */ - public AddCommand(Person person) { - requireNonNull(person); - toAdd = person; - } - - @Override - public CommandResult execute(Model model) throws CommandException { - requireNonNull(model); - - if (model.hasPerson(toAdd)) { - throw new CommandException(MESSAGE_DUPLICATE_PERSON); - } - - model.addPerson(toAdd); - return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof AddCommand // instanceof handles nulls - && toAdd.equals(((AddCommand) other).toAdd)); - } -} 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/DeleteCommand.java b/src/main/java/seedu/address/logic/commands/DeleteCommand.java deleted file mode 100644 index 02fd256acba..00000000000 --- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java +++ /dev/null @@ -1,53 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; - -import java.util.List; - -import seedu.address.commons.core.Messages; -import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; -import seedu.address.model.person.Person; - -/** - * Deletes a person identified using it's displayed index from the address book. - */ -public class DeleteCommand extends Command { - - public static final String COMMAND_WORD = "delete"; - - public static final String MESSAGE_USAGE = COMMAND_WORD - + ": Deletes 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_DELETE_PERSON_SUCCESS = "Deleted Person: %1$s"; - - private final Index targetIndex; - - public DeleteCommand(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 personToDelete = lastShownList.get(targetIndex.getZeroBased()); - model.deletePerson(personToDelete); - return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, personToDelete)); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof DeleteCommand // instanceof handles nulls - && targetIndex.equals(((DeleteCommand) other).targetIndex)); // state check - } -} diff --git a/src/main/java/seedu/address/logic/commands/EditCommand.java b/src/main/java/seedu/address/logic/commands/EditCommand.java deleted file mode 100644 index 7e36114902f..00000000000 --- a/src/main/java/seedu/address/logic/commands/EditCommand.java +++ /dev/null @@ -1,226 +0,0 @@ -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_EMAIL; -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; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; - -import seedu.address.commons.core.Messages; -import seedu.address.commons.core.index.Index; -import seedu.address.commons.util.CollectionUtil; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * 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 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" - + "Example: " + COMMAND_WORD + " 1 " - + PREFIX_PHONE + "91234567 " - + PREFIX_EMAIL + "johndoe@example.com"; - - public static final String MESSAGE_EDIT_PERSON_SUCCESS = "Edited Person: %1$s"; - public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; - public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book."; - - private final Index index; - private final EditPersonDescriptor editPersonDescriptor; - - /** - * @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); - requireNonNull(editPersonDescriptor); - - this.index = index; - this.editPersonDescriptor = new EditPersonDescriptor(editPersonDescriptor); - } - - @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 personToEdit = lastShownList.get(index.getZeroBased()); - Person editedPerson = createEditedPerson(personToEdit, editPersonDescriptor); - - if (!personToEdit.isSamePerson(editedPerson) && model.hasPerson(editedPerson)) { - throw new CommandException(MESSAGE_DUPLICATE_PERSON); - } - - model.setPerson(personToEdit, editedPerson); - model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); - return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, editedPerson)); - } - - /** - * Creates and returns a {@code Person} with the details of {@code personToEdit} - * edited with {@code editPersonDescriptor}. - */ - private static Person createEditedPerson(Person personToEdit, EditPersonDescriptor editPersonDescriptor) { - assert personToEdit != null; - - Name updatedName = editPersonDescriptor.getName().orElse(personToEdit.getName()); - Phone updatedPhone = editPersonDescriptor.getPhone().orElse(personToEdit.getPhone()); - 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); - } - - @Override - public boolean equals(Object other) { - // short circuit if same object - if (other == this) { - return true; - } - - // instanceof handles nulls - if (!(other instanceof EditCommand)) { - return false; - } - - // state check - EditCommand e = (EditCommand) other; - return index.equals(e.index) - && editPersonDescriptor.equals(e.editPersonDescriptor); - } - - /** - * 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 Email email; - private Address address; - private Set tags; - - public EditPersonDescriptor() {} - - /** - * Copy constructor. - * A defensive copy of {@code tags} is used internally. - */ - public EditPersonDescriptor(EditPersonDescriptor toCopy) { - setName(toCopy.name); - setPhone(toCopy.phone); - setEmail(toCopy.email); - setAddress(toCopy.address); - setTags(toCopy.tags); - } - - /** - * Returns true if at least one field is edited. - */ - public boolean isAnyFieldEdited() { - return CollectionUtil.isAnyNonNull(name, phone, email, address, tags); - } - - public void setName(Name name) { - this.name = name; - } - - public Optional getName() { - return Optional.ofNullable(name); - } - - public void setPhone(Phone phone) { - this.phone = phone; - } - - public Optional getPhone() { - return Optional.ofNullable(phone); - } - - public void setEmail(Email email) { - this.email = email; - } - - public Optional getEmail() { - return Optional.ofNullable(email); - } - - public void setAddress(Address address) { - this.address = address; - } - - public Optional
getAddress() { - return Optional.ofNullable(address); - } - - /** - * Sets {@code tags} to this object's {@code tags}. - * A defensive copy of {@code tags} is used internally. - */ - public void setTags(Set tags) { - this.tags = (tags != null) ? new HashSet<>(tags) : null; - } - - /** - * Returns an unmodifiable tag set, which throws {@code UnsupportedOperationException} - * if modification is attempted. - * Returns {@code Optional#empty()} if {@code tags} is null. - */ - public Optional> getTags() { - return (tags != null) ? Optional.of(Collections.unmodifiableSet(tags)) : Optional.empty(); - } - - @Override - public boolean equals(Object other) { - // short circuit if same object - if (other == this) { - return true; - } - - // instanceof handles nulls - if (!(other instanceof EditPersonDescriptor)) { - return false; - } - - // state check - EditPersonDescriptor e = (EditPersonDescriptor) other; - - return getName().equals(e.getName()) - && getPhone().equals(e.getPhone()) - && getEmail().equals(e.getEmail()) - && getAddress().equals(e.getAddress()) - && getTags().equals(e.getTags()); - } - } -} diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/seedu/address/logic/commands/ExitCommand.java deleted file mode 100644 index 3dd85a8ba90..00000000000 --- a/src/main/java/seedu/address/logic/commands/ExitCommand.java +++ /dev/null @@ -1,19 +0,0 @@ -package seedu.address.logic.commands; - -import seedu.address.model.Model; - -/** - * Terminates the program. - */ -public class ExitCommand extends Command { - - public static final String COMMAND_WORD = "exit"; - - public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting Address Book as requested ..."; - - @Override - public CommandResult execute(Model model) { - return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true); - } - -} diff --git a/src/main/java/seedu/address/logic/commands/FindCommand.java b/src/main/java/seedu/address/logic/commands/FindCommand.java deleted file mode 100644 index d6b19b0a0de..00000000000 --- a/src/main/java/seedu/address/logic/commands/FindCommand.java +++ /dev/null @@ -1,42 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; - -import seedu.address.commons.core.Messages; -import seedu.address.model.Model; -import seedu.address.model.person.NameContainsKeywordsPredicate; - -/** - * 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 MESSAGE_USAGE = COMMAND_WORD + ": Finds all persons whose names contain any of " - + "the specified keywords (case-insensitive) and displays them as a list with index numbers.\n" - + "Parameters: KEYWORD [MORE_KEYWORDS]...\n" - + "Example: " + COMMAND_WORD + " alice bob charlie"; - - private final NameContainsKeywordsPredicate predicate; - - public FindCommand(NameContainsKeywordsPredicate predicate) { - this.predicate = predicate; - } - - @Override - public CommandResult execute(Model model) { - requireNonNull(model); - model.updateFilteredPersonList(predicate); - return new CommandResult( - String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredPersonList().size())); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof FindCommand // instanceof handles nulls - && predicate.equals(((FindCommand) other).predicate)); // state check - } -} diff --git a/src/main/java/seedu/address/logic/commands/ListCommand.java b/src/main/java/seedu/address/logic/commands/ListCommand.java deleted file mode 100644 index 84be6ad2596..00000000000 --- a/src/main/java/seedu/address/logic/commands/ListCommand.java +++ /dev/null @@ -1,24 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; -import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; - -import seedu.address.model.Model; - -/** - * Lists all persons in the address book to the user. - */ -public class ListCommand extends Command { - - public static final String COMMAND_WORD = "list"; - - public static final String MESSAGE_SUCCESS = "Listed all persons"; - - - @Override - public CommandResult execute(Model model) { - requireNonNull(model); - model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); - return new CommandResult(MESSAGE_SUCCESS); - } -} diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/seedu/address/logic/parser/AddCommandParser.java deleted file mode 100644 index 3b8bfa035e8..00000000000 --- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java +++ /dev/null @@ -1,60 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -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.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.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * Parses input arguments and creates a new AddCommand object - */ -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); - - if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL) - || !argMultimap.getPreamble().isEmpty()) { - throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); - } - - Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()); - Phone phone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).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)); - - Person person = new Person(name, phone, email, address, tagList); - - return new AddCommand(person); - } - - /** - * 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 deleted file mode 100644 index 1e466792b46..00000000000 --- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java +++ /dev/null @@ -1,76 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import seedu.address.logic.commands.AddCommand; -import seedu.address.logic.commands.ClearCommand; -import seedu.address.logic.commands.Command; -import seedu.address.logic.commands.DeleteCommand; -import seedu.address.logic.commands.EditCommand; -import seedu.address.logic.commands.ExitCommand; -import seedu.address.logic.commands.FindCommand; -import seedu.address.logic.commands.HelpCommand; -import seedu.address.logic.commands.ListCommand; -import seedu.address.logic.parser.exceptions.ParseException; - -/** - * Parses user input. - */ -public class AddressBookParser { - - /** - * Used for initial separation of command word and args. - */ - private static final Pattern BASIC_COMMAND_FORMAT = Pattern.compile("(?\\S+)(?.*)"); - - /** - * Parses user input into command for execution. - * - * @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 - */ - public Command parseCommand(String userInput) throws ParseException { - final Matcher matcher = BASIC_COMMAND_FORMAT.matcher(userInput.trim()); - if (!matcher.matches()) { - throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE)); - } - - final String commandWord = matcher.group("commandWord"); - final String arguments = matcher.group("arguments"); - switch (commandWord) { - - case AddCommand.COMMAND_WORD: - return new AddCommandParser().parse(arguments); - - case EditCommand.COMMAND_WORD: - return new EditCommandParser().parse(arguments); - - case DeleteCommand.COMMAND_WORD: - return new DeleteCommandParser().parse(arguments); - - case ClearCommand.COMMAND_WORD: - return new ClearCommand(); - - case FindCommand.COMMAND_WORD: - return new FindCommandParser().parse(arguments); - - case ListCommand.COMMAND_WORD: - return new ListCommand(); - - case ExitCommand.COMMAND_WORD: - return new ExitCommand(); - - case HelpCommand.COMMAND_WORD: - return new HelpCommand(); - - default: - throw new ParseException(MESSAGE_UNKNOWN_COMMAND); - } - } - -} diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java deleted file mode 100644 index 75b1a9bf119..00000000000 --- a/src/main/java/seedu/address/logic/parser/CliSyntax.java +++ /dev/null @@ -1,15 +0,0 @@ -package seedu.address.logic.parser; - -/** - * Contains Command Line Interface (CLI) syntax definitions common to multiple commands - */ -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_EMAIL = new Prefix("e/"); - public static final Prefix PREFIX_ADDRESS = new Prefix("a/"); - public static final Prefix PREFIX_TAG = new Prefix("t/"); - -} diff --git a/src/main/java/seedu/address/logic/parser/EditCommandParser.java b/src/main/java/seedu/address/logic/parser/EditCommandParser.java deleted file mode 100644 index 845644b7dea..00000000000 --- a/src/main/java/seedu/address/logic/parser/EditCommandParser.java +++ /dev/null @@ -1,82 +0,0 @@ -package seedu.address.logic.parser; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -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; -import java.util.Optional; -import java.util.Set; - -import seedu.address.commons.core.index.Index; -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; - -/** - * Parses input arguments and creates a new EditCommand object - */ -public class EditCommandParser 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 EditCommand parse(String args) throws ParseException { - requireNonNull(args); - ArgumentMultimap argMultimap = - ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG); - - Index index; - - try { - index = ParserUtil.parseIndex(argMultimap.getPreamble()); - } catch (ParseException pe) { - throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE), pe); - } - - EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor(); - if (argMultimap.getValue(PREFIX_NAME).isPresent()) { - editPersonDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).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_ADDRESS).isPresent()) { - editPersonDescriptor.setAddress(ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get())); - } - parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editPersonDescriptor::setTags); - - if (!editPersonDescriptor.isAnyFieldEdited()) { - throw new ParseException(EditCommand.MESSAGE_NOT_EDITED); - } - - return new EditCommand(index, editPersonDescriptor); - } - - /** - * 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. - */ - private Optional> parseTagsForEdit(Collection tags) throws ParseException { - assert tags != null; - - if (tags.isEmpty()) { - return Optional.empty(); - } - Collection tagSet = tags.size() == 1 && tags.contains("") ? Collections.emptySet() : tags; - return Optional.of(ParserUtil.parseTags(tagSet)); - } - -} diff --git a/src/main/java/seedu/address/logic/parser/FindCommandParser.java b/src/main/java/seedu/address/logic/parser/FindCommandParser.java deleted file mode 100644 index 4fb71f23103..00000000000 --- a/src/main/java/seedu/address/logic/parser/FindCommandParser.java +++ /dev/null @@ -1,33 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; - -import java.util.Arrays; - -import seedu.address.logic.commands.FindCommand; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.NameContainsKeywordsPredicate; - -/** - * Parses input arguments and creates a new FindCommand object - */ -public class FindCommandParser implements Parser { - - /** - * Parses the given {@code String} of arguments in the context of the FindCommand - * and returns a FindCommand object for execution. - * @throws ParseException if the user input does not conform the expected format - */ - public FindCommand parse(String args) throws ParseException { - String trimmedArgs = args.trim(); - if (trimmedArgs.isEmpty()) { - throw new ParseException( - String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); - } - - String[] nameKeywords = trimmedArgs.split("\\s+"); - - return new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList(nameKeywords))); - } - -} diff --git a/src/main/java/seedu/address/logic/parser/ParserUtil.java b/src/main/java/seedu/address/logic/parser/ParserUtil.java deleted file mode 100644 index b117acb9c55..00000000000 --- a/src/main/java/seedu/address/logic/parser/ParserUtil.java +++ /dev/null @@ -1,124 +0,0 @@ -package seedu.address.logic.parser; - -import static java.util.Objects.requireNonNull; - -import java.util.Collection; -import java.util.HashSet; -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.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * Contains utility methods used for parsing strings in the various *Parser classes. - */ -public class ParserUtil { - - public static final String MESSAGE_INVALID_INDEX = "Index is not a non-zero unsigned integer."; - - /** - * 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). - */ - public static Index parseIndex(String oneBasedIndex) throws ParseException { - String trimmedIndex = oneBasedIndex.trim(); - if (!StringUtil.isNonZeroUnsignedInteger(trimmedIndex)) { - throw new ParseException(MESSAGE_INVALID_INDEX); - } - return Index.fromOneBased(Integer.parseInt(trimmedIndex)); - } - - /** - * Parses a {@code String name} into a {@code Name}. - * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code name} is invalid. - */ - public static Name parseName(String name) throws ParseException { - requireNonNull(name); - String trimmedName = name.trim(); - if (!Name.isValidName(trimmedName)) { - throw new ParseException(Name.MESSAGE_CONSTRAINTS); - } - return new Name(trimmedName); - } - - /** - * Parses a {@code String phone} into a {@code Phone}. - * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code phone} is invalid. - */ - public static Phone parsePhone(String phone) throws ParseException { - requireNonNull(phone); - String trimmedPhone = phone.trim(); - if (!Phone.isValidPhone(trimmedPhone)) { - throw new ParseException(Phone.MESSAGE_CONSTRAINTS); - } - return new Phone(trimmedPhone); - } - - /** - * Parses a {@code String address} into an {@code Address}. - * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code address} 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); - } - return new Address(trimmedAddress); - } - - /** - * Parses a {@code String email} into an {@code Email}. - * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code email} is invalid. - */ - public static Email parseEmail(String email) throws ParseException { - requireNonNull(email); - String trimmedEmail = email.trim(); - if (!Email.isValidEmail(trimmedEmail)) { - throw new ParseException(Email.MESSAGE_CONSTRAINTS); - } - return new Email(trimmedEmail); - } - - /** - * Parses a {@code String tag} into a {@code Tag}. - * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code tag} 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); - } - return new Tag(trimmedTag); - } - - /** - * Parses {@code Collection tags} into a {@code Set}. - */ - public static Set parseTags(Collection tags) throws ParseException { - requireNonNull(tags); - final Set tagSet = new HashSet<>(); - for (String tagName : tags) { - tagSet.add(parseTag(tagName)); - } - return tagSet; - } -} diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java deleted file mode 100644 index 1a943a0781a..00000000000 --- a/src/main/java/seedu/address/model/AddressBook.java +++ /dev/null @@ -1,120 +0,0 @@ -package seedu.address.model; - -import static java.util.Objects.requireNonNull; - -import java.util.List; - -import javafx.collections.ObservableList; -import seedu.address.model.person.Person; -import seedu.address.model.person.UniquePersonList; - -/** - * Wraps all data at the address-book level - * Duplicates are not allowed (by .isSamePerson comparison) - */ -public class AddressBook implements ReadOnlyAddressBook { - - private final UniquePersonList persons; - - /* - * 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. - */ - { - persons = new UniquePersonList(); - } - - public AddressBook() {} - - /** - * Creates an AddressBook using the Persons in the {@code toBeCopied} - */ - public AddressBook(ReadOnlyAddressBook toBeCopied) { - this(); - resetData(toBeCopied); - } - - //// list overwrite operations - - /** - * Replaces the contents of the person list with {@code persons}. - * {@code persons} must not contain duplicate persons. - */ - public void setPersons(List persons) { - this.persons.setPersons(persons); - } - - /** - * Resets the existing data of this {@code AddressBook} with {@code newData}. - */ - public void resetData(ReadOnlyAddressBook newData) { - requireNonNull(newData); - - setPersons(newData.getPersonList()); - } - - //// person-level operations - - /** - * 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); - } - - /** - * Adds a person to the address book. - * The person must not already exist in the address book. - */ - public void addPerson(Person p) { - persons.add(p); - } - - /** - * 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. - */ - public void setPerson(Person target, Person editedPerson) { - requireNonNull(editedPerson); - - persons.setPerson(target, editedPerson); - } - - /** - * Removes {@code key} from this {@code AddressBook}. - * {@code key} must exist in the address book. - */ - public void removePerson(Person key) { - persons.remove(key); - } - - //// util methods - - @Override - public String toString() { - return persons.asUnmodifiableObservableList().size() + " persons"; - // TODO: refine later - } - - @Override - public ObservableList getPersonList() { - return persons.asUnmodifiableObservableList(); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof AddressBook // instanceof handles nulls - && persons.equals(((AddressBook) other).persons)); - } - - @Override - public int hashCode() { - return persons.hashCode(); - } -} diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java deleted file mode 100644 index d54df471c1f..00000000000 --- a/src/main/java/seedu/address/model/Model.java +++ /dev/null @@ -1,87 +0,0 @@ -package seedu.address.model; - -import java.nio.file.Path; -import java.util.function.Predicate; - -import javafx.collections.ObservableList; -import seedu.address.commons.core.GuiSettings; -import seedu.address.model.person.Person; - -/** - * The API of the Model component. - */ -public interface Model { - /** {@code Predicate} that always evaluate to true */ - Predicate PREDICATE_SHOW_ALL_PERSONS = unused -> true; - - /** - * Replaces user prefs data with the data in {@code userPrefs}. - */ - void setUserPrefs(ReadOnlyUserPrefs userPrefs); - - /** - * Returns the user prefs. - */ - ReadOnlyUserPrefs getUserPrefs(); - - /** - * Returns the user prefs' GUI settings. - */ - GuiSettings getGuiSettings(); - - /** - * Sets the user prefs' GUI settings. - */ - void setGuiSettings(GuiSettings guiSettings); - - /** - * Returns the user prefs' address book file path. - */ - Path getAddressBookFilePath(); - - /** - * Sets the user prefs' address book file path. - */ - void setAddressBookFilePath(Path addressBookFilePath); - - /** - * Replaces address book data with the data in {@code addressBook}. - */ - void setAddressBook(ReadOnlyAddressBook addressBook); - - /** Returns the AddressBook */ - ReadOnlyAddressBook getAddressBook(); - - /** - * Returns true if a person with the same identity as {@code person} exists in the address book. - */ - boolean hasPerson(Person person); - - /** - * Deletes the given person. - * The person must exist in the address book. - */ - void deletePerson(Person target); - - /** - * Adds the given person. - * {@code person} must not already exist in the address book. - */ - void addPerson(Person person); - - /** - * 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. - */ - void setPerson(Person target, Person editedPerson); - - /** Returns an unmodifiable view of the filtered person list */ - ObservableList getFilteredPersonList(); - - /** - * 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); -} diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java deleted file mode 100644 index 86c1df298d7..00000000000 --- a/src/main/java/seedu/address/model/ModelManager.java +++ /dev/null @@ -1,150 +0,0 @@ -package seedu.address.model; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; - -import java.nio.file.Path; -import java.util.function.Predicate; -import java.util.logging.Logger; - -import javafx.collections.ObservableList; -import javafx.collections.transformation.FilteredList; -import seedu.address.commons.core.GuiSettings; -import seedu.address.commons.core.LogsCenter; -import seedu.address.model.person.Person; - -/** - * 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 final AddressBook addressBook; - private final UserPrefs userPrefs; - private final FilteredList filteredPersons; - - /** - * Initializes a ModelManager with the given addressBook and userPrefs. - */ - public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs) { - requireAllNonNull(addressBook, userPrefs); - - logger.fine("Initializing with address book: " + addressBook + " and user prefs " + userPrefs); - - this.addressBook = new AddressBook(addressBook); - this.userPrefs = new UserPrefs(userPrefs); - filteredPersons = new FilteredList<>(this.addressBook.getPersonList()); - } - - public ModelManager() { - this(new AddressBook(), new UserPrefs()); - } - - //=========== UserPrefs ================================================================================== - - @Override - public void setUserPrefs(ReadOnlyUserPrefs userPrefs) { - requireNonNull(userPrefs); - this.userPrefs.resetData(userPrefs); - } - - @Override - public ReadOnlyUserPrefs getUserPrefs() { - return userPrefs; - } - - @Override - public GuiSettings getGuiSettings() { - return userPrefs.getGuiSettings(); - } - - @Override - public void setGuiSettings(GuiSettings guiSettings) { - requireNonNull(guiSettings); - userPrefs.setGuiSettings(guiSettings); - } - - @Override - public Path getAddressBookFilePath() { - return userPrefs.getAddressBookFilePath(); - } - - @Override - public void setAddressBookFilePath(Path addressBookFilePath) { - requireNonNull(addressBookFilePath); - userPrefs.setAddressBookFilePath(addressBookFilePath); - } - - //=========== AddressBook ================================================================================ - - @Override - public void setAddressBook(ReadOnlyAddressBook addressBook) { - this.addressBook.resetData(addressBook); - } - - @Override - public ReadOnlyAddressBook getAddressBook() { - return addressBook; - } - - @Override - public boolean hasPerson(Person person) { - requireNonNull(person); - return addressBook.hasPerson(person); - } - - @Override - public void deletePerson(Person target) { - addressBook.removePerson(target); - } - - @Override - public void addPerson(Person person) { - addressBook.addPerson(person); - updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); - } - - @Override - public void setPerson(Person target, Person editedPerson) { - requireAllNonNull(target, editedPerson); - - addressBook.setPerson(target, editedPerson); - } - - //=========== Filtered Person List Accessors ============================================================= - - /** - * Returns an unmodifiable view of the list of {@code Person} backed by the internal list of - * {@code versionedAddressBook} - */ - @Override - public ObservableList getFilteredPersonList() { - return filteredPersons; - } - - @Override - public void updateFilteredPersonList(Predicate predicate) { - requireNonNull(predicate); - filteredPersons.setPredicate(predicate); - } - - @Override - public boolean equals(Object obj) { - // short circuit if same object - if (obj == this) { - return true; - } - - // instanceof handles nulls - if (!(obj instanceof ModelManager)) { - return false; - } - - // state check - ModelManager other = (ModelManager) obj; - return addressBook.equals(other.addressBook) - && userPrefs.equals(other.userPrefs) - && filteredPersons.equals(other.filteredPersons); - } - -} diff --git a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java deleted file mode 100644 index 6ddc2cd9a29..00000000000 --- a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java +++ /dev/null @@ -1,17 +0,0 @@ -package seedu.address.model; - -import javafx.collections.ObservableList; -import seedu.address.model.person.Person; - -/** - * Unmodifiable view of an address book - */ -public interface ReadOnlyAddressBook { - - /** - * Returns an unmodifiable view of the persons list. - * This list will not contain any duplicate persons. - */ - ObservableList getPersonList(); - -} 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 60472ca22a0..00000000000 --- a/src/main/java/seedu/address/model/person/Address.java +++ /dev/null @@ -1,57 +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) { - return other == this // short circuit if same object - || (other instanceof Address // instanceof handles nulls - && value.equals(((Address) other).value)); // state check - } - - @Override - public int hashCode() { - return value.hashCode(); - } - -} diff --git a/src/main/java/seedu/address/model/person/Email.java b/src/main/java/seedu/address/model/person/Email.java deleted file mode 100644 index f866e7133de..00000000000 --- a/src/main/java/seedu/address/model/person/Email.java +++ /dev/null @@ -1,71 +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 email in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidEmail(String)} - */ -public class Email { - - private static final String SPECIAL_CHARACTERS = "+_.-"; - public static final String MESSAGE_CONSTRAINTS = "Emails should be of the format local-part@domain " - + "and adhere to the following constraints:\n" - + "1. The local-part should only contain alphanumeric characters and these special characters, excluding " - + "the parentheses, (" + SPECIAL_CHARACTERS + "). The local-part may not start or end with any special " - + "characters.\n" - + "2. This is followed by a '@' and then a domain name. The domain name is made up of domain labels " - + "separated by periods.\n" - + "The domain name must:\n" - + " - end with a domain label at least 2 characters long\n" - + " - have each domain label start and end with alphanumeric characters\n" - + " - have each domain label consist of alphanumeric characters, separated only by hyphens, if any."; - // alphanumeric and special characters - private static final String ALPHANUMERIC_NO_UNDERSCORE = "[^\\W_]+"; // alphanumeric characters except underscore - private static final String LOCAL_PART_REGEX = "^" + ALPHANUMERIC_NO_UNDERSCORE + "([" + SPECIAL_CHARACTERS + "]" - + ALPHANUMERIC_NO_UNDERSCORE + ")*"; - private static final String DOMAIN_PART_REGEX = ALPHANUMERIC_NO_UNDERSCORE - + "(-" + ALPHANUMERIC_NO_UNDERSCORE + ")*"; - private static final String DOMAIN_LAST_PART_REGEX = "(" + DOMAIN_PART_REGEX + "){2,}$"; // At least two chars - private static final String DOMAIN_REGEX = "(" + DOMAIN_PART_REGEX + "\\.)*" + DOMAIN_LAST_PART_REGEX; - public static final String VALIDATION_REGEX = LOCAL_PART_REGEX + "@" + DOMAIN_REGEX; - - public final String value; - - /** - * Constructs an {@code Email}. - * - * @param email A valid email address. - */ - public Email(String email) { - requireNonNull(email); - checkArgument(isValidEmail(email), MESSAGE_CONSTRAINTS); - value = email; - } - - /** - * Returns if a given string is a valid email. - */ - public static boolean isValidEmail(String test) { - return test.matches(VALIDATION_REGEX); - } - - @Override - public String toString() { - return value; - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Email // instanceof handles nulls - && value.equals(((Email) other).value)); // state check - } - - @Override - public int hashCode() { - return value.hashCode(); - } - -} diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/address/model/person/Person.java deleted file mode 100644 index 8ff1d83fe89..00000000000 --- a/src/main/java/seedu/address/model/person/Person.java +++ /dev/null @@ -1,123 +0,0 @@ -package seedu.address.model.person; - -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; - -import java.util.Collections; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; - -import seedu.address.model.tag.Tag; - -/** - * Represents a Person in the address book. - * Guarantees: details are present and not null, field values are validated, immutable. - */ -public class Person { - - // Identity fields - private final Name name; - private final Phone phone; - private final Email email; - - // Data fields - private final Address address; - private final Set tags = new HashSet<>(); - - /** - * Every field must be present and not null. - */ - public Person(Name name, Phone phone, Email email, Address address, Set tags) { - requireAllNonNull(name, phone, email, address, tags); - this.name = name; - this.phone = phone; - this.email = email; - this.address = address; - this.tags.addAll(tags); - } - - public Name getName() { - return name; - } - - public Phone getPhone() { - return phone; - } - - public Email getEmail() { - return email; - } - - public Address getAddress() { - return address; - } - - /** - * Returns an immutable tag set, which throws {@code UnsupportedOperationException} - * if modification is attempted. - */ - public Set getTags() { - return Collections.unmodifiableSet(tags); - } - - /** - * Returns true if both persons have the same name. - * This defines a weaker notion of equality between two persons. - */ - public boolean isSamePerson(Person otherPerson) { - if (otherPerson == this) { - return true; - } - - return otherPerson != null - && otherPerson.getName().equals(getName()); - } - - /** - * Returns true if both persons have the same identity and data fields. - * This defines a stronger notion of equality between two persons. - */ - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - - if (!(other instanceof Person)) { - return false; - } - - Person otherPerson = (Person) other; - return otherPerson.getName().equals(getName()) - && otherPerson.getPhone().equals(getPhone()) - && otherPerson.getEmail().equals(getEmail()) - && otherPerson.getAddress().equals(getAddress()) - && otherPerson.getTags().equals(getTags()); - } - - @Override - public int hashCode() { - // use this method for custom fields hashing instead of implementing your own - return Objects.hash(name, phone, email, address, tags); - } - - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append(getName()) - .append("; Phone: ") - .append(getPhone()) - .append("; Email: ") - .append(getEmail()) - .append("; Address: ") - .append(getAddress()); - - Set tags = getTags(); - if (!tags.isEmpty()) { - builder.append("; Tags: "); - tags.forEach(builder::append); - } - return builder.toString(); - } - -} diff --git a/src/main/java/seedu/address/model/person/Phone.java b/src/main/java/seedu/address/model/person/Phone.java deleted file mode 100644 index 872c76b382f..00000000000 --- a/src/main/java/seedu/address/model/person/Phone.java +++ /dev/null @@ -1,53 +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 phone number in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidPhone(String)} - */ -public class Phone { - - - public static final String MESSAGE_CONSTRAINTS = - "Phone numbers should only contain numbers, and it should be at least 3 digits long"; - public static final String VALIDATION_REGEX = "\\d{3,}"; - public final String value; - - /** - * Constructs a {@code Phone}. - * - * @param phone A valid phone number. - */ - public Phone(String phone) { - requireNonNull(phone); - checkArgument(isValidPhone(phone), MESSAGE_CONSTRAINTS); - value = phone; - } - - /** - * Returns true if a given string is a valid phone number. - */ - public static boolean isValidPhone(String test) { - return test.matches(VALIDATION_REGEX); - } - - @Override - public String toString() { - return value; - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Phone // instanceof handles nulls - && value.equals(((Phone) other).value)); // state check - } - - @Override - public int hashCode() { - return value.hashCode(); - } - -} diff --git a/src/main/java/seedu/address/model/person/UniquePersonList.java b/src/main/java/seedu/address/model/person/UniquePersonList.java deleted file mode 100644 index 0fee4fe57e6..00000000000 --- a/src/main/java/seedu/address/model/person/UniquePersonList.java +++ /dev/null @@ -1,137 +0,0 @@ -package seedu.address.model.person; - -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.person.exceptions.DuplicatePersonException; -import seedu.address.model.person.exceptions.PersonNotFoundException; - -/** - * A list of persons that enforces uniqueness between its elements and does not allow nulls. - * A person is considered unique by comparing using {@code Person#isSamePerson(Person)}. As such, adding and updating of - * persons uses Person#isSamePerson(Person) for equality so as to ensure that the person being added or updated is - * unique in terms of identity in the UniquePersonList. However, the removal of a person uses Person#equals(Object) so - * as to ensure that the person with exactly the same fields will be removed. - * - * Supports a minimal set of list operations. - * - * @see Person#isSamePerson(Person) - */ -public class UniquePersonList implements Iterable { - - private final ObservableList internalList = FXCollections.observableArrayList(); - private final ObservableList internalUnmodifiableList = - FXCollections.unmodifiableObservableList(internalList); - - /** - * Returns true if the list contains an equivalent person as the given argument. - */ - public boolean contains(Person toCheck) { - requireNonNull(toCheck); - return internalList.stream().anyMatch(toCheck::isSamePerson); - } - - /** - * Adds a person to the list. - * The person must not already exist in the list. - */ - public void add(Person toAdd) { - requireNonNull(toAdd); - if (contains(toAdd)) { - throw new DuplicatePersonException(); - } - internalList.add(toAdd); - } - - /** - * Replaces the person {@code target} in the list with {@code editedPerson}. - * {@code target} must exist in the list. - * The person identity of {@code editedPerson} must not be the same as another existing person in the list. - */ - public void setPerson(Person target, Person editedPerson) { - requireAllNonNull(target, editedPerson); - - int index = internalList.indexOf(target); - if (index == -1) { - throw new PersonNotFoundException(); - } - - if (!target.isSamePerson(editedPerson) && contains(editedPerson)) { - throw new DuplicatePersonException(); - } - - internalList.set(index, editedPerson); - } - - /** - * Removes the equivalent person from the list. - * The person must exist in the list. - */ - public void remove(Person toRemove) { - requireNonNull(toRemove); - if (!internalList.remove(toRemove)) { - throw new PersonNotFoundException(); - } - } - - public void setPersons(UniquePersonList 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 setPersons(List persons) { - requireAllNonNull(persons); - if (!personsAreUnique(persons)) { - throw new DuplicatePersonException(); - } - - internalList.setAll(persons); - } - - /** - * 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) { - return other == this // short circuit if same object - || (other instanceof UniquePersonList // instanceof handles nulls - && internalList.equals(((UniquePersonList) other).internalList)); - } - - @Override - public int hashCode() { - return internalList.hashCode(); - } - - /** - * Returns true if {@code persons} contains only unique persons. - */ - private boolean personsAreUnique(List persons) { - for (int i = 0; i < persons.size() - 1; i++) { - for (int j = i + 1; j < persons.size(); j++) { - if (persons.get(i).isSamePerson(persons.get(j))) { - return false; - } - } - } - return true; - } -} diff --git a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java b/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java deleted file mode 100644 index d7290f59442..00000000000 --- a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java +++ /dev/null @@ -1,11 +0,0 @@ -package seedu.address.model.person.exceptions; - -/** - * Signals that the operation will result in duplicate Persons (Persons are considered duplicates if they have the same - * identity). - */ -public class DuplicatePersonException extends RuntimeException { - public DuplicatePersonException() { - super("Operation would result in duplicate persons"); - } -} diff --git a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java b/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java deleted file mode 100644 index fa764426ca7..00000000000 --- a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java +++ /dev/null @@ -1,6 +0,0 @@ -package seedu.address.model.person.exceptions; - -/** - * Signals that the operation is unable to find the specified person. - */ -public class PersonNotFoundException extends RuntimeException {} 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 b0ea7e7dad7..00000000000 --- a/src/main/java/seedu/address/model/tag/Tag.java +++ /dev/null @@ -1,54 +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) { - return other == this // short circuit if same object - || (other instanceof Tag // instanceof handles nulls - && tagName.equals(((Tag) other).tagName)); // state check - } - - @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 deleted file mode 100644 index 1806da4facf..00000000000 --- a/src/main/java/seedu/address/model/util/SampleDataUtil.java +++ /dev/null @@ -1,60 +0,0 @@ -package seedu.address.model.util; - -import java.util.Arrays; -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.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * Contains utility methods for populating {@code AddressBook} with sample data. - */ -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")) - }; - } - - public static ReadOnlyAddressBook getSampleAddressBook() { - AddressBook sampleAb = new AddressBook(); - for (Person samplePerson : getSamplePersons()) { - sampleAb.addPerson(samplePerson); - } - return sampleAb; - } - - /** - * Returns a tag set containing the list of strings given. - */ - public static Set getTagSet(String... strings) { - return Arrays.stream(strings) - .map(Tag::new) - .collect(Collectors.toSet()); - } - -} diff --git a/src/main/java/seedu/address/storage/AddressBookStorage.java b/src/main/java/seedu/address/storage/AddressBookStorage.java deleted file mode 100644 index 4599182b3f9..00000000000 --- a/src/main/java/seedu/address/storage/AddressBookStorage.java +++ /dev/null @@ -1,45 +0,0 @@ -package seedu.address.storage; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.Optional; - -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyAddressBook; - -/** - * Represents a storage for {@link seedu.address.model.AddressBook}. - */ -public interface AddressBookStorage { - - /** - * Returns the file path of the data file. - */ - Path getAddressBookFilePath(); - - /** - * Returns AddressBook data as a {@link ReadOnlyAddressBook}. - * Returns {@code Optional.empty()} if storage file is not found. - * @throws DataConversionException if the data in storage is not in the expected format. - * @throws IOException if there was any problem when reading from the storage. - */ - Optional readAddressBook() throws DataConversionException, IOException; - - /** - * @see #getAddressBookFilePath() - */ - Optional readAddressBook(Path filePath) throws DataConversionException, IOException; - - /** - * Saves the given {@link ReadOnlyAddressBook} to the storage. - * @param addressBook cannot be null. - * @throws IOException if there was any problem writing to the file. - */ - void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException; - - /** - * @see #saveAddressBook(ReadOnlyAddressBook) - */ - void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException; - -} diff --git a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java b/src/main/java/seedu/address/storage/JsonAdaptedPerson.java deleted file mode 100644 index a6321cec2ea..00000000000 --- a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java +++ /dev/null @@ -1,109 +0,0 @@ -package seedu.address.storage; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -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.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * Jackson-friendly version of {@link Person}. - */ -class JsonAdaptedPerson { - - public static final String MISSING_FIELD_MESSAGE_FORMAT = "Person's %s field is missing!"; - - private final String name; - private final String phone; - private final String email; - private final String address; - private final List tagged = 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("tagged") List tagged) { - this.name = name; - this.phone = phone; - this.email = email; - this.address = address; - if (tagged != null) { - this.tagged.addAll(tagged); - } - } - - /** - * Converts a given {@code Person} into this class for Jackson use. - */ - public JsonAdaptedPerson(Person source) { - name = source.getName().fullName; - phone = source.getPhone().value; - email = source.getEmail().value; - address = source.getAddress().value; - tagged.addAll(source.getTags().stream() - .map(JsonAdaptedTag::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 Person toModelType() throws IllegalValueException { - final List personTags = new ArrayList<>(); - for (JsonAdaptedTag tag : tagged) { - personTags.add(tag.toModelType()); - } - - 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 (phone == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName())); - } - if (!Phone.isValidPhone(phone)) { - throw new IllegalValueException(Phone.MESSAGE_CONSTRAINTS); - } - final Phone modelPhone = new Phone(phone); - - if (email == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.class.getSimpleName())); - } - if (!Email.isValidEmail(email)) { - throw new IllegalValueException(Email.MESSAGE_CONSTRAINTS); - } - final Email modelEmail = new Email(email); - - if (address == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName())); - } - if (!Address.isValidAddress(address)) { - throw new IllegalValueException(Address.MESSAGE_CONSTRAINTS); - } - final Address modelAddress = new Address(address); - - final Set modelTags = new HashSet<>(personTags); - return new Person(modelName, modelPhone, modelEmail, modelAddress, modelTags); - } - -} 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 deleted file mode 100644 index dfab9daaa0d..00000000000 --- a/src/main/java/seedu/address/storage/JsonAddressBookStorage.java +++ /dev/null @@ -1,80 +0,0 @@ -package seedu.address.storage; - -import static java.util.Objects.requireNonNull; - -import java.io.IOException; -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.DataConversionException; -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.commons.util.FileUtil; -import seedu.address.commons.util.JsonUtil; -import seedu.address.model.ReadOnlyAddressBook; - -/** - * A class to access AddressBook data stored as a json file on the hard disk. - */ -public class JsonAddressBookStorage implements AddressBookStorage { - - private static final Logger logger = LogsCenter.getLogger(JsonAddressBookStorage.class); - - private Path filePath; - - public JsonAddressBookStorage(Path filePath) { - this.filePath = filePath; - } - - public Path getAddressBookFilePath() { - return filePath; - } - - @Override - public Optional readAddressBook() throws DataConversionException { - return readAddressBook(filePath); - } - - /** - * Similar to {@link #readAddressBook()}. - * - * @param filePath location of the data. Cannot be null. - * @throws DataConversionException if the file is not in the correct format. - */ - public Optional readAddressBook(Path filePath) throws DataConversionException { - requireNonNull(filePath); - - Optional jsonAddressBook = JsonUtil.readJsonFile( - filePath, JsonSerializableAddressBook.class); - if (!jsonAddressBook.isPresent()) { - return Optional.empty(); - } - - try { - return Optional.of(jsonAddressBook.get().toModelType()); - } catch (IllegalValueException ive) { - logger.info("Illegal values found in " + filePath + ": " + ive.getMessage()); - throw new DataConversionException(ive); - } - } - - @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException { - saveAddressBook(addressBook, filePath); - } - - /** - * Similar to {@link #saveAddressBook(ReadOnlyAddressBook)}. - * - * @param filePath location of the data. Cannot be null. - */ - public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException { - requireNonNull(addressBook); - requireNonNull(filePath); - - FileUtil.createIfMissing(filePath); - JsonUtil.saveJsonFile(new JsonSerializableAddressBook(addressBook), filePath); - } - -} diff --git a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java b/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java deleted file mode 100644 index 5efd834091d..00000000000 --- a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java +++ /dev/null @@ -1,60 +0,0 @@ -package seedu.address.storage; - -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 com.fasterxml.jackson.annotation.JsonRootName; - -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.AddressBook; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; - -/** - * An Immutable AddressBook that is serializable to JSON format. - */ -@JsonRootName(value = "addressbook") -class JsonSerializableAddressBook { - - public static final String MESSAGE_DUPLICATE_PERSON = "Persons list contains duplicate person(s)."; - - private final List persons = new ArrayList<>(); - - /** - * Constructs a {@code JsonSerializableAddressBook} with the given persons. - */ - @JsonCreator - public JsonSerializableAddressBook(@JsonProperty("persons") List persons) { - this.persons.addAll(persons); - } - - /** - * Converts a given {@code ReadOnlyAddressBook} into this class for Jackson use. - * - * @param source future changes to this will not affect the created {@code JsonSerializableAddressBook}. - */ - public JsonSerializableAddressBook(ReadOnlyAddressBook source) { - persons.addAll(source.getPersonList().stream().map(JsonAdaptedPerson::new).collect(Collectors.toList())); - } - - /** - * Converts this address book into the model's {@code AddressBook} object. - * - * @throws IllegalValueException if there were any data constraints violated. - */ - public AddressBook toModelType() throws IllegalValueException { - AddressBook addressBook = new AddressBook(); - for (JsonAdaptedPerson jsonAdaptedPerson : persons) { - Person person = jsonAdaptedPerson.toModelType(); - if (addressBook.hasPerson(person)) { - throw new IllegalValueException(MESSAGE_DUPLICATE_PERSON); - } - addressBook.addPerson(person); - } - return addressBook; - } - -} diff --git a/src/main/java/seedu/address/storage/Storage.java b/src/main/java/seedu/address/storage/Storage.java deleted file mode 100644 index beda8bd9f11..00000000000 --- a/src/main/java/seedu/address/storage/Storage.java +++ /dev/null @@ -1,32 +0,0 @@ -package seedu.address.storage; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.Optional; - -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; - -/** - * API of the Storage component - */ -public interface Storage extends AddressBookStorage, UserPrefsStorage { - - @Override - Optional readUserPrefs() throws DataConversionException, IOException; - - @Override - void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException; - - @Override - Path getAddressBookFilePath(); - - @Override - Optional readAddressBook() throws DataConversionException, IOException; - - @Override - void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException; - -} diff --git a/src/main/java/seedu/address/storage/StorageManager.java b/src/main/java/seedu/address/storage/StorageManager.java deleted file mode 100644 index 6cfa0162164..00000000000 --- a/src/main/java/seedu/address/storage/StorageManager.java +++ /dev/null @@ -1,78 +0,0 @@ -package seedu.address.storage; - -import java.io.IOException; -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.DataConversionException; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; - -/** - * Manages storage of AddressBook data in local storage. - */ -public class StorageManager implements Storage { - - private static final Logger logger = LogsCenter.getLogger(StorageManager.class); - private AddressBookStorage addressBookStorage; - private UserPrefsStorage userPrefsStorage; - - /** - * Creates a {@code StorageManager} with the given {@code AddressBookStorage} and {@code UserPrefStorage}. - */ - public StorageManager(AddressBookStorage addressBookStorage, UserPrefsStorage userPrefsStorage) { - this.addressBookStorage = addressBookStorage; - this.userPrefsStorage = userPrefsStorage; - } - - // ================ UserPrefs methods ============================== - - @Override - public Path getUserPrefsFilePath() { - return userPrefsStorage.getUserPrefsFilePath(); - } - - @Override - public Optional readUserPrefs() throws DataConversionException, IOException { - return userPrefsStorage.readUserPrefs(); - } - - @Override - public void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException { - userPrefsStorage.saveUserPrefs(userPrefs); - } - - - // ================ AddressBook methods ============================== - - @Override - public Path getAddressBookFilePath() { - return addressBookStorage.getAddressBookFilePath(); - } - - @Override - public Optional readAddressBook() throws DataConversionException, IOException { - return readAddressBook(addressBookStorage.getAddressBookFilePath()); - } - - @Override - public Optional readAddressBook(Path filePath) throws DataConversionException, IOException { - logger.fine("Attempting to read data from file: " + filePath); - return addressBookStorage.readAddressBook(filePath); - } - - @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException { - saveAddressBook(addressBook, addressBookStorage.getAddressBookFilePath()); - } - - @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException { - logger.fine("Attempting to write to data file: " + filePath); - addressBookStorage.saveAddressBook(addressBook, filePath); - } - -} diff --git a/src/main/java/seedu/address/ui/PersonCard.java b/src/main/java/seedu/address/ui/PersonCard.java deleted file mode 100644 index 7fc927bc5d9..00000000000 --- a/src/main/java/seedu/address/ui/PersonCard.java +++ /dev/null @@ -1,77 +0,0 @@ -package seedu.address.ui; - -import java.util.Comparator; - -import javafx.fxml.FXML; -import javafx.scene.control.Label; -import javafx.scene.layout.FlowPane; -import javafx.scene.layout.HBox; -import javafx.scene.layout.Region; -import seedu.address.model.person.Person; - -/** - * An UI component that displays information of a {@code Person}. - */ -public class PersonCard extends UiPart { - - private static final String FXML = "PersonListCard.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 Person person; - - @FXML - private HBox cardPane; - @FXML - private Label name; - @FXML - private Label id; - @FXML - private Label phone; - @FXML - private Label address; - @FXML - private Label email; - @FXML - private FlowPane tags; - - /** - * Creates a {@code PersonCode} with the given {@code Person} and index to display. - */ - public PersonCard(Person person, int displayedIndex) { - super(FXML); - this.person = person; - id.setText(displayedIndex + ". "); - name.setText(person.getName().fullName); - phone.setText(person.getPhone().value); - address.setText(person.getAddress().value); - email.setText(person.getEmail().value); - person.getTags().stream() - .sorted(Comparator.comparing(tag -> tag.tagName)) - .forEach(tag -> tags.getChildren().add(new Label(tag.tagName))); - } - - @Override - public boolean equals(Object other) { - // short circuit if same object - if (other == this) { - return true; - } - - // instanceof handles nulls - if (!(other instanceof PersonCard)) { - return false; - } - - // state check - PersonCard card = (PersonCard) other; - return id.getText().equals(card.id.getText()) - && person.equals(card.person); - } -} diff --git a/src/main/java/seedu/address/ui/PersonListPanel.java b/src/main/java/seedu/address/ui/PersonListPanel.java deleted file mode 100644 index f4c501a897b..00000000000 --- a/src/main/java/seedu/address/ui/PersonListPanel.java +++ /dev/null @@ -1,49 +0,0 @@ -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 persons. - */ -public class PersonListPanel extends UiPart { - private static final String FXML = "PersonListPanel.fxml"; - private final Logger logger = LogsCenter.getLogger(PersonListPanel.class); - - @FXML - private ListView personListView; - - /** - * Creates a {@code PersonListPanel} with the given {@code ObservableList}. - */ - public PersonListPanel(ObservableList personList) { - super(FXML); - personListView.setItems(personList); - personListView.setCellFactory(listView -> new PersonListViewCell()); - } - - /** - * Custom {@code ListCell} that displays the graphics of a {@code Person} using a {@code PersonCard}. - */ - class PersonListViewCell 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/AppParameters.java b/src/main/java/seedu/medinfo/AppParameters.java similarity index 93% rename from src/main/java/seedu/address/AppParameters.java rename to src/main/java/seedu/medinfo/AppParameters.java index ab552c398f3..faf1915254f 100644 --- a/src/main/java/seedu/address/AppParameters.java +++ b/src/main/java/seedu/medinfo/AppParameters.java @@ -1,4 +1,4 @@ -package seedu.address; +package seedu.medinfo; import java.nio.file.Path; import java.nio.file.Paths; @@ -7,8 +7,8 @@ import java.util.logging.Logger; import javafx.application.Application; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.util.FileUtil; +import seedu.medinfo.commons.core.LogsCenter; +import seedu.medinfo.commons.util.FileUtil; /** * Represents the parsed command-line parameters given to the application. diff --git a/src/main/java/seedu/address/Main.java b/src/main/java/seedu/medinfo/Main.java similarity index 97% rename from src/main/java/seedu/address/Main.java rename to src/main/java/seedu/medinfo/Main.java index 052a5068631..e772532a3f2 100644 --- a/src/main/java/seedu/address/Main.java +++ b/src/main/java/seedu/medinfo/Main.java @@ -1,4 +1,4 @@ -package seedu.address; +package seedu.medinfo; import javafx.application.Application; diff --git a/src/main/java/seedu/address/MainApp.java b/src/main/java/seedu/medinfo/MainApp.java similarity index 63% rename from src/main/java/seedu/address/MainApp.java rename to src/main/java/seedu/medinfo/MainApp.java index 4133aaa0151..d34272d5888 100644 --- a/src/main/java/seedu/address/MainApp.java +++ b/src/main/java/seedu/medinfo/MainApp.java @@ -1,4 +1,4 @@ -package seedu.address; +package seedu.medinfo; import java.io.IOException; import java.nio.file.Path; @@ -7,36 +7,37 @@ import javafx.application.Application; import javafx.stage.Stage; -import seedu.address.commons.core.Config; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.core.Version; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.commons.util.ConfigUtil; -import seedu.address.commons.util.StringUtil; -import seedu.address.logic.Logic; -import seedu.address.logic.LogicManager; -import seedu.address.model.AddressBook; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; -import seedu.address.model.util.SampleDataUtil; -import seedu.address.storage.AddressBookStorage; -import seedu.address.storage.JsonAddressBookStorage; -import seedu.address.storage.JsonUserPrefsStorage; -import seedu.address.storage.Storage; -import seedu.address.storage.StorageManager; -import seedu.address.storage.UserPrefsStorage; -import seedu.address.ui.Ui; -import seedu.address.ui.UiManager; +import seedu.medinfo.commons.core.Config; +import seedu.medinfo.commons.core.LogsCenter; +import seedu.medinfo.commons.core.Version; +import seedu.medinfo.commons.exceptions.DataConversionException; +import seedu.medinfo.commons.util.ConfigUtil; +import seedu.medinfo.commons.util.StringUtil; +import seedu.medinfo.logic.Logic; +import seedu.medinfo.logic.LogicManager; +import seedu.medinfo.logic.commands.exceptions.CommandException; +import seedu.medinfo.model.MedInfo; +import seedu.medinfo.model.Model; +import seedu.medinfo.model.ModelManager; +import seedu.medinfo.model.ReadOnlyMedInfo; +import seedu.medinfo.model.ReadOnlyUserPrefs; +import seedu.medinfo.model.UserPrefs; +import seedu.medinfo.model.util.SampleDataUtil; +import seedu.medinfo.storage.JsonMedInfoStorage; +import seedu.medinfo.storage.JsonUserPrefsStorage; +import seedu.medinfo.storage.MedInfoStorage; +import seedu.medinfo.storage.Storage; +import seedu.medinfo.storage.StorageManager; +import seedu.medinfo.storage.UserPrefsStorage; +import seedu.medinfo.ui.Ui; +import seedu.medinfo.ui.UiManager; /** * Runs the application. */ public class MainApp extends Application { - public static final Version VERSION = new Version(0, 2, 0, true); + public static final Version VERSION = new Version(1, 3, 0, true); private static final Logger logger = LogsCenter.getLogger(MainApp.class); @@ -48,7 +49,7 @@ public class MainApp extends Application { @Override public void init() throws Exception { - logger.info("=============================[ Initializing AddressBook ]==========================="); + logger.info("=============================[ Initializing MedInfo ]==========================="); super.init(); AppParameters appParameters = AppParameters.parse(getParameters()); @@ -56,8 +57,8 @@ public void init() throws Exception { UserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(config.getUserPrefsFilePath()); UserPrefs userPrefs = initPrefs(userPrefsStorage); - AddressBookStorage addressBookStorage = new JsonAddressBookStorage(userPrefs.getAddressBookFilePath()); - storage = new StorageManager(addressBookStorage, userPrefsStorage); + MedInfoStorage medInfoStorage = new JsonMedInfoStorage(userPrefs.getMedInfoFilePath()); + storage = new StorageManager(medInfoStorage, userPrefsStorage); initLogging(config); @@ -69,27 +70,34 @@ 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 medinfo book and {@code userPrefs}.
+ * The data from the sample medinfo book will be used instead if {@code storage}'s medinfo book is not found, + * or an empty medinfo book will be used instead if errors occur when reading {@code storage}'s medinfo book. */ private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) { - Optional addressBookOptional; - ReadOnlyAddressBook initialData; + Optional medInfoOptional; + ReadOnlyMedInfo initialData; try { - addressBookOptional = storage.readAddressBook(); - if (!addressBookOptional.isPresent()) { - logger.info("Data file not found. Will be starting with a sample AddressBook"); + medInfoOptional = storage.readMedInfo(); + if (!medInfoOptional.isPresent()) { + logger.info("Data file not found. Will be starting with a sample MedInfo"); } - initialData = addressBookOptional.orElseGet(SampleDataUtil::getSampleAddressBook); - } catch (DataConversionException e) { - logger.warning("Data file not in the correct format. Will be starting with an empty AddressBook"); - initialData = new AddressBook(); + Optional sampleData = Optional.ofNullable(SampleDataUtil.getSampleMedInfo()); + initialData = medInfoOptional.orElseGet(() -> { + try { + return SampleDataUtil.getSampleMedInfo(); + } catch (CommandException e) { + logger.warning("Data file not in the correct format. Will be starting with an empty MedInfo"); + return new MedInfo(); + } + }); + } catch (CommandException | DataConversionException e) { + logger.warning("Data file not in the correct format. Will be starting with an empty MedInfo"); + initialData = new MedInfo(); } catch (IOException e) { - logger.warning("Problem while reading from the file. Will be starting with an empty AddressBook"); - initialData = new AddressBook(); + logger.warning("Problem while reading from the file. Will be starting with an empty MedInfo"); + initialData = new MedInfo(); } - return new ModelManager(initialData, userPrefs); } @@ -151,7 +159,7 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) { + "Using default user prefs"); initializedPrefs = new UserPrefs(); } catch (IOException e) { - logger.warning("Problem while reading from the file. Will be starting with an empty AddressBook"); + logger.warning("Problem while reading from the file. Will be starting with an empty MedInfo"); initializedPrefs = new UserPrefs(); } @@ -167,13 +175,13 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) { @Override public void start(Stage primaryStage) { - logger.info("Starting AddressBook " + MainApp.VERSION); + logger.info("Starting MedInfo " + MainApp.VERSION); ui.start(primaryStage); } @Override public void stop() { - logger.info("============================ [ Stopping Address Book ] ============================="); + logger.info("============================ [ Stopping MedInfo ] ============================="); try { storage.saveUserPrefs(model.getUserPrefs()); } catch (IOException e) { diff --git a/src/main/java/seedu/address/commons/core/Config.java b/src/main/java/seedu/medinfo/commons/core/Config.java similarity index 97% rename from src/main/java/seedu/address/commons/core/Config.java rename to src/main/java/seedu/medinfo/commons/core/Config.java index 91145745521..96f54bdb89d 100644 --- a/src/main/java/seedu/address/commons/core/Config.java +++ b/src/main/java/seedu/medinfo/commons/core/Config.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.medinfo.commons.core; import java.nio.file.Path; import java.nio.file.Paths; diff --git a/src/main/java/seedu/address/commons/core/GuiSettings.java b/src/main/java/seedu/medinfo/commons/core/GuiSettings.java similarity index 98% rename from src/main/java/seedu/address/commons/core/GuiSettings.java rename to src/main/java/seedu/medinfo/commons/core/GuiSettings.java index ba33653be67..5b0ecd54841 100644 --- a/src/main/java/seedu/address/commons/core/GuiSettings.java +++ b/src/main/java/seedu/medinfo/commons/core/GuiSettings.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.medinfo.commons.core; import java.awt.Point; import java.io.Serializable; diff --git a/src/main/java/seedu/address/commons/core/LogsCenter.java b/src/main/java/seedu/medinfo/commons/core/LogsCenter.java similarity index 97% rename from src/main/java/seedu/address/commons/core/LogsCenter.java rename to src/main/java/seedu/medinfo/commons/core/LogsCenter.java index 431e7185e76..89ff45bfd0a 100644 --- a/src/main/java/seedu/address/commons/core/LogsCenter.java +++ b/src/main/java/seedu/medinfo/commons/core/LogsCenter.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.medinfo.commons.core; import java.io.IOException; import java.util.Arrays; @@ -18,7 +18,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 = "medinfo.log"; private static Level currentLogLevel = Level.INFO; private static final Logger logger = LogsCenter.getLogger(LogsCenter.class); private static FileHandler fileHandler; diff --git a/src/main/java/seedu/medinfo/commons/core/Messages.java b/src/main/java/seedu/medinfo/commons/core/Messages.java new file mode 100644 index 00000000000..a8e8d34113a --- /dev/null +++ b/src/main/java/seedu/medinfo/commons/core/Messages.java @@ -0,0 +1,21 @@ +package seedu.medinfo.commons.core; + +/** + * Container for user visible messages. + */ +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_PATIENT_DISPLAYED_INDEX = "The patient index provided is invalid"; + public static final String MESSAGE_INVALID_WARD_DISPLAYED_INDEX = "The ward index provided is invalid"; + public static final String MESSAGE_DELETE_WAITING_ROOM = "The Waiting Room cannot be deleted"; + public static final String MESSAGE_ABORT_DELETE = "Deletion cancelled"; + + public static final String MESSAGE_DELETE_WARD_WITH_PATIENTS = + "The ward cannot be deleted as there are patients inside"; + + public static final String MESSAGE_ALL_PATIENTS_LISTED_OVERVIEW = "All %1$d patients listed!"; + public static final String MESSAGE_PATIENTS_LISTED_OVERVIEW = "%1$d out of %2$d patients listed!"; + +} diff --git a/src/main/java/seedu/address/commons/core/Version.java b/src/main/java/seedu/medinfo/commons/core/Version.java similarity index 98% rename from src/main/java/seedu/address/commons/core/Version.java rename to src/main/java/seedu/medinfo/commons/core/Version.java index 12142ec1e32..cd658844227 100644 --- a/src/main/java/seedu/address/commons/core/Version.java +++ b/src/main/java/seedu/medinfo/commons/core/Version.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.medinfo.commons.core; import java.util.regex.Matcher; import java.util.regex.Pattern; diff --git a/src/main/java/seedu/address/commons/core/index/Index.java b/src/main/java/seedu/medinfo/commons/core/index/Index.java similarity index 97% rename from src/main/java/seedu/address/commons/core/index/Index.java rename to src/main/java/seedu/medinfo/commons/core/index/Index.java index 19536439c09..bcfb9ec3fd1 100644 --- a/src/main/java/seedu/address/commons/core/index/Index.java +++ b/src/main/java/seedu/medinfo/commons/core/index/Index.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core.index; +package seedu.medinfo.commons.core.index; /** * Represents a zero-based or one-based index. diff --git a/src/main/java/seedu/address/commons/exceptions/DataConversionException.java b/src/main/java/seedu/medinfo/commons/exceptions/DataConversionException.java similarity index 84% rename from src/main/java/seedu/address/commons/exceptions/DataConversionException.java rename to src/main/java/seedu/medinfo/commons/exceptions/DataConversionException.java index 1f689bd8e3f..50d4b069b47 100644 --- a/src/main/java/seedu/address/commons/exceptions/DataConversionException.java +++ b/src/main/java/seedu/medinfo/commons/exceptions/DataConversionException.java @@ -1,4 +1,4 @@ -package seedu.address.commons.exceptions; +package seedu.medinfo.commons.exceptions; /** * Represents an error during conversion of data from one format to another diff --git a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java b/src/main/java/seedu/medinfo/commons/exceptions/IllegalValueException.java similarity index 93% rename from src/main/java/seedu/address/commons/exceptions/IllegalValueException.java rename to src/main/java/seedu/medinfo/commons/exceptions/IllegalValueException.java index 19124db485c..4f834f2bdd3 100644 --- a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java +++ b/src/main/java/seedu/medinfo/commons/exceptions/IllegalValueException.java @@ -1,4 +1,4 @@ -package seedu.address.commons.exceptions; +package seedu.medinfo.commons.exceptions; /** * Signals that some given data does not fulfill some constraints. diff --git a/src/main/java/seedu/address/commons/util/AppUtil.java b/src/main/java/seedu/medinfo/commons/util/AppUtil.java similarity index 94% rename from src/main/java/seedu/address/commons/util/AppUtil.java rename to src/main/java/seedu/medinfo/commons/util/AppUtil.java index 87aa89c0326..ee5c9601cf1 100644 --- a/src/main/java/seedu/address/commons/util/AppUtil.java +++ b/src/main/java/seedu/medinfo/commons/util/AppUtil.java @@ -1,9 +1,9 @@ -package seedu.address.commons.util; +package seedu.medinfo.commons.util; import static java.util.Objects.requireNonNull; import javafx.scene.image.Image; -import seedu.address.MainApp; +import seedu.medinfo.MainApp; /** * A container for App specific utility functions diff --git a/src/main/java/seedu/address/commons/util/CollectionUtil.java b/src/main/java/seedu/medinfo/commons/util/CollectionUtil.java similarity index 96% rename from src/main/java/seedu/address/commons/util/CollectionUtil.java rename to src/main/java/seedu/medinfo/commons/util/CollectionUtil.java index eafe4dfd681..8f1e3e39afd 100644 --- a/src/main/java/seedu/address/commons/util/CollectionUtil.java +++ b/src/main/java/seedu/medinfo/commons/util/CollectionUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.medinfo.commons.util; import static java.util.Objects.requireNonNull; diff --git a/src/main/java/seedu/address/commons/util/ConfigUtil.java b/src/main/java/seedu/medinfo/commons/util/ConfigUtil.java similarity index 77% rename from src/main/java/seedu/address/commons/util/ConfigUtil.java rename to src/main/java/seedu/medinfo/commons/util/ConfigUtil.java index f7f8a2bd44c..8ffe09bfc0b 100644 --- a/src/main/java/seedu/address/commons/util/ConfigUtil.java +++ b/src/main/java/seedu/medinfo/commons/util/ConfigUtil.java @@ -1,11 +1,11 @@ -package seedu.address.commons.util; +package seedu.medinfo.commons.util; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; -import seedu.address.commons.core.Config; -import seedu.address.commons.exceptions.DataConversionException; +import seedu.medinfo.commons.core.Config; +import seedu.medinfo.commons.exceptions.DataConversionException; /** * A class for accessing the Config File. diff --git a/src/main/java/seedu/address/commons/util/FileUtil.java b/src/main/java/seedu/medinfo/commons/util/FileUtil.java similarity index 98% rename from src/main/java/seedu/address/commons/util/FileUtil.java rename to src/main/java/seedu/medinfo/commons/util/FileUtil.java index b1e2767cdd9..0f6bd0e354b 100644 --- a/src/main/java/seedu/address/commons/util/FileUtil.java +++ b/src/main/java/seedu/medinfo/commons/util/FileUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.medinfo.commons.util; import java.io.IOException; import java.nio.file.Files; diff --git a/src/main/java/seedu/address/commons/util/JsonUtil.java b/src/main/java/seedu/medinfo/commons/util/JsonUtil.java similarity index 97% rename from src/main/java/seedu/address/commons/util/JsonUtil.java rename to src/main/java/seedu/medinfo/commons/util/JsonUtil.java index 8ef609f055d..d3a75235022 100644 --- a/src/main/java/seedu/address/commons/util/JsonUtil.java +++ b/src/main/java/seedu/medinfo/commons/util/JsonUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.medinfo.commons.util; import static java.util.Objects.requireNonNull; @@ -20,8 +20,8 @@ import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.exceptions.DataConversionException; +import seedu.medinfo.commons.core.LogsCenter; +import seedu.medinfo.commons.exceptions.DataConversionException; /** * Converts a Java object instance to JSON and vice versa diff --git a/src/main/java/seedu/address/commons/util/StringUtil.java b/src/main/java/seedu/medinfo/commons/util/StringUtil.java similarity index 77% rename from src/main/java/seedu/address/commons/util/StringUtil.java rename to src/main/java/seedu/medinfo/commons/util/StringUtil.java index 61cc8c9a1cb..d47d43f4a06 100644 --- a/src/main/java/seedu/address/commons/util/StringUtil.java +++ b/src/main/java/seedu/medinfo/commons/util/StringUtil.java @@ -1,7 +1,7 @@ -package seedu.address.commons.util; +package seedu.medinfo.commons.util; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; +import static seedu.medinfo.commons.util.AppUtil.checkArgument; import java.io.PrintWriter; import java.io.StringWriter; @@ -14,24 +14,28 @@ public class StringUtil { /** * Returns true if the {@code sentence} contains the {@code word}. - * Ignores case, but a full word match is required. - *
examples:
+     * Ignores case, but a full word match is required.
+     * 
+ * examples: + * + *
      *       containsWordIgnoreCase("ABc def", "abc") == true
      *       containsWordIgnoreCase("ABc def", "DEF") == true
      *       containsWordIgnoreCase("ABc def", "AB") == false //not a full word match
-     *       
- * @param sentence cannot be null - * @param word cannot be null, cannot be empty, must be a single word + *
+ * + * @param fullName cannot be null + * @param word cannot be null, cannot be empty, must be a single word */ - public static boolean containsWordIgnoreCase(String sentence, String word) { - requireNonNull(sentence); + public static boolean containsWordIgnoreCase(String fullName, String word) { + requireNonNull(fullName); requireNonNull(word); String preppedWord = word.trim(); checkArgument(!preppedWord.isEmpty(), "Word parameter cannot be empty"); checkArgument(preppedWord.split("\\s+").length == 1, "Word parameter should be a single word"); - String preppedSentence = sentence; + String preppedSentence = fullName.toString(); String[] wordsInPreppedSentence = preppedSentence.split("\\s+"); return Arrays.stream(wordsInPreppedSentence) @@ -52,7 +56,9 @@ public static String getDetails(Throwable t) { * Returns true if {@code s} represents a non-zero unsigned integer * e.g. 1, 2, 3, ..., {@code Integer.MAX_VALUE}
* Will return false for any other non-null string input - * e.g. empty string, "-1", "0", "+1", and " 2 " (untrimmed), "3 0" (contains whitespace), "1 a" (contains letters) + * e.g. empty string, "-1", "0", "+1", and " 2 " (untrimmed), "3 0" (contains + * whitespace), "1 a" (contains letters) + * * @throws NullPointerException if {@code s} is null. */ public static boolean isNonZeroUnsignedInteger(String s) { diff --git a/src/main/java/seedu/medinfo/logic/Logic.java b/src/main/java/seedu/medinfo/logic/Logic.java new file mode 100644 index 00000000000..ba547bc7763 --- /dev/null +++ b/src/main/java/seedu/medinfo/logic/Logic.java @@ -0,0 +1,58 @@ +package seedu.medinfo.logic; + +import java.nio.file.Path; +import java.util.List; + +import javafx.collections.ObservableList; +import seedu.medinfo.commons.core.GuiSettings; +import seedu.medinfo.logic.commands.CommandResult; +import seedu.medinfo.logic.commands.exceptions.CommandException; +import seedu.medinfo.logic.parser.exceptions.ParseException; +import seedu.medinfo.model.ReadOnlyMedInfo; +import seedu.medinfo.model.patient.Patient; +import seedu.medinfo.model.ward.Ward; + +/** + * API of the Logic component + */ +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. + */ + CommandResult execute(String commandText) throws CommandException, ParseException; + + /** + * Returns the MedInfo. + * + * @see seedu.medinfo.model.Model#getMedInfo() + */ + ReadOnlyMedInfo getMedInfo(); + + List getStatsInfo(); + + /** Returns an unmodifiable view of the filtered list of patients */ + ObservableList getFilteredPatientList(); + + /** Returns an unmodifiable view of the filtered list of wards */ + ObservableList getFilteredWardList(); + + /** + * Returns the user prefs' medinfo book file path. + */ + Path getMedInfoFilePath(); + + /** + * Returns the user prefs' GUI settings. + */ + GuiSettings getGuiSettings(); + + /** + * Set the user prefs' GUI settings. + */ + void setGuiSettings(GuiSettings guiSettings); + +} diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/medinfo/logic/LogicManager.java similarity index 51% rename from src/main/java/seedu/address/logic/LogicManager.java rename to src/main/java/seedu/medinfo/logic/LogicManager.java index 9d9c6d15bdc..85edc837e04 100644 --- a/src/main/java/seedu/address/logic/LogicManager.java +++ b/src/main/java/seedu/medinfo/logic/LogicManager.java @@ -1,21 +1,23 @@ -package seedu.address.logic; +package seedu.medinfo.logic; import java.io.IOException; import java.nio.file.Path; +import java.util.List; import java.util.logging.Logger; import javafx.collections.ObservableList; -import seedu.address.commons.core.GuiSettings; -import seedu.address.commons.core.LogsCenter; -import seedu.address.logic.commands.Command; -import seedu.address.logic.commands.CommandResult; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.logic.parser.AddressBookParser; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.Model; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; -import seedu.address.storage.Storage; +import seedu.medinfo.commons.core.GuiSettings; +import seedu.medinfo.commons.core.LogsCenter; +import seedu.medinfo.logic.commands.Command; +import seedu.medinfo.logic.commands.CommandResult; +import seedu.medinfo.logic.commands.exceptions.CommandException; +import seedu.medinfo.logic.parser.MedInfoParser; +import seedu.medinfo.logic.parser.exceptions.ParseException; +import seedu.medinfo.model.Model; +import seedu.medinfo.model.ReadOnlyMedInfo; +import seedu.medinfo.model.patient.Patient; +import seedu.medinfo.model.ward.Ward; +import seedu.medinfo.storage.Storage; /** * The main LogicManager of the app. @@ -26,7 +28,7 @@ public class LogicManager implements Logic { private final Model model; private final Storage storage; - private final AddressBookParser addressBookParser; + private final MedInfoParser medInfoParser; /** * Constructs a {@code LogicManager} with the given {@code Model} and {@code Storage}. @@ -34,7 +36,12 @@ public class LogicManager implements Logic { public LogicManager(Model model, Storage storage) { this.model = model; this.storage = storage; - addressBookParser = new AddressBookParser(); + medInfoParser = new MedInfoParser(); + } + + @Override + public List getStatsInfo() { + return model.getStatsInfo(); } @Override @@ -42,11 +49,11 @@ public CommandResult execute(String commandText) throws CommandException, ParseE logger.info("----------------[USER COMMAND][" + commandText + "]"); CommandResult commandResult; - Command command = addressBookParser.parseCommand(commandText); + Command command = medInfoParser.parseCommand(commandText); commandResult = command.execute(model); try { - storage.saveAddressBook(model.getAddressBook()); + storage.saveMedInfo(model.getMedInfo()); } catch (IOException ioe) { throw new CommandException(FILE_OPS_ERROR_MESSAGE + ioe, ioe); } @@ -55,18 +62,23 @@ public CommandResult execute(String commandText) throws CommandException, ParseE } @Override - public ReadOnlyAddressBook getAddressBook() { - return model.getAddressBook(); + public ReadOnlyMedInfo getMedInfo() { + return model.getMedInfo(); + } + + @Override + public ObservableList getFilteredPatientList() { + return model.getFilteredPatientList(); } @Override - public ObservableList getFilteredPersonList() { - return model.getFilteredPersonList(); + public ObservableList getFilteredWardList() { + return model.getFilteredWardList(); } @Override - public Path getAddressBookFilePath() { - return model.getAddressBookFilePath(); + public Path getMedInfoFilePath() { + return model.getMedInfoFilePath(); } @Override diff --git a/src/main/java/seedu/medinfo/logic/commands/AddCommand.java b/src/main/java/seedu/medinfo/logic/commands/AddCommand.java new file mode 100644 index 00000000000..adc0381e10d --- /dev/null +++ b/src/main/java/seedu/medinfo/logic/commands/AddCommand.java @@ -0,0 +1,64 @@ +package seedu.medinfo.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.medinfo.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.medinfo.logic.parser.CliSyntax.PREFIX_NRIC; +import static seedu.medinfo.logic.parser.CliSyntax.PREFIX_STATUS; + +import seedu.medinfo.logic.commands.exceptions.CommandException; +import seedu.medinfo.model.Model; +import seedu.medinfo.model.patient.Patient; + +/** + * Adds a patient to MedInfo. + */ +public class AddCommand extends Command { + + public static final String COMMAND_WORD = "add"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a patient to MedInfo. \n" + + "Parameters: " + + PREFIX_NAME + "NAME " + + PREFIX_NRIC + "NRIC " + + "[" + PREFIX_STATUS + "STATUS] \n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_NAME + "John Doe " + PREFIX_NRIC + "S1234567A " + PREFIX_STATUS + "GRAY \n"; + + public static final String MESSAGE_SUCCESS = "New patient added: %1$s"; + public static final String MESSAGE_DUPLICATE_PATIENT = "This patient already exists in MedInfo"; + + private final Patient toAdd; + + /** + * Constructs a new {@code AddCommand} to add the specified {@code Patient}. + */ + public AddCommand(Patient patient) { + requireNonNull(patient); + toAdd = patient; + } + + /** + * Executes the {@code AddCommand} on the given model. + * @param model {@code Model} which the command should operate on. + * @return CommandResult which is the result of the operation. + * @throws CommandException + */ + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + if (model.hasPatientNric(toAdd)) { + throw new CommandException(MESSAGE_DUPLICATE_PATIENT); + } + + model.addPatient(toAdd); + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof AddCommand // instanceof handles nulls + && toAdd.equals(((AddCommand) other).toAdd)); + } +} diff --git a/src/main/java/seedu/medinfo/logic/commands/AddWardCommand.java b/src/main/java/seedu/medinfo/logic/commands/AddWardCommand.java new file mode 100644 index 00000000000..4ce0d7bba54 --- /dev/null +++ b/src/main/java/seedu/medinfo/logic/commands/AddWardCommand.java @@ -0,0 +1,63 @@ +package seedu.medinfo.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.medinfo.logic.parser.CliSyntax.PREFIX_CAPACITY; +import static seedu.medinfo.logic.parser.CliSyntax.PREFIX_WARD; + +import seedu.medinfo.logic.commands.exceptions.CommandException; +import seedu.medinfo.model.Model; +import seedu.medinfo.model.ward.Ward; + +/** + * Adds a ward to MedInfo. + */ +public class AddWardCommand extends Command { + + public static final String COMMAND_WORD = "addward"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a ward to MedInfo. \n" + + "Parameters: " + + PREFIX_WARD + "WARD " + + "[" + PREFIX_CAPACITY + "CAPACITY] \n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_WARD + "A03 " + PREFIX_CAPACITY + "100\n"; + + public static final String MESSAGE_SUCCESS = "New ward added: %1$s"; + public static final String MESSAGE_DUPLICATE_WARD = "This ward already exists in MedInfo"; + + private final Ward toAdd; + + /** + * Constructs a new {@code AddWardCommand} to add the specified {@code Ward}. + */ + public AddWardCommand(Ward ward) { + requireNonNull(ward); + toAdd = ward; + } + + /** + * Executes the {@code AddWardCommand} on the given model. + * @param model {@code Model} which the command should operate on. + * @return CommandResult which is the result of the operation. + * @throws CommandException + */ + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + if (model.hasWard(toAdd)) { + throw new CommandException(MESSAGE_DUPLICATE_WARD); + } + + model.addWard(toAdd); + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof AddWardCommand // instanceof handles nulls + && toAdd.equals(((AddWardCommand) other).toAdd)); + } + +} diff --git a/src/main/java/seedu/medinfo/logic/commands/ClearCommand.java b/src/main/java/seedu/medinfo/logic/commands/ClearCommand.java new file mode 100644 index 00000000000..f036ed0c75b --- /dev/null +++ b/src/main/java/seedu/medinfo/logic/commands/ClearCommand.java @@ -0,0 +1,31 @@ +package seedu.medinfo.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.medinfo.logic.commands.exceptions.CommandException; +import seedu.medinfo.model.MedInfo; +import seedu.medinfo.model.Model; + +/** + * Clears the medinfo book. + */ +public class ClearCommand extends Command { + + public static final String COMMAND_WORD = "clear"; + public static final String MESSAGE_SUCCESS = "MedInfo has been cleared!"; + + /** + * Executes the {@code ClearCommand} on the given model. + * @param model {@code Model} which the command should operate on. + * @return CommandResult which is the result of the operation. + * @throws CommandException + */ + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + MedInfo newMedInfo = new MedInfo(); + newMedInfo.newMedInfo(); + model.setMedInfo(newMedInfo); + return new CommandResult(MESSAGE_SUCCESS); + } +} diff --git a/src/main/java/seedu/address/logic/commands/Command.java b/src/main/java/seedu/medinfo/logic/commands/Command.java similarity index 55% rename from src/main/java/seedu/address/logic/commands/Command.java rename to src/main/java/seedu/medinfo/logic/commands/Command.java index 64f18992160..ca0961bbf79 100644 --- a/src/main/java/seedu/address/logic/commands/Command.java +++ b/src/main/java/seedu/medinfo/logic/commands/Command.java @@ -1,15 +1,15 @@ -package seedu.address.logic.commands; +package seedu.medinfo.logic.commands; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; +import seedu.medinfo.logic.commands.exceptions.CommandException; +import seedu.medinfo.model.Model; /** - * Represents a command with hidden internal logic and the ability to be executed. + * Represents a {@code Command} with hidden internal logic and the ability to be executed. */ public abstract class Command { /** - * Executes the command and returns the result message. + * Executes the {@code Command} and returns the result message. * * @param model {@code Model} which the command should operate on. * @return feedback message of the operation result for display diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/seedu/medinfo/logic/commands/CommandResult.java similarity index 92% rename from src/main/java/seedu/address/logic/commands/CommandResult.java rename to src/main/java/seedu/medinfo/logic/commands/CommandResult.java index 92f900b7916..95b24a93342 100644 --- a/src/main/java/seedu/address/logic/commands/CommandResult.java +++ b/src/main/java/seedu/medinfo/logic/commands/CommandResult.java @@ -1,14 +1,15 @@ -package seedu.address.logic.commands; +package seedu.medinfo.logic.commands; import static java.util.Objects.requireNonNull; import java.util.Objects; /** - * Represents the result of a command execution. + * Represents the result of a {@code Command} execution. */ public class CommandResult { + /** Result of the executed {@code Command} */ private final String feedbackToUser; /** Help information should be shown to the user. */ diff --git a/src/main/java/seedu/medinfo/logic/commands/DeleteCommand.java b/src/main/java/seedu/medinfo/logic/commands/DeleteCommand.java new file mode 100644 index 00000000000..b64cf36c267 --- /dev/null +++ b/src/main/java/seedu/medinfo/logic/commands/DeleteCommand.java @@ -0,0 +1,63 @@ +package seedu.medinfo.logic.commands; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import seedu.medinfo.commons.core.Messages; +import seedu.medinfo.commons.core.index.Index; +import seedu.medinfo.logic.commands.exceptions.CommandException; +import seedu.medinfo.model.Model; +import seedu.medinfo.model.patient.Patient; + +/** + * Deletes a patient identified using its displayed index from MedInfo. + */ +public class DeleteCommand extends Command { + + public static final String COMMAND_WORD = "delete"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Deletes the patient identified by the index number used in the displayed patient list.\n" + + "Parameters: INDEX (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " 1"; + + public static final String MESSAGE_DELETE_PATIENT_SUCCESS = "Deleted Patient: %1$s"; + + private final Index targetIndex; + + /** + * Constructs a new {@code DeleteCommand} to delete the {@code Patient} at the specified index. + * @param targetIndex Index of the {@code Patient} to be deleted in the list. + */ + public DeleteCommand(Index targetIndex) { + this.targetIndex = targetIndex; + } + + /** + * Executes the {@code DeleteCommand} on the given model. + * @param model {@code Model} which the command should operate on. + * @return CommandResult which is the result of the operation. + * @throws CommandException + */ + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredPatientList(); + + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_PATIENT_DISPLAYED_INDEX); + } + + Patient patientToDelete = lastShownList.get(targetIndex.getZeroBased()); + model.deletePatient(patientToDelete); + return new CommandResult(String.format(MESSAGE_DELETE_PATIENT_SUCCESS, patientToDelete)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof DeleteCommand // instanceof handles nulls + && targetIndex.equals(((DeleteCommand) other).targetIndex)); // state check + } +} diff --git a/src/main/java/seedu/medinfo/logic/commands/DeleteWardCommand.java b/src/main/java/seedu/medinfo/logic/commands/DeleteWardCommand.java new file mode 100644 index 00000000000..389ab5406de --- /dev/null +++ b/src/main/java/seedu/medinfo/logic/commands/DeleteWardCommand.java @@ -0,0 +1,72 @@ +package seedu.medinfo.logic.commands; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import seedu.medinfo.commons.core.Messages; +import seedu.medinfo.commons.core.index.Index; +import seedu.medinfo.logic.commands.exceptions.CommandException; +import seedu.medinfo.model.Model; +import seedu.medinfo.model.ward.Ward; + +/** + * Deletes a ward identified using its displayed index from MedInfo. + */ +public class DeleteWardCommand extends Command { + + public static final String COMMAND_WORD = "deleteward"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Deletes the ward identified by the index number used in the displayed ward list.\n" + + "Parameters: INDEX (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " 1"; + + public static final String MESSAGE_DELETE_WARD_SUCCESS = "Deleted Ward: %1$s"; + + private final Index targetIndex; + + /** + * Constructs a new {@code DeleteWardCommand} to delete the {@code Ward} at the specified index. + * @param targetIndex Index of the {@code Ward} to be deleted in the list. + */ + public DeleteWardCommand(Index targetIndex) { + this.targetIndex = targetIndex; + } + + /** + * Executes the {@code DeleteWardCommand} on the given model. + * @param model {@code Model} which the command should operate on. + * @return CommandResult which is the result of the operation. + * @throws CommandException + */ + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredWardList(); + + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_WARD_DISPLAYED_INDEX); + } + + if (targetIndex.getZeroBased() == 0) { + throw new CommandException(Messages.MESSAGE_DELETE_WAITING_ROOM); + } + + Ward wardToDelete = lastShownList.get(targetIndex.getZeroBased()); + + if (wardToDelete.getOccupancy() > 0) { + throw new CommandException(Messages.MESSAGE_DELETE_WARD_WITH_PATIENTS); + } + + model.deleteWard(wardToDelete); + return new CommandResult(String.format(MESSAGE_DELETE_WARD_SUCCESS, wardToDelete)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof DeleteWardCommand // instanceof handles nulls + && targetIndex.equals(((DeleteWardCommand) other).targetIndex)); // state check + } +} diff --git a/src/main/java/seedu/medinfo/logic/commands/EditCommand.java b/src/main/java/seedu/medinfo/logic/commands/EditCommand.java new file mode 100644 index 00000000000..f71b312aca1 --- /dev/null +++ b/src/main/java/seedu/medinfo/logic/commands/EditCommand.java @@ -0,0 +1,226 @@ +package seedu.medinfo.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.medinfo.logic.parser.CliSyntax.PREFIX_DISCHARGE; +import static seedu.medinfo.logic.parser.CliSyntax.PREFIX_STATUS; +import static seedu.medinfo.logic.parser.CliSyntax.PREFIX_WARD; +import static seedu.medinfo.model.Model.PREDICATE_SHOW_ALL_PATIENTS; + +import java.util.List; +import java.util.Optional; + +import seedu.medinfo.commons.core.Messages; +import seedu.medinfo.commons.core.index.Index; +import seedu.medinfo.commons.util.CollectionUtil; +import seedu.medinfo.logic.commands.exceptions.CommandException; +import seedu.medinfo.model.Model; +import seedu.medinfo.model.patient.Discharge; +import seedu.medinfo.model.patient.Name; +import seedu.medinfo.model.patient.Nric; +import seedu.medinfo.model.patient.Patient; +import seedu.medinfo.model.patient.Status; +import seedu.medinfo.model.ward.WardName; + +/** + * Edits the details of an existing patient in the medinfo book. + */ +public class EditCommand extends Command { + + public static final String COMMAND_WORD = "edit"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the status and/or ward and/or discharge " + + "date-time of the patient identified by the index number used in the displayed patient list. " + + "Existing values will be overwritten by the input values.\n" + + "Parameters: INDEX (must be a positive integer) " + + "[" + PREFIX_STATUS + "STATUS] " + + "[" + PREFIX_WARD + "WARD] " + + "[" + PREFIX_DISCHARGE + "DISCHARGE] \n" + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_STATUS + "GREEN " + PREFIX_WARD + "A1 " + PREFIX_DISCHARGE + "14/07/2023 1600 "; + + public static final String MESSAGE_EDIT_PATIENT_SUCCESS = "Edited Patient: %1$s"; + public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; + public static final String MESSAGE_DUPLICATE_PATIENT = "This patient already exists in MedInfo."; + public static final String MESSAGE_WARD_NOT_FOUND = "Ward not found."; + + private final Index index; + private final EditPatientDescriptor editPatientDescriptor; + + /** + * Constructs {@code EditCommand} to edit {@code Patient} at specified index. + * @param index Index of the {@code Patient} to be edited in the list. + * @param editPatientDescriptor Description of the {@code Patient} details to be edited. + */ + public EditCommand(Index index, EditPatientDescriptor editPatientDescriptor) { + requireNonNull(index); + requireNonNull(editPatientDescriptor); + + this.index = index; + this.editPatientDescriptor = new EditPatientDescriptor(editPatientDescriptor); + } + + /** + * Executes the {@code EditCommand} on the given model. + * @param model {@code Model} which the command should operate on. + * @return CommandResult which is the result of the operation. + * @throws CommandException + */ + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredPatientList(); + + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_PATIENT_DISPLAYED_INDEX); + } + + Patient patientToEdit = lastShownList.get(index.getZeroBased()); + Patient editedPatient = createEditedPatient(patientToEdit, editPatientDescriptor); + + if (!patientToEdit.isSamePatient(editedPatient) && model.hasPatient(editedPatient)) { + throw new CommandException(MESSAGE_DUPLICATE_PATIENT); + } + + if (!model.hasWard(editedPatient.getWard())) { + throw new CommandException(MESSAGE_WARD_NOT_FOUND); + } + + model.setPatient(patientToEdit, editedPatient); + model.updateFilteredPatientList(PREDICATE_SHOW_ALL_PATIENTS); + return new CommandResult(String.format(MESSAGE_EDIT_PATIENT_SUCCESS, editedPatient)); + } + + /** + * Creates and returns a {@code Patient} with the details of + * {@code patientToEdit} + * edited with {@code editPatientDescriptor}. + */ + private static Patient createEditedPatient(Patient patientToEdit, EditPatientDescriptor editPatientDescriptor) { + assert patientToEdit != null; + + Name updatedName = editPatientDescriptor.getName().orElse((Name) patientToEdit.getName()); + Nric updatedNric = editPatientDescriptor.getNric().orElse((Nric) patientToEdit.getNric()); + Status updatedStatus = editPatientDescriptor.getStatus().orElse((Status) patientToEdit.getStatus()); + WardName updatedWard = editPatientDescriptor.getWard().orElse(patientToEdit.getWardName()); + Discharge updatedDischarge = editPatientDescriptor.getDischarge() + .orElse((Discharge) patientToEdit.getDischarge()); + + return new Patient(updatedNric, updatedName, updatedStatus, updatedWard, updatedDischarge); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditCommand)) { + return false; + } + + // state check + EditCommand e = (EditCommand) other; + return index.equals(e.index) + && editPatientDescriptor.equals(e.editPatientDescriptor); + } + + /** + * Stores the details to edit the patient with. Each non-empty field value will + * replace the + * corresponding field value of the patient. + */ + public static class EditPatientDescriptor { + private Name name; + private Nric nric; + private Status status; + private WardName ward; + private Discharge discharge; + + public EditPatientDescriptor() { + } + + /** + * Copy constructor. + * A defensive copy of {@code tags} is used internally. + */ + public EditPatientDescriptor(EditPatientDescriptor toCopy) { + setName(toCopy.name); + setNric(toCopy.nric); + setStatus(toCopy.status); + setWard(toCopy.ward); + setDischarge(toCopy.discharge); + + } + + /** + * Returns true if at least one field is edited. + */ + public boolean isAnyFieldEdited() { + return CollectionUtil.isAnyNonNull(name, nric, status, ward, discharge); + } + + public void setName(Name name) { + this.name = name; + } + + public void setNric(Nric nric) { + this.nric = nric; + } + + public void setStatus(Status status) { + this.status = status; + } + + public void setWard(WardName ward) { + this.ward = ward; + } + + public void setDischarge(Discharge discharge) { + this.discharge = discharge; + } + + public Optional getName() { + return Optional.ofNullable(name); + } + + public Optional getNric() { + return Optional.ofNullable(nric); + } + + public Optional getStatus() { + return Optional.ofNullable(status); + } + + public Optional getWard() { + return Optional.ofNullable(ward); + } + + public Optional getDischarge() { + return Optional.ofNullable(discharge); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditPatientDescriptor)) { + return false; + } + + // state check + EditPatientDescriptor e = (EditPatientDescriptor) other; + + return getName().equals(e.getName()) + && getNric().equals(e.getNric()) + && getStatus().equals(e.getStatus()) + && getWard().equals(e.getWard()) + && getDischarge().equals(e.getDischarge()); + } + } +} diff --git a/src/main/java/seedu/medinfo/logic/commands/EditWardCommand.java b/src/main/java/seedu/medinfo/logic/commands/EditWardCommand.java new file mode 100644 index 00000000000..f0a0eda4e91 --- /dev/null +++ b/src/main/java/seedu/medinfo/logic/commands/EditWardCommand.java @@ -0,0 +1,185 @@ +package seedu.medinfo.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.medinfo.logic.parser.CliSyntax.PREFIX_CAPACITY; +import static seedu.medinfo.logic.parser.CliSyntax.PREFIX_WARD; +import static seedu.medinfo.model.Model.PREDICATE_SHOW_ALL_PATIENTS; + +import java.util.List; +import java.util.Optional; + +import seedu.medinfo.commons.core.Messages; +import seedu.medinfo.commons.core.index.Index; +import seedu.medinfo.commons.util.CollectionUtil; +import seedu.medinfo.logic.commands.exceptions.CommandException; +import seedu.medinfo.model.Model; +import seedu.medinfo.model.ward.Capacity; +import seedu.medinfo.model.ward.Ward; +import seedu.medinfo.model.ward.WardName; + +/** + * Edits the details of an existing patient in the medinfo book. + */ +public class EditWardCommand extends Command { + + public static final String COMMAND_WORD = "editward"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the ward name or capacity " + + "of the ward identified by the index number used in the displayed ward list. " + + "Existing values will be overwritten by the input values.\n" + + "Parameters: INDEX (must be a positive integer) " + + "[" + PREFIX_WARD + "WARD " + PREFIX_CAPACITY + "CAPACITY]\n" + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_WARD + "A1" + " " + PREFIX_CAPACITY + "35"; + + public static final String MESSAGE_EDIT_PATIENT_SUCCESS = "Edited Ward: %1$s"; + public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; + public static final String MESSAGE_DUPLICATE_WARD = "This ward already exists in MedInfo."; + public static final String MESSAGE_EDITED_WARD_INSUFFICIENT_CAPACITY = "The given capacity is insufficient" + + " for this ward."; + public static final String MESSAGE_WAITING_ROOM_NAME_EDIT = "Waiting Room name cannot be edited."; + private final Index index; + private final EditWardDescriptor editWardDescriptor; + + /** + * Constructs {@code EditWardCommand} to edit {@code Ward} at specified index. + * @param index Index of the {@code Ward} to be edited in the list. + * @param editWardDescriptor Description of the {@code Ward} details to be edited. + */ + public EditWardCommand(Index index, EditWardDescriptor editWardDescriptor) { + requireNonNull(index); + requireNonNull(editWardDescriptor); + + this.index = index; + this.editWardDescriptor = new EditWardDescriptor(editWardDescriptor); + } + + /** + * Executes the {@code EditWardCommand} on the given model. + * @param model {@code Model} which the command should operate on. + * @return CommandResult which is the result of the operation. + * @throws CommandException + */ + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredWardList(); + + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_WARD_DISPLAYED_INDEX); + } + + Ward wardToEdit = lastShownList.get(index.getZeroBased()); + Ward editedWard = createEditedWard(wardToEdit, editWardDescriptor); + + if (!wardToEdit.isSameWard(editedWard) && model.hasWard(editedWard)) { + throw new CommandException(MESSAGE_DUPLICATE_WARD); + } + + if (!editedWard.canSupport(wardToEdit.getOccupancy())) { + throw new CommandException(MESSAGE_EDITED_WARD_INSUFFICIENT_CAPACITY); + } + + model.setWard(wardToEdit, editedWard); + model.updateFilteredPatientList(PREDICATE_SHOW_ALL_PATIENTS); + return new CommandResult(String.format(MESSAGE_EDIT_PATIENT_SUCCESS, editedWard)); + } + + /** + * Creates and returns a {@code Patient} with the details of + * {@code wardToEdit} + * edited with {@code editWardDescriptor}. + */ + private static Ward createEditedWard(Ward wardToEdit, EditWardDescriptor editWardDescriptor) { + assert wardToEdit != null; + + WardName updatedName = editWardDescriptor.getWard().orElse(wardToEdit.getName()); + + Capacity updatedCapacity = editWardDescriptor.getCapacity() + .orElse(wardToEdit.getCapacity()); + + return new Ward(updatedName, updatedCapacity); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditWardCommand)) { + return false; + } + + // state check + EditWardCommand e = (EditWardCommand) other; + return index.equals(e.index) + && editWardDescriptor.equals(e.editWardDescriptor); + } + + /** + * Stores the details to edit the patient with. Each non-empty field value will + * replace the + * corresponding field value of the patient. + */ + public static class EditWardDescriptor { + private WardName ward; + private Capacity capacity; + + public EditWardDescriptor() { + } + + /** + * Copy constructor. + * A defensive copy of {@code tags} is used internally. + */ + public EditWardDescriptor(EditWardDescriptor toCopy) { + setWard(toCopy.ward); + setCapacity(toCopy.capacity); + } + + /** + * Returns true if at least one field is edited. + */ + public boolean isAnyFieldEdited() { + return CollectionUtil.isAnyNonNull(ward, capacity); + } + + public void setWard(WardName ward) { + this.ward = ward; + } + + public void setCapacity(Capacity capacity) { + this.capacity = capacity; + } + + public Optional getWard() { + return Optional.ofNullable(ward); + } + + public Optional getCapacity() { + return Optional.ofNullable(capacity); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditWardDescriptor)) { + return false; + } + + // state check + EditWardDescriptor e = (EditWardDescriptor) other; + + return getWard().equals(e.getWard()) + && getCapacity().equals(e.getCapacity()); + } + } +} diff --git a/src/main/java/seedu/medinfo/logic/commands/ExitCommand.java b/src/main/java/seedu/medinfo/logic/commands/ExitCommand.java new file mode 100644 index 00000000000..84f9fd54abc --- /dev/null +++ b/src/main/java/seedu/medinfo/logic/commands/ExitCommand.java @@ -0,0 +1,26 @@ +package seedu.medinfo.logic.commands; + +import seedu.medinfo.logic.commands.exceptions.CommandException; +import seedu.medinfo.model.Model; + +/** + * Terminates the program. + */ +public class ExitCommand extends Command { + + public static final String COMMAND_WORD = "exit"; + + public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting MedInfo as requested ..."; + + /** + * Executes the {@code ExitCommand} on the given model. + * @param model {@code Model} which the command should operate on. + * @return CommandResult which is the result of the operation. + * @throws CommandException + */ + @Override + public CommandResult execute(Model model) { + return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true); + } + +} diff --git a/src/main/java/seedu/medinfo/logic/commands/FindCommand.java b/src/main/java/seedu/medinfo/logic/commands/FindCommand.java new file mode 100644 index 00000000000..459caa1ed30 --- /dev/null +++ b/src/main/java/seedu/medinfo/logic/commands/FindCommand.java @@ -0,0 +1,80 @@ +package seedu.medinfo.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.medinfo.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.medinfo.logic.parser.CliSyntax.PREFIX_NRIC; +import static seedu.medinfo.logic.parser.CliSyntax.PREFIX_STATUS; +import static seedu.medinfo.logic.parser.CliSyntax.PREFIX_WARD; +import static seedu.medinfo.model.Model.PREDICATE_SHOW_ALL_PATIENTS; + +import java.util.function.Predicate; + +import seedu.medinfo.commons.core.Messages; +import seedu.medinfo.logic.commands.exceptions.CommandException; +import seedu.medinfo.model.Model; +import seedu.medinfo.model.patient.Patient; + +/** + * Finds and lists all patients in MedInfo 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 MESSAGE_USAGE = COMMAND_WORD + " " + + PREFIX_NAME + + ": Finds all patients whose names contain any of " + + "the specified keywords (case-insensitive) and displays them as a list with index numbers.\n" + + "Parameters: KEYWORD [MORE_KEYWORDS]...\n" + + "Example: " + COMMAND_WORD + " " + PREFIX_NAME + " alice bob charlie\n" + + COMMAND_WORD + " " + PREFIX_NRIC + + ": Finds all patients whose NRIC matches 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 + " " + PREFIX_NRIC + " S1234567A\n" + + COMMAND_WORD + " " + PREFIX_STATUS + + ": Finds all patients whose Status is 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 + " " + PREFIX_STATUS + " GRAY GREEN\n" + + COMMAND_WORD + " " + PREFIX_WARD + + ": Finds all patients whose ward is 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 + " " + PREFIX_WARD + " A03\n"; + + private final Predicate predicate; + + /** + * Constructs a {@code FindCommand} to display {@code Patients} which meet the given predicate. + * @param predicate Condition to be met by the displayed {@code Patients}. + */ + public FindCommand(Predicate predicate) { + this.predicate = predicate; + } + + /** + * Executes the {@code FindCommand} on the given model. + * @param model {@code Model} which the command should operate on. + * @return CommandResult which is the result of the operation. + * @throws CommandException + */ + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.updateFilteredPatientList(PREDICATE_SHOW_ALL_PATIENTS); + int total = model.getFilteredPatientList().size(); + model.updateFilteredPatientList(predicate); + return new CommandResult( + String.format(Messages.MESSAGE_PATIENTS_LISTED_OVERVIEW, model.getFilteredPatientList().size(), total)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof FindCommand // instanceof handles nulls + && predicate.equals(((FindCommand) other).predicate)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/HelpCommand.java b/src/main/java/seedu/medinfo/logic/commands/HelpCommand.java similarity index 58% rename from src/main/java/seedu/address/logic/commands/HelpCommand.java rename to src/main/java/seedu/medinfo/logic/commands/HelpCommand.java index bf824f91bd0..9e62b785bf0 100644 --- a/src/main/java/seedu/address/logic/commands/HelpCommand.java +++ b/src/main/java/seedu/medinfo/logic/commands/HelpCommand.java @@ -1,6 +1,7 @@ -package seedu.address.logic.commands; +package seedu.medinfo.logic.commands; -import seedu.address.model.Model; +import seedu.medinfo.logic.commands.exceptions.CommandException; +import seedu.medinfo.model.Model; /** * Format full help instructions for every command for display. @@ -14,6 +15,12 @@ public class HelpCommand extends Command { public static final String SHOWING_HELP_MESSAGE = "Opened help window."; + /** + * Executes the {@code HelpCommand} on the given model. + * @param model {@code Model} which the command should operate on. + * @return CommandResult which is the result of the operation. + * @throws CommandException + */ @Override public CommandResult execute(Model model) { return new CommandResult(SHOWING_HELP_MESSAGE, true, false); diff --git a/src/main/java/seedu/medinfo/logic/commands/ListCommand.java b/src/main/java/seedu/medinfo/logic/commands/ListCommand.java new file mode 100644 index 00000000000..4d10a70e9ac --- /dev/null +++ b/src/main/java/seedu/medinfo/logic/commands/ListCommand.java @@ -0,0 +1,30 @@ +package seedu.medinfo.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.medinfo.model.Model.PREDICATE_SHOW_ALL_PATIENTS; + +import seedu.medinfo.commons.core.Messages; +import seedu.medinfo.logic.commands.exceptions.CommandException; +import seedu.medinfo.model.Model; + +/** + * Lists all patients in MedInfo to the user. + */ +public class ListCommand extends Command { + + public static final String COMMAND_WORD = "list"; + + /** + * Executes the {@code ListCommand} on the given model. + * @param model {@code Model} which the command should operate on. + * @return CommandResult which is the result of the operation. + * @throws CommandException + */ + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.updateFilteredPatientList(PREDICATE_SHOW_ALL_PATIENTS); + return new CommandResult( + String.format(Messages.MESSAGE_ALL_PATIENTS_LISTED_OVERVIEW, model.getFilteredPatientList().size())); + } +} diff --git a/src/main/java/seedu/medinfo/logic/commands/SortCommand.java b/src/main/java/seedu/medinfo/logic/commands/SortCommand.java new file mode 100644 index 00000000000..9dc5e32ec79 --- /dev/null +++ b/src/main/java/seedu/medinfo/logic/commands/SortCommand.java @@ -0,0 +1,139 @@ +package seedu.medinfo.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.medinfo.logic.parser.CliSyntax.PREFIX_DISCHARGE; +import static seedu.medinfo.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.medinfo.logic.parser.CliSyntax.PREFIX_STATUS; +import static seedu.medinfo.logic.parser.CliSyntax.PREFIX_WARD; + +import java.util.Comparator; + +import seedu.medinfo.logic.commands.exceptions.CommandException; +import seedu.medinfo.model.Model; +import seedu.medinfo.model.patient.Patient; + + + +/** + * Sort all patients in MedInfo based on the field and the order. + */ + +// Solution below adapted from +// https://github.com/AY2223S1-CS2103T-T09-4/tp/blob/master/src/main/java/seedu/address/logic/commands/SortCommand.java +public class SortCommand extends Command { + + public static final String COMMAND_WORD = "sort"; + + public static final String MESSAGE_UNKNOWN_ORDER_KEYWORD = + "The order of Sort Command should be 'ASC' or 'DESC'."; + public static final String MESSAGE_UNKNOWN_TYPE_KEYWORD = "You may only sort by 'name', 'status', 'discharge date'" + + "or 'ward name' " + + "followed by 'asc' or 'desc' order.\n" + + "Example: sort name/asc"; + + public static final String MESSAGE_SUCCESS = "Sorted all patients by the given order\n"; + public static final String MESSAGE_USAGE = COMMAND_WORD + + " " + PREFIX_NAME + + ": Sorts the list of all patients by name. \n" + + "Parameters: asc/desc\n" + + "Example: " + COMMAND_WORD + " " + PREFIX_NAME + "asc\n" + + COMMAND_WORD + " " + PREFIX_STATUS + + ": Sorts all patients by status. \n" + + "Parameters: asc/desc\n" + + "Example: " + COMMAND_WORD + " " + PREFIX_STATUS + "asc\n" + + COMMAND_WORD + " " + PREFIX_DISCHARGE + + ": Sorts all patients by discharge date. \n" + + "Parameters: asc/desc\n" + + "Example: " + COMMAND_WORD + " " + PREFIX_DISCHARGE + "asc\n" + + COMMAND_WORD + " " + PREFIX_WARD + + ": Sorts all patients by ward name. \n" + + "Parameters: asc/desc\n" + + "Example: " + COMMAND_WORD + " " + PREFIX_WARD + "asc\n"; + + /** + * {@code FIELD} specifies what possible types {@code SortCommand} can accept. + */ + public static enum Field { + NAME, + STATUS, + DISCHARGE, + WARD + }; + + /** + * {@code ORDER} specifies what possible order {@code SortCommand} can accept. + */ + public static enum Order { + ASC, + DESC + }; + + private final Comparator comparator; + + /** + * Constructs a {@code SortCommand} to sort {@code Patients} by the given field in the given order. + * @param field The {@code Patient} field to be sorted by. + * @param order The order for {@code Patients} to be displayed. + */ + public SortCommand(Field field, Order order) { + this.comparator = generateComparator(field, order); + } + + /** + * Generates a Comparator for {@code Patients} based on parameters. + * + * @param field field of attribute to be compared + * @param order order of sorting + */ + public static Comparator generateComparator(Field field, Order order) { + switch (field) { + case NAME: + if (order.equals(Order.ASC)) { + return Patient::compareToByNameAsc; + } else { + return Patient::compareToByNameDesc; + } + case STATUS: + if (order.equals(Order.ASC)) { + return Patient::compareToByStatusAsc; + } else { + return Patient::compareToByStatusDesc; + } + case DISCHARGE: + if (order.equals(Order.ASC)) { + return Patient::compareToByDischargeAsc; + } else { + return Patient::compareToByDischargeDesc; + } + case WARD: + if (order.equals(Order.ASC)) { + return Patient::compareToByWardAsc; + } else { + return Patient::compareToByWardDesc; + } + default: + // default sorting order is by Name Asc + return Patient::compareToByNameAsc; + } + } + + /** + * Executes the {@code SortCommand} on the given model. + * @param model {@code Model} which the command should operate on. + * @return CommandResult which is the result of the operation. + * @throws CommandException + */ + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.sortPatients(this.comparator); + return new CommandResult(MESSAGE_SUCCESS); + } + + @Override + public boolean equals(Object other) { + return this == other // short circuit if same object + || (other instanceof SortCommand // instanceof handles null + && this.comparator.equals(((SortCommand) other).comparator)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java b/src/main/java/seedu/medinfo/logic/commands/exceptions/CommandException.java similarity index 82% rename from src/main/java/seedu/address/logic/commands/exceptions/CommandException.java rename to src/main/java/seedu/medinfo/logic/commands/exceptions/CommandException.java index a16bd14f2cd..775bf4e2cc3 100644 --- a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java +++ b/src/main/java/seedu/medinfo/logic/commands/exceptions/CommandException.java @@ -1,4 +1,6 @@ -package seedu.address.logic.commands.exceptions; +package seedu.medinfo.logic.commands.exceptions; + +import seedu.medinfo.logic.commands.Command; /** * Represents an error which occurs during execution of a {@link Command}. diff --git a/src/main/java/seedu/medinfo/logic/parser/AddCommandParser.java b/src/main/java/seedu/medinfo/logic/parser/AddCommandParser.java new file mode 100644 index 00000000000..15e189e1316 --- /dev/null +++ b/src/main/java/seedu/medinfo/logic/parser/AddCommandParser.java @@ -0,0 +1,64 @@ +package seedu.medinfo.logic.parser; + +import static seedu.medinfo.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.medinfo.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.medinfo.logic.parser.CliSyntax.PREFIX_NRIC; +import static seedu.medinfo.logic.parser.CliSyntax.PREFIX_STATUS; + +import java.util.stream.Stream; + +import seedu.medinfo.logic.commands.AddCommand; +import seedu.medinfo.logic.parser.exceptions.ParseException; +import seedu.medinfo.model.patient.Name; +import seedu.medinfo.model.patient.Nric; +import seedu.medinfo.model.patient.Patient; +import seedu.medinfo.model.patient.Status; + +/** + * Parses input arguments and creates a new AddCommand object + */ +public class AddCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the {@code AddCommand} + * and returns an {@code 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_NRIC, PREFIX_STATUS); + + if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_NRIC) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); + } + + Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()); + assert (name != null); + Nric nric = ParserUtil.parseNric(argMultimap.getValue(PREFIX_NRIC).get()); + assert (nric != null); + + Patient patient; + + if (arePrefixesPresent(argMultimap, PREFIX_STATUS)) { + Status status = ParserUtil.parseStatus(argMultimap.getValue(PREFIX_STATUS).get()); + assert(status != null); + patient = new Patient(nric, name, status); + } else { + patient = new Patient(nric, name); + } + + return new AddCommand(patient); + } + + /** + * 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/medinfo/logic/parser/AddWardCommandParser.java b/src/main/java/seedu/medinfo/logic/parser/AddWardCommandParser.java new file mode 100644 index 00000000000..303fc4e09e7 --- /dev/null +++ b/src/main/java/seedu/medinfo/logic/parser/AddWardCommandParser.java @@ -0,0 +1,56 @@ +package seedu.medinfo.logic.parser; + +import static seedu.medinfo.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.medinfo.logic.parser.CliSyntax.PREFIX_CAPACITY; +import static seedu.medinfo.logic.parser.CliSyntax.PREFIX_WARD; + +import java.util.stream.Stream; + +import seedu.medinfo.logic.commands.AddWardCommand; +import seedu.medinfo.logic.parser.exceptions.ParseException; +import seedu.medinfo.model.ward.Capacity; +import seedu.medinfo.model.ward.Ward; +import seedu.medinfo.model.ward.WardName; + + +/** + * Parses input arguments and creates a new AddWardCommand object + */ +public class AddWardCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the {@code AddWardCommand} + * and returns an {@code AddWardCommand} object for execution. + * + * @throws ParseException if the user input does not conform the expected format + */ + public AddWardCommand parse(String args) throws ParseException { + + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_WARD, PREFIX_CAPACITY); + + if (!arePrefixesPresent(argMultimap, PREFIX_WARD) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddWardCommand.MESSAGE_USAGE)); + } + + if (!arePrefixesPresent(argMultimap, PREFIX_CAPACITY)) { + WardName wardName = ParserUtil.parseWardName(argMultimap.getValue(PREFIX_WARD).get()); + Ward ward = new Ward(wardName); + return new AddWardCommand(ward); + } + + WardName wardName = ParserUtil.parseWardName(argMultimap.getValue(PREFIX_WARD).get()); + Capacity wardCapacity = ParserUtil.parseCapacity(argMultimap.getValue(PREFIX_CAPACITY).get()); + Ward ward = new Ward(wardName, wardCapacity); + return new AddWardCommand(ward); + } + + /** + * 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/ArgumentMultimap.java b/src/main/java/seedu/medinfo/logic/parser/ArgumentMultimap.java similarity index 98% rename from src/main/java/seedu/address/logic/parser/ArgumentMultimap.java rename to src/main/java/seedu/medinfo/logic/parser/ArgumentMultimap.java index 954c8e18f8e..e9f5ac17de1 100644 --- a/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java +++ b/src/main/java/seedu/medinfo/logic/parser/ArgumentMultimap.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package seedu.medinfo.logic.parser; import java.util.ArrayList; import java.util.HashMap; diff --git a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java b/src/main/java/seedu/medinfo/logic/parser/ArgumentTokenizer.java similarity index 99% rename from src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java rename to src/main/java/seedu/medinfo/logic/parser/ArgumentTokenizer.java index 5c9aebfa488..bdadbd3c5bc 100644 --- a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java +++ b/src/main/java/seedu/medinfo/logic/parser/ArgumentTokenizer.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package seedu.medinfo.logic.parser; import java.util.ArrayList; import java.util.Arrays; diff --git a/src/main/java/seedu/medinfo/logic/parser/CliSyntax.java b/src/main/java/seedu/medinfo/logic/parser/CliSyntax.java new file mode 100644 index 00000000000..a2a89bc017a --- /dev/null +++ b/src/main/java/seedu/medinfo/logic/parser/CliSyntax.java @@ -0,0 +1,16 @@ +package seedu.medinfo.logic.parser; + +/** + * Contains Command Line Interface (CLI) syntax definitions common to multiple + * commands + */ +public class CliSyntax { + + /* Prefix definitions */ + public static final Prefix PREFIX_NRIC = new Prefix("nric/"); + public static final Prefix PREFIX_NAME = new Prefix("name/"); + public static final Prefix PREFIX_STATUS = new Prefix("s/"); + public static final Prefix PREFIX_WARD = new Prefix("w/"); + public static final Prefix PREFIX_DISCHARGE = new Prefix("d/"); + public static final Prefix PREFIX_CAPACITY = new Prefix("c/"); +} diff --git a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java b/src/main/java/seedu/medinfo/logic/parser/DeleteCommandParser.java similarity index 66% rename from src/main/java/seedu/address/logic/parser/DeleteCommandParser.java rename to src/main/java/seedu/medinfo/logic/parser/DeleteCommandParser.java index 522b93081cc..d8a36c9cb9e 100644 --- a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java +++ b/src/main/java/seedu/medinfo/logic/parser/DeleteCommandParser.java @@ -1,10 +1,10 @@ -package seedu.address.logic.parser; +package seedu.medinfo.logic.parser; -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.medinfo.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.DeleteCommand; -import seedu.address.logic.parser.exceptions.ParseException; +import seedu.medinfo.commons.core.index.Index; +import seedu.medinfo.logic.commands.DeleteCommand; +import seedu.medinfo.logic.parser.exceptions.ParseException; /** * Parses input arguments and creates a new DeleteCommand object @@ -12,8 +12,8 @@ public class DeleteCommandParser implements Parser { /** - * Parses the given {@code String} of arguments in the context of the DeleteCommand - * and returns a DeleteCommand object for execution. + * Parses the given {@code String} of arguments in the context of the {@code DeleteCommand} + * and returns a {@code DeleteCommand} object for execution. * @throws ParseException if the user input does not conform the expected format */ public DeleteCommand parse(String args) throws ParseException { diff --git a/src/main/java/seedu/medinfo/logic/parser/DeleteWardCommandParser.java b/src/main/java/seedu/medinfo/logic/parser/DeleteWardCommandParser.java new file mode 100644 index 00000000000..10947c52e82 --- /dev/null +++ b/src/main/java/seedu/medinfo/logic/parser/DeleteWardCommandParser.java @@ -0,0 +1,29 @@ +package seedu.medinfo.logic.parser; + +import static seedu.medinfo.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.medinfo.commons.core.index.Index; +import seedu.medinfo.logic.commands.DeleteWardCommand; +import seedu.medinfo.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new DeleteWardCommand object + */ +public class DeleteWardCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the {@code DeleteWardCommand} + * and returns a {@code DeleteWardCommand} object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public DeleteWardCommand parse(String args) throws ParseException { + try { + Index index = ParserUtil.parseIndex(args); + return new DeleteWardCommand(index); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteWardCommand.MESSAGE_USAGE), pe); + } + } + +} diff --git a/src/main/java/seedu/medinfo/logic/parser/EditCommandParser.java b/src/main/java/seedu/medinfo/logic/parser/EditCommandParser.java new file mode 100644 index 00000000000..2864938d9c9 --- /dev/null +++ b/src/main/java/seedu/medinfo/logic/parser/EditCommandParser.java @@ -0,0 +1,56 @@ +package seedu.medinfo.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.medinfo.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.medinfo.logic.parser.CliSyntax.PREFIX_DISCHARGE; +import static seedu.medinfo.logic.parser.CliSyntax.PREFIX_STATUS; +import static seedu.medinfo.logic.parser.CliSyntax.PREFIX_WARD; + +import seedu.medinfo.commons.core.index.Index; +import seedu.medinfo.logic.commands.EditCommand; +import seedu.medinfo.logic.commands.EditCommand.EditPatientDescriptor; +import seedu.medinfo.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new EditCommand object + */ +public class EditCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the + * {@code EditCommand} + * and returns an {@code EditCommand} object for execution. + * + * @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_STATUS, PREFIX_WARD, PREFIX_DISCHARGE); + + Index index; + + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (ParseException pe) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE), pe); + } + + EditPatientDescriptor editPatientDescriptor = new EditCommand.EditPatientDescriptor(); + if (argMultimap.getValue(PREFIX_STATUS).isPresent()) { + editPatientDescriptor.setStatus(ParserUtil.parseStatus(argMultimap.getValue(PREFIX_STATUS).get())); + } + if (argMultimap.getValue(PREFIX_WARD).isPresent()) { + editPatientDescriptor.setWard(ParserUtil.parseWardName(argMultimap.getValue(PREFIX_WARD).get())); + } + if (argMultimap.getValue(PREFIX_DISCHARGE).isPresent()) { + editPatientDescriptor.setDischarge(ParserUtil.parseDischarge(argMultimap.getValue(PREFIX_DISCHARGE).get())); + } + + if (!editPatientDescriptor.isAnyFieldEdited()) { + throw new ParseException(EditCommand.MESSAGE_NOT_EDITED); + } + + return new EditCommand(index, editPatientDescriptor); + } + +} diff --git a/src/main/java/seedu/medinfo/logic/parser/EditWardCommandParser.java b/src/main/java/seedu/medinfo/logic/parser/EditWardCommandParser.java new file mode 100644 index 00000000000..6efa256a764 --- /dev/null +++ b/src/main/java/seedu/medinfo/logic/parser/EditWardCommandParser.java @@ -0,0 +1,56 @@ +package seedu.medinfo.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.medinfo.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.medinfo.logic.parser.CliSyntax.PREFIX_CAPACITY; +import static seedu.medinfo.logic.parser.CliSyntax.PREFIX_WARD; + +import seedu.medinfo.commons.core.index.Index; +import seedu.medinfo.logic.commands.EditWardCommand; +import seedu.medinfo.logic.commands.EditWardCommand.EditWardDescriptor; +import seedu.medinfo.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new EditCommand object + */ +public class EditWardCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the + * {@code EditWardCommand} + * and returns an {@code EditWardCommand} object for execution. + * + * @throws ParseException if the user input does not conform the expected format + */ + public EditWardCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_WARD, PREFIX_CAPACITY); + + Index index; + + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (ParseException pe) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditWardCommand.MESSAGE_USAGE), pe); + } + + EditWardDescriptor editWardDescriptor = new EditWardCommand.EditWardDescriptor(); + if (argMultimap.getValue(PREFIX_WARD).isPresent()) { + if (index.getOneBased() == 1) { + throw new ParseException(EditWardCommand.MESSAGE_WAITING_ROOM_NAME_EDIT); + } else { + editWardDescriptor.setWard(ParserUtil.parseWardName(argMultimap.getValue(PREFIX_WARD).get())); + } + } + if (argMultimap.getValue(PREFIX_CAPACITY).isPresent()) { + editWardDescriptor.setCapacity(ParserUtil.parseCapacity(argMultimap.getValue(PREFIX_CAPACITY).get())); + } + + if (!editWardDescriptor.isAnyFieldEdited()) { + throw new ParseException(EditWardCommand.MESSAGE_NOT_EDITED); + } + + return new EditWardCommand(index, editWardDescriptor); + } + +} diff --git a/src/main/java/seedu/medinfo/logic/parser/FindCommandParser.java b/src/main/java/seedu/medinfo/logic/parser/FindCommandParser.java new file mode 100644 index 00000000000..7453aff0f0e --- /dev/null +++ b/src/main/java/seedu/medinfo/logic/parser/FindCommandParser.java @@ -0,0 +1,78 @@ +package seedu.medinfo.logic.parser; + +import static seedu.medinfo.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.medinfo.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.medinfo.logic.parser.CliSyntax.PREFIX_NRIC; +import static seedu.medinfo.logic.parser.CliSyntax.PREFIX_STATUS; +import static seedu.medinfo.logic.parser.CliSyntax.PREFIX_WARD; + +import java.util.Arrays; +import java.util.stream.Stream; + +import seedu.medinfo.logic.commands.FindCommand; +import seedu.medinfo.logic.parser.exceptions.ParseException; +import seedu.medinfo.model.patient.NameContainsKeywordsPredicate; +import seedu.medinfo.model.patient.NricContainsKeywordsPredicate; +import seedu.medinfo.model.patient.StatusContainsKeywordsPredicate; +import seedu.medinfo.model.ward.WardNameContainsKeywordsPredicate; + +/** + * Parses input arguments and creates a new FindCommand object + */ +public class FindCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the FindCommand + * and returns a FindCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public FindCommand parse(String args) throws ParseException { + + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_NRIC, PREFIX_STATUS, + PREFIX_WARD); + + if (!anyPrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_NRIC, PREFIX_STATUS, PREFIX_WARD) + || !argMultimap.getPreamble().isEmpty() + || manyPrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_NRIC, PREFIX_STATUS, PREFIX_WARD)) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); + } + + if (anyPrefixesPresent(argMultimap, PREFIX_NAME)) { + String trimmedNames = argMultimap.getValue(PREFIX_NAME).get(); + checkArgsEmpty(trimmedNames); + String[] nameKeywords = trimmedNames.split("\\s+"); + return new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList(nameKeywords))); + } else if (anyPrefixesPresent(argMultimap, PREFIX_NRIC)) { + String trimmedNric = argMultimap.getValue(PREFIX_NRIC).get(); + checkArgsEmpty(trimmedNric); + String [] nricKeywords = trimmedNric.split("\\s+"); + return new FindCommand(new NricContainsKeywordsPredicate(Arrays.asList(nricKeywords))); + } else if (anyPrefixesPresent(argMultimap, PREFIX_STATUS)) { + String trimmedStatus = argMultimap.getValue(PREFIX_STATUS).get(); + checkArgsEmpty(trimmedStatus); + String [] statusKeywords = trimmedStatus.split("\\s+"); + return new FindCommand(new StatusContainsKeywordsPredicate(Arrays.asList(statusKeywords))); + } else { + String trimmedWardName = argMultimap.getValue(PREFIX_WARD).get(); + checkArgsEmpty(trimmedWardName); + String [] statusKeywords = trimmedWardName.split("\\s+"); + return new FindCommand(new WardNameContainsKeywordsPredicate(Arrays.asList(statusKeywords))); + } + } + + private static boolean anyPrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).anyMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + + private static boolean manyPrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).filter(prefix -> argumentMultimap.getValue(prefix).isPresent()).count() > 1; + } + + private void checkArgsEmpty(String trimmedArgs) throws ParseException { + if (trimmedArgs.isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); + } + } + +} diff --git a/src/main/java/seedu/medinfo/logic/parser/MedInfoParser.java b/src/main/java/seedu/medinfo/logic/parser/MedInfoParser.java new file mode 100644 index 00000000000..e3e47286129 --- /dev/null +++ b/src/main/java/seedu/medinfo/logic/parser/MedInfoParser.java @@ -0,0 +1,121 @@ +package seedu.medinfo.logic.parser; + +import static seedu.medinfo.commons.core.Messages.MESSAGE_ABORT_DELETE; +import static seedu.medinfo.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.medinfo.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; + +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javafx.scene.control.Alert; +import javafx.scene.control.ButtonType; +import seedu.medinfo.logic.commands.AddCommand; +import seedu.medinfo.logic.commands.AddWardCommand; +import seedu.medinfo.logic.commands.ClearCommand; +import seedu.medinfo.logic.commands.Command; +import seedu.medinfo.logic.commands.DeleteCommand; +import seedu.medinfo.logic.commands.DeleteWardCommand; +import seedu.medinfo.logic.commands.EditCommand; +import seedu.medinfo.logic.commands.EditWardCommand; +import seedu.medinfo.logic.commands.ExitCommand; +import seedu.medinfo.logic.commands.FindCommand; +import seedu.medinfo.logic.commands.HelpCommand; +import seedu.medinfo.logic.commands.ListCommand; +import seedu.medinfo.logic.commands.SortCommand; +import seedu.medinfo.logic.parser.exceptions.ParseException; + +/** + * Parses user input. + */ +public class MedInfoParser { + + /** + * Used for initial separation of command word and args. + */ + private static final Pattern BASIC_COMMAND_FORMAT = Pattern.compile("(?\\S+)(?.*)"); + + /** + * Parses user input into command for execution. + * + * @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 + */ + public Command parseCommand(String userInput) throws ParseException { + final Matcher matcher = BASIC_COMMAND_FORMAT.matcher(userInput.trim()); + if (!matcher.matches()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE)); + } + + final String commandWord = matcher.group("commandWord"); + final String arguments = matcher.group("arguments"); + + // for confirmation window + Alert confirmationDialog; + Optional result; + + switch (commandWord) { + case AddCommand.COMMAND_WORD: + return new AddCommandParser().parse(arguments); + + case EditCommand.COMMAND_WORD: + return new EditCommandParser().parse(arguments); + + case DeleteCommand.COMMAND_WORD: + confirmationDialog = new Alert(Alert.AlertType.CONFIRMATION, + "Are you sure you want to delete the patient?"); + result = confirmationDialog.showAndWait(); + if (result.isPresent() && result.get() == ButtonType.OK) { + return new DeleteCommandParser().parse(arguments); + } else { + throw new ParseException(MESSAGE_ABORT_DELETE); // cancel the deletion command + } + + case ClearCommand.COMMAND_WORD: + confirmationDialog = new Alert(Alert.AlertType.CONFIRMATION, + "Are you sure you want to clear ALL patients and wards?"); + result = confirmationDialog.showAndWait(); + if (result.isPresent() && result.get() == ButtonType.OK) { + return new ClearCommand(); + } else { + throw new ParseException(MESSAGE_ABORT_DELETE); // cancel the deletion command + } + + case FindCommand.COMMAND_WORD: + return new FindCommandParser().parse(arguments); + + case ListCommand.COMMAND_WORD: + return new ListCommand(); + + case ExitCommand.COMMAND_WORD: + return new ExitCommand(); + + case HelpCommand.COMMAND_WORD: + return new HelpCommand(); + + case AddWardCommand.COMMAND_WORD: + return new AddWardCommandParser().parse(arguments); + + case EditWardCommand.COMMAND_WORD: + return new EditWardCommandParser().parse(arguments); + + case DeleteWardCommand.COMMAND_WORD: + confirmationDialog = new Alert(Alert.AlertType.CONFIRMATION, + "Are you sure you want to delete the ward?"); + result = confirmationDialog.showAndWait(); + if (result.isPresent() && result.get() == ButtonType.OK) { + return new DeleteWardCommandParser().parse(arguments); + } else { + throw new ParseException(MESSAGE_ABORT_DELETE); // cancel the deletion command + } + + case SortCommand.COMMAND_WORD: + return new SortCommandParser().parse(arguments); + + default: + throw new ParseException(MESSAGE_UNKNOWN_COMMAND); + } + } + +} diff --git a/src/main/java/seedu/address/logic/parser/Parser.java b/src/main/java/seedu/medinfo/logic/parser/Parser.java similarity index 72% rename from src/main/java/seedu/address/logic/parser/Parser.java rename to src/main/java/seedu/medinfo/logic/parser/Parser.java index d6551ad8e3f..1f25d243f69 100644 --- a/src/main/java/seedu/address/logic/parser/Parser.java +++ b/src/main/java/seedu/medinfo/logic/parser/Parser.java @@ -1,7 +1,7 @@ -package seedu.address.logic.parser; +package seedu.medinfo.logic.parser; -import seedu.address.logic.commands.Command; -import seedu.address.logic.parser.exceptions.ParseException; +import seedu.medinfo.logic.commands.Command; +import seedu.medinfo.logic.parser.exceptions.ParseException; /** * Represents a Parser that is able to parse user input into a {@code Command} of type {@code T}. diff --git a/src/main/java/seedu/medinfo/logic/parser/ParserUtil.java b/src/main/java/seedu/medinfo/logic/parser/ParserUtil.java new file mode 100644 index 00000000000..cb505c49752 --- /dev/null +++ b/src/main/java/seedu/medinfo/logic/parser/ParserUtil.java @@ -0,0 +1,147 @@ +package seedu.medinfo.logic.parser; + +import static java.util.Objects.requireNonNull; + +import seedu.medinfo.commons.core.index.Index; +import seedu.medinfo.commons.util.StringUtil; +import seedu.medinfo.logic.commands.SortCommand; +import seedu.medinfo.logic.commands.SortCommand.Order; +import seedu.medinfo.logic.parser.exceptions.ParseException; +import seedu.medinfo.model.patient.Discharge; +import seedu.medinfo.model.patient.Name; +import seedu.medinfo.model.patient.Nric; +import seedu.medinfo.model.patient.Status; +import seedu.medinfo.model.ward.Capacity; +import seedu.medinfo.model.ward.WardName; + +/** + * Contains utility methods used for parsing strings in the various *Parser + * classes. + */ +public class ParserUtil { + + public static final String MESSAGE_INVALID_INDEX = "Index is not a non-zero unsigned integer."; + + /** + * 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). + */ + public static Index parseIndex(String oneBasedIndex) throws ParseException { + String trimmedIndex = oneBasedIndex.trim(); + if (!StringUtil.isNonZeroUnsignedInteger(trimmedIndex)) { + throw new ParseException(MESSAGE_INVALID_INDEX); + } + return Index.fromOneBased(Integer.parseInt(trimmedIndex)); + } + + /** + * 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); + } + + /** + * Parses a {@code String} name into a {@code Name}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code name} is invalid. + */ + public static Name parseName(String name) throws ParseException { + requireNonNull(name); + String trimmedName = name.trim(); + if (!Name.isValidName(trimmedName)) { + throw new ParseException(Name.MESSAGE_CONSTRAINTS); + } + return new Name(trimmedName); + } + + /** + * Parses a {@code String} status into a {@code Status}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code status} is invalid. + */ + public static Status parseStatus(String status) throws ParseException { + requireNonNull(status); + String trimmedStatus = status.trim(); + if (!Status.isValidStatus(trimmedStatus)) { + throw new ParseException(Status.MESSAGE_CONSTRAINTS); + } + return new Status(trimmedStatus); + } + + /** + * Parses a {@code String} ward into a {@code WardName}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code ward} is invalid. + */ + public static WardName parseWardName(String ward) throws ParseException { + requireNonNull(ward); + String trimmedWard = ward.trim(); + if (!WardName.isValidWardName(trimmedWard)) { + throw new ParseException(WardName.MESSAGE_CONSTRAINTS); + } + return new WardName(trimmedWard); + } + + /** + * Parses a {@code String} capacity into a {@code Capacity}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code ward} is invalid. + */ + public static Capacity parseCapacity(String capacity) throws ParseException { + requireNonNull(capacity); + String trimmedCapacity = capacity.trim(); + if (!Capacity.isValidCapacity(trimmedCapacity)) { + throw new ParseException(Capacity.MESSAGE_CONSTRAINTS); + } + return new Capacity(trimmedCapacity); + } + + /** + * Parses a {@code String} discharge into a {@code Discharge}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code discharge} is invalid. + */ + public static Discharge parseDischarge(String discharge) throws ParseException { + requireNonNull(discharge); + String trimmedDischarge = discharge.trim(); + if (!Discharge.isValidDischarge(trimmedDischarge)) { + throw new ParseException(Discharge.MESSAGE_CONSTRAINTS); + } + return new Discharge(trimmedDischarge); + } + + + /** + * Parses {@code String} order into a {@code Order}. + * + * @throws ParseException if the given {@code order} is invalid. + */ + // @@author {Echomo-Xinyu}-reused + // Referenced the parseSortOrder method + public static Order parseSortOrder(String order) throws ParseException { + try { + return Order.valueOf(order.toUpperCase()); + } catch (IllegalArgumentException e) { + throw new ParseException(SortCommand.MESSAGE_UNKNOWN_ORDER_KEYWORD); + } + } + //@@author +} diff --git a/src/main/java/seedu/address/logic/parser/Prefix.java b/src/main/java/seedu/medinfo/logic/parser/Prefix.java similarity index 95% rename from src/main/java/seedu/address/logic/parser/Prefix.java rename to src/main/java/seedu/medinfo/logic/parser/Prefix.java index c859d5fa5db..1b54e4751a0 100644 --- a/src/main/java/seedu/address/logic/parser/Prefix.java +++ b/src/main/java/seedu/medinfo/logic/parser/Prefix.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package seedu.medinfo.logic.parser; /** * A prefix that marks the beginning of an argument in an arguments string. diff --git a/src/main/java/seedu/medinfo/logic/parser/SortCommandParser.java b/src/main/java/seedu/medinfo/logic/parser/SortCommandParser.java new file mode 100644 index 00000000000..f3566874c31 --- /dev/null +++ b/src/main/java/seedu/medinfo/logic/parser/SortCommandParser.java @@ -0,0 +1,91 @@ +package seedu.medinfo.logic.parser; + +import static seedu.medinfo.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.medinfo.logic.parser.CliSyntax.PREFIX_DISCHARGE; +import static seedu.medinfo.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.medinfo.logic.parser.CliSyntax.PREFIX_STATUS; +import static seedu.medinfo.logic.parser.CliSyntax.PREFIX_WARD; +import static seedu.medinfo.logic.parser.ParserUtil.parseSortOrder; + +import java.util.stream.Stream; + +import seedu.medinfo.logic.commands.SortCommand; +import seedu.medinfo.logic.commands.SortCommand.Field; +import seedu.medinfo.logic.commands.SortCommand.Order; +import seedu.medinfo.logic.parser.exceptions.ParseException; + + +/** + * Parses input arguments and creates a new SortCommand object + */ +public class SortCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the FindCommand + * and returns a FindCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public SortCommand parse(String args) throws ParseException { + + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_STATUS, PREFIX_DISCHARGE, + PREFIX_WARD); + + if (!anyPrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_STATUS, PREFIX_DISCHARGE, PREFIX_WARD) + || !argMultimap.getPreamble().isEmpty() + || manyPrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_STATUS, PREFIX_DISCHARGE, PREFIX_WARD)) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, SortCommand.MESSAGE_USAGE)); + } + + if (anyPrefixesPresent(argMultimap, PREFIX_NAME)) { + String trimmedName = argMultimap.getValue(PREFIX_NAME).get(); + checkArgsEmpty(trimmedName); + Order order = parseSortOrder(trimmedName); + return new SortCommand(Field.NAME, order); + } else if (anyPrefixesPresent(argMultimap, PREFIX_STATUS)) { + String trimmedStatus = argMultimap.getValue(PREFIX_STATUS).get(); + checkArgsEmpty(trimmedStatus); + Order order = parseSortOrder(trimmedStatus); + return new SortCommand(Field.STATUS, order); + } else if (anyPrefixesPresent(argMultimap, PREFIX_DISCHARGE)) { + String trimmedDischarge = argMultimap.getValue(PREFIX_DISCHARGE).get(); + checkArgsEmpty(trimmedDischarge); + Order order = parseSortOrder(trimmedDischarge); + return new SortCommand(Field.DISCHARGE, order); + } else { + String trimmedWard = argMultimap.getValue(PREFIX_WARD).get(); + checkArgsEmpty(trimmedWard); + Order order = parseSortOrder(trimmedWard); + return new SortCommand(Field.WARD, order); + } + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values + * in the given + * {@code ArgumentMultimap}. + */ + private static boolean anyPrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).anyMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + + /** + * Returns true if more than one of the prefixes contains {@code Optional} values + * in the given + * {@code ArgumentMultimap}. + */ + private static boolean manyPrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).filter(prefix -> argumentMultimap.getValue(prefix).isPresent()).count() > 1; + } + + /** + * Checks if a given argument is empty + * @param trimmedArgs if the argument is empty + */ + private void checkArgsEmpty(String trimmedArgs) throws ParseException { + if (trimmedArgs.isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, SortCommand.MESSAGE_USAGE)); + } + } + +} diff --git a/src/main/java/seedu/address/logic/parser/exceptions/ParseException.java b/src/main/java/seedu/medinfo/logic/parser/exceptions/ParseException.java similarity index 60% rename from src/main/java/seedu/address/logic/parser/exceptions/ParseException.java rename to src/main/java/seedu/medinfo/logic/parser/exceptions/ParseException.java index 158a1a54c1c..bbf90187acf 100644 --- a/src/main/java/seedu/address/logic/parser/exceptions/ParseException.java +++ b/src/main/java/seedu/medinfo/logic/parser/exceptions/ParseException.java @@ -1,9 +1,9 @@ -package seedu.address.logic.parser.exceptions; +package seedu.medinfo.logic.parser.exceptions; -import seedu.address.commons.exceptions.IllegalValueException; +import seedu.medinfo.commons.exceptions.IllegalValueException; /** - * Represents a parse error encountered by a parser. + * Signals a parse error encountered by a parser. */ public class ParseException extends IllegalValueException { diff --git a/src/main/java/seedu/medinfo/model/MedInfo.java b/src/main/java/seedu/medinfo/model/MedInfo.java new file mode 100644 index 00000000000..9face4f3cc5 --- /dev/null +++ b/src/main/java/seedu/medinfo/model/MedInfo.java @@ -0,0 +1,256 @@ +package seedu.medinfo.model; + +import static java.util.Objects.requireNonNull; +import static seedu.medinfo.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +import javafx.collections.ObservableList; +import seedu.medinfo.logic.commands.exceptions.CommandException; +import seedu.medinfo.model.patient.Patient; +import seedu.medinfo.model.patient.UniquePatientList; +import seedu.medinfo.model.ward.UniqueWardList; +import seedu.medinfo.model.ward.Ward; +import seedu.medinfo.model.ward.exceptions.WardFullException; +import seedu.medinfo.model.ward.exceptions.WardNotFoundException; + +/** + * Wraps all data at the medinfo-book level + * Duplicates are not allowed (by .isSamePatient comparison) + */ +public class MedInfo implements ReadOnlyMedInfo { + + private final UniquePatientList patients; + private final UniqueWardList wards; + + /* + * 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. + */ + { + patients = new UniquePatientList(); + wards = new UniqueWardList(); + } + + public MedInfo() { + } + + /** + * Creates an MedInfo using the Patients in the {@code toBeCopied} + */ + public MedInfo(ReadOnlyMedInfo toBeCopied) { + this(); + resetData(toBeCopied); + } + + /** + * Adds Waiting room for clear command. + */ + public void newMedInfo() { + wards.newUniqueWardList(); + } + + //// List overwrite operations ==================================================================================== + + /** + * FOR TESTING + * Replaces the contents of the patient list with a COPY of {@code patients}. + * {@code patients} must not contain duplicate patients. + */ + public void setPatients(List patients) { + List patientsCopy = new ArrayList<>(); + for (Patient patient : patients) { + Patient toCopy = new Patient(patient.getNric(), patient.getName(), + patient.getStatus(), patient.getWardName(), patient.getDischarge()); + patientsCopy.add(toCopy); + } + this.patients.setPatients(patientsCopy); + } + + /** + * FOR TESTING + * Replaces the contents of the ward list with {@code wards}. + * {@code wards} must not contain duplicate wards. + */ + public void setWards(List wards) { + this.wards.setWards(wards); + } + + /** + * Resets the existing data of this {@code MedInfo} with {@code newData}. + */ + public void resetData(ReadOnlyMedInfo newData) { + requireNonNull(newData); + setWards(newData.getWardList()); + setPatients(newData.getPatientList()); + } + + //// Patient operations =========================================================================================== + + /** + * Returns true if a patient with the same identity as {@code patient} exists in + * the medinfo book. + */ + public boolean hasPatient(Patient patient) { + requireNonNull(patient); + return patients.contains(patient); + } + + /** + * Returns true if a patient with the same NRIC as {@code patient} exists in + * the medinfo book. + */ + public boolean hasPatientNric(Patient patient) { + requireNonNull(patient); + return patients.containsNric(patient); + } + + /** + * Adds a patient to the medinfo book. + * The patient must not already exist in the medinfo book. + */ + public void addPatient(Patient p) throws CommandException, WardNotFoundException { + if (!wards.contains(p.getWardNameString())) { + throw new WardNotFoundException(p.getWardNameString()); + } + try { + wards.addPatient(p); + patients.add(p); + } catch (WardFullException e) { + throw new CommandException(e.toString(), e); + } + } + + /** + * Replaces the given patient {@code target} in the list with + * {@code editedPatient}. + * {@code target} must exist in the medinfo book. + * The patient identity of {@code editedPatient} must not be the same as another + * existing patient in the medinfo book. + */ + public void setPatient(Patient target, Patient editedPatient) throws CommandException { + requireAllNonNull(target, editedPatient); + try { + wards.setPatient(target, editedPatient); + patients.setPatient(target, editedPatient); + } catch (WardFullException e) { + throw new CommandException(e.toString(), e); + } + } + + /** + * Removes {@code key} from this {@code MedInfo}. + * {@code key} must exist in the medinfo book. + */ + public void removePatient(Patient key) { + requireNonNull(key); + patients.remove(key); + wards.remove(key); + } + + @Override + public void sortPatients(Comparator comparator) { + patients.sortPatients(comparator); + } + + //// Ward operations ============================================================================================== + + /** + * Returns true if a ward with the same identity as {@code ward} exists in + * the medinfo book. + */ + public boolean hasWard(Ward ward) { + requireNonNull(ward); + return wards.contains(ward); + } + + /** + * Adds a ward to the medinfo book. + * The ward must not already exist in the medinfo book. + */ + public void addWard(Ward ward) { + wards.add(ward); + } + + /** + * Replaces the given ward {@code target} in the list with + * {@code editedWard}. + * {@code target} must exist in the medinfo book. + * The ward identity of {@code editedWard} must not be the same as another + * existing ward in the medinfo book. + */ + public void setWard(Ward target, Ward editedWard) { + requireNonNull(editedWard); + wards.setWard(target, editedWard); + + for (Patient patient : patients) { + if (patient.getWardName().equals(target.getName())) { + Patient editedPatient = new Patient(patient.getNric(), patient.getName(), patient.getStatus(), + editedWard.getName(), + patient.getDischarge()); + patients.setPatient(patient, editedPatient); + } + } + } + + /** + * Removes {@code key} from this {@code MedInfo}. + * {@code key} must exist in the medinfo book. + */ + public void removeWard(Ward ward) { + wards.remove(ward); + } + + //// Util methods ================================================================================================= + + @Override + public String toString() { + return patients.asUnmodifiableObservableList().size() + " patients"; + } + + @Override + public ObservableList getPatientList() { + return patients.asUnmodifiableObservableList(); + } + + @Override + public ObservableList getWardList() { + return wards.asUnmodifiableObservableList(); + } + + /** + * Stats to be displayed on StatusBarFooter. + * This is the method to modify to choose what you want to display. + * + * @return List of information to display. + */ + @Override + public List getStatsInfo() { + List statsInfo = new ArrayList<>(); + String currentOccupancy = "Current Occupancy: " + patients.size() + "/" + wards.capacity(); + String currentCriticals = "Critical Patients: " + patients.numberOfCritical(); + statsInfo.add(currentOccupancy); + statsInfo.add(currentCriticals); + return statsInfo; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof MedInfo // instanceof handles nulls + && patients.equals(((MedInfo) other).patients)); + } + + @Override + public int hashCode() { + return patients.hashCode(); + } +} diff --git a/src/main/java/seedu/medinfo/model/Model.java b/src/main/java/seedu/medinfo/model/Model.java new file mode 100644 index 00000000000..77a7c0f035f --- /dev/null +++ b/src/main/java/seedu/medinfo/model/Model.java @@ -0,0 +1,142 @@ +package seedu.medinfo.model; + +import java.nio.file.Path; +import java.util.Comparator; +import java.util.List; +import java.util.function.Predicate; + +import javafx.collections.ObservableList; +import seedu.medinfo.commons.core.GuiSettings; +import seedu.medinfo.logic.commands.exceptions.CommandException; +import seedu.medinfo.model.patient.Patient; +import seedu.medinfo.model.ward.Ward; + +/** + * The API of the Model component. + */ +public interface Model { + /** {@code Predicate} that always evaluate to true */ + Predicate PREDICATE_SHOW_ALL_PATIENTS = unused -> true; + + /** {@code Predicate} that always evaluate to true */ + Predicate PREDICATE_SHOW_ALL_WARDS = unused -> true; + + /** + * Replaces user prefs data with the data in {@code userPrefs}. + */ + void setUserPrefs(ReadOnlyUserPrefs userPrefs); + + /** + * Returns the user prefs. + */ + ReadOnlyUserPrefs getUserPrefs(); + + /** + * Returns the user prefs' GUI settings. + */ + GuiSettings getGuiSettings(); + + /** + * Sets the user prefs' GUI settings. + */ + void setGuiSettings(GuiSettings guiSettings); + + /** + * Returns the user prefs' medinfo book file path. + */ + Path getMedInfoFilePath(); + + /** + * Sets the user prefs' medinfo book file path. + */ + void setMedInfoFilePath(Path medInfoFilePath); + + /** + * Replaces medinfo book data with the data in {@code medInfo}. + */ + void setMedInfo(ReadOnlyMedInfo medInfo); + + /** Returns the MedInfo */ + ReadOnlyMedInfo getMedInfo(); + + /** + * Returns true if a patient with the same identity as {@code patient} exists in the medinfo book. + */ + boolean hasPatient(Patient patient); + + /** + * Deletes the given patient. + * The patient must exist in the medinfo book. + */ + void deletePatient(Patient target); + + /** + * Adds the given patient. + * {@code patient} must not already exist in the medinfo book. + */ + void addPatient(Patient patient) throws CommandException; + + /** + * Replaces the given patient {@code target} with {@code editedPatient}. + * {@code target} must exist in the medinfo book. + * The patient identity of {@code editedPatient} must not be the same as another + * existing patient in the medinfo book. + */ + void setPatient(Patient target, Patient editedPatient) throws CommandException; + + /** + * Sorts the list of patients by given {@code comparator} + */ + void sortPatients(Comparator comparator); + + /** + * Returns true if a ward with the same identity as {@code ward} exists in the medinfo book. + */ + boolean hasWard(Ward ward); + + /** + * Deletes the given ward. + * The ward must exist in the medinfo book. + */ + void deleteWard(Ward target); + + /** + * Adds the given ward. + * {@code ward} must not already exist in the medinfo book. + */ + void addWard(Ward ward); + + /** + * Replaces the given ward {@code target} with {@code editedWard}. + * {@code target} must exist in the medinfo book. + * The ward identity of {@code editedWard} must not be the same as another + * existing ward in the medinfo book. + */ + void setWard(Ward target, Ward editedWard); + + List getStatsInfo(); + + /** Returns an unmodifiable view of the filtered patient list */ + ObservableList getFilteredPatientList(); + + /** Returns an unmodifiable view of the filtered ward list */ + ObservableList getFilteredWardList(); + + /** + * Updates the filter of the filtered patient list to filter by the given {@code predicate}. + * @throws NullPointerException if {@code predicate} is null. + */ + void updateFilteredPatientList(Predicate predicate); + + /** + * Updates the filter of the filtered ward list to filter by the given {@code predicate}. + * @throws NullPointerException if {@code predicate} is null. + */ + void updateFilteredWardList(Predicate predicate); + + /** + * Returns true if a patient with the same NRIC as {@code patient} exists in the medinfo book. + */ + boolean hasPatientNric(Patient patient); + +} diff --git a/src/main/java/seedu/medinfo/model/ModelManager.java b/src/main/java/seedu/medinfo/model/ModelManager.java new file mode 100644 index 00000000000..b2af9e1650f --- /dev/null +++ b/src/main/java/seedu/medinfo/model/ModelManager.java @@ -0,0 +1,213 @@ +package seedu.medinfo.model; + +import static java.util.Objects.requireNonNull; +import static seedu.medinfo.commons.util.CollectionUtil.requireAllNonNull; + +import java.nio.file.Path; +import java.util.Comparator; +import java.util.List; +import java.util.function.Predicate; +import java.util.logging.Logger; + +import javafx.collections.ObservableList; +import javafx.collections.transformation.FilteredList; +import seedu.medinfo.commons.core.GuiSettings; +import seedu.medinfo.commons.core.LogsCenter; +import seedu.medinfo.logic.commands.exceptions.CommandException; +import seedu.medinfo.model.patient.Patient; +import seedu.medinfo.model.ward.Ward; + +/** + * Represents the in-memory model of the MedInfo data. + */ +public class ModelManager implements Model { + private static final Logger logger = LogsCenter.getLogger(ModelManager.class); + + private final MedInfo medInfo; + private final UserPrefs userPrefs; + private final FilteredList filteredPatients; + private final FilteredList filteredWards; + + /** + * Initializes a ModelManager with the given medInfo and userPrefs. + */ + public ModelManager(ReadOnlyMedInfo medInfo, ReadOnlyUserPrefs userPrefs) { + requireAllNonNull(medInfo, userPrefs); + + logger.fine("Initializing with MedInfo: " + medInfo + " and user prefs " + userPrefs); + + this.medInfo = new MedInfo(medInfo); + this.userPrefs = new UserPrefs(userPrefs); + filteredPatients = new FilteredList<>(this.medInfo.getPatientList()); + filteredWards = new FilteredList<>(this.medInfo.getWardList()); + } + + public ModelManager() { + this(new MedInfo(), new UserPrefs()); + } + + //=========== UserPrefs ================================================================================== + + @Override + public void setUserPrefs(ReadOnlyUserPrefs userPrefs) { + requireNonNull(userPrefs); + this.userPrefs.resetData(userPrefs); + } + + @Override + public ReadOnlyUserPrefs getUserPrefs() { + return userPrefs; + } + + @Override + public GuiSettings getGuiSettings() { + return userPrefs.getGuiSettings(); + } + + @Override + public void setGuiSettings(GuiSettings guiSettings) { + requireNonNull(guiSettings); + userPrefs.setGuiSettings(guiSettings); + } + + @Override + public Path getMedInfoFilePath() { + return userPrefs.getMedInfoFilePath(); + } + + @Override + public void setMedInfoFilePath(Path medInfoFilePath) { + requireNonNull(medInfoFilePath); + userPrefs.setMedInfoFilePath(medInfoFilePath); + } + + //=========== MedInfo ================================================================================ + + @Override + public void setMedInfo(ReadOnlyMedInfo medInfo) { + this.medInfo.resetData(medInfo); + } + + @Override + public ReadOnlyMedInfo getMedInfo() { + return medInfo; + } + + //// Patient methods ===================================================================================== + @Override + public boolean hasPatient(Patient patient) { + requireNonNull(patient); + return medInfo.hasPatient(patient); + } + + @Override + public boolean hasPatientNric(Patient patient) { + requireNonNull(patient); + return medInfo.hasPatientNric(patient); + } + + @Override + public void deletePatient(Patient target) { + medInfo.removePatient(target); + } + + @Override + public void addPatient(Patient patient) throws CommandException { + medInfo.addPatient(patient); + } + + @Override + public void setPatient(Patient target, Patient editedPatient) throws CommandException { + requireAllNonNull(target, editedPatient); + medInfo.setPatient(target, editedPatient); + } + + @Override + public void sortPatients(Comparator comparator) { + requireNonNull(comparator); + medInfo.sortPatients(comparator); + + } + + //// Ward methods ===================================================================================== + @Override + public boolean hasWard(Ward ward) { + requireNonNull(ward); + return medInfo.hasWard(ward); + } + + @Override + public void deleteWard(Ward target) { + medInfo.removeWard(target); + } + + @Override + public void addWard(Ward ward) { + medInfo.addWard(ward); + } + + @Override + public void setWard(Ward target, Ward editedWard) { + requireAllNonNull(target, editedWard); + medInfo.setWard(target, editedWard); + } + + //=========== Filtered Patient List Accessors ============================================================= + + /** + * Returns an unmodifiable view of the list of {@code Patient} backed by the internal list of + * {@code versionedMedInfo} + */ + @Override + public ObservableList getFilteredPatientList() { + return filteredPatients; + } + + @Override + public void updateFilteredPatientList(Predicate predicate) { + requireNonNull(predicate); + filteredPatients.setPredicate(predicate); + } + + //=========== Filtered Ward List Accessors ============================================================= + + /** + * Returns an unmodifiable view of the list of {@code Ward} backed by the internal list of + * {@code versionedMedInfo} + */ + @Override + public ObservableList getFilteredWardList() { + return filteredWards; + } + + @Override + public void updateFilteredWardList(Predicate predicate) { + requireNonNull(predicate); + filteredWards.setPredicate(predicate); + } + + @Override + public List getStatsInfo() { + return medInfo.getStatsInfo(); + } + + @Override + public boolean equals(Object obj) { + // short circuit if same object + if (obj == this) { + return true; + } + + // instanceof handles nulls + if (!(obj instanceof ModelManager)) { + return false; + } + + // state check + ModelManager other = (ModelManager) obj; + return medInfo.equals(other.medInfo) + && userPrefs.equals(other.userPrefs) + && filteredPatients.equals(other.filteredPatients); + } + +} diff --git a/src/main/java/seedu/medinfo/model/ReadOnlyMedInfo.java b/src/main/java/seedu/medinfo/model/ReadOnlyMedInfo.java new file mode 100644 index 00000000000..b7f457aaad3 --- /dev/null +++ b/src/main/java/seedu/medinfo/model/ReadOnlyMedInfo.java @@ -0,0 +1,27 @@ +package seedu.medinfo.model; + +import java.util.Comparator; +import java.util.List; + +import javafx.collections.ObservableList; +import seedu.medinfo.model.patient.Patient; +import seedu.medinfo.model.ward.Ward; + + +/** + * Unmodifiable view of a MedInfo + */ +public interface ReadOnlyMedInfo { + + void sortPatients(Comparator comparator); + + /** + * Returns an unmodifiable view of the patients list. + * This list will not contain any duplicate patients. + */ + ObservableList getPatientList(); + + ObservableList getWardList(); + + List getStatsInfo(); +} diff --git a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java b/src/main/java/seedu/medinfo/model/ReadOnlyUserPrefs.java similarity index 57% rename from src/main/java/seedu/address/model/ReadOnlyUserPrefs.java rename to src/main/java/seedu/medinfo/model/ReadOnlyUserPrefs.java index befd58a4c73..cbc571c76ee 100644 --- a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java +++ b/src/main/java/seedu/medinfo/model/ReadOnlyUserPrefs.java @@ -1,8 +1,8 @@ -package seedu.address.model; +package seedu.medinfo.model; import java.nio.file.Path; -import seedu.address.commons.core.GuiSettings; +import seedu.medinfo.commons.core.GuiSettings; /** * Unmodifiable view of user prefs. @@ -11,6 +11,6 @@ public interface ReadOnlyUserPrefs { GuiSettings getGuiSettings(); - Path getAddressBookFilePath(); + Path getMedInfoFilePath(); } diff --git a/src/main/java/seedu/address/model/UserPrefs.java b/src/main/java/seedu/medinfo/model/UserPrefs.java similarity index 70% rename from src/main/java/seedu/address/model/UserPrefs.java rename to src/main/java/seedu/medinfo/model/UserPrefs.java index 25a5fd6eab9..bfecc3cdea7 100644 --- a/src/main/java/seedu/address/model/UserPrefs.java +++ b/src/main/java/seedu/medinfo/model/UserPrefs.java @@ -1,4 +1,4 @@ -package seedu.address.model; +package seedu.medinfo.model; import static java.util.Objects.requireNonNull; @@ -6,7 +6,7 @@ import java.nio.file.Paths; import java.util.Objects; -import seedu.address.commons.core.GuiSettings; +import seedu.medinfo.commons.core.GuiSettings; /** * Represents User's preferences. @@ -14,7 +14,7 @@ public class UserPrefs implements ReadOnlyUserPrefs { private GuiSettings guiSettings = new GuiSettings(); - private Path addressBookFilePath = Paths.get("data" , "addressbook.json"); + private Path medInfoFilePath = Paths.get("data" , "medinfo.json"); /** * Creates a {@code UserPrefs} with default values. @@ -35,7 +35,7 @@ public UserPrefs(ReadOnlyUserPrefs userPrefs) { public void resetData(ReadOnlyUserPrefs newUserPrefs) { requireNonNull(newUserPrefs); setGuiSettings(newUserPrefs.getGuiSettings()); - setAddressBookFilePath(newUserPrefs.getAddressBookFilePath()); + setMedInfoFilePath(newUserPrefs.getMedInfoFilePath()); } public GuiSettings getGuiSettings() { @@ -47,13 +47,13 @@ public void setGuiSettings(GuiSettings guiSettings) { this.guiSettings = guiSettings; } - public Path getAddressBookFilePath() { - return addressBookFilePath; + public Path getMedInfoFilePath() { + return medInfoFilePath; } - public void setAddressBookFilePath(Path addressBookFilePath) { - requireNonNull(addressBookFilePath); - this.addressBookFilePath = addressBookFilePath; + public void setMedInfoFilePath(Path medInfoFilePath) { + requireNonNull(medInfoFilePath); + this.medInfoFilePath = medInfoFilePath; } @Override @@ -68,19 +68,19 @@ public boolean equals(Object other) { UserPrefs o = (UserPrefs) other; return guiSettings.equals(o.guiSettings) - && addressBookFilePath.equals(o.addressBookFilePath); + && medInfoFilePath.equals(o.medInfoFilePath); } @Override public int hashCode() { - return Objects.hash(guiSettings, addressBookFilePath); + return Objects.hash(guiSettings, medInfoFilePath); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("Gui Settings : " + guiSettings); - sb.append("\nLocal data file location : " + addressBookFilePath); + sb.append("\nLocal data file location : " + medInfoFilePath); return sb.toString(); } diff --git a/src/main/java/seedu/medinfo/model/patient/Discharge.java b/src/main/java/seedu/medinfo/model/patient/Discharge.java new file mode 100644 index 00000000000..02d3ca62eaa --- /dev/null +++ b/src/main/java/seedu/medinfo/model/patient/Discharge.java @@ -0,0 +1,84 @@ +package seedu.medinfo.model.patient; + +import static java.util.Objects.requireNonNull; +import static seedu.medinfo.commons.util.AppUtil.checkArgument; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * Represents a Patient's discharge date in MedInfo. + */ +public class Discharge { + + public static final String MESSAGE_CONSTRAINTS = "Discharge date-time should be a valid future date-time" + + " of the format dd/MM/yyyy HHmm"; + public static final String DEFAULT_DISCHARGE = "To Be Confirmed"; + public static final String DATE_FORMAT = "dd/MM/yyyy HHmm"; + + public final String value; + + /** + * Constructs a {@code Discharge}. + * + * @param discharge A valid discharge date. + */ + public Discharge(String discharge) { + requireNonNull(discharge); + checkArgument(isValidDischarge(discharge), MESSAGE_CONSTRAINTS); + value = discharge; + } + + /** + * Returns true if a given discharge date-time is valid. + * @param date Date to check. + */ + public static boolean isValidDischarge(String date) { + if (date.equals(DEFAULT_DISCHARGE)) { + return true; + } + try { + DateFormat df = new SimpleDateFormat(DATE_FORMAT); + df.setLenient(false); + df.parse(date); + return true; + } catch (ParseException e) { + return false; + } + } + + /** + * Returns the dateTime. + * @return LocalDateTime + */ + public Date getDateTime() { + try { + DateFormat df = new SimpleDateFormat(DATE_FORMAT); + df.setLenient(false); + return df.parse(value); + } catch (ParseException e) { + return new Date(); + } + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this + || (other instanceof Discharge + && value.equals(((Discharge) other).value)); + } + + @Override + public int hashCode() { + return value.hashCode(); + } + +} + diff --git a/src/main/java/seedu/address/model/person/Name.java b/src/main/java/seedu/medinfo/model/patient/Name.java similarity index 69% rename from src/main/java/seedu/address/model/person/Name.java rename to src/main/java/seedu/medinfo/model/patient/Name.java index 79244d71cf7..308b322e867 100644 --- a/src/main/java/seedu/address/model/person/Name.java +++ b/src/main/java/seedu/medinfo/model/patient/Name.java @@ -1,19 +1,19 @@ -package seedu.address.model.person; +package seedu.medinfo.model.patient; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; +import static seedu.medinfo.commons.util.AppUtil.checkArgument; /** - * Represents a Person's name in the address book. + * Represents a Patient's name in MedInfo. * Guarantees: immutable; is valid as declared in {@link #isValidName(String)} */ public class Name { - public static final String MESSAGE_CONSTRAINTS = - "Names should only contain alphanumeric characters and spaces, and it should not be blank"; + public static final String MESSAGE_CONSTRAINTS = "Names should only contain alphanumeric characters" + + " and spaces, 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} ]*"; @@ -22,7 +22,6 @@ public class Name { /** * Constructs a {@code Name}. - * * @param name A valid name. */ public Name(String name) { @@ -33,12 +32,12 @@ public Name(String name) { /** * Returns true if a given string is a valid name. + * @param test The string to be tested. */ public static boolean isValidName(String test) { return test.matches(VALIDATION_REGEX); } - @Override public String toString() { return fullName; @@ -48,7 +47,7 @@ public String toString() { public boolean equals(Object other) { return other == this // short circuit if same object || (other instanceof Name // instanceof handles nulls - && fullName.equals(((Name) other).fullName)); // state check + && fullName.equals(((Name) other).fullName)); // state check } @Override diff --git a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java b/src/main/java/seedu/medinfo/model/patient/NameContainsKeywordsPredicate.java similarity index 62% rename from src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java rename to src/main/java/seedu/medinfo/model/patient/NameContainsKeywordsPredicate.java index c9b5868427c..9b62bd19142 100644 --- a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java +++ b/src/main/java/seedu/medinfo/model/patient/NameContainsKeywordsPredicate.java @@ -1,14 +1,15 @@ -package seedu.address.model.person; +package seedu.medinfo.model.patient; import java.util.List; import java.util.function.Predicate; -import seedu.address.commons.util.StringUtil; +import seedu.medinfo.commons.util.StringUtil; /** - * Tests that a {@code Person}'s {@code Name} matches any of the keywords given. + * Tests that a {@code Patient}'s {@code Name} matches any of the keywords + * given. */ -public class NameContainsKeywordsPredicate implements Predicate { +public class NameContainsKeywordsPredicate implements Predicate { private final List keywords; public NameContainsKeywordsPredicate(List keywords) { @@ -16,16 +17,16 @@ public NameContainsKeywordsPredicate(List keywords) { } @Override - public boolean test(Person person) { + public boolean test(Patient patient) { return keywords.stream() - .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getName().fullName, keyword)); + .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(patient.getName().fullName, keyword)); } @Override public boolean equals(Object other) { return other == this // short circuit if same object || (other instanceof NameContainsKeywordsPredicate // instanceof handles nulls - && keywords.equals(((NameContainsKeywordsPredicate) other).keywords)); // state check + && keywords.equals(((NameContainsKeywordsPredicate) other).keywords)); // state check } } diff --git a/src/main/java/seedu/medinfo/model/patient/Nric.java b/src/main/java/seedu/medinfo/model/patient/Nric.java new file mode 100644 index 00000000000..5caf01b3d08 --- /dev/null +++ b/src/main/java/seedu/medinfo/model/patient/Nric.java @@ -0,0 +1,59 @@ +package seedu.medinfo.model.patient; + +import static java.util.Objects.requireNonNull; +import static seedu.medinfo.commons.util.AppUtil.checkArgument; + +/** + * Represents a Patient's Nric in MedInfo. + * Guarantees: immutable; is valid as declared in {@link #isValidNric(String)} + */ +public class Nric { + public static final String MESSAGE_CONSTRAINTS = "NRIC should not be blank. It should only begin with either " + + "the letter S, T, F or G, followed by 7 numbers, then ending with a capital letter."; + + /** + * The National Registry Identification Number (NRIC) of Singapore + * is made up of the first character being a S/F/T or G. + * The next 2 numbers is the year of birth for people born 1967 and later. + * The last character is a checksum done on the numbers, + * and the algorithm will not be released. + */ + public static final String VALIDATION_REGEX = "^[STFG]\\d{7}[A-Z]$"; + + public final String value; + + /** + * Constructs an {@code Nric}. + * + * @param nric A valid NRIC. + */ + public Nric(String nric) { + requireNonNull(nric); + checkArgument(isValidNric(nric), MESSAGE_CONSTRAINTS); + value = 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 value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Nric // instanceof handles nulls + && value.equals(((Nric) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/src/main/java/seedu/medinfo/model/patient/NricContainsKeywordsPredicate.java b/src/main/java/seedu/medinfo/model/patient/NricContainsKeywordsPredicate.java new file mode 100644 index 00000000000..faceefc87c3 --- /dev/null +++ b/src/main/java/seedu/medinfo/model/patient/NricContainsKeywordsPredicate.java @@ -0,0 +1,32 @@ +package seedu.medinfo.model.patient; + +import java.util.List; +import java.util.function.Predicate; + +import seedu.medinfo.commons.util.StringUtil; + +/** + * Tests that a {@code Patient}'s {@code NRIC} matches any of the keywords + * given. + */ +public class NricContainsKeywordsPredicate implements Predicate { + private final List keywords; + + public NricContainsKeywordsPredicate(List keywords) { + this.keywords = keywords; + } + + @Override + public boolean test(Patient patient) { + return keywords.stream() + .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(patient.getNric().value, keyword)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof NricContainsKeywordsPredicate // instanceof handles nulls + && keywords.equals(((NricContainsKeywordsPredicate) other).keywords)); // state check + } + +} diff --git a/src/main/java/seedu/medinfo/model/patient/Patient.java b/src/main/java/seedu/medinfo/model/patient/Patient.java new file mode 100644 index 00000000000..af5c3b80243 --- /dev/null +++ b/src/main/java/seedu/medinfo/model/patient/Patient.java @@ -0,0 +1,393 @@ +package seedu.medinfo.model.patient; + +import static seedu.medinfo.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.medinfo.model.ward.Ward.wardWithName; + +import java.util.Date; +import java.util.Objects; + +import seedu.medinfo.model.ward.Ward; +import seedu.medinfo.model.ward.WardName; + +/** + * Represents a Patient in the medinfo book. + * Guarantees: details are present and not null, field values are validated, + * immutable. + */ +public class Patient { + private static final int INVERTER = -1; + + // Identity fields + private final Nric nric; + private final Name name; + + // Data fields + private Status status = new Status("GRAY"); + private WardName ward = new WardName("Waiting Room"); + private Discharge discharge = new Discharge("To Be Confirmed"); + + /** + * Constructor for Patient taking in nric and name. + * + * @param nric Patient NRIC + * @param name Patient name + * Every field must be present and not null. + */ + public Patient(Nric nric, Name name) { + requireAllNonNull(nric, name); + this.nric = nric; + this.name = name; + } + + /** + * Constructor for Patient taking in nric, name, status. + * + * @param nric Patient NRIC + * @param name Patient name + * @param status Patient status + * Every field must be present and not null. + */ + public Patient(Nric nric, Name name, Status status) { + requireAllNonNull(nric, name, status); + this.nric = nric; + this.name = name; + this.status = status; + } + + /** + * Constructor for Patient taking in nric, name, ward. + * + * @param nric Patient NRIC + * @param name Patient name + * @param ward Patient ward + * Every field must be present and not null. + */ + public Patient(Nric nric, Name name, WardName ward) { + requireAllNonNull(nric, name, ward); + this.nric = nric; + this.name = name; + this.ward = ward; + } + + /** + * Constructor for Patient taking in nric, name, status, ward. + * + * @param nric Patient NRIC + * @param name Patient name + * @param status Patient status + * @param ward Patient ward + * Every field must be present and not null. + */ + public Patient(Nric nric, Name name, Status status, WardName ward) { + requireAllNonNull(nric, name, status, ward); + this.nric = nric; + this.name = name; + this.status = status; + this.ward = ward; + } + + /** + * Constructor for Patient taking in nric, name, status, ward. + * + * @param nric Patient NRIC + * @param name Patient name + * @param status Patient status + * @param ward Patient ward + * @param discharge Patient discharge + * Every field must be present and not null. + */ + public Patient(Nric nric, Name name, Status status, WardName ward, Discharge discharge) { + requireAllNonNull(nric, name, status, ward, discharge); + this.nric = nric; + this.name = name; + this.status = status; + this.ward = ward; + this.discharge = discharge; + } + + /** + * Returns the Nric. + * + * @return Patient Nric. + */ + public Nric getNric() { + return nric; + } + + /** + * Returns the Name. + * + * @return Patient Name. + */ + public Name getName() { + return name; + } + + /** + * Returns the Name as a String. + * + * @return Patient Name as a String. + */ + public String getNameString() { + return name.fullName; + } + + /** + * Returns the Status. + * + * @return Patient Status. + */ + public Status getStatus() { + return status; + } + + /** + * Returns the Status as a String. + * + * @return Patient Status as a String. + */ + public String getStatusDesc() { + return status.getDesc(); + } + + /** + * Returns the Ward for comparison. + * + * @return Placeholder Ward for comparison. + */ + public Ward getWard() { + return wardWithName(ward.wardName); + } + + /** + * Returns the WardName. + * + * @return Patient WardName. + */ + public WardName getWardName() { + return ward; + } + + /** + * Returns the WardName as a String. + * + * @return Patient WardName as a String. + */ + public String getWardNameString() { + return wardWithName(ward.wardName).getNameString(); + } + + /** + * Returns the discharge date. + * + * @return Patient Discharge. + */ + public Discharge getDischarge() { + return discharge; + } + + /** + * Returns the discharge date as a String. + * + * @return Patient Discharge as a String. + */ + public String getDischargeString() { + return discharge.value; + } + + /** + * Returns the discharge date as LocalDateTime. + * + * @return LocalDateTime representing the discharge date-time. + */ + public Date getDischargeDateTime() { + return discharge.getDateTime(); + } + + /** + * Sets a new Status. + * + * @param newStatus Status to be set. + */ + public void setStatus(Status newStatus) { + requireAllNonNull(newStatus); + status = newStatus; + } + + /** + * Sets a new Ward. + * + * @param newWard Ward to be set. + */ + public void setWard(WardName newWard) { + requireAllNonNull(newWard); + ward = newWard; + } + + /** + * Sets a new Discharge. + * + * @param newDischarge Discharge to be set. + */ + public void setDischarge(Discharge newDischarge) { + requireAllNonNull(newDischarge); + discharge = newDischarge; + } + + /** + * Returns true if both patients have the same nric and name. + * This defines a weaker notion of equality between two patients. + * + * @param otherPatient Patient to be compared with. + * @return If the patients are the same. + */ + public boolean isSamePatient(Patient otherPatient) { + return this.equals(otherPatient); + } + + /** + * Returns true if both patients have the same Nric. + * This defines a weaker notion of equality between two patients in order to prevent duplicate Nric + * being added to the list. + * + * @param otherPatient Patient to be compared with. + * @return If the patients have the same Nric. + */ + public boolean isSameNric(Patient otherPatient) { + if (otherPatient == this) { + return true; + } + + return otherPatient != null + && otherPatient.getNric().equals(this.getNric()); + } + + /** + * Returns compared result between {@code this} and the given {@code patient} by Name in ascending order. + * Returns positive integer if {@code this} should be placed after, 0 if same, and negative if before. + * + * @param otherPatient Patient to be compared with. + * @return If the patient should be placed before or after. + */ + public int compareToByNameAsc(Patient otherPatient) { + return this.getNameString().compareTo(otherPatient.getNameString()); + } + + + /** + * Returns compared result between {@code this} and the given {@code patient} by Name in descending order. + * Returns positive integer if {@code this} should be placed after, 0 if same, and negative if before. + * + * @param otherPatient Patient to be compared with. + * @return If the patient should be placed before or after. + */ + public int compareToByNameDesc(Patient otherPatient) { + return INVERTER * this.getNameString().compareTo(otherPatient.getNameString()); + } + + /** + * Returns compared result between {@code this} and the given {@code patient} by Status in ascending order. + * Returns positive integer if {@code this} should be placed after, 0 if same, and negative if before. + * + * @param otherPatient Patient to be compared with. + * @return If the patient should be placed before or after. + */ + public int compareToByStatusAsc(Patient otherPatient) { + return this.getStatus().getValue().compareTo(otherPatient.getStatus().getValue()); + } + + /** + * Returns compared result between {@code this} and the given {@code patient} by Status in descending order. + * Returns positive integer if {@code this} should be placed after, 0 if same, and negative if before. + * + * @param otherPatient Patient to be compared with. + * @return If the patient should be placed before or after. + */ + public int compareToByStatusDesc(Patient otherPatient) { + return INVERTER * this.getStatus().getValue().compareTo(otherPatient.getStatus().getValue()); + } + + /** + * Returns compared result between {@code this} and the given {@code patient} by Discharge in ascending order. + * Returns positive integer if {@code this} should be placed after, 0 if same, and negative if before. + * + * @param otherPatient Patient to be compared with. + * @return If the patient should be placed before or after. + */ + public int compareToByDischargeAsc(Patient otherPatient) { + return this.getDischargeDateTime().compareTo(otherPatient.getDischargeDateTime()); + } + + /** + * Returns compared result between {@code this} and the given {@code patient} by Discharge in descending order. + * Returns positive integer if {@code this} should be placed after, 0 if same, and negative if before. + * + * @param otherPatient Patient to be compared with. + * @return If the patient should be placed before or after. + */ + public int compareToByDischargeDesc(Patient otherPatient) { + return INVERTER * this.getDischargeDateTime().compareTo(otherPatient.getDischargeDateTime()); + } + + /** + * Returns compared result between {@code this} and the given {@code patient} by Ward in ascending order. + * Returns positive integer if {@code this} should be placed after, 0 if same, and negative if before. + * + * @param otherPatient Patient to be compared with. + * @return If the patient should be placed before or after. + */ + public int compareToByWardAsc(Patient otherPatient) { + return this.getWardNameString().compareTo(otherPatient.getWardNameString()); + } + + /** + * Returns compared result between {@code this} and the given {@code patient} by Ward in descending order. + * Returns positive integer if {@code this} should be placed after, 0 if same, and negative if before. + * + * @param otherPatient Patient to be compared with. + * @return If the patient should be placed before or after. + */ + public int compareToByWardDesc(Patient otherPatient) { + return INVERTER * this.getWardNameString().compareTo(otherPatient.getWardNameString()); + } + + /** + * Returns true if both patients have the same identity and data fields. + * This defines a stronger notion of equality between two patients. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof Patient)) { + return false; + } + + Patient otherPatient = (Patient) other; + return otherPatient.getNric().equals(getNric()) + && otherPatient.getName().equals(getName()); + } + + @Override + public int hashCode() { + return Objects.hash(nric, name, status, ward, discharge); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(getNric()) + .append("; Name: ") + .append(getName()) + .append("; Status: ") + .append(getStatus()) + .append("; Ward: ") + .append(getWardNameString()) + .append("; Discharge: ") + .append(getDischarge()); + + return builder.toString(); + } +} diff --git a/src/main/java/seedu/medinfo/model/patient/Status.java b/src/main/java/seedu/medinfo/model/patient/Status.java new file mode 100644 index 00000000000..0d0f5efef70 --- /dev/null +++ b/src/main/java/seedu/medinfo/model/patient/Status.java @@ -0,0 +1,100 @@ +package seedu.medinfo.model.patient; + +import static java.util.Objects.requireNonNull; +import static seedu.medinfo.commons.util.AppUtil.checkArgument; + +/** + * Represents a patient status in the medinfo book. + */ +public class Status { + /** + * The colour codes below reflect the severity of a patient's condition + * and the urgency of treatment needed. + * + * GRAY: Unknown condition, waiting for evaluation + * GREEN: Stable condition, re-evaluation every 180 min + * YELLOW: Serious condition, re-evaluation every 60 min + * RED: Critical condition, requires immediate evaluation by physician + */ + + public static final String MESSAGE_CONSTRAINTS = "Statuses should only be 'GRAY', 'GREEN', 'YELLOW', or 'RED'"; + private static String[] values = { "GRAY", "GREEN", "YELLOW", "RED" }; + + public final String value; + + /** + * Constructs an {@code status}. + * + * @param status A valid status. + */ + public Status(String status) { + requireNonNull(status); + checkArgument(isValidStatus(status), MESSAGE_CONSTRAINTS); + value = status; + } + + /** + * Returns true if a given string is a valid status. + */ + public static boolean isValidStatus(String test) { + for (String value : values) { + if (test.equals(value)) { + return true; + } + } + return false; + } + + /** + * Returns description of the status code. + * + * @return Description of status code. + */ + public String getDesc() { + switch(value) { + case "GREEN": + return "STABLE"; + case "YELLOW": + return "SERIOUS"; + case "RED": + return "CRITICAL"; + default: + return "UNKNOWN"; + } + } + + /** + * Returns the numeric value of each status. + * + * @return Numeric value of each status. + */ + public Integer getValue() { + switch ((value)) { + case "GREEN": + return 1; + case "YELLOW": + return 2; + case "RED": + return 3; + default: + return 0; + } + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Status // instanceof handles nulls + && value.equals(((Status) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/src/main/java/seedu/medinfo/model/patient/StatusContainsKeywordsPredicate.java b/src/main/java/seedu/medinfo/model/patient/StatusContainsKeywordsPredicate.java new file mode 100644 index 00000000000..81ad3c4e5f6 --- /dev/null +++ b/src/main/java/seedu/medinfo/model/patient/StatusContainsKeywordsPredicate.java @@ -0,0 +1,32 @@ +package seedu.medinfo.model.patient; + +import java.util.List; +import java.util.function.Predicate; + +import seedu.medinfo.commons.util.StringUtil; + +/** + * Tests that a {@code Patient}'s {@code Status} matches any of the keywords + * given. + */ +public class StatusContainsKeywordsPredicate implements Predicate { + private final List keywords; + + public StatusContainsKeywordsPredicate(List keywords) { + this.keywords = keywords; + } + + @Override + public boolean test(Patient patient) { + return keywords.stream() + .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(patient.getStatus().value, keyword)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof StatusContainsKeywordsPredicate // instanceof handles nulls + && keywords.equals(((StatusContainsKeywordsPredicate) other).keywords)); // state check + } + +} diff --git a/src/main/java/seedu/medinfo/model/patient/UniquePatientList.java b/src/main/java/seedu/medinfo/model/patient/UniquePatientList.java new file mode 100644 index 00000000000..359aab5240a --- /dev/null +++ b/src/main/java/seedu/medinfo/model/patient/UniquePatientList.java @@ -0,0 +1,212 @@ +package seedu.medinfo.model.patient; + +import static java.util.Objects.requireNonNull; +import static seedu.medinfo.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import seedu.medinfo.model.patient.exceptions.DuplicatePatientException; +import seedu.medinfo.model.patient.exceptions.PatientNotFoundException; + +/** + * A list of patients that enforces uniqueness between its elements and does not + * allow nulls. + * A patient is considered unique by comparing using + * {@code Patient#isSamePatient(Patient)}. As such, adding and updating of + * patients uses Patient#isSamePatient(Patient) for equality so as to ensure that + * the patient being added or updated is + * unique in terms of identity in the UniquePatientList. However, the removal of + * a patient uses Patient#equals(Object) so + * as to ensure that the patient with exactly the same fields will be removed. + * + * Supports a minimal set of list operations. + * + * @see Patient#isSamePatient(Patient) + */ +public class UniquePatientList implements Iterable { + + private final ObservableList internalList = FXCollections.observableArrayList(); + private final ObservableList internalUnmodifiableList = FXCollections + .unmodifiableObservableList(internalList); + + + /** + * Returns size of list. + */ + public int size() { + return internalList.size(); + } + + /** + * Returns total number of critical patients. + */ + public int numberOfCritical() { + int critical = 0; + for (Patient patient:internalList) { + critical = patient.getStatusDesc().equals("CRITICAL") ? critical + 1 : critical; + } + return critical; + } + + /** + * Returns true if the list contains an equivalent patient as the given + * argument. + * @param toCheck Patient to be checked. + */ + public boolean contains(Patient toCheck) { + requireNonNull(toCheck); + return internalList.stream().anyMatch(toCheck::isSamePatient); + } + + /** + * Returns true if the list contains a patient with equivalent NRIC as the given + * argument. + * @param toCheck Patient to be checked. + */ + public boolean containsNric(Patient toCheck) { + requireNonNull(toCheck); + return internalList.stream().anyMatch(toCheck::isSameNric); + } + + /** + * Adds a patient to the list. + * The patient must not already exist in the list. + * @param toAdd Patient to be added. + */ + public void add(Patient toAdd) { + requireNonNull(toAdd); + if (contains(toAdd)) { + throw new DuplicatePatientException(); + } + internalList.add(toAdd); + } + + /** + * Replaces the patient {@code target} in the list with {@code editedPatient}. + * {@code target} must exist in the list. + * The patient identity of {@code editedPatient} must not be the same as another + * existing patient in the list. + */ + public void setPatient(Patient target, Patient editedPatient) { + requireAllNonNull(target, editedPatient); + + int index = internalList.indexOf(target); + if (index == -1) { + throw new PatientNotFoundException(); + } + + if (!target.isSamePatient(editedPatient) && contains(editedPatient)) { + throw new DuplicatePatientException(); + } + + internalList.set(index, editedPatient); + } + + /** + * Removes the equivalent patient from the list. + * The patient must exist in the list. + * @param toRemove Patient to be removed. + */ + public void remove(Patient toRemove) { + requireNonNull(toRemove); + if (!internalList.remove(toRemove)) { + throw new PatientNotFoundException(); + } + } + + /** + * Replaces the contents of this list with {@code replacement}. + * {@code replacement} must not contain duplicate patients. + * @param replacement List of patients to be set. + */ + public void setPatients(UniquePatientList replacement) { + requireNonNull(replacement); + internalList.setAll(replacement.internalList); + } + + /** + * Replaces the contents of this list with {@code patients}. + * {@code patients} must not contain duplicate patients. + * @param patients List of patients to be set. + */ + public void setPatients(List patients) { + requireAllNonNull(patients); + if (!patientsAreUnique(patients)) { + throw new DuplicatePatientException(); + } + internalList.setAll(patients); + } + + + // @@author {Echomo-Xinyu}-reused + // Referenced the sortPatient and replaceSort methods + /** + * Sorts the patient list with {@code comparator}. + * @param comparator Comparator used. + */ + public void sortPatients(Comparator comparator) { + requireNonNull(comparator); + ArrayList sortedList = replaceSort(internalList, comparator); + internalList.setAll(sortedList); + } + + /** + * Sorts the list of patients and returns the sorted list. + * @param observableList List to be sorted. + * @param comparator Comparator used. + * @return The sorted list. + */ + private static ArrayList replaceSort( + ObservableList observableList, Comparator comparator) { + ArrayList duplicatedList = new ArrayList<>(); + for (int i = 0; i < observableList.size(); i++) { + duplicatedList.add(observableList.get(i)); + } + duplicatedList.sort(comparator); + return duplicatedList; + } + //@@author + + /** + * 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) { + return other == this // short circuit if same object + || (other instanceof UniquePatientList // instanceof handles nulls + && internalList.equals(((UniquePatientList) other).internalList)); + } + + @Override + public int hashCode() { + return internalList.hashCode(); + } + + /** + * Returns true if {@code patients} contains only unique patients. + */ + private boolean patientsAreUnique(List patients) { + for (int i = 0; i < patients.size() - 1; i++) { + for (int j = i + 1; j < patients.size(); j++) { + if (patients.get(i).isSamePatient(patients.get(j))) { + return false; + } + } + } + return true; + } +} diff --git a/src/main/java/seedu/medinfo/model/patient/exceptions/DuplicatePatientException.java b/src/main/java/seedu/medinfo/model/patient/exceptions/DuplicatePatientException.java new file mode 100644 index 00000000000..abb739c5a0f --- /dev/null +++ b/src/main/java/seedu/medinfo/model/patient/exceptions/DuplicatePatientException.java @@ -0,0 +1,12 @@ +package seedu.medinfo.model.patient.exceptions; + +/** + * Signals that the operation will result in duplicate Patients. + * Patients are considered duplicates if they have the same identity. + */ +public class DuplicatePatientException extends RuntimeException { + public DuplicatePatientException() { + super("Operation would result in duplicate patients"); + } + +} diff --git a/src/main/java/seedu/medinfo/model/patient/exceptions/PatientNotFoundException.java b/src/main/java/seedu/medinfo/model/patient/exceptions/PatientNotFoundException.java new file mode 100644 index 00000000000..f439eb3f063 --- /dev/null +++ b/src/main/java/seedu/medinfo/model/patient/exceptions/PatientNotFoundException.java @@ -0,0 +1,6 @@ +package seedu.medinfo.model.patient.exceptions; + +/** + * Signals that the operation is unable to find the specified patient. + */ +public class PatientNotFoundException extends RuntimeException {} diff --git a/src/main/java/seedu/medinfo/model/util/SampleDataUtil.java b/src/main/java/seedu/medinfo/model/util/SampleDataUtil.java new file mode 100644 index 00000000000..fd188002ef3 --- /dev/null +++ b/src/main/java/seedu/medinfo/model/util/SampleDataUtil.java @@ -0,0 +1,64 @@ +package seedu.medinfo.model.util; + +import seedu.medinfo.logic.commands.exceptions.CommandException; +import seedu.medinfo.model.MedInfo; +import seedu.medinfo.model.ReadOnlyMedInfo; +import seedu.medinfo.model.patient.Name; +import seedu.medinfo.model.patient.Nric; +import seedu.medinfo.model.patient.Patient; +import seedu.medinfo.model.patient.Status; +import seedu.medinfo.model.ward.Ward; +import seedu.medinfo.model.ward.WardName; + +/** + * Contains utility methods for populating {@code MedInfo} with sample data. + */ +public class SampleDataUtil { + + /** + * Sample Patients for testing. + * @return Array of sample Patients. + */ + public static Patient[] getSamplePatients() { + return new Patient[] { + new Patient(new Nric("S1234567A"), new Name("Alex Yeoh"), new Status("RED"), + new WardName("Intensive Care")), + new Patient(new Nric("S0000000A"), new Name("Bernice Yu"), new WardName("Class C")), + new Patient(new Nric("S0000001A"), new Name("Charlotte Oliveiro"), new Status("GRAY")), + new Patient(new Nric("S0000002A"), new Name("David Li"), new Status("GREEN")), + new Patient(new Nric("S0000003A"), new Name("Irfan Ibrahim"), new Status("YELLOW")), + new Patient(new Nric("S0000004A"), new Name("Roy Balakrishnan"), new Status("RED")) + }; + } + + /** + * Sample Wards for testing. + * @return Array of sample Wards. + */ + public static Ward[] getSampleWards() { + return new Ward[] { + new Ward(new WardName("Waiting Room")), + new Ward(new WardName("Class A")), + new Ward(new WardName("Class B")), + new Ward(new WardName("Class C")), + new Ward(new WardName("Intensive Care")) + }; + } + + /** + * Creates a {@code ReadOnlyMedInfo} with the sample data in this class. + * @return ReadOnlyMedInfo with sample data initialized. + */ + public static ReadOnlyMedInfo getSampleMedInfo() throws CommandException { + MedInfo sampleAb = new MedInfo(); + for (Ward sampleWard : getSampleWards()) { + sampleAb.addWard(sampleWard); + } + + for (Patient samplePatient : getSamplePatients()) { + sampleAb.addPatient(samplePatient); + } + + return sampleAb; + } +} diff --git a/src/main/java/seedu/medinfo/model/ward/Capacity.java b/src/main/java/seedu/medinfo/model/ward/Capacity.java new file mode 100644 index 00000000000..0ef7f66ec5b --- /dev/null +++ b/src/main/java/seedu/medinfo/model/ward/Capacity.java @@ -0,0 +1,75 @@ +package seedu.medinfo.model.ward; + +/** + * Represents the capacity and occupancy of a ward. + */ +public class Capacity { + private static final int MIN_CAPACITY = 1; + private static final int MAX_CAPACITY = 1000; + + public static final String MESSAGE_CONSTRAINTS = "Capacity should be a" + + " positive integer (at least " + Integer.valueOf(MIN_CAPACITY) + " and less than or equal to " + + Integer.valueOf(MAX_CAPACITY) + ")" + + " and it should not be blank"; + + private int value; + + /** + * Constructs a {@code Capacity} with the given capacity. + * + * @param capacity Integer value of the capacity. + */ + public Capacity(int capacity) { + value = capacity; + } + + /** + * Constructs a {@code Capacity} with the given capacity. + * + * @param capacity String value of the capacity. + */ + public Capacity(String capacity) { + value = Integer.parseInt(capacity); + } + + /** + * Sets the Capacity value. + * + * @param capacity Integer value to be set. + */ + public void setCapacity(int capacity) { + value = capacity; + } + + /** + * Returns true if a given string is a valid capacity. + * + * @param test String capacity value to be tested. + * @return If the given string is a valid capacity. + */ + public static boolean isValidCapacity(String test) { + try { + int value = Integer.parseInt(test); + if (value < MIN_CAPACITY || value > MAX_CAPACITY) { + return false; + } + return true; + } catch (NumberFormatException e) { + return false; + } + } + + /** + * Returns the Integer value of {@code this}. + * + * @return + */ + public int getValue() { + return value; + } + + @Override + public String toString() { + return Integer.toString(value); + } +} diff --git a/src/main/java/seedu/medinfo/model/ward/UniqueWardList.java b/src/main/java/seedu/medinfo/model/ward/UniqueWardList.java new file mode 100644 index 00000000000..4a4f8ea78f8 --- /dev/null +++ b/src/main/java/seedu/medinfo/model/ward/UniqueWardList.java @@ -0,0 +1,305 @@ +package seedu.medinfo.model.ward; + +import static java.util.Objects.requireNonNull; +import static seedu.medinfo.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.medinfo.model.ward.Ward.wardWithName; + +import java.util.Iterator; +import java.util.List; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import seedu.medinfo.logic.commands.exceptions.CommandException; +import seedu.medinfo.model.patient.Patient; +import seedu.medinfo.model.patient.exceptions.DuplicatePatientException; +import seedu.medinfo.model.ward.exceptions.DuplicateWardException; +import seedu.medinfo.model.ward.exceptions.InsufficientCapacityException; +import seedu.medinfo.model.ward.exceptions.WardFullException; +import seedu.medinfo.model.ward.exceptions.WardNotFoundException; + +/** + * A list of wards that enforces uniqueness between its elements and does not + * allow nulls. + * A ward is considered unique by comparing using + * {@code Ward#isSameWard(Ward)}. As such, adding and updating of + * wards uses Ward#isSameWard(Ward) for equality so as to ensure that + * the ward being added or updated is + * unique in terms of identity in the UniqueWardList. However, the removal of + * a ward uses Ward#equals(Object) so + * as to ensure that the ward with exactly the same fields will be removed. + * + * Supports a minimal set of list operations. + * + * @see Ward#isSameWard(Ward) + */ +public class UniqueWardList implements Iterable { + + private static final WardName WAITING_ROOM = new WardName("Waiting Room"); + private final ObservableList internalList = FXCollections.observableArrayList(); + private final ObservableList internalUnmodifiableList = FXCollections + .unmodifiableObservableList(internalList); + + /** + * Constructor for a UniqueWardList. + */ + public UniqueWardList() { + + } + + /** + * Initializes default Waiting Room ward with capacity of 10 inside. + */ + public void newUniqueWardList() { + Ward waitingRoom = new Ward(WAITING_ROOM); + internalList.add(waitingRoom); + } + + /** + * Returns size of list. + * + * @return Size of List. + */ + public int size() { + return internalList.size(); + } + + /** + * Returns total capacity of all wards in the list. + * + * @return Total capacity of all wards. + */ + public int capacity() { + int capacity = 0; + for (Ward ward: internalList) { + Capacity cap = ward.getCapacity(); + capacity += cap.getValue(); + } + return capacity; + } + + /** + * Returns specified ward to edit. + * + * @param wardName String name of ward to be edited. + * @return Target ward. + */ + public Ward getWard(String wardName) { + return internalList.get(internalList.indexOf(wardWithName(wardName))); + } + + /** + * Returns true if the list contains an equivalent ward as the given + * {@code Ward}. + * + * @param toCheck Ward to be checked. + */ + public boolean contains(Ward toCheck) { + requireNonNull(toCheck); + return internalList.stream().anyMatch(toCheck::isSameWard); + } + + /** + * Returns true if the list contains an equivalent ward as the given + * {@code String}. + * + * @param toCheckName String to be checked. + */ + public boolean contains(String toCheckName) { + requireNonNull(toCheckName); + WardName wardName = new WardName(toCheckName); + Ward toCheck = new Ward(wardName); + return internalList.stream().anyMatch(toCheck::isSameWard); + } + + /** + * Adds a ward to the list. + * The ward must not already exist in the list. + * + * @param toAdd Ward to be added. + */ + public void add(Ward toAdd) { + requireNonNull(toAdd); + if (!contains(toAdd)) { + internalList.add(toAdd); + } + } + + /** + * Adds patient p to their assigned ward. + * + * @param p Patient to be added. + */ + public void addPatient(Patient p) throws CommandException, WardFullException { + requireNonNull(p); + String targetName = p.getWardNameString(); + int index = internalList.indexOf(wardWithName(targetName)); + Ward target = internalList.get(index); + target.addPatient(p); + internalList.set(index, target); + } + + /** + * Replaces the ward {@code target} in the list with {@code editedWard}. + * {@code target} must exist in the list. + * The ward identity of {@code editedWard} must not be the same as another + * existing ward in the list. + * + * @param target Target ward. + * @param editedWard Ward to edit {@code target} to. + */ + public void setWard(Ward target, Ward editedWard) { + requireAllNonNull(target, editedWard); + + int index = internalList.indexOf(target); + if (index == -1) { + throw new WardNotFoundException(target.getNameString()); + } + + if (!target.isSameWard(editedWard) && contains(editedWard)) { + throw new DuplicatePatientException(); + } + + if (editedWard.getCapacity().getValue() < target.getOccupancy()) { + throw new InsufficientCapacityException(); + } + + ObservableList patients = target.getPatientList(); + for (Patient patient : patients) { + editedWard.addPatient(patient); + } + + internalList.set(index, editedWard); + } + + /** + * Replaces the ward {@code target} in the target's ward with + * {@code editedPatient}. + * {@code target} must exist in the ward. + * + * @param target Target patient + * @param editedPatient Patient to edit {@code target} to. + */ + public void setPatient(Patient target, Patient editedPatient) throws CommandException, WardFullException { + String targetName = target.getWardNameString(); + String editedName = editedPatient.getWardNameString(); + int targetIndex = internalList.indexOf(wardWithName(targetName)); + int editedIndex = internalList.indexOf(wardWithName(editedName)); + + if (!targetName.equals(editedName)) { + changePatientWard(target, targetIndex, editedIndex); + } else { + Ward targetWard = internalList.get(targetIndex); + targetWard.setPatient(target, editedPatient); + internalList.set(targetIndex, targetWard); + } + } + + /** + * Moves patient from one ward to another + * + * @param target The target patient + * @param from The patient's current ward index in internalList. + * @param to The patient's next ward index in internalList. + */ + public void changePatientWard(Patient target, int from, int to) throws WardFullException { + Ward start = internalList.get(from); + Ward end = internalList.get(to); + end.addPatient(target); + start.removePatient(target); + internalList.set(from, start); + internalList.set(to, end); + } + + /** + * Removes the equivalent ward from the list. + * The ward must exist in the list. + * + * @param toRemove Ward to be removed. + */ + public void remove(Ward toRemove) { + requireNonNull(toRemove); + if (!internalList.remove(toRemove)) { + throw new WardNotFoundException(toRemove.getNameString()); + } + } + + /** + * Removes the equivalent patient from their assigned ward. + * + * @param toRemove Patient to be removed. + */ + public void remove(Patient toRemove) { + requireNonNull(toRemove); + String targetName = toRemove.getWardNameString(); + int index = internalList.indexOf(wardWithName(targetName)); + Ward targetWard = internalList.get(index); + targetWard.removePatient(toRemove); + internalList.set(index, targetWard); + } + + /** + * Replaces the entire ward list with {@code replacement}. + * + * @param replacement Ward list to be replaced with. + */ + public void setWards(UniqueWardList replacement) { + requireNonNull(replacement); + internalList.setAll(replacement.internalList); + } + + /** + * Replaces the contents of this list with {@code wards}. + * {@code wards} must not contain duplicate wards. + * + * @param wards Ward list to be replaced with. + */ + public void setWards(List wards) { + requireAllNonNull(wards); + if (!wardsAreUnique(wards)) { + throw new DuplicateWardException(); + } + + internalList.setAll(wards); + } + + /** + * Returns true if {@code patients} contains only unique patients. + * + * @param wards Ward list to be checked. + * @return If the ward list contains only unique patients. + */ + private boolean wardsAreUnique(List wards) { + for (int i = 0; i < wards.size() - 1; i++) { + for (int j = i + 1; j < wards.size(); j++) { + if (wards.get(i).isSameWard(wards.get(j))) { + return false; + } + } + } + return true; + } + + /** + * 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) { + return other == this // short circuit if same object + || (other instanceof UniqueWardList // instanceof handles nulls + && internalList.equals(((UniqueWardList) other).internalList)); + } + + @Override + public int hashCode() { + return internalList.hashCode(); + } +} diff --git a/src/main/java/seedu/medinfo/model/ward/Ward.java b/src/main/java/seedu/medinfo/model/ward/Ward.java new file mode 100644 index 00000000000..006b2a47afa --- /dev/null +++ b/src/main/java/seedu/medinfo/model/ward/Ward.java @@ -0,0 +1,189 @@ +package seedu.medinfo.model.ward; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import javafx.collections.ObservableList; +import seedu.medinfo.model.patient.Patient; +import seedu.medinfo.model.patient.UniquePatientList; +import seedu.medinfo.model.ward.exceptions.WardFullException; + +/** + * Represents a ward which stores patients. + */ +public class Ward { + + public final WardName value; + + private Capacity capacity; + + private UniquePatientList patients; + + /** + * Constructs a {@code Ward}. + * + * @param name A valid name. + */ + public Ward(WardName name) { + requireNonNull(name); + this.value = name; + this.capacity = new Capacity(10); + patients = new UniquePatientList(); + } + + /** + * Constructs a {@code Ward}. + * + * @param name A valid name. + * @param capacity A specified capacity. + */ + public Ward(WardName name, Capacity capacity) { + requireNonNull(name); + this.value = name; + this.capacity = capacity; + patients = new UniquePatientList(); + } + + /** + * Ward factory constructor with string for comparing. + * + * @param name Name of the ward. + * @return placeholder Ward for comparison. + */ + public static Ward wardWithName(String name) { + WardName wardName = new WardName(name); + return new Ward(wardName); + } + + /** + * Edit capacity of this ward + * + * @param capacity + * @return Ward with edited capacity + */ + public Ward withCapacity(int capacity) { + this.capacity = new Capacity(capacity); + return this; + } + + /** + * Returns true if a given occupany can fit in the + * ward's capacity + * + * @param occupancy Occupancy to check. + * @return If occupancy can fit in the capacity. + */ + public boolean canSupport(int occupancy) { + return capacity.getValue() >= occupancy; + } + + /** + * Returns true if a given String is a valid Ward name. + * + * @param test String to check. + * @return If the String is a valid Ward name. + */ + public static boolean isValidWardName(String test) { + return WardName.isValidWardName(test); + } + + public WardName getName() { + return value; + } + + public String getNameString() { + return value.wardName; + } + + public Capacity getCapacity() { + return capacity; + } + + public String getCapacityString() { + return capacity.toString(); + } + + public int getOccupancy() { + return patients.size(); + } + + public String getOccupancyString() { + return "Current occupancy: " + getOccupancy() + "/" + capacity.getValue(); + } + + public boolean isSameWard(Ward other) { + return this.equals(other); + } + + /** + * Replaces the contents of the patient list with {@code patients}. + * {@code patients} must not contain duplicate patients. + * + * @param patients List of patients to replace with. + */ + public void setPatients(List patients) { + this.patients.setPatients(patients); + } + + /** + * Adds a patient to the ward. + * The patient must not already exist in the medinfo book. + * + * @param patient Patient to be added. + */ + public void addPatient(Patient patient) throws WardFullException { + requireNonNull(patient); + if (patients.size() == capacity.getValue()) { + throw new WardFullException(value.toString()); + } + patients.add(patient); + } + + /** + * Replaces the given patient {@code target} in the list with + * {@code editedPatient}. + * {@code target} must exist in the medinfo book. + * The patient identity of {@code editedPatient} must not be the same as another + * existing patient in the medinfo book. + * + * @param target Target Patient. + * @param editedPatient Patient to edit {@code target} to. + */ + public void setPatient(Patient target, Patient editedPatient) { + requireNonNull(editedPatient); + patients.setPatient(target, editedPatient); + } + + /** + * Removes {@code key} from this {@code Ward}. + * {@code key} must exist in the medinfo book. + * + * @param patient Patient to be removed. + */ + public void removePatient(Patient patient) { + patients.remove(patient); + } + + @Override + public String toString() { + return patients.asUnmodifiableObservableList().size() + " patients"; + } + + public ObservableList getPatientList() { + return patients.asUnmodifiableObservableList(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Ward // instanceof handles nulls + && value.equals(((Ward) other).value)); + } + + @Override + public int hashCode() { + return patients.hashCode(); + } + +} diff --git a/src/main/java/seedu/medinfo/model/ward/WardName.java b/src/main/java/seedu/medinfo/model/ward/WardName.java new file mode 100644 index 00000000000..60af11a34cd --- /dev/null +++ b/src/main/java/seedu/medinfo/model/ward/WardName.java @@ -0,0 +1,61 @@ +package seedu.medinfo.model.ward; + +import static java.util.Objects.requireNonNull; +import static seedu.medinfo.commons.util.AppUtil.checkArgument; + +/** + * Represents a Ward's name in MedInfo. + * Guarantees: immutable; is valid as declared in {@link #isValidWardName(String)} + */ +public class WardName { + + public static final String MESSAGE_CONSTRAINTS = "Ward names should only contain alphanumeric characters" + + "and spaces, and it should not be blank"; + + /* + * 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 final String wardName; + + /** + * Constructs a {@code WardName}. + * + * @param name A valid ward name. + */ + public WardName(String name) { + requireNonNull(name); + checkArgument(isValidWardName(name), MESSAGE_CONSTRAINTS); + wardName = name; + } + + /** + * Returns true if a given string is a valid ward name. + * + * @param test String to be tested. + * @return if the given string is a valid ward name. + */ + public static boolean isValidWardName(String test) { + return test.matches(VALIDATION_REGEX); + } + + @Override + public String toString() { + return wardName; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof WardName // instanceof handles nulls + && wardName.equalsIgnoreCase(((WardName) other).wardName)); // state check + } + + @Override + public int hashCode() { + return wardName.hashCode(); + } + +} diff --git a/src/main/java/seedu/medinfo/model/ward/WardNameContainsKeywordsPredicate.java b/src/main/java/seedu/medinfo/model/ward/WardNameContainsKeywordsPredicate.java new file mode 100644 index 00000000000..e37970626a3 --- /dev/null +++ b/src/main/java/seedu/medinfo/model/ward/WardNameContainsKeywordsPredicate.java @@ -0,0 +1,33 @@ +package seedu.medinfo.model.ward; + +import java.util.List; +import java.util.function.Predicate; + +import seedu.medinfo.commons.util.StringUtil; +import seedu.medinfo.model.patient.Patient; + +/** + * Tests that a {@code Patient}'s {@code Status} matches any of the keywords + * given. + */ +public class WardNameContainsKeywordsPredicate implements Predicate { + private final List keywords; + + public WardNameContainsKeywordsPredicate(List keywords) { + this.keywords = keywords; + } + + @Override + public boolean test(Patient patient) { + return keywords.stream() + .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(patient.getWard().getNameString(), keyword)); + } + + @Override + public boolean equals(Object other) { + return other == this + || (other instanceof WardNameContainsKeywordsPredicate + && keywords.equals(((WardNameContainsKeywordsPredicate) other).keywords)); + } + +} diff --git a/src/main/java/seedu/medinfo/model/ward/exceptions/DuplicateWardException.java b/src/main/java/seedu/medinfo/model/ward/exceptions/DuplicateWardException.java new file mode 100644 index 00000000000..6219c440e32 --- /dev/null +++ b/src/main/java/seedu/medinfo/model/ward/exceptions/DuplicateWardException.java @@ -0,0 +1,11 @@ +package seedu.medinfo.model.ward.exceptions; + +/** + * Signals that the operation will result in duplicate Wards. + * Wards are considered duplicates if they have the same name. + */ +public class DuplicateWardException extends RuntimeException { + public DuplicateWardException() { + super("Operation would result in duplicate wards"); + } +} diff --git a/src/main/java/seedu/medinfo/model/ward/exceptions/InsufficientCapacityException.java b/src/main/java/seedu/medinfo/model/ward/exceptions/InsufficientCapacityException.java new file mode 100644 index 00000000000..38ecdbdc639 --- /dev/null +++ b/src/main/java/seedu/medinfo/model/ward/exceptions/InsufficientCapacityException.java @@ -0,0 +1,11 @@ +package seedu.medinfo.model.ward.exceptions; + +/** + * Signals that the operation will result in an edited Ward with insufficient + * capacity. + */ +public class InsufficientCapacityException extends RuntimeException { + public InsufficientCapacityException() { + super("Operation would result in the edited ward having insufficient capacity!"); + } +} diff --git a/src/main/java/seedu/medinfo/model/ward/exceptions/WardFullException.java b/src/main/java/seedu/medinfo/model/ward/exceptions/WardFullException.java new file mode 100644 index 00000000000..49c64cba657 --- /dev/null +++ b/src/main/java/seedu/medinfo/model/ward/exceptions/WardFullException.java @@ -0,0 +1,21 @@ +package seedu.medinfo.model.ward.exceptions; + +/** + * Signals that the ward is full. + */ +public class WardFullException extends RuntimeException { + private String name; + + /** + * Constructs a new {@code WardFullException} with the specified ward name {@code name}. + */ + public WardFullException(String name) { + super(name + " is full!"); + this.name = name; + } + + @Override + public String toString() { + return name + " is full!"; + } +} diff --git a/src/main/java/seedu/medinfo/model/ward/exceptions/WardNotFoundException.java b/src/main/java/seedu/medinfo/model/ward/exceptions/WardNotFoundException.java new file mode 100644 index 00000000000..e203ae73a61 --- /dev/null +++ b/src/main/java/seedu/medinfo/model/ward/exceptions/WardNotFoundException.java @@ -0,0 +1,10 @@ +package seedu.medinfo.model.ward.exceptions; + +/** + * Signals that the operation is unable to find the specified ward. + */ +public class WardNotFoundException extends RuntimeException { + public WardNotFoundException(String name) { + super("Ward " + name + " not found!"); + } +} diff --git a/src/main/java/seedu/medinfo/storage/JsonAdaptedPatient.java b/src/main/java/seedu/medinfo/storage/JsonAdaptedPatient.java new file mode 100644 index 00000000000..51c42309d81 --- /dev/null +++ b/src/main/java/seedu/medinfo/storage/JsonAdaptedPatient.java @@ -0,0 +1,108 @@ +package seedu.medinfo.storage; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import seedu.medinfo.commons.exceptions.IllegalValueException; +import seedu.medinfo.model.patient.Discharge; +import seedu.medinfo.model.patient.Name; +import seedu.medinfo.model.patient.Nric; +import seedu.medinfo.model.patient.Patient; +import seedu.medinfo.model.patient.Status; +import seedu.medinfo.model.ward.Ward; +import seedu.medinfo.model.ward.WardName; + + +/** + * Jackson-friendly version of {@link Patient}. + */ +class JsonAdaptedPatient { + + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Patient's %s field is missing!"; + + private final String name; + private final String nric; + private final String status; + private final String ward; + private final String discharge; + + + /** + * Constructs a {@code JsonAdaptedPatient} with the given patient details. + */ + @JsonCreator + public JsonAdaptedPatient(@JsonProperty("name") String name, @JsonProperty("nric") String nric, + @JsonProperty("status") String status, @JsonProperty("ward") String ward, + @JsonProperty("discharge") String discharge) { + this.name = name; + this.nric = nric; + this.status = status; + this.ward = ward; + this.discharge = discharge; + } + + /** + * Converts a given {@code Patient} into this class for Jackson use. + */ + public JsonAdaptedPatient(Patient source) { + name = source.getName().fullName; + nric = source.getNric().value; + status = source.getStatus().value; + ward = source.getWardNameString(); + discharge = source.getDischarge().value; + + } + + /** + * Converts this Jackson-friendly adapted patient object into the model's + * {@code Patient} object. + * + * @throws IllegalValueException if there were any data constraints violated in + * the adapted patient. + */ + public Patient 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 (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); + + if (status == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Status.class.getSimpleName())); + } + if (!Status.isValidStatus(status)) { + throw new IllegalValueException(Status.MESSAGE_CONSTRAINTS); + } + final Status modelStatus = new Status(status); + + if (ward == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Ward.class.getSimpleName())); + } + if (!Ward.isValidWardName(ward)) { + throw new IllegalValueException(WardName.MESSAGE_CONSTRAINTS); + } + final WardName modelWard = new WardName(ward); + + if (discharge == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + Discharge.class.getSimpleName())); + } + if (!Discharge.isValidDischarge(discharge)) { + throw new IllegalValueException(Discharge.MESSAGE_CONSTRAINTS); + } + final Discharge modelDischarge = new Discharge(discharge); + return new Patient(modelNric, modelName, modelStatus, modelWard, modelDischarge); + } + +} diff --git a/src/main/java/seedu/medinfo/storage/JsonAdaptedWard.java b/src/main/java/seedu/medinfo/storage/JsonAdaptedWard.java new file mode 100644 index 00000000000..0628d5092f1 --- /dev/null +++ b/src/main/java/seedu/medinfo/storage/JsonAdaptedWard.java @@ -0,0 +1,71 @@ +package seedu.medinfo.storage; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import seedu.medinfo.commons.exceptions.IllegalValueException; +import seedu.medinfo.model.patient.Name; +import seedu.medinfo.model.ward.Capacity; +import seedu.medinfo.model.ward.Ward; +import seedu.medinfo.model.ward.WardName; + + +/** + * Jackson-friendly version of {@link Ward}. + */ +class JsonAdaptedWard { + + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Ward %s field is missing!"; + + private final String name; + private final String capacity; + + + /** + * Constructs a {@code JsonAdaptedWard} with the given details. + */ + @JsonCreator + public JsonAdaptedWard(@JsonProperty("name") String name, @JsonProperty("capacity") String capacity) { + this.name = name; + this.capacity = capacity; + } + + /** + * Converts a given {@code Ward} into this class for Jackson use. + */ + public JsonAdaptedWard(Ward source) { + name = source.getNameString(); + capacity = source.getCapacityString(); + + } + + /** + * Converts this Jackson-friendly adapted patient object into the model's + * {@code Ward} object. + * + * @throws IllegalValueException if there were any data constraints violated in + * the adapted patient. + */ + public Ward 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 WardName modelName = new WardName(name); + + if (capacity == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + Capacity.class.getSimpleName())); + } + if (!Capacity.isValidCapacity(capacity)) { + throw new IllegalValueException(Capacity.MESSAGE_CONSTRAINTS); + } + final Capacity modelCapacity = new Capacity(capacity); + + return new Ward(modelName, modelCapacity); + } + +} diff --git a/src/main/java/seedu/medinfo/storage/JsonMedInfoStorage.java b/src/main/java/seedu/medinfo/storage/JsonMedInfoStorage.java new file mode 100644 index 00000000000..9be278bc771 --- /dev/null +++ b/src/main/java/seedu/medinfo/storage/JsonMedInfoStorage.java @@ -0,0 +1,81 @@ +package seedu.medinfo.storage; + +import static java.util.Objects.requireNonNull; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; +import java.util.logging.Logger; + +import seedu.medinfo.commons.core.LogsCenter; +import seedu.medinfo.commons.exceptions.DataConversionException; +import seedu.medinfo.commons.exceptions.IllegalValueException; +import seedu.medinfo.commons.util.FileUtil; +import seedu.medinfo.commons.util.JsonUtil; +import seedu.medinfo.logic.commands.exceptions.CommandException; +import seedu.medinfo.model.ReadOnlyMedInfo; + +/** + * A class to access MedInfo data stored as a json file on the hard disk. + */ +public class JsonMedInfoStorage implements MedInfoStorage { + + private static final Logger logger = LogsCenter.getLogger(JsonMedInfoStorage.class); + + private Path filePath; + + public JsonMedInfoStorage(Path filePath) { + this.filePath = filePath; + } + + public Path getMedInfoFilePath() { + return filePath; + } + + @Override + public Optional readMedInfo() throws DataConversionException, CommandException { + return readMedInfo(filePath); + } + + /** + * Similar to {@link #readMedInfo()}. + * + * @param filePath location of the data. Cannot be null. + * @throws DataConversionException if the file is not in the correct format. + */ + public Optional readMedInfo(Path filePath) throws DataConversionException, CommandException { + requireNonNull(filePath); + + Optional jsonMedInfo = JsonUtil.readJsonFile( + filePath, JsonSerializableMedInfo.class); + if (!jsonMedInfo.isPresent()) { + return Optional.empty(); + } + + try { + return Optional.of(jsonMedInfo.get().toModelType()); + } catch (IllegalValueException ive) { + logger.info("Illegal values found in " + filePath + ": " + ive.getMessage()); + throw new DataConversionException(ive); + } + } + + @Override + public void saveMedInfo(ReadOnlyMedInfo medInfo) throws IOException { + saveMedInfo(medInfo, filePath); + } + + /** + * Similar to {@link #saveMedInfo(ReadOnlyMedInfo)}. + * + * @param filePath location of the data. Cannot be null. + */ + public void saveMedInfo(ReadOnlyMedInfo medInfo, Path filePath) throws IOException { + requireNonNull(medInfo); + requireNonNull(filePath); + + FileUtil.createIfMissing(filePath); + JsonUtil.saveJsonFile(new JsonSerializableMedInfo(medInfo), filePath); + } + +} diff --git a/src/main/java/seedu/medinfo/storage/JsonSerializableMedInfo.java b/src/main/java/seedu/medinfo/storage/JsonSerializableMedInfo.java new file mode 100644 index 00000000000..27d94c55600 --- /dev/null +++ b/src/main/java/seedu/medinfo/storage/JsonSerializableMedInfo.java @@ -0,0 +1,80 @@ +package seedu.medinfo.storage; + +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 com.fasterxml.jackson.annotation.JsonRootName; + +import seedu.medinfo.commons.exceptions.IllegalValueException; +import seedu.medinfo.logic.commands.exceptions.CommandException; +import seedu.medinfo.model.MedInfo; +import seedu.medinfo.model.ReadOnlyMedInfo; +import seedu.medinfo.model.patient.Patient; +import seedu.medinfo.model.ward.Ward; +import seedu.medinfo.model.ward.exceptions.WardNotFoundException; + +/** + * An Immutable MedInfo that is serializable to JSON format. + */ +@JsonRootName(value = "medinfo") +class JsonSerializableMedInfo { + + public static final String MESSAGE_DUPLICATE_PATIENT = "Patients list contains duplicate patient(s)."; + public static final String MESSAGE_DUPLICATE_WARD = "Ward list contains duplicate ward(s)."; + public static final String MESSAGE_MISSING_WARD = "Patient(s) linked to wards that do not exist."; + + private final List patients = new ArrayList<>(); + private final List wards = new ArrayList<>(); + + /** + * Constructs a {@code JsonSerializableMedInfo} with the given patients. + */ + @JsonCreator + public JsonSerializableMedInfo(@JsonProperty("patients") List patients, + @JsonProperty("wards") List wards) { + this.patients.addAll(patients); + this.wards.addAll(wards); + } + + /** + * Converts a given {@code ReadOnlyMedInfo} into this class for Jackson use. + * + * @param source future changes to this will not affect the created {@code JsonSerializableMedInfo}. + */ + public JsonSerializableMedInfo(ReadOnlyMedInfo source) { + patients.addAll(source.getPatientList().stream().map(JsonAdaptedPatient::new).collect(Collectors.toList())); + wards.addAll(source.getWardList().stream().map(JsonAdaptedWard::new).collect(Collectors.toList())); + } + + /** + * Converts this medinfo book into the model's {@code MedInfo} object. + * + * @throws IllegalValueException if there were any data constraints violated. + */ + public MedInfo toModelType() throws IllegalValueException, CommandException { + MedInfo medInfo = new MedInfo(); + for (JsonAdaptedWard jsonAdaptedWard : wards) { + Ward ward = jsonAdaptedWard.toModelType(); + if (!medInfo.hasWard(ward)) { + medInfo.addWard(ward); + } + } + + for (JsonAdaptedPatient jsonAdaptedPatient : patients) { + Patient patient = jsonAdaptedPatient.toModelType(); + if (medInfo.hasPatient(patient)) { + throw new IllegalValueException(MESSAGE_DUPLICATE_PATIENT); + } + try { + medInfo.addPatient(patient); + } catch (WardNotFoundException e) { + throw new IllegalValueException(MESSAGE_MISSING_WARD); + } + } + return medInfo; + } + +} diff --git a/src/main/java/seedu/address/storage/JsonUserPrefsStorage.java b/src/main/java/seedu/medinfo/storage/JsonUserPrefsStorage.java similarity index 83% rename from src/main/java/seedu/address/storage/JsonUserPrefsStorage.java rename to src/main/java/seedu/medinfo/storage/JsonUserPrefsStorage.java index bc2bbad84aa..3276a2de7ab 100644 --- a/src/main/java/seedu/address/storage/JsonUserPrefsStorage.java +++ b/src/main/java/seedu/medinfo/storage/JsonUserPrefsStorage.java @@ -1,13 +1,13 @@ -package seedu.address.storage; +package seedu.medinfo.storage; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.commons.util.JsonUtil; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; +import seedu.medinfo.commons.exceptions.DataConversionException; +import seedu.medinfo.commons.util.JsonUtil; +import seedu.medinfo.model.ReadOnlyUserPrefs; +import seedu.medinfo.model.UserPrefs; /** * A class to access UserPrefs stored in the hard disk as a json file diff --git a/src/main/java/seedu/medinfo/storage/MedInfoStorage.java b/src/main/java/seedu/medinfo/storage/MedInfoStorage.java new file mode 100644 index 00000000000..9fc4d171452 --- /dev/null +++ b/src/main/java/seedu/medinfo/storage/MedInfoStorage.java @@ -0,0 +1,47 @@ +package seedu.medinfo.storage; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; + +import seedu.medinfo.commons.exceptions.DataConversionException; +import seedu.medinfo.logic.commands.exceptions.CommandException; +import seedu.medinfo.model.MedInfo; +import seedu.medinfo.model.ReadOnlyMedInfo; + +/** + * Represents a storage for {@link MedInfo}. + */ +public interface MedInfoStorage { + + /** + * Returns the file path of the data file. + */ + Path getMedInfoFilePath(); + + /** + * Returns MedInfo data as a {@link ReadOnlyMedInfo}. + * Returns {@code Optional.empty()} if storage file is not found. + * @throws DataConversionException if the data in storage is not in the expected format. + * @throws IOException if there was any problem when reading from the storage. + */ + Optional readMedInfo() throws DataConversionException, IOException, CommandException; + + /** + * @see #getMedInfoFilePath() + */ + Optional readMedInfo(Path filePath) throws DataConversionException, IOException, CommandException; + + /** + * Saves the given {@link ReadOnlyMedInfo} to the storage. + * @param medInfo cannot be null. + * @throws IOException if there was any problem writing to the file. + */ + void saveMedInfo(ReadOnlyMedInfo medInfo) throws IOException; + + /** + * @see #saveMedInfo(ReadOnlyMedInfo) + */ + void saveMedInfo(ReadOnlyMedInfo medInfo, Path filePath) throws IOException; + +} diff --git a/src/main/java/seedu/medinfo/storage/Storage.java b/src/main/java/seedu/medinfo/storage/Storage.java new file mode 100644 index 00000000000..f5062dabc56 --- /dev/null +++ b/src/main/java/seedu/medinfo/storage/Storage.java @@ -0,0 +1,33 @@ +package seedu.medinfo.storage; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; + +import seedu.medinfo.commons.exceptions.DataConversionException; +import seedu.medinfo.logic.commands.exceptions.CommandException; +import seedu.medinfo.model.ReadOnlyMedInfo; +import seedu.medinfo.model.ReadOnlyUserPrefs; +import seedu.medinfo.model.UserPrefs; + +/** + * API of the Storage component + */ +public interface Storage extends MedInfoStorage, UserPrefsStorage { + + @Override + Optional readUserPrefs() throws DataConversionException, IOException; + + @Override + void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException; + + @Override + Path getMedInfoFilePath(); + + @Override + Optional readMedInfo() throws DataConversionException, IOException, CommandException; + + @Override + void saveMedInfo(ReadOnlyMedInfo medInfo) throws IOException; + +} diff --git a/src/main/java/seedu/medinfo/storage/StorageManager.java b/src/main/java/seedu/medinfo/storage/StorageManager.java new file mode 100644 index 00000000000..a5114af6887 --- /dev/null +++ b/src/main/java/seedu/medinfo/storage/StorageManager.java @@ -0,0 +1,80 @@ +package seedu.medinfo.storage; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; +import java.util.logging.Logger; + +import seedu.medinfo.commons.core.LogsCenter; +import seedu.medinfo.commons.exceptions.DataConversionException; +import seedu.medinfo.logic.commands.exceptions.CommandException; +import seedu.medinfo.model.ReadOnlyMedInfo; +import seedu.medinfo.model.ReadOnlyUserPrefs; +import seedu.medinfo.model.UserPrefs; + +/** + * Manages storage of MedInfo data in local storage. + */ +public class StorageManager implements Storage { + + private static final Logger logger = LogsCenter.getLogger(StorageManager.class); + private MedInfoStorage medInfoStorage; + private UserPrefsStorage userPrefsStorage; + + /** + * Creates a {@code StorageManager} with the given {@code MedInfoStorage} and {@code UserPrefStorage}. + */ + public StorageManager(MedInfoStorage medInfoStorage, UserPrefsStorage userPrefsStorage) { + this.medInfoStorage = medInfoStorage; + this.userPrefsStorage = userPrefsStorage; + } + + // ================ UserPrefs methods ============================== + + @Override + public Path getUserPrefsFilePath() { + return userPrefsStorage.getUserPrefsFilePath(); + } + + @Override + public Optional readUserPrefs() throws DataConversionException, IOException { + return userPrefsStorage.readUserPrefs(); + } + + @Override + public void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException { + userPrefsStorage.saveUserPrefs(userPrefs); + } + + + // ================ MedInfo methods ============================== + + @Override + public Path getMedInfoFilePath() { + return medInfoStorage.getMedInfoFilePath(); + } + + @Override + public Optional readMedInfo() throws DataConversionException, IOException, CommandException { + return readMedInfo(medInfoStorage.getMedInfoFilePath()); + } + + @Override + public Optional readMedInfo(Path filePath) throws DataConversionException, IOException, + CommandException { + logger.fine("Attempting to read data from file: " + filePath); + return medInfoStorage.readMedInfo(filePath); + } + + @Override + public void saveMedInfo(ReadOnlyMedInfo medInfo) throws IOException { + saveMedInfo(medInfo, medInfoStorage.getMedInfoFilePath()); + } + + @Override + public void saveMedInfo(ReadOnlyMedInfo medInfo, Path filePath) throws IOException { + logger.fine("Attempting to write to data file: " + filePath); + medInfoStorage.saveMedInfo(medInfo, filePath); + } + +} diff --git a/src/main/java/seedu/address/storage/UserPrefsStorage.java b/src/main/java/seedu/medinfo/storage/UserPrefsStorage.java similarity index 73% rename from src/main/java/seedu/address/storage/UserPrefsStorage.java rename to src/main/java/seedu/medinfo/storage/UserPrefsStorage.java index 29eef178dbc..98e13643331 100644 --- a/src/main/java/seedu/address/storage/UserPrefsStorage.java +++ b/src/main/java/seedu/medinfo/storage/UserPrefsStorage.java @@ -1,15 +1,15 @@ -package seedu.address.storage; +package seedu.medinfo.storage; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; +import seedu.medinfo.commons.exceptions.DataConversionException; +import seedu.medinfo.model.ReadOnlyUserPrefs; +import seedu.medinfo.model.UserPrefs; /** - * Represents a storage for {@link seedu.address.model.UserPrefs}. + * Represents a storage for {@link seedu.medinfo.model.UserPrefs}. */ public interface UserPrefsStorage { @@ -27,7 +27,7 @@ public interface UserPrefsStorage { Optional readUserPrefs() throws DataConversionException, IOException; /** - * Saves the given {@link seedu.address.model.ReadOnlyUserPrefs} to the storage. + * Saves the given {@link seedu.medinfo.model.ReadOnlyUserPrefs} to the storage. * @param userPrefs cannot be null. * @throws IOException if there was any problem writing to the file. */ diff --git a/src/main/java/seedu/address/ui/CommandBox.java b/src/main/java/seedu/medinfo/ui/CommandBox.java similarity index 89% rename from src/main/java/seedu/address/ui/CommandBox.java rename to src/main/java/seedu/medinfo/ui/CommandBox.java index 9e75478664b..ff961310b93 100644 --- a/src/main/java/seedu/address/ui/CommandBox.java +++ b/src/main/java/seedu/medinfo/ui/CommandBox.java @@ -1,12 +1,12 @@ -package seedu.address.ui; +package seedu.medinfo.ui; import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.scene.control.TextField; import javafx.scene.layout.Region; -import seedu.address.logic.commands.CommandResult; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.logic.parser.exceptions.ParseException; +import seedu.medinfo.logic.commands.CommandResult; +import seedu.medinfo.logic.commands.exceptions.CommandException; +import seedu.medinfo.logic.parser.exceptions.ParseException; /** * The UI component that is responsible for receiving user command inputs. @@ -77,7 +77,7 @@ public interface CommandExecutor { /** * Executes the command and returns the result. * - * @see seedu.address.logic.Logic#execute(String) + * @see seedu.medinfo.logic.Logic#execute(String) */ CommandResult execute(String commandText) throws CommandException, ParseException; } diff --git a/src/main/java/seedu/address/ui/HelpWindow.java b/src/main/java/seedu/medinfo/ui/HelpWindow.java similarity index 90% rename from src/main/java/seedu/address/ui/HelpWindow.java rename to src/main/java/seedu/medinfo/ui/HelpWindow.java index 3f16b2fcf26..befaac588b2 100644 --- a/src/main/java/seedu/address/ui/HelpWindow.java +++ b/src/main/java/seedu/medinfo/ui/HelpWindow.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.medinfo.ui; import java.util.logging.Logger; @@ -8,14 +8,14 @@ import javafx.scene.input.Clipboard; import javafx.scene.input.ClipboardContent; import javafx.stage.Stage; -import seedu.address.commons.core.LogsCenter; +import seedu.medinfo.commons.core.LogsCenter; /** * 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 USERGUIDE_URL = "https://ay2223s2-cs2103t-t12-2.github.io/tp/UserGuide.html"; public static final String HELP_MESSAGE = "Refer to the user guide: " + USERGUIDE_URL; private static final Logger logger = LogsCenter.getLogger(HelpWindow.class); @@ -70,6 +70,8 @@ public void show() { /** * Returns true if the help window is currently being shown. + * + * @return Boolean indicating if help window is being shown. */ public boolean isShowing() { return getRoot().isShowing(); diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/medinfo/ui/MainWindow.java similarity index 80% rename from src/main/java/seedu/address/ui/MainWindow.java rename to src/main/java/seedu/medinfo/ui/MainWindow.java index 9106c3aa6e5..36498c6d7c3 100644 --- a/src/main/java/seedu/address/ui/MainWindow.java +++ b/src/main/java/seedu/medinfo/ui/MainWindow.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.medinfo.ui; import java.util.logging.Logger; @@ -10,12 +10,12 @@ import javafx.scene.input.KeyEvent; import javafx.scene.layout.StackPane; import javafx.stage.Stage; -import seedu.address.commons.core.GuiSettings; -import seedu.address.commons.core.LogsCenter; -import seedu.address.logic.Logic; -import seedu.address.logic.commands.CommandResult; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.logic.parser.exceptions.ParseException; +import seedu.medinfo.commons.core.GuiSettings; +import seedu.medinfo.commons.core.LogsCenter; +import seedu.medinfo.logic.Logic; +import seedu.medinfo.logic.commands.CommandResult; +import seedu.medinfo.logic.commands.exceptions.CommandException; +import seedu.medinfo.logic.parser.exceptions.ParseException; /** * The Main Window. Provides the basic application layout containing @@ -31,7 +31,8 @@ public class MainWindow extends UiPart { private Logic logic; // Independent Ui parts residing in this Ui container - private PersonListPanel personListPanel; + private PatientListPanel patientListPanel; + private WardListPanel wardListPanel; private ResultDisplay resultDisplay; private HelpWindow helpWindow; @@ -42,7 +43,9 @@ public class MainWindow extends UiPart { private MenuItem helpMenuItem; @FXML - private StackPane personListPanelPlaceholder; + private StackPane patientListPanelPlaceholder; + @FXML + private StackPane wardListPanelPlaceholder; @FXML private StackPane resultDisplayPlaceholder; @@ -68,6 +71,11 @@ public MainWindow(Stage primaryStage, Logic logic) { helpWindow = new HelpWindow(); } + /** + * Returns the primary stage. + * + * @return The primary stage of the app. + */ public Stage getPrimaryStage() { return primaryStage; } @@ -78,7 +86,7 @@ private void setAccelerators() { /** * Sets the accelerator of a MenuItem. - * @param keyCombination the KeyCombination value of the accelerator + * @param keyCombination the KeyCombination value of the accelerator. */ private void setAccelerator(MenuItem menuItem, KeyCombination keyCombination) { menuItem.setAccelerator(keyCombination); @@ -110,19 +118,30 @@ private void setAccelerator(MenuItem menuItem, KeyCombination keyCombination) { * Fills up all the placeholders of this window. */ void fillInnerParts() { - personListPanel = new PersonListPanel(logic.getFilteredPersonList()); - personListPanelPlaceholder.getChildren().add(personListPanel.getRoot()); + patientListPanel = new PatientListPanel(logic.getFilteredPatientList()); + patientListPanelPlaceholder.getChildren().add(patientListPanel.getRoot()); + + wardListPanel = new WardListPanel(logic.getFilteredWardList()); + wardListPanelPlaceholder.getChildren().add(wardListPanel.getRoot()); resultDisplay = new ResultDisplay(); resultDisplayPlaceholder.getChildren().add(resultDisplay.getRoot()); - StatusBarFooter statusBarFooter = new StatusBarFooter(logic.getAddressBookFilePath()); + StatusBarFooter statusBarFooter = new StatusBarFooter(logic.getMedInfoFilePath(), logic.getStatsInfo()); statusbarPlaceholder.getChildren().add(statusBarFooter.getRoot()); CommandBox commandBox = new CommandBox(this::executeCommand); commandBoxPlaceholder.getChildren().add(commandBox.getRoot()); } + /** + * Updates Stats placeholder. + */ + void updateStats() { + StatusBarFooter statusBarFooter = new StatusBarFooter(logic.getMedInfoFilePath(), logic.getStatsInfo()); + statusbarPlaceholder.getChildren().add(statusBarFooter.getRoot()); + } + /** * Sets the default size based on {@code guiSettings}. */ @@ -163,18 +182,16 @@ private void handleExit() { primaryStage.hide(); } - public PersonListPanel getPersonListPanel() { - return personListPanel; - } - /** * Executes the command and returns the result. * - * @see seedu.address.logic.Logic#execute(String) + * @see seedu.medinfo.logic.Logic#execute(String) */ private CommandResult executeCommand(String commandText) throws CommandException, ParseException { try { + CommandResult commandResult = logic.execute(commandText); + updateStats(); logger.info("Result: " + commandResult.getFeedbackToUser()); resultDisplay.setFeedbackToUser(commandResult.getFeedbackToUser()); diff --git a/src/main/java/seedu/medinfo/ui/PatientCard.java b/src/main/java/seedu/medinfo/ui/PatientCard.java new file mode 100644 index 00000000000..0451876d821 --- /dev/null +++ b/src/main/java/seedu/medinfo/ui/PatientCard.java @@ -0,0 +1,86 @@ +package seedu.medinfo.ui; + +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import seedu.medinfo.model.patient.Patient; + + +/** + * An UI component that displays information of a {@code Patient}. + */ +public class PatientCard extends UiPart { + + private static final String FXML = "PatientListCard.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 MedInfo level 4 + */ + + public final Patient patient; + + @FXML + private HBox cardPane; + @FXML + private Label name; + @FXML + private Label id; + @FXML + private Label nric; + @FXML + private Label status; + @FXML + private Label ward; + @FXML + private Label discharge; + + /** + * Creates a {@code PatientCode} with the given {@code Patient} and index to + * display. + */ + public PatientCard(Patient patient, int displayedIndex) { + super(FXML); + this.patient = patient; + id.setText(displayedIndex + ". "); + name.setText(patient.getName().fullName); + nric.setText(patient.getNric().value); + String statusString = patient.getStatusDesc(); + status.setText(statusString); + status.getStyleClass().clear(); + status.getStyleClass().add("status-" + statusString); + ward.setText(patient.getWardNameString()); + discharge.setText(patient.getDischargeString()); + } + + /** + * Overrides equality check for PatientCard to compare two patient cards by + * the NRICs and the patients. + * + * @param other Object to check equality with. + * @return Boolean indicating whether objects are equal. + */ + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof PatientCard)) { + return false; + } + + // state check + PatientCard card = (PatientCard) other; + return nric.getText().equals(card.nric.getText()) + && patient.equals(card.patient); + } +} diff --git a/src/main/java/seedu/medinfo/ui/PatientListPanel.java b/src/main/java/seedu/medinfo/ui/PatientListPanel.java new file mode 100644 index 00000000000..a487847be6d --- /dev/null +++ b/src/main/java/seedu/medinfo/ui/PatientListPanel.java @@ -0,0 +1,46 @@ +package seedu.medinfo.ui; + +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.medinfo.model.patient.Patient; + +/** + * Panel containing the list of patients. + */ +public class PatientListPanel extends UiPart { + private static final String FXML = "PatientListPanel.fxml"; + + @FXML + private ListView patientListView; + + /** + * Creates a {@code PatientListPanel} with the given {@code ObservableList}. + */ + public PatientListPanel(ObservableList patientList) { + super(FXML); + patientListView.setItems(patientList); + patientListView.setCellFactory(listView -> new PatientListViewCell()); + } + + /** + * Custom {@code ListCell} that displays the graphics of a {@code Patient} using + * a {@code PatientCard}. + */ + class PatientListViewCell extends ListCell { + @Override + protected void updateItem(Patient patient, boolean empty) { + super.updateItem(patient, empty); + + if (empty || patient == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(new PatientCard(patient, getIndex() + 1).getRoot()); + } + } + } + +} diff --git a/src/main/java/seedu/address/ui/ResultDisplay.java b/src/main/java/seedu/medinfo/ui/ResultDisplay.java similarity index 72% rename from src/main/java/seedu/address/ui/ResultDisplay.java rename to src/main/java/seedu/medinfo/ui/ResultDisplay.java index 7d98e84eedf..9d4ddc6d765 100644 --- a/src/main/java/seedu/address/ui/ResultDisplay.java +++ b/src/main/java/seedu/medinfo/ui/ResultDisplay.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.medinfo.ui; import static java.util.Objects.requireNonNull; @@ -16,10 +16,18 @@ public class ResultDisplay extends UiPart { @FXML private TextArea resultDisplay; + /** + * Creates a {@code ResultDisplay}. + */ public ResultDisplay() { super(FXML); } + /** + * Sets text to feedback to the user in the result display. + * + * @param feedbackToUser String to feedback to user. + */ public void setFeedbackToUser(String feedbackToUser) { requireNonNull(feedbackToUser); resultDisplay.setText(feedbackToUser); diff --git a/src/main/java/seedu/address/ui/StatusBarFooter.java b/src/main/java/seedu/medinfo/ui/StatusBarFooter.java similarity index 55% rename from src/main/java/seedu/address/ui/StatusBarFooter.java rename to src/main/java/seedu/medinfo/ui/StatusBarFooter.java index b577f829423..b1b6d5958f3 100644 --- a/src/main/java/seedu/address/ui/StatusBarFooter.java +++ b/src/main/java/seedu/medinfo/ui/StatusBarFooter.java @@ -1,7 +1,8 @@ -package seedu.address.ui; +package seedu.medinfo.ui; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.List; import javafx.fxml.FXML; import javafx.scene.control.Label; @@ -16,13 +17,20 @@ public class StatusBarFooter extends UiPart { @FXML private Label saveLocationStatus; + @FXML + private Label stats; /** * Creates a {@code StatusBarFooter} with the given {@code Path}. */ - public StatusBarFooter(Path saveLocation) { + public StatusBarFooter(Path saveLocation, List statsInfo) { super(FXML); - saveLocationStatus.setText(Paths.get(".").resolve(saveLocation).toString()); + statsInfo.get(0); + statsInfo.get(1); + this.saveLocationStatus.setText(Paths.get(".").resolve(saveLocation).toString()); + for (String stat:statsInfo) { + stats.setText(stats.getText() + " " + stat + " "); + } } } diff --git a/src/main/java/seedu/address/ui/Ui.java b/src/main/java/seedu/medinfo/ui/Ui.java similarity index 61% rename from src/main/java/seedu/address/ui/Ui.java rename to src/main/java/seedu/medinfo/ui/Ui.java index 17aa0b494fe..cdf8e60ba92 100644 --- a/src/main/java/seedu/address/ui/Ui.java +++ b/src/main/java/seedu/medinfo/ui/Ui.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.medinfo.ui; import javafx.stage.Stage; @@ -7,7 +7,9 @@ */ public interface Ui { - /** Starts the UI (and the App). */ + /** + * Starts the UI (and the App). + */ void start(Stage primaryStage); } diff --git a/src/main/java/seedu/address/ui/UiManager.java b/src/main/java/seedu/medinfo/ui/UiManager.java similarity index 87% rename from src/main/java/seedu/address/ui/UiManager.java rename to src/main/java/seedu/medinfo/ui/UiManager.java index fdf024138bc..35c5d4e38d1 100644 --- a/src/main/java/seedu/address/ui/UiManager.java +++ b/src/main/java/seedu/medinfo/ui/UiManager.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.medinfo.ui; import java.util.logging.Logger; @@ -7,10 +7,10 @@ import javafx.scene.control.Alert.AlertType; import javafx.scene.image.Image; import javafx.stage.Stage; -import seedu.address.MainApp; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.util.StringUtil; -import seedu.address.logic.Logic; +import seedu.medinfo.MainApp; +import seedu.medinfo.commons.core.LogsCenter; +import seedu.medinfo.commons.util.StringUtil; +import seedu.medinfo.logic.Logic; /** * The manager of the UI component. @@ -20,7 +20,7 @@ public class UiManager implements Ui { public static final String ALERT_DIALOG_PANE_FIELD_ID = "alertDialogPane"; private static final Logger logger = LogsCenter.getLogger(UiManager.class); - private static final String ICON_APPLICATION = "/images/address_book_32.png"; + private static final String ICON_APPLICATION = "/images/MedInfo.png"; private Logic logic; private MainWindow mainWindow; @@ -32,6 +32,11 @@ public UiManager(Logic logic) { this.logic = logic; } + /** + * Start the Ui components with the given primary stage. + * + * @param primaryStage Primary stage of the app. + */ @Override public void start(Stage primaryStage) { logger.info("Starting UI..."); diff --git a/src/main/java/seedu/address/ui/UiPart.java b/src/main/java/seedu/medinfo/ui/UiPart.java similarity index 97% rename from src/main/java/seedu/address/ui/UiPart.java rename to src/main/java/seedu/medinfo/ui/UiPart.java index fc820e01a9c..b33e39e4d28 100644 --- a/src/main/java/seedu/address/ui/UiPart.java +++ b/src/main/java/seedu/medinfo/ui/UiPart.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.medinfo.ui; import static java.util.Objects.requireNonNull; @@ -6,7 +6,7 @@ import java.net.URL; import javafx.fxml.FXMLLoader; -import seedu.address.MainApp; +import seedu.medinfo.MainApp; /** * Represents a distinct part of the UI. e.g. Windows, dialogs, panels, status bars, etc. diff --git a/src/main/java/seedu/medinfo/ui/WardCard.java b/src/main/java/seedu/medinfo/ui/WardCard.java new file mode 100644 index 00000000000..30ffa39f129 --- /dev/null +++ b/src/main/java/seedu/medinfo/ui/WardCard.java @@ -0,0 +1,68 @@ +package seedu.medinfo.ui; + +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import seedu.medinfo.model.ward.Ward; + + +/** + * An UI component that displays information of a {@code Patient}. + */ +public class WardCard extends UiPart { + + private static final String FXML = "WardListCard.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 MedInfo level 4 + */ + + public final Ward ward; + + @FXML + private HBox cardPane; + @FXML + private Label name; + @FXML + private Label id; + @FXML + private Label capacity; + + /** + * Creates a {@code PatientCode} with the given {@code Patient} and index to + * display. + */ + public WardCard(Ward ward, int displayedIndex) { + super(FXML); + this.ward = ward; + id.setText(displayedIndex + ". "); + + name.setText(ward.getNameString()); + capacity.setText(ward.getOccupancyString()); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof WardCard)) { + return false; + } + + // state check + WardCard card = (WardCard) other; + return name.getText().equals(card.name.getText()) + && ward.equals(card.ward); + } +} diff --git a/src/main/java/seedu/medinfo/ui/WardListPanel.java b/src/main/java/seedu/medinfo/ui/WardListPanel.java new file mode 100644 index 00000000000..6042b170ced --- /dev/null +++ b/src/main/java/seedu/medinfo/ui/WardListPanel.java @@ -0,0 +1,46 @@ +package seedu.medinfo.ui; + +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.medinfo.model.ward.Ward; + +/** + * Panel containing the list of wards. + */ +public class WardListPanel extends UiPart { + private static final String FXML = "WardListPanel.fxml"; + + @FXML + private ListView wardListView; + + /** + * Creates a {@code WardListPanel} with the given {@code ObservableList}. + */ + public WardListPanel(ObservableList wardList) { + super(FXML); + wardListView.setItems(wardList); + wardListView.setCellFactory(listView -> new WardListViewCell()); + } + + /** + * Custom {@code ListCell} that displays the graphics of a {@code Ward} using + * a {@code WardCard}. + */ + class WardListViewCell extends ListCell { + @Override + protected void updateItem(Ward ward, boolean empty) { + super.updateItem(ward, empty); + + if (empty || ward == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(new WardCard(ward, getIndex() + 1).getRoot()); + } + } + } + +} diff --git a/src/main/resources/images/MedInfo.png b/src/main/resources/images/MedInfo.png new file mode 100644 index 00000000000..7bf880f0476 Binary files /dev/null and b/src/main/resources/images/MedInfo.png differ diff --git a/src/main/resources/images/address_book_32.png b/src/main/resources/images/address_book_32.png deleted file mode 100644 index 29810cf1fd9..00000000000 Binary files a/src/main/resources/images/address_book_32.png and /dev/null differ diff --git a/src/main/resources/images/calendar.png b/src/main/resources/images/calendar.png deleted file mode 100644 index 8b2bdf4f1c1..00000000000 Binary files a/src/main/resources/images/calendar.png and /dev/null differ diff --git a/src/main/resources/images/clock.png b/src/main/resources/images/clock.png deleted file mode 100644 index 0807cbf6451..00000000000 Binary files a/src/main/resources/images/clock.png and /dev/null differ diff --git a/src/main/resources/images/fail.png b/src/main/resources/images/fail.png deleted file mode 100644 index 6daf01290dd..00000000000 Binary files a/src/main/resources/images/fail.png and /dev/null differ diff --git a/src/main/resources/images/help_icon.png b/src/main/resources/images/help_icon.png index f8e80d6c1c5..bda6e8f44ca 100644 Binary files a/src/main/resources/images/help_icon.png and b/src/main/resources/images/help_icon.png differ diff --git a/src/main/resources/images/info_icon.png b/src/main/resources/images/info_icon.png index f8cef714095..54f7c434ab3 100644 Binary files a/src/main/resources/images/info_icon.png and b/src/main/resources/images/info_icon.png differ diff --git a/src/main/resources/view/DarkTheme.css b/src/main/resources/view/DarkTheme.css index 36e6b001cd8..b54e3b86861 100644 --- a/src/main/resources/view/DarkTheme.css +++ b/src/main/resources/view/DarkTheme.css @@ -328,7 +328,7 @@ -fx-text-fill: white; } -#filterField, #personListPanel, #personWebpage { +#filterField, #patientListPanel, #patientWebpage { -fx-effect: innershadow(gaussian, black, 10, 0, 0, 0); } diff --git a/src/main/resources/view/Extensions.css b/src/main/resources/view/Extensions.css index bfe82a85964..5a2ef0b24fc 100644 --- a/src/main/resources/view/Extensions.css +++ b/src/main/resources/view/Extensions.css @@ -1,10 +1,9 @@ .error { - -fx-text-fill: #d06651 !important; /* The error class should always override the default text-fill style */ + -fx-text-fill: #d06651 !important; } .list-cell:empty { - /* Empty cells will not have alternating colours */ -fx-background: #383838; } diff --git a/src/main/resources/view/HelpWindow.css b/src/main/resources/view/HelpWindow.css index 17e8a8722cd..bee23565408 100644 --- a/src/main/resources/view/HelpWindow.css +++ b/src/main/resources/view/HelpWindow.css @@ -1,5 +1,5 @@ #copyButton, #helpMessage { - -fx-text-fill: white; + -fx-text-fill: black; } #copyButton { @@ -15,5 +15,5 @@ } #helpMessageContainer { - -fx-background-color: derive(#1d1d1d, 20%); + -fx-background-color: derive(#ccedee, 10%); } diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml index a431648f6c0..eec28607eb5 100644 --- a/src/main/resources/view/MainWindow.fxml +++ b/src/main/resources/view/MainWindow.fxml @@ -7,19 +7,22 @@ + + + + title="MedInfo" minWidth="450" minHeight="600" onCloseRequest="#handleExit"> - + - + @@ -39,20 +42,23 @@ - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/MedInfoTheme.css b/src/main/resources/view/MedInfoTheme.css new file mode 100644 index 00000000000..598592c818b --- /dev/null +++ b/src/main/resources/view/MedInfoTheme.css @@ -0,0 +1,454 @@ +.background { + -fx-background-color: derive(#1d1d1d, 20%); + background-color: #1d1d1d; /* Used in the default.html file */ +} + +.label { + -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; +} + +.label-header { + -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"; +} + +.tab-pane { + -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; +} + +.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; +} + +.table-view .column-header-background { + -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 .label { + -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; +} + +.split-pane-divider { + -fx-background-color: derive(#A8C5C9, 10%); + -fx-border-color: transparent; +} + +.split-pane { + -fx-border-radius: 5; + -fx-border-width: 0; + -fx-background-color: derive(#FFFFFF, 20%); +} + +.list-view { + -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; +} + +.list-cell:filled:even { + -fx-background-color: #a8acad; +} + +.list-cell:filled:odd { + -fx-background-color: #8c9091; +} + +.list-cell:filled:selected { + -fx-background-color: #424d5f; +} + +.list-cell:filled:selected #cardPane { + -fx-border-color: #3e7b91; + -fx-border-width: 1; +} + +.list-cell .label { + -fx-text-fill: black; +} + +.cell_big_label { + -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; +} + +/** + * CLI Text box + * MainWindow.fxml + */ +.stack-pane { + -fx-background-color: derive(#A8C5C9, 20%); + -fx-border-radius: 100; +} + +/** + * Main body holding CLI and lists + * MainWindow.fxml + */ +.pane-with-border { + -fx-background-color: derive(#FFFFFF, 20%); + -fx-border-color: derive(#FFFFFF, 10%); + -fx-border-top-width: 1px; + -fx-border-radius: 100; +} + +/** + * Used at the bottom status-bar + * StatusBarFooter.fxml + */ +.status-bar { + -fx-background-color: derive(#A8C5C9, 30%); +} + +/** + * Status indicators GRAY/GREEN/YELLOW/RED + * PatientListCard.fxml + */ +.status-UNKNOWN { + -fx-background-color: derive(#1d1d1d, 30%); + -fx-font-family: "Segoe UI Light"; + -fx-font-size: 13pt; + -fx-text-fill: white; + -fx-padding: 20px; +} + +.status-STABLE { + -fx-background-color: derive(#023020, 30%); + -fx-font-family: "Segoe UI Light"; + -fx-font-size: 13pt; + -fx-text-fill: white; + -fx-padding: 20px; +} + +.status-SERIOUS { + -fx-background-color: derive(#c29500, 30%); + -fx-font-family: "Segoe UI Light"; + -fx-font-size: 13pt; + -fx-text-fill: black; + -fx-padding: 20px; +} + +.status-CRITICAL { + -fx-background-color: derive(#8B0000, 30%); + -fx-font-family: "Segoe UI Light"; + -fx-font-size: 13pt; + -fx-text-fill: white; + -fx-padding: 20px; +} + +.status-bar .label { + -fx-font-family: "Segoe UI Light"; + -fx-text-fill: black; + -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-text-fill: black; +} + +.status-bar-with-border .label { + -fx-text-fill: black; +} + +.grid-pane { + -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%); +} + +.context-menu { + -fx-background-color: derive(#1d1d1d, 50%); +} + +.context-menu .label { + -fx-text-fill: white; +} + +/** + * Menu + */ +.menu-bar { + -fx-background-color: derive(#A8C5C9, 20%); +} + +.menu-bar .label { + -fx-font-size: 14pt; + -fx-font-family: "Segoe UI Light"; + -fx-text-fill: black; + -fx-opacity: 0.9; +} + +.menu .left-container { + -fx-background-color: black; +} + +/* + * Metro style Push Button + * Author: Pedro Duque Vieira + * 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; +} + +.button:hover { + -fx-background-color: #3a3a3a; +} + +.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; +} + +.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; +} + +.button:default:hover { + -fx-background-color: derive(-fx-focus-color, 30%); +} + +.dialog-pane { + -fx-background-color: #1d1d1d; +} + +.dialog-pane > *.button-bar > *.container { + -fx-background-color: #1d1d1d; +} + +.dialog-pane > *.label.content { + -fx-font-size: 14px; + -fx-font-weight: bold; + -fx-text-fill: white; +} + +.dialog-pane:header *.header-panel { + -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; +} + +.scroll-bar { + -fx-background-color: derive(#A8C5C9, 20%); +} + +.scroll-bar .thumb { + -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-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:horizontal .increment-arrow, .scroll-bar:horizontal .decrement-arrow { + -fx-padding: 8 1 8 1; +} + + + +/** + * Patient card + * PatientListCard.fxml + */ +#cardPane { + -fx-background-color: transparent; + -fx-border-width: 0; +} + +#cardPaneRed { + -fx-background-color: red; + -fx-border-width: 0; +} + +/** + * List for patients/wards + * PatientListPanel.fxml + * WardListPanel.fxml + */ +#listView { + -fx-background-color: transparent; + -fx-border-width: 0; + -fx-border-radius: 400; + -fx-background-radius: 400; +} + +#commandTypeLabel { + -fx-font-size: 11px; + -fx-text-fill: #F70D1A; +} + +/** + * CLI text field + * MainWindow.fxml + */ +#commandTextField { + -fx-background-color: transparent; + -fx-background-insets: 0; + -fx-border-color: transparent; + -fx-border-insets: 0; + -fx-border-width: 1; + -fx-border-radius: 100; + -fx-font-family: "Segoe UI Light"; + -fx-font-size: 13pt; + -fx-text-fill: black; + -fx-prompt-text-fill: grey; +} + +#filterField, #patientListPanel, #patientWebpage { + -fx-effect: innershadow(gaussian, black, 10, 0, 0, 0); +} + +/** + * Results Display + * ResultDisplay.fxml + */ +#resultDisplay .content { + -fx-background-color: #A8C5C9; + -fx-background-radius: 0; +} + +/** + * Stats + */ +#stats { + -fx-background-color: gray; + -fx-background-radius: 50; + -fx-font-family: "Segoe UI Semibold"; + -fx-text-fill: white; +} + +/** + * RESULT display output font + * ResultDisplay.fxml + */ +.result-display { + -fx-background-color: #A8C5C9; + -fx-font-family: "Segoe UI Light"; + -fx-font-size: 11pt; + -fx-text-fill: black; + -fx-padding: 5px; +} + +.result-display .label { + -fx-text-fill: black !important; +} + +#tags { + -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; +} diff --git a/src/main/resources/view/PatientListCard.fxml b/src/main/resources/view/PatientListCard.fxml new file mode 100644 index 00000000000..76f63260f01 --- /dev/null +++ b/src/main/resources/view/PatientListCard.fxml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/PatientListPanel.fxml b/src/main/resources/view/PatientListPanel.fxml new file mode 100644 index 00000000000..a93537aa97e --- /dev/null +++ b/src/main/resources/view/PatientListPanel.fxml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/main/resources/view/ResultDisplay.fxml b/src/main/resources/view/ResultDisplay.fxml index 58d5ad3dc56..2173e64bf0b 100644 --- a/src/main/resources/view/ResultDisplay.fxml +++ b/src/main/resources/view/ResultDisplay.fxml @@ -5,5 +5,5 @@ -