diff --git a/README.adoc b/README.adoc index e36efe534bb..19828ee8c97 100644 --- a/README.adoc +++ b/README.adoc @@ -1,11 +1,7 @@ -= Address Book (Level 3) += NOVA ifdef::env-github,env-browser[:relfileprefix: docs/] -https://travis-ci.org/se-edu/addressbook-level3[image:https://travis-ci.org/se-edu/addressbook-level3.svg?branch=master[Build Status]] -https://ci.appveyor.com/project/damithc/addressbook-level3[image:https://ci.appveyor.com/api/projects/status/3boko2x2vr5cc3w2?svg=true[Build status]] -https://coveralls.io/github/se-edu/addressbook-level3?branch=master[image:https://coveralls.io/repos/github/se-edu/addressbook-level3/badge.svg?branch=master[Coverage Status]] -https://www.codacy.com/app/damith/addressbook-level3?utm_source=github.com&utm_medium=referral&utm_content=se-edu/addressbook-level3&utm_campaign=Badge_Grade[image:https://api.codacy.com/project/badge/Grade/fc0b7775cf7f4fdeaf08776f3d8e364a[Codacy Badge]] - +https://travis-ci.org/AY1920S2-CS2103T-F10-3/main.svg?branch=master[image:https://travis-ci.org/AY1920S2-CS2103T-F10-3/main.svg?branch=master[Build Status]] ifdef::env-github[] image::docs/images/Ui.png[width="600"] @@ -15,22 +11,19 @@ ifndef::env-github[] image::images/Ui.png[width="600"] endif::[] -* This is a desktop Address Book application. It has a GUI but most of the user interactions happen using a CLI (Command Line Interface). -* It is a Java sample application intended for students learning Software Engineering while using Java as the main programming language. -* It is *written in OOP fashion*. It provides a *reasonably well-written* code example that is *significantly bigger* (around 6 KLoC)than what students usually write in beginner-level SE modules. +* Next-gen Organizing Virtual Assistant (NOVA) is a one-stop desktop application for CS2103T students to manage all CS2103T related activities, from studying to planning project meetings. NOVA is designed for users who prefer to use the Command Line Interface (CLI). +* Target Users are students who are currently enrolled in CS2103T or equivalent. == Site Map * <> * <> -* <> * <> * <> == Acknowledgements -* Some parts of this sample application were inspired by the excellent http://code.makery.ch/library/javafx-8-tutorial/[Java FX tutorial] by -_Marco Jakob_. -* Libraries used: https://openjfx.io/[JavaFX], https://github.com/FasterXML/jackson[Jackson], https://github.com/junit-team/junit5[JUnit5] +* AddressBook-Level3 project created by SE-EDU initiative at https://se-education.org == Licence : link:LICENSE[MIT] + diff --git a/build.gradle b/build.gradle index 93029ef8262..a9d684022a7 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,7 @@ plugins { } // Specifies the entry point of the application -mainClassName = 'seedu.address.Main' +mainClassName = 'seedu.nova.Main' sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 @@ -67,7 +67,7 @@ dependencies { } shadowJar { - archiveName = 'addressbook.jar' + archiveName = 'nova.jar' destinationDir = file("${buildDir}/jar/") } @@ -133,8 +133,8 @@ asciidoctor { idprefix: '', // for compatibility with GitHub preview idseparator: '-', 'site-root': "${sourceDir}", // must be the same as sourceDir, do not modify - 'site-name': 'AddressBook-Level3', - 'site-githuburl': 'https://github.com/se-edu/addressbook-level3', + 'site-name': 'nova', + 'site-githuburl': 'https://github.com/AY1920S2-CS2103T-F10-3/main', 'site-seedu': true, // delete this line if your project is not a fork (not a SE-EDU project) ] diff --git a/docs/AboutUs.adoc b/docs/AboutUs.adoc index 458e6134f45..1ee58ee910a 100644 --- a/docs/AboutUs.adoc +++ b/docs/AboutUs.adoc @@ -4,53 +4,53 @@ :imagesDir: images :stylesDir: stylesheets -AddressBook - Level 3 was developed by the https://se-edu.github.io/docs/Team.html[se-edu] team. + -_{The dummy content given below serves as a placeholder to be used by future forks of the project.}_ + -{empty} + We are a team based in the http://www.comp.nus.edu.sg[School of Computing, National University of Singapore]. +NOVA is developed by the following members within the https://github.com/AY1920S2-CS2103T-F10-3/main[AY1920S2-CS2103T-F10-3] team. == Project Team -=== John Doe -image::damithc.jpg[width="150", align="left"] -{empty}[http://www.comp.nus.edu.sg/~damithch[homepage]] [https://github.com/damithc[github]] [<>] +=== Terence Ng +image::xcelestialphoenix.png[width="150", align="left"] +{empty} [https://github.com/xcelestialphoenix[github]] [<>] + +Role: Developer + +Responsibilities: Schedule Builder -Role: Project Advisor ''' -=== John Roe -image::lejolly.jpg[width="150", align="left"] -{empty}[http://github.com/lejolly[github]] [<>] +=== Bryan Yap +image::bryanyap972.png[width="150", align="left"] +{empty}[http://github.com/bryanyap972[github]] [<>] -Role: Team Lead + -Responsibilities: UI +Role: Developer + +Responsibilities: Progress Tracker ''' -=== Johnny Doe -image::yijinl.jpg[width="150", align="left"] -{empty}[http://github.com/yijinl[github]] [<>] +=== Chua Hui Xian +image::huixianc.png[width="150", align="left"] +{empty}[http://github.com/huixianc[github]] [<>] Role: Developer + -Responsibilities: Data +Responsibilities: Event ''' -=== Johnny Roe -image::m133225.jpg[width="150", align="left"] -{empty}[http://github.com/m133225[github]] [<>] +=== Tan Wah Ken +image::mightyrabbit99.png[width="150", align="left"] +{empty}[http://github.com/mightyrabbit99[github]] [<>] Role: Developer + -Responsibilities: Dev Ops + Threading +Responsibilities: Study List ''' -=== Benson Meier -image::yl_coder.jpg[width="150", align="left"] -{empty}[http://github.com/yl-coder[github]] [<>] +=== Loh Sze Ying +image::lohszeying.png[width="150", align="left"] +{empty}[http://github.com/lohszeying[github]] [<>] Role: Developer + -Responsibilities: UI +Responsibilities: Address Book ''' diff --git a/docs/ContactUs.adoc b/docs/ContactUs.adoc index 81be279ef6d..32dab6d18dc 100644 --- a/docs/ContactUs.adoc +++ b/docs/ContactUs.adoc @@ -2,6 +2,6 @@ :site-section: ContactUs :stylesDir: stylesheets -* *Bug reports, Suggestions* : Post in our https://github.com/se-edu/addressbook-level3/issues[issue tracker] if you noticed bugs or have suggestions on how to improve. +* *Bug reports, Suggestions* : Post in our https://github.com/AY1920S2-CS2103T-F10-3/main/issues[issue tracker] if you noticed bugs or have suggestions on how to improve. * *Contributing* : We welcome pull requests. Follow the process described https://github.com/oss-generic/process[here] -* *Email us* : You can also reach us at `damith [at] comp.nus.edu.sg` +* *Email us* : You can also reach us at `e0325944@u.nus.edu` diff --git a/docs/DevOps.adoc b/docs/DevOps.adoc index 2aa5a6bc0c1..17f71aff022 100644 --- a/docs/DevOps.adoc +++ b/docs/DevOps.adoc @@ -1,4 +1,4 @@ -= AddressBook Level 3 - Dev Ops += NOVA - Dev Ops :site-section: DeveloperGuide :toc: :toc-title: @@ -12,7 +12,7 @@ ifdef::env-github[] :note-caption: :information_source: :warning-caption: :warning: endif::[] -:repoURL: https://github.com/se-edu/addressbook-level3/tree/master +:repoURL: https://github.com/AY1920S2-CS2103T-F10-3/main == Build Automation diff --git a/docs/DeveloperGuide.adoc b/docs/DeveloperGuide.adoc index 3d65905a853..774fb19f909 100644 --- a/docs/DeveloperGuide.adoc +++ b/docs/DeveloperGuide.adoc @@ -1,8 +1,9 @@ -= AddressBook Level 3 - Developer Guide += NOVA - Developer Guide :site-section: DeveloperGuide :toc: :toc-title: :toc-placement: preamble +:toclevels: 4 :sectnums: :imagesDir: images :stylesDir: stylesheets @@ -12,13 +13,14 @@ ifdef::env-github[] :note-caption: :information_source: :warning-caption: :warning: endif::[] -:repoURL: https://github.com/se-edu/addressbook-level3/tree/master -By: `Team SE-EDU`      Since: `Jun 2016`      Licence: `MIT` +:repoURL: https://github.com/AY1920S2-CS2103T-F10-3/main + +By: `CS2103T-F10-3`      Since: `Apr 2020`      Licence: `MIT` == Setting up -Refer to the guide <>. +Refer to the guide https://github.com/AY1920S2-CS2103T-F10-3/main/blob/master/docs/SettingUp.adoc[here]. == Design @@ -31,10 +33,10 @@ image::ArchitectureDiagram.png[] The *_Architecture Diagram_* given above explains the high-level design of the App. Given below is a quick overview of each component. [TIP] -The `.puml` files used to create diagrams in this document can be found in the link:{repoURL}/docs/diagrams/[diagrams] folder. -Refer to the <> to learn how to create and edit diagrams. +The `.puml` files used to create diagrams in this document can be found in the link:{repoURL}/tree/master/docs/diagrams[diagrams] folder. +Refer to the https://github.com/AY1920S2-CS2103T-F10-3/main/blob/master/docs/UsingPlantUml.adoc[Using PlantUML guide] to learn how to create and edit diagrams. -`Main` has two classes called link:{repoURL}/src/main/java/seedu/address/Main.java[`Main`] and link:{repoURL}/src/main/java/seedu/address/MainApp.java[`MainApp`]. It is responsible for, +`Main` has two classes called link:{repoURL}/blob/master/src/main/java/seedu/nova/Main.java[`Main`] and link:{repoURL}/blob/master/src/main/java/seedu/nova/MainApp.java[`MainApp`]. 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 method where necessary. @@ -58,78 +60,78 @@ Each of the four components For example, the `Logic` component (see the class diagram given below) defines it's API in the `Logic.java` interface and exposes its functionality using the `LogicManager.java` class. -.Class Diagram of the Logic Component -image::LogicClassDiagram.png[] +.Architecture Diagram of the Logic Component +image::LogicArchitecture.png[] [discrete] -==== How the architecture components interact with each other +==== How the architecture components interact with each other (Loh Sze Ying) -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 i\1` after navigating to Address Book mode with `nav ab`. + +The same type of interaction applies to other mode within NOVA, such as Schedule mode, Planner mode or Progress Tracker mode. -.Component interactions for `delete 1` command -image::ArchitectureSequenceDiagram.png[] +.Component interactions for `delete i\1` command +image::ArchitectureSequenceDiagram.png[width=500] The sections below give more details of each component. +//tag::UI[] [[Design-Ui]] -=== UI component +=== UI component (Yap Wen Jun Bryan) .Structure of the UI Component image::UiClassDiagram.png[] -*API* : link:{repoURL}/src/main/java/seedu/address/ui/Ui.java[`Ui.java`] +*API* : link:{repoURL}/blob/master/src/main/java/seedu/nova/ui/Ui.java[`Ui.java`] -The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `PersonListPanel`, `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class. +The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay` and `HelpBox`. All these, including the `MainWindow`, inherit from the abstract `UiPart` class. -The `UI` component uses JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files that are in the `src/main/resources/view` folder. For example, the layout of the link:{repoURL}/src/main/java/seedu/address/ui/MainWindow.java[`MainWindow`] is specified in link:{repoURL}/src/main/resources/view/MainWindow.fxml[`MainWindow.fxml`] +The `UI` component uses JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files (HelpBox does not have a `.fxml` file) that are in the `src/main/resources/view` folder. For example, the layout of the link:{repoURL}/blob/master/src/main/java/seedu/nova/ui/MainWindow.java[`MainWindow`] is specified in link:{repoURL}/blob/master/src/main/resources/view/MainWindow.fxml[`MainWindow.fxml`] 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. +//end::UI[] [[Design-Logic]] -=== Logic component + +// tag::logic[] +=== Logic component (Chua Huixian) [[fig-LogicClassDiagram]] .Structure of the Logic Component image::LogicClassDiagram.png[] *API* : -link:{repoURL}/src/main/java/seedu/address/logic/Logic.java[`Logic.java`] +link:{repoURL}/blob/master/src/main/java/seedu/nova/logic/Logic.java[`Logic.java`] -. `Logic` uses the `AddressBookParser` class to parse the user command. +. `Logic` uses the `LogicParser` class to determine which mode the user is in when they input a command. +. After which, the relevant parser is called (e.g. `AddressBookParser`). . This results in a `Command` object which is executed by the `LogicManager`. . The command execution can affect the `Model` (e.g. adding a person). . The result of the command execution is encapsulated as a `CommandResult` object which is passed back to the `Ui`. . In addition, the `CommandResult` object can also instruct the `Ui` to perform certain actions, such as displaying help to the user. - -Given below is the Sequence Diagram for interactions within the `Logic` component for the `execute("delete 1")` API call. - -.Interactions Inside the Logic Component for the `delete 1` Command -image::DeleteSequenceDiagram.png[] - -NOTE: The lifeline for `DeleteCommandParser` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram. +// end::logic[] [[Design-Model]] -=== Model component +// tag::model[] +=== Model component (Terence) .Structure of the Model Component image::ModelClassDiagram.png[] -*API* : link:{repoURL}/src/main/java/seedu/address/model/Model.java[`Model.java`] +*API* : link:{repoURL}/blob/master/src/main/java/seedu/nova/model/Model.java[`Model.java`] The `Model`, * stores a `UserPref` object that represents the user's preferences. * stores the Address Book data. +* stores a 'Schedule' object that represents the user's schedule. +* stores a 'ProgressTracker' object that represents the user's progress in their project tasks. * exposes 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. * does not depend on any of the other three components. -[NOTE] -As a more OOP model, we can store a `Tag` list in `Address Book`, which `Person` can reference. This would allow `Address Book` to only require one `Tag` object per unique `Tag`, instead of each `Person` needing their own `Tag` object. An example of how such a model may look like is given below. + - + -image:BetterModelClassDiagram.png[] +// end::model[] [[Design-Storage]] === Storage component @@ -137,25 +139,97 @@ image:BetterModelClassDiagram.png[] .Structure of the Storage Component image::StorageClassDiagram.png[] -*API* : link:{repoURL}/src/main/java/seedu/address/storage/Storage.java[`Storage.java`] +*API* : link:{repoURL}/blob/master/src/main/java/seedu/nova/storage/Storage.java[`Storage.java`] The `Storage` component, * can save `UserPref` objects in json format and read it back. -* can save the Address Book data in json format and read it back. +* can save NOVA's data in json format and read it back. [[Design-Commons]] -=== Common classes +=== Common classes (Loh Sze Ying) + +Classes used by multiple components are in the `seedu.nova.commons` package. +All of the classes under `Commons` work independently. -Classes used by multiple components are in the `seedu.addressbook.commons` package. +Most notably, + +* *API* : link:{repoURL}/blob/master/src/main/java/seedu/nova/commons/core/LogsCenter.java[`LogsCenter.java`] + +The `LogsCenter` is used by NOVA to display logs when running NOVA in terminal. + +* *API* : link:{repoURL}/blob/master/src/main/java/seedu/nova/commons/core/Messages.java[`Messages.java`] + +The `Messages` is used by NOVA and deals with messages to display relating to NOVA's features. == Implementation This section describes some noteworthy details on how certain features are implemented. +// tag::events[] + +=== Manage Events (Chua Huixian) +The manage events feature handles the events of the user, including meetings, consultations, study sessions and lessons. +Users are able to: + +* add events +* delete events +* add notes to events + +==== Implementation - Deleting an event +The delete feature allows users to remove events from the schedule. +This feature is facilitated by `ScheduleParser`, `EventDeleteCommandParser` and `EventDeleteCommand`. +The operation is exposed in the `Model` interface as `Model#deleteEvent()`. + +Given below is an example usage scenario and how the delete mechanism behaves at each step. + +1. The user does `view t\2020-03-20` to view their events on 20th March 2020. + +2. The user executes `delete t\2020-03-20 i\2` command to delete the second event on 20th March 2020. + +3. `EventDeleteCommandParser` creates a new `EventDeleteCommand`. + +4. `LogicManager` executes the `EventDeleteCommand`. + +5. `Model#deleteEvent()` is called, and the `Schedule` object in `ModelManager` is updated. + +The following sequence diagram shows how the delete operation works: + +image::EventDeleteSeqDiagram.png[] + +The following activity diagram shows what happens when a user inputs a delete command: + +image::EventDeleteActDiagram.png[] + +==== Design Considerations +Aspect: Syntax of Deleting an Event + +* **Alternative 1 (current choice):** choosing the event by its date and its index in the list of events on that date +** Pros: relatively short to type, greater ease of implementation +** Cons: users have to view the list of events on that date before determining which event to mark as done + +* **Alternative 2:** choosing the event by description +** Pros: more recognisable for users +** Cons: difficulty in implementing as certain events may have the exact same descriptions + +// end::events[] + // tag::undoredo[] -=== [Proposed] Undo/Redo feature -==== Proposed Implementation +=== Address Book (Loh Sze Ying) +The address book feature handles the contact list of the users. To enter address book mode, users need to enter `nav ab` command. Users are able to: + +* add contacts +* edit contacts +* delete contacts +* find contacts +* list all contacts +* list category specific contacts +* add category specific remark for contacts +* edit category specific remark for contacts +* delete category specific remark for contacts +* undo or redo command +* add profile picture to contacts +* delete profile picture to contacts + +==== Implementation - Undo or redo command The undo/redo mechanism is facilitated by `VersionedAddressBook`. It extends `AddressBook` with an undo/redo history, stored internally as an `addressBookStateList` and `currentStatePointer`. @@ -173,13 +247,13 @@ Step 1. The user launches the application for the first time. The `VersionedAddr image::UndoRedoState0.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. +Step 2. The user executes `delete i\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 i\5` command executes to be saved in the `addressBookStateList`, and the `currentStatePointer` is shifted to the newly inserted address book state. -image::UndoRedoState1.png[] +image::UndoRedoState1.png[width=446] -Step 3. The user executes `add n/David ...` to add a new person. The `add` command also calls `Model#commitAddressBook()`, causing another modified address book state to be saved into the `addressBookStateList`. +Step 3. The user executes `add 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`. -image::UndoRedoState2.png[] +image::UndoRedoState2.png[width=446] [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`. @@ -195,18 +269,16 @@ The following sequence diagram shows how the undo operation works: image::UndoSequenceDiagram.png[] -NOTE: The lifeline for `UndoCommand` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram. - The `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. [NOTE] If the `currentStatePointer` is at index `addressBookStateList.size() - 1`, pointing to the latest address book state, then there are no undone address book 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. -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. +Step 5. The user then decides to execute the command `list`. Commands that do not modify the address book, such as `list`, `list c\classmate`, `list c\teammate` or `find`, will usually not call `Model#commitAddressBook()`, `Model#undoAddressBook()` or `Model#redoAddressBook()`. Thus, the `addressBookStateList` remains unchanged. image::UndoRedoState4.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. We designed it this way because it no longer makes sense to redo the `add n/David ...` command. This is the behavior that most modern desktop applications follow. +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. We designed it this way because it no longer makes sense to redo the `add n\David ...` command. This is the behavior that most modern desktop applications follow. image::UndoRedoState5.png[] @@ -214,7 +286,7 @@ The following activity diagram summarizes what happens when a user executes a ne image::CommitActivityDiagram.png[] -==== Design Considerations +==== Design Considerations - Undo or redo command ===== Aspect: How undo & redo executes @@ -235,12 +307,204 @@ image::CommitActivityDiagram.png[] ** Cons: Requires dealing with commands that have already been undone: We must remember to skip these commands. Violates Single Responsibility Principle and Separation of Concerns as `HistoryManager` now needs to do two different things. // end::undoredo[] -// tag::dataencryption[] -=== [Proposed] Data Encryption +==== Implementation - Delete a contact + +The edit feature allows users to edit a contact from Address Book. This feature is facilitated by `AddressBookParser`, `AbDeleteCommandParser` and `AbDeleteCommand`. +The operation is exposed in the `Model` interface as `Model#deletePerson()`. + +Given below is an example usage scenario and how the delete mechanism behaves at each step. + +. The user does `list`, `list c\classmate`, `list c\teammate`, or `find n\[name]` to view their contacts. +. The user executes `delete i\1` command to delete the first contact. +. `AbDeleteCommandParser` creates a new `AbDeleteCommand`. +. `LogicManager` executes the `AbDeleteCommand`. +. `Model#deletePerson()` is called, and the `AddressBook` object in `ModelManager` is updated. + +The following sequence diagram shows how the delete operation works: + +image::AbDeleteSequenceDiagram.png[] + +The following activity diagram shows how the delete mechanism works: + +image::AbDeleteActivityDiagram.png[width=470] + +==== Design Considerations - Delete a contact +===== Aspect: Deleting a contact by index or name + +* **Alternative 1 (current choice):** Use index tag to delete contact +** Pros: Shorter command to type by using index +** Cons: Users need to use `list`, `list c\classmate`, `list c\teammate` or `find n\[name]` command before deleting contact +* **Alternative 2:** Use name to delete contact +** Pros: No need to use `list`, `list c\classmate`, `list c\teammate` or `find n\[name]` prior to deleting contact +** Cons: Need to handle deletion of contacts with the same name + +//tag::listPt[] + +=== List tasks under IP project (Yap Wen Jun Bryan) +The list tasks feature for the IP project allows the user to view a list of tasks that were added. + +==== Implementation +Given below is an example usage scenario and how the list tasks mechanism behaves at each step. + +1. The user keys in `list p\ip w\2` into the command box. +2. The user executes `list p\ip w\2` to view the list of tasks in week 2 of the IP project. +3. `PtListCommandParser` creates a new `PtListCommand`. +4. `LogicManager` executes the `PtListCommand`. +5. `Model#listTasks()` is called and the list of tasks is retrieved. + +The following sequence diagram shows how the list tasks operation works: + +image::PtSeqDiagram.png[] + +The following activity diagram shows what happens when a user inputs a list command: + +image::ListPtActivityDiagram.png[] + +==== Design Considerations +Aspect: Adding choice of week to view tasks + +* **Alternative 1 (current choice):** adding in choice of week to view tasks +** Pros: more freedom to choose what to see as a user +** Cons: longer command to type + +* **Alternative 2:** listing out the whole project tasks rather than letting user choose based on week +** Pros: shorter command to type and user can see all their tasks at once +** Cons: if user wants to see tasks only for a specific week will be harder to scroll and find + +//end::listPt[] +// tag::view[] + +=== View schedule (Terence) +The view schedule feature allows users to view the events they have added into the schedule. Users are able to view the +schedule by two time frames: + +* By date +* By week + +The following activity diagram shows what happens when a user inputs a view command: + +image::ViewCommandActivityDiagram.png[] + +==== Implementation - View schedule by date +The view feature allows users to see the events happening on the specified date. +This feature is facilitated by `ScheduleParser`, `ScViewCommandParser` and `ScViewDayCommand`. +The operation is exposed in the `Model` interface as `Model#view(LocalDate)`. + +Given below is an example usage scenario and how the view by date mechanism behaves at each step. + +1. The user keys in 'view t\2020-03-10' into the command box. +2. The user executes 'view t\2020-03-10' to view their schedule on the 10 Mar 2020. +3. `LogicManager` calls LogicParser to parse the command. +4. `LogicParser` gets the mode from Model and passes the command word and the argument to ScheduleParser. +5. `ScheduleParser` checks the command word and calls ScViewDayCommandParser. +6. 'ScViewDayCommandParser' creates a new 'ScViewDayCommand'. +7. 'LogicManager' executes the 'ScViewDayCommand'. +8. 'ModelManger#viewSechdule(LocalDate)' is called and the schedule for the day is retrieved. + +The following sequence diagram shows how the view tasks operation works: + +image::viewDaySequenceDiagram.png[] + +View week: Given below is an example usage scenario and how the view week mechanism behaves at each step. -_{Explain here how the data encryption feature will be implemented}_ +1. The user keys in 'view week i\6' into the command box. +2. The user executes 'view week i\6' to view their schedule on the 6th week. +3. `LogicManager` calls LogicParser to parse the command. +4. `LogicParser` gets the mode from Model and passes the command word and the argument to ScheduleParser. +5. `ScheduleParser` checks the command word and the preamble and calls ScViewWeekCommandParser. +6. 'ScViewWeekCommandParser' creates a new 'ScViewWeekCommand'. +7. 'LogicManager' executes the 'ScViewWeekCommand'. +8. 'ModelManger#viewSechdule(int)' is called and the schedule for the week is retrieved. -// end::dataencryption[] +==== Implementation - View schedule by week +The view feature allows users to see the events happening throughout the specified week. +This feature is facilitated by `ScheduleParser`, `ScViewCommandParser` and `ScViewWeekCommand`. +The operation is exposed in the `Model` interface as `Model#view(int)`. + +The following sequence diagram shows what happens when a user inputs a view week command: + +image::viewWeekSequenceDiagram.png[] + +==== Design Considerations +Aspect: View schedule by at most week and not month. + +* **Alternative 1 (current choice):** View schedule up to week +** Pros: Easier to fit the events into the display. +** Cons: Less ways for user to view schedule. + +* **Alternative 2:** View schedule up to month +** Pros: User can see their whole month's schedule at once. +** Cons: Might be too long and cannot fit into the display box. Time to gather all the events may be too long. +// end::view[] + +//tag::studyplannerfeature[] + +=== View available free slot of a specific day (Tan Wah Ken) +The view free slots feature allows the user to view their available free slots on their schedule. + +==== Implementation +Given below is an example usage scenario and how the view free slot mechanism behaves at each step. + +1. The user keys in 'freeslot t\2020-03-10' into the command box. +2. The user executes 'freeslot t\2020-03-10' to view the free slots on their schedule on the 10th of March 2020. +3. 'ScViewFreeSlotCommandParser' creates a new 'ScViewFreeSlotCommand'. +4. 'LogicManager' executes the 'ScViewFreeSlotCommand'. +5. 'ModelManger#viewFreeSlot()' is called and the free slots for the day is retrieved. + +The following sequence diagram shows how the view tasks operation works: + +image::ScViewFreeSlotDiagram.png[] + +==== Design Considerations + +===== Aspect: Calculating free slots given a schedule + +* **Alternative 1 (current choice):** Embeds a free slot data structure to keep track of the free slots whenever +events are added +** Pros: no need to calculate free slots whenever user execute freeslot. +** Cons: overhead to add event commands, making its execution slower. + +* **Alternative 2:** Calculates free slot based on the events whenever user executes freeslot +** Pros: easier to implement. +** Cons: slower freeslot execution. + + +=== Schedule events based on a task in plan (Tan Wah Ken) +The plan feature allows the user to create an event based on the task user created in the plan. + +==== Implementation +Given below is an example usage scenario and how the plan task mechanism behaves at each step. + +1. The user keys in 'schedule p\task name t\2020-03-10' into the command box. +2. The user executes 'schedule p\task name t\2020-03-10' to create an event "task name" on their schedule on the 10th of +March 2020. +3. 'PlannerScheduleTaskCommandParser' creates a new 'PlannerScheduleTaskCommand'. +4. 'LogicManager' executes the 'PlannerScheduleTaskCommand'. +5. 'ModelManger#searchTask()' is called to search for the task user specified. +6. 'ModelManger#generateTaskEvent()' is called and one event with time determined by algorithm is created on the day +in schedule. + +The following sequence diagram shows how the schedule task operation works: + +image::PlannerScheduleTaskSequenceDiagram.png[] + +The following activity diagram summarizes what happens when a user schedules a task: + +image::PlannerScheduleTaskActivityDiagram.png[] + +==== Design Considerations + +===== Aspect: Calculating best fit time frame for a task + +* **Alternative 1 (current choice):** Plan gets free slot from schedule and generate event based on it. +** Pros: Isolation of modules. +** Cons: Redundant code. + +* **Alternative 2:** Schedule decides whether to schedule or discard an event generated from task. +** Pros: more robust schedule. +** Cons: more difficult to implement. + +//end::studyplannerfeature[] === Logging @@ -264,48 +528,126 @@ Certain properties of the application can be controlled (e.g user prefs file loc == Documentation -Refer to the guide <>. +Refer to the guide https://github.com/AY1920S2-CS2103T-F10-3/main/blob/master/docs/Documentation.adoc[here]. == Testing -Refer to the guide <>. +Refer to the guide https://github.com/AY1920S2-CS2103T-F10-3/main/blob/master/docs/Testing.adoc[here]. == Dev Ops -Refer to the guide <>. +Refer to the guide https://github.com/AY1920S2-CS2103T-F10-3/main/blob/master/docs/DevOps.adoc[here]. [appendix] == Product Scope *Target user profile*: -* has a need to manage a significant number of contacts * prefer desktop apps over other types * can type fast * prefers typing over mouse input * is reasonably comfortable using CLI apps +* is a CS2103T student -*Value proposition*: manage contacts faster than a typical mouse/GUI driven app +*Value proposition*: a one-stop study aid platform for CS2103T students [appendix] == User Stories Priorities: High (must have) - `* * \*`, Medium (nice to have) - `* \*`, Low (unlikely to have) - `*` -[width="59%",cols="22%,<23%,<25%,<30%",options="header",] +[width="100%",cols="^15%,<15%,<35%,<35%",options="header",] |======================================================================= -|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 +<|Priority |As a ... |I can ... |So that I ... +// tag::addressbookuserstory[] +|`* * *` |student |add classmate or teammate’s name and contact information |contact them easily + +|`* * *` |student |edit classmate or teammate’s name and contact information |Edit them if the information are changed + +|`* * *` |student |delete classmate or teammate’s contact |delete if not necessary anymore + +|`* * *` |student |categorise contacts into teammate or classmate |sort contacts according to category + +|`* * *` |student |add category specific remark |filter out a contact’s remark according to type of contact + +|`* * *` |student |edit category specific remark |edit remark if necessary + +|`* * *` |student |delete category specific remark |delete remark if it is not needed + +|`*` |student |add profile picture to added contact |know at a glance who is the person in my contact + +|`*` |student |edit profile picture to added contact |edit the picture if changes are necessary + +|`*` |student |delete profile picture to added contact|don't need the contact picture within NOVA anymore +// end::addressbookuserstory[] + +// tag::eventuserstory[] +|`* * *` |student |create meeting events | can keep track of my schedule + +|`* * *` |student |create study session events | can keep track of my schedule + +|`* * *` |student |create consultation events | can keep track of my schedule + +|`* * *` |student |create lesson events | can keep track of my schedule + +|`* * *` |student |note down the location of the meeting | know where to go + +|`* * *` |student |delete events | can get rid of events that I do not need anymore + +|`* * *` |student |add notes to events | can jot down additional details about the events + +|`* *` |student |mark events as done | know which events I have completed + +|`* *` |student |find events | can check if I have any specific events according to keyword(s) + +|`* *` |student |repeat events | can add multiple similar events at one go +// end::eventuserstory[] + +|`* * *` |forgetful student |keep track of my project tasks |make sure all my project tasks are completed on time + +|`* * *` |student |mark tasks as done |track how many tasks I have finished + +|`* * *` |student |view tasks added |see the tasks I have to finish + +|`* * *` |student |add notes to project tasks |keep track of details regarding the tasks + +|`* * *` |student |delete project tasks |remove unwanted project tasks from the tracker + +|`* * *` |student |add project tasks |keep track of those project tasks + +|`* * *` |student |delete notes |remove unwanted notes from tasks + +|`* *` |lazy student |edit project tasks |can correct mistakes made to task descriptions with little effort + +|`* *` |lazy student |edit notes to project tasks |can correct mistakes made to notes with little effort + +// tag::viewScheduleUserStories[] + +|`* * *` |Student | View my schedule for a day| Know the flow of events on that day + +|`* * *` |Student | View my schedule for a week | Know what will happen for that week + +|`*` |Student | View my timetable | Can check when my classes are -|`* * *` |user |add a new person | +// end::viewScheduleUserStories[] -|`* * *` |user |delete a person |remove entries that I no longer need +|`* *` |Student | Easily find my free slots without looking at my schedule | Do not need to strain my eyes -|`* * *` |user |find a person by name |locate details of persons without having to go through the entire list +|`* *` |Forgetful student | Set reminders for upcoming events | Will remember to attend them -|`* *` |user |hide <> by default |minimize chance of someone else seeing them by accident +//tag::studyplannerusecase[] + +|`* * *` |Student | Add tasks to study plan | Can add study tasks to my study plan + +|`* * *` |Student | Delete tasks on study plan | Can delete study tasks if I don't need it anymore + +|`* * *` |Student | Generate event from a task | If I feel like I want to do a study task today, I can generate an +event on today's schedule so that I can keep up with my study plan. + +|`* *` |Student | View statistics of my task progress | Can see how much I've done for each task on my study plan. + +//end::studyplannerusecase[] -|`*` |user with many persons in the address book |sort persons by name |locate a person easily |======================================================================= _{More to be added}_ @@ -313,70 +655,585 @@ _{More to be added}_ [appendix] == Use Cases -(For all use cases below, the *System* is the `AddressBook` and the *Actor* is the `user`, unless specified otherwise) +(For all use cases below, the *System* is the `nova` and the *Actor* is the `student`, unless specified otherwise) [discrete] -=== Use case: Delete person +=== Use case 1: Add a contact (Loh Sze Ying) *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. Student enters add command with the contact’s name, phone number, email and category +2. NOVA saves the contact + Use case ends. *Extensions* [none] -* 2a. The list is empty. +* 1a. Student did not include compulsory field ++ +[none] +** 1a1. NOVA informs student to include compulsory field ++ +Use case resumes at step 1 +* 1b. Student did not adhere to format required for adding contact ++ +[none] +** 1b1. NOVA informs student that the format is invalid, and provides an example of a correct format ++ +Use case resumes at step 1 + +[discrete] +=== Use case 2: List all contacts (Loh Sze Ying) + +*MSS* + +1. Student enters list command +2. NOVA list all the contacts ++ +Use case ends + +*Extensions* + +[none] +* 1a. There is no contacts saved ++ +[none] +** 1a1. NOVA informs student that the list is empty ++ +Use case end + +[discrete] +=== Use case 3: List category specific contacts (Loh Sze Ying) + +*MSS* + +1. Student enters list category command +2. NOVA list all the contacts under that category ++ +Use case ends + +*Extensions* + +[none] +* 1a. There is no contacts saved under that category ++ +[none] +** 1a1. NOVA informs student that the list of that category is empty ++ +Use case end + +[discrete] +=== Use case 4: Find saved contacts (Loh Sze Ying) + +*MSS* + +1. Student enters find command +2. NOVA finds the name of contact that the student typed and list all the matching names ++ +Use case ends + +*Extensions* + +[none] +* 1a. There is no contact that matches what the student type ++ +[none] +** 1a1. NOVA prints an empty list ++ +Use case end + +[discrete] +=== Use case 5: Edit a contact (Loh Sze Ying) + +*MSS* + +1. Student used `list`, `list c\classmate`, `list c\teammate` or `find` command +2. Student enters edit command with index, and at least 1 field to edit +3. NOVA saves the edited contact ++ +Use case ends + +*Extensions* + +[none] +* 1a. Student did not use `list`, `list c\classmate`, `list c\teammate` or `find` command before using `edit` command ++ +[none] +** 1a1. Student edits information of the wrong contact, and NOVA informs the student to use `undo` command if wrong contact is edited ++ +Use case resumes at step 1 +[none] +* 2a. Student did not adhere to format required for editing contact ++ +[none] +** 2a1. NOVA informs student that the format is invalid, and provides an example of a correct format ++ +Use case resumes at step 2 +[none] +* 2b. Student did not include at least 1 compulsory field ++ +[none] +** 2b1. NOVA informs student to include at least 1 compulsory field ++ +Use case resumes at step 2 + +[discrete] +=== Use case 6: Delete a contact (Loh Sze Ying) + +*MSS* + +1. Student used `list`, `list c\classmate`, `list c\teammate` or `find` command +2. Student enters delete command +3. NOVA deletes the contact ++ +Use case ends + +*Extensions* + +[none] +* 1a. Student did not use `list`, `list c\classmate`, `list c\teammate` or `find` command before using `delete` command ++ +[none] +** 1a1. Student deletes the wrong contact, and NOVA informs the student to use `undo` command if wrong contact is deleted ++ +Use case resumes at step 1 +[none] +* 2a. NOVA cannot find the contact in the contact list ++ +[none] +** 2a1. NOVA informs student that the contact to delete does not exist ++ +Use case resumes at step 2 +[none] +* 2b. Student provides a wrong format to delete ++ +[none] +** 2b1. NOVA informs student that the format is invalid, and provides an example of a correct format ++ +Use case resumes at step 2 + +[discrete] +=== Use case 7: Add, edit or delete remark to a contact (Loh Sze Ying) + +*MSS* + +1. Student used `list`, `list c\classmate`, `list c\teammate` or `find` command +2. Student enters remark command +3. NOVA adds, edits or deletes remark to a contact ++ +Use case ends + +*Extensions* + +[none] +* 1a. Student did not use `list`, `list c\classmate`, `list c\teammate` or `find` command before using `remark` command ++ +[none] +** 1a1. Student adds, edits or deletes remark of the wrong contact, and NOVA informs the student to use `undo` command if student add, edit or delete remark on the wrong contact ++ +Use case resumes at step 1 +[none] +* 2a. NOVA cannot find the contact in the contact list ++ +[none] +** 2a1. NOVA informs student that the contact to add, edit or delete mark does not exist ++ +Use case resumes at step 2 +[none] +* 2b. Student provides a wrong format to add, edit or delete ++ +[none] +** 2b1. NOVA informs student that the format is invalid, and provides an example of a correct format ++ +Use case resumes at step 2 + +[discrete] +=== Use case 8: Undoing in address book (Loh Sze Ying) + +*MSS* + +1. Student used `add`, `edit`, `delete` or `remark` prior +2. Student enters undo command +3. NOVA undone the changes that the student made ++ +Use case ends + +[none] +* 1a. Student did not use `add`, `edit`, `delete` or `remark` prior to using `undo` ++ +[none] +** 1a1. NOVA informs the student that there are no more commands to undo ++ + +resumes at step 1 + +[discrete] +=== Use case 9: Redoing in address book (Loh Sze Ying) + +*MSS* + +1. Student used `undo` successfully prior to using `redo` +2. Student enters redo command +3. NOVA redo the changes that the student made ++ +Use case ends + +*Extensions* + +[none] +* 1a. Student did not use `undo` prior to using `redo` ++ +[none] +** 1a1. NOVA informs the student that there are no more commands to redo ++ +Use case resumes at step 1 + +// tag::eventusecase[] +[discrete] +=== Use case 10: Adding a consultation event (Chua Huixian) + +*MSS* + +1. Student enters consultation command with details of the consultation +2. NOVA adds consultation event to the schedule ++ +Use case ends. + +*Extensions* + +[none] +* 1a. NOVA detects error in data inputted +** 1a1. NOVA informs student of the error + Use case ends. -* 3a. The given index is invalid. +[discrete] +=== Use case 11: Delete an event (Chua Huixian) + +*MSS* + +1. Student enters delete command with details of the event +2. NOVA deletes the event + +Use case ends. + +*Extensions* + [none] -** 3a1. AddressBook shows an error message. +* 1a. NOVA cannot find the event given +** 1a1. NOVA informs student that the event does not exist + -Use case resumes at step 2. +Use case ends. -_{More to be added}_ +[discrete] +=== Use case 12: Adding a note to an event (Chua Huixian) -[appendix] -== Non Functional Requirements +*MSS* -. Should work on any <> as long as it has Java `11` or above installed. -. Should be able to hold up to 1000 persons without a noticeable sluggishness in performance for typical usage. -. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse. +1. Student enters note command with details of the event +2. NOVA adds note to the event ++ +Use case ends. -_{More to be added}_ +*Extensions* -[appendix] -== Glossary +[none] -[[mainstream-os]] Mainstream OS:: -Windows, Linux, Unix, OS-X +* 1a. NOVA cannot find the event given +** 1a1. NOVA informs student that the event does not exist +// end::eventusecase[] -[[private-contact-detail]] Private contact detail:: -A contact detail that is not meant to be shared with others -[appendix] -== Product Survey +//tag::UseCasesPt[] +[discrete] +=== Use case 13: Add task to a project of progress tracker (Yap Wen Jun Bryan) + +*MSS* + +1. User enter command to add task to a project. +2. Progress tracker adds task to the project. ++ +Use case ends. + +*Extensions* + +[none] +* 1a. No such project exist. +* 1a1. NOVA shows an error message. ++ +Use case ends. + +[discrete] +=== Use case 14: Edit a task (Yap Wen Jun Bryan) + +*MSS* + +1. User enter command to edit task. +2. Progress tracker replaces old task description with new description. ++ +Use case ends. + +*Extensions* + +[none] +* 1a. No such task exist. +* 1a1. NOVA shows an error message. ++ +Use case ends. + +[discrete] +=== Use case 15: Delete a task (Yap Wen Jun Bryan) + +*MSS* + +1. User enter command to delete task. +2. Progress tracker deletes task. ++ +Use case ends. + +*Extensions* + +[none] + +* 1a. Task to be deleted does not exist. +* 1a1. NOVA shows error message. ++ +Use case ends. -*Product Name* +[discrete] +=== Use case 16: List tasks in a week of a project (Yap Wen Jun Bryan) -Author: ... +*MSS* -Pros: +1. User enter command to list tasks. +2. Progress tracker lists task. ++ +Use case ends. -* ... -* ... +*Extensions* -Cons: +[none] +* 1a. No such tasks exists in the week specified. +* 1a1. NOVA shows error message. ++ +Use case ends. -* ... -* ... +[discrete] +=== Use case 17: Set an added task as done (Yap Wen Jun Bryan) + +*MSS* + +1. User enter command to set task as done. +2. Progress tracker sets task as done. ++ +Use case ends. + +*Extensions* + +[none] +* 1a. No such tasks exists. +* 1a1. NOVA shows error message. ++ +Use case ends. + +[discrete] +=== Use case 18: Add notes to a task in progress tracker (Yap Wen Jun Bryan) + +*MSS* + +1. User enter command to add notes to the project task. +2. Progress tracker adds notes to the project task. ++ +Use case ends. + +*Extensions* + +[none] +* 1a. No such project task exist. +* 1a1. NOVA shows an error message. ++ +Use case ends. + +[discrete] +=== Use case 19: Edit a note (Yap Wen Jun Bryan) + +*MSS* + +1. User enter command to edit note. +2. Progress tracker replace old note with new note. ++ +Use case ends. + +*Extensions* + +[none] +* 1a. No prior note was added. +* 1a1. NOVA shows an error message. ++ +Use case ends. + +[discrete] +=== Use case 20: Delete a note (Yap Wen Jun Bryan) + +*MSS* + +1. User enter command to delete note. +2. Progress tracker deletes note. ++ +Use case ends. + +*Extensions* + +[none] +* 1a. Note to be deleted does not exist. +* 1a1. NOVA shows error message. ++ +Use case ends. +//end::UseCasesPt[] + +// tag::viewScheduleUseCases[] + +[discrete] +=== Use case 21: View the schedule for a day + +*MSS* + +1. User requests for the schedule of a day. +2. NOVA shows the schedule for the day. ++ +Use case ends. + +*Extensions* + +[none] +* 1a. User enters the wrong format. +* 1a1. NOVA displays the correct format for the command. +* 2a. The schedule for the day is empty. +* 2a1. NOVA displays that day does not have any events. ++ +Use case ends. + +[discrete] +=== Use case 22: View the schedule for a week + +*MSS* + +1. User requests for the schedule of a week. +2. NOVA shows the schedule for the week. ++ +Use case ends. + +*Extensions* + +[none] + +* 1a. User enters the wrong format. +* 1a1. NOVA displays the correct format for the command. +* 2a. The schedule for the week is empty. +* 2a1. NOVA displays that week does not have any events. ++ +Use case ends. + +// end::viewScheduleUseCases[] +//tag::studyplannermss[] + +[discrete] +=== Use case 23: User add a task into study plan. + +*MSS* + +1. User enter command to create a task with name specified by user. +2. Study Planner of NOVA adds the task into study plan. ++ +Use case ends. + +*Extensions* + +[none] +* 1a. There is already a task with the same name. +* 1a1. NOVA shows error message. ++ +Use case ends. + +[discrete] +=== Use case 24: User add a task into study plan + +*MSS* + +1. User enter command to delete a task with name specified by user. +2. Study Planner of NOVA deletes the task. ++ +Use case ends. + +*Extensions* + +[none] +* 1a. No task with the name specified exists in study plan. +* 1a1. NOVA shows error message. ++ +Use case ends. + +[discrete] +=== Use case 25: User view statistics of every tasks in study plan + +*MSS* + +1. User enter command to view statistics of every tasks in study plan. +2. NOVA calculates and shows all the statistics of every task. ++ +Use case ends. + +*Extensions* + +[none] +* 1a. No task in study plan. +* 1a1. NOVA shows error message. ++ +Use case ends. + +[discrete] +=== Use case 26: User schedules a task into a particular day. + +*MSS* + +1. User enter command to schedules a task into a particular day. +2. NOVA generates and adds the event into schedule. ++ +Use case ends. + +*Extensions* + +[none] +* 1a. Unable to generate event. +* 1a1. NOVA shows error message. ++ +Use case ends. +//end::studyplannermss[] + +_{More to be added}_ + +[appendix] +== Non Functional Requirements + +. The application should work on any <> provided that Java `11` or above is installed. +. The application should work on both 32-bit and 64-bit environments. +. 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. +// tag::SzeYingNFR[] +. The application should work without internet connection. +// end::SzeYingNFR[] +// tag::TerenceNFR[] +. The application should respond to every command within one second. +. The application should be easily modifiable to meet changing curriculum of CS2103T. +// end::TerenceNFR[] + +_{More to be added}_ + +[appendix] +== Glossary + +[[mainstream-os]] Mainstream OS:: +Windows, Linux, OS-X [appendix] == Instructions for Manual Testing @@ -386,7 +1243,7 @@ Given below are instructions to test the app manually. [NOTE] These instructions only provide a starting point for testers to work on; testers are expected to do more _exploratory_ testing. -=== Launch and Shutdown +=== Launch . Initial launch @@ -400,26 +1257,81 @@ These instructions only provide a starting point for testers to work on; testers .. Re-launch the app by double-clicking the jar file. + Expected: The most recent window size and location is retained. -_{ more test cases ... }_ +//tag::shutdown[] +=== Shutdown (Yap Wen Jun Bryan) +. Exiting the application + +.. Enter `exit` in the command line. + +//end::shutdown[] -=== Deleting a person +//tag::deletepersoninab[] +=== Deleting a person in Address Book (Loh Sze Ying) +. Enter address book mode via `nav ab` . Deleting a person while all persons are listed -.. Prerequisites: List all persons using the `list` command. Multiple persons in the list. -.. 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. -.. Test case: `delete 0` + +.. Prerequisites: List contacts using the `list`, `list c\classmate`, `list c\teammate` or `find` command. There are multiple contacts in the list. +.. Test case: `delete i\1` + + Expected: First contact is deleted from the list. Details of the deleted contact shown in the status message. +.. Test case: `delete i\0` + Expected: No person is deleted. Error details shown in the status message. Status bar remains the same. -.. Other incorrect delete commands to try: `delete`, `delete x` (where x is larger than the list size) _{give more}_ + +.. Other incorrect delete commands to try: `delete`, `delete i\x` (where x is larger than the list size), `delete x` (where x is number or letter) Expected: Similar to previous. +//end::deletepersoninab[] + +//tag::Pt[] +=== Adding a project task in Progress Tracker (Yap Wen Jun Bryan) + +. Enter progress tracker mode via `nav progresstracker` +. Adding a project task + +.. Test case: `add p\ip w\1 d\new task` + +Expected: New project task with description "new task" is added to IP project week 1. Message showing successful execution is shown. + +.. Test case: `add p\ip w\14 d\new task` + +Expected: No new project task is added. Error details shown in the status message. + +.. Other incorrect add commands to try: `add`, `add p\ppp w\1 d\new task`. + +Expected: Similar to previous. + + +=== Saving data (Yap Wen Jun Bryan) + +. Dealing with corrupted data file + +.. Edit the Nova data file to contain invalid data. + +Example: Adding a @ to a week in a ptTask. + +.. Run the application. The application should start with an empty Nova data file. + +. Dealing with missing data file -_{ more test cases ... }_ +.. Delete the current Nova data file. -=== Saving data +.. Run the application. The application should start with a sample Nova data file with pre-included data. +//end::Pt[] -. Dealing with missing/corrupted data files +//tag::testView[] +=== View schedule (Terence) +. Enter schedule mode via `nav schedule` +. View the schedule +.. Prerequisite: An event has been added to 13 Jan 2020. +.. Test case: `view t\2020-01-13` + +Expected: The event that was pre-added will be displayed. +.. Test case: `view t\2020-01-12` + +Expected: Error message is shown to tell user that the date is out of the range of schedule. +//end::testView[] -.. _{explain how to simulate a missing/corrupted file and the expected behavior}_ +//tag::eventTest[] +=== Adding an event (Chua Huixian) +. Enter schedule mode via `nav schedule` +. Adding an event +.. Test case: `meeting d\DG Meeting v\COM1 t\2020-03-13 13:00 14:00` + +Expected: A meeting event will be added to the schedule. The details of the meeting will be displayed. +.. Test case: `meeting d\DG Meeting v\COM1 t\2020-03-13 14:00 12:00` + +Expected: Error message is shown. The end time must be later than start time. +.. Other incorrect commands to try: `meeting`, `meeting d\DG Meeting v\COM1 t\2020-03-13 14:00`. + +Expected: Error messages will be shown as well. +//end::eventTest[] -_{ more test cases ... }_ diff --git a/docs/Documentation.adoc b/docs/Documentation.adoc index ad90ac87bda..f2aef1a41c7 100644 --- a/docs/Documentation.adoc +++ b/docs/Documentation.adoc @@ -1,4 +1,4 @@ -= AddressBook Level 3 - Documentation += NOVA - Documentation :site-section: DeveloperGuide :toc: :toc-title: @@ -12,7 +12,7 @@ ifdef::env-github[] :note-caption: :information_source: :warning-caption: :warning: endif::[] -:repoURL: https://github.com/se-edu/addressbook-level3/tree/master +:repoURL: https://github.com/AY1920S2-CS2103T-F10-3/main == Introduction diff --git a/docs/SettingUp.adoc b/docs/SettingUp.adoc index c0659782fab..4c77a3fab7f 100644 --- a/docs/SettingUp.adoc +++ b/docs/SettingUp.adoc @@ -1,4 +1,4 @@ -= AddressBook Level 3 - Setting Up += NOVA - Setting Up :site-section: DeveloperGuide :toc: :toc-title: @@ -37,7 +37,7 @@ Do not disable them. If you have disabled them, go to `File` > `Settings` > `Plu == Verifying the setup -. Run the `seedu.address.Main` and try a few commands +. Run the `seedu.nova.Main` and try a few commands . <> to ensure they all pass. == Configurations to do before writing code @@ -57,9 +57,9 @@ Optionally, you can follow the <> docume === Updating documentation to match your fork -After forking the repo, the documentation will still have the SE-EDU branding and refer to the `se-edu/addressbook-level3` repo. +After forking the repo, the documentation will still have the NOVA branding and refer to the `ay1920S2-CS2103T-F10-3/main` repo. -If you plan to develop this fork as a separate product (i.e. instead of contributing to `se-edu/addressbook-level3`), you should do the following: +If you plan to develop this fork as a separate product (i.e. instead of contributing to `ay1920S2-CS2103T-F10-3/main`), you should do the following: . Configure the <> in link:{repoURL}/build.gradle[`build.gradle`], such as the `site-name`, to suit your own project. @@ -81,4 +81,4 @@ Having both Travis and AppVeyor ensures your App works on both Unix-based platfo === Getting started with coding -When you are ready to start coding, we recommend that you get some sense of the overall design by reading about <>. +When you are ready to start coding, we recommend that you get some sense of the overall design by reading about <>. diff --git a/docs/Testing.adoc b/docs/Testing.adoc index 5767b92912c..0a501f1dc83 100644 --- a/docs/Testing.adoc +++ b/docs/Testing.adoc @@ -1,4 +1,4 @@ -= AddressBook Level 3 - Testing += NOVA - Testing :site-section: DeveloperGuide :toc: :toc-title: @@ -12,7 +12,7 @@ ifdef::env-github[] :note-caption: :information_source: :warning-caption: :warning: endif::[] -:repoURL: https://github.com/se-edu/addressbook-level3/tree/master +:repoURL: https://github.com/AY1920S2-CS2103T-F10-3/main == Running Tests @@ -35,11 +35,11 @@ See <> for more info on how to run tests using G We have three types of tests: . _Unit tests_ targeting the lowest level methods/classes. + -e.g. `seedu.address.commons.StringUtilTest` +e.g. `seedu.NOVA.commons.StringUtilTest` . _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.NOVA.storage.StorageManagerTest` . 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.NOVA.logic.LogicManagerTest` == Troubleshooting Testing diff --git a/docs/UserGuide.adoc b/docs/UserGuide.adoc index 4e5d297a19f..7cdb44c94f2 100644 --- a/docs/UserGuide.adoc +++ b/docs/UserGuide.adoc @@ -1,8 +1,10 @@ -= AddressBook Level 3 - User Guide +[[Top]] += NOVA - User Guide :site-section: UserGuide :toc: :toc-title: :toc-placement: preamble +:toclevels: 4 :sectnums: :imagesDir: images :stylesDir: stylesheets @@ -12,166 +14,1028 @@ ifdef::env-github[] :tip-caption: :bulb: :note-caption: :information_source: endif::[] -:repoURL: https://github.com/se-edu/addressbook-level3 -By: `Team SE-EDU` Since: `Jun 2016` Licence: `MIT` +:repoURL: https://github.com/AY1920S2-CS2103T-F10-3/main -== Introduction +By: `CS2103T-F10-3` Since: `Apr 2020` Licence: `MIT` -AddressBook Level 3 (AB3) is for those who *prefer to use a desktop app for managing contacts*. More importantly, AB3 is *optimized for those who prefer to work with 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. Interested? Jump to the <> to get started. Enjoy! +[[Intro]] +// tag::intro[] +== Introduction (Terence) +Welcome to the user guide (UG) for Next-gen Organizing Virtual Assistant (NOVA). + +NOVA is a one-stop desktop application for CS2103T students to manage all CS2103T related activities. Be it studying +or planning project meetings, NOVA has you covered. NOVA is designed for users who prefer to interact with programs +through typing. + +image::userguide/guiComponents.png[width="790" align="center"] +[.text-center] +_[.small]#Fig 1: UI Components of NOVA#_ + +// end::intro[] +// tag::about[] + +[[About]] +== About (Terence) + +This UG is a reference guide to help you use NOVA. This document contains a <> section to get +you up and +running soon. + +There is an entire <> dedicated to how to use any particular feature in NOVA. Examples are provided +for your +better understanding. We have also provided a <> for your quick reference. Should you +face any problems, take a look at the <>. + +// end::about[] +// tag::notation[] + +[[Notation]] +== Notations (Terence) +In this guide, you'll be seeing the following markup being used. + +[NOTE] +This icon indicates details that could help you better understand how to use NOVA. + +[WARNING] +This icon indicates actions that could negatively affect your experience while using NOVA. + +`Commands` and `input fields` are highlighted. + +// end::notation[] + +[[QuickStart]] == Quick Start +Here are some steps to get you started: -. Ensure you have Java `11` or above installed in your Computer. -. Download the latest `addressbook.jar` link:{repoURL}/releases[here]. -. Copy the file to the folder you want to use as the home folder for your Address Book. +. Ensure you have Java *11* or above installed in your Computer. +. Download the latest *nova.jar* link:{repoURL}/releases[here]. +. Copy the file to the folder you want to use as the home folder for your NOVA. . Double-click the file to start the app. The GUI should appear in a few seconds. + -image::Ui.png[width="790"] +image::Ui.png[width="790" align="center"] +[.text-center] +_[.small]#Fig 4.1: GUI of NOVA#_ + -. Type the command in the command box and press kbd:[Enter] to execute it. + -e.g. typing *`help`* and pressing kbd:[Enter] will open the help window. -. Some example commands you can try: +. Type the command in the command box and press kbd:[Enter] to execute it. +. Try the following commands to get you warmed up: + +* *`nav ab`*: navigates to address book mode. + +[NOTE] +==== +You can use the following commands once you are in address book mode: * *`list`* : lists all contacts -* **`add`**`n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01` : adds a contact named `John Doe` to the Address Book. -* **`delete`**`3` : deletes the 3rd contact shown in the current list + +* `add n\John Doe p\98765432 e\\john@gmail.com c\classmate` : adds a contact named John Doe to the Address Book + and categorise as classmate +==== * *`exit`* : exits the app -. Refer to <> for details of each command. +Refer to <> for details of each command. [[Features]] == Features +Features are grouped together in modes of operation. There is a set of common commands which can be used in any mode, +and within every mode, there is also a set of commands for you to use and get things done. + +=== Common +NOVA offers a set of common functionalities across all modes. These commands can be executed within any mode in NOVA. + +==== Exit : `exit` +You can exit NOVA with this command. While exiting NOVA, contacts, schedules and notes will be saved. + +Format: + +`exit` +<> + +==== Navigation: `nav` +You can navigate to the desired mode to use its features. + +Format: + +`nav [home/ab/schedule/planner/progresstracker]` + +[NOTE] +`ab` refers to address book. + +Example: + +Suppose you want to use the schedule, + +enter the command: `nav schedule` + +NOVA will change the mode to schedule as seen below. + +image::userguide/schedule_nav.png[width="790" align="center"] +[.text-center] +_[.small]#Fig 5.1.2: GUI of NOVA after entering `nav schedule`#_ + +<> + +=== Address Book (Loh Sze Ying) +Learn how to work with the Address Book in NOVA. The address book feature allows you to keep in contact with +your teammates and classmates, and contacts are automatically sorted by alphabetical order. + + +You need to be in address book mode to use the features. Access this mode by entering the command `nav ab`. Your NOVA should look something +like Figure 5.2 below. + +image::userguide/addressbook.png[width="790" align="center"] +[.text-center] +_[.small]#Fig 5.2: GUI of NOVA after entering `nav ab`#_ + +==== Add Contact: `add` +You can add your classmate or teammate as contact. + +Format: `add n\[name] p\[phone number] e\[email address] c\[classmate/teammate]` + +[NOTE] +==== +* `[name]` is not case-sensitive. (E.g. "Jane doe" is the same as "jane Doe"). + +The name you add will be automatically formatted. (E.g. "jane doe" will become "Jane Doe") +* `[classmate/teammate]` is not case-sensitive. (E.g. "ClassMate" is the same as "classmate") +* You can only add either `classmate` or `teammate` as category ==== -*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`. -* 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 with `…`​ after them can be used multiple times including zero times e.g. `[t/TAG]...` can be used as `{nbsp}` (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. +Example: + +Suppose you want to add your classmate named Jane Doe, with phone number 12345678 and email address janedoe@gmail.com +into NOVA, + +enter the command: `add n\Jane Doe p\12345678 e\\janedoe@gmail.com c\classmate` + +NOVA will add a new contact named Jane Doe, phone number 12345678 and email address janedoe@gmail.com into the +classmate category and display a confirmation message as seen below. + +image::userguide/addressbook_add.PNG[width="690" align="center"] +[.text-center] +_[.small]#Fig 5.2.1: Display message after entering + +`add n\Jane Doe p\12345678 e\\janedoe@gmail.com c\classmate`#_ + +<> + +==== List all Contacts: `list` +You can list the contact's name, phone number and category of all contacts. + +Format: `list` + +<> + +==== List Category Contact: `list` +You can list the name and phone number of all the contacts under a specified category, be it classmate or teammate. + +Format: `list c\[classmate/teammate]` + +[NOTE] +==== +* `[classmate/teammate]` is not case-sensitive. (E.g. "ClasSMate" is the same as "classmate") +* There are only `classmate` and `teammate` categories ==== -=== Viewing help : `help` +Example: + +* Suppose you want to view all the classmate contacts that you have added into NOVA, ++ +enter the command: `list c\classmate` ++ +NOVA will lists all your contacts in the `classmate` category. + + + + +* Suppose you want to view all the teammate contacts that you have added into NOVA, ++ +enter the command: `list c\teammate` ++ +NOVA will lists all your contacts in the `teammate` category. -Format: `help` +<> -=== Adding a person: `add` +==== Find Contact: `find` +You can find a contact added to the address book easily, either with the full name or just with the person's first or the last name. -Adds a person to the address book + -Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]...` +Format: `find n\[name]` -[TIP] -A person can have any number of tags (including 0) +[NOTE] +==== +* `[name]` is not case-sensitive. (E.g. "Jane doe" is the same as "jane Doe") +==== -Examples: +Example: -* `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` +* Suppose you want to find Jane Doe within NOVA, ++ +enter the command: `find n\Jane doe` ++ +NOVA will find and list saved contacts named Jane Doe. -=== Listing all persons : `list` +* Or you can find Jane Doe with just Jane, ++ +enter the command: `find n\Jane` ++ +NOVA will find and list saved contacts named Jane. -Shows a list of all persons in the address book. + -Format: `list` +<> -=== Editing a person : `edit` +==== Edit Contact: `edit` +You can edit the contacts that you have added. If the contact you want to edit does not exist, NOVA will let you know. + -Edits an existing person in the address book. + -Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]...` +Format: `edit i\[index] n\[name] p\[phone number] e\[email address] c\[classmate/teammate]` -**** -* 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. -**** +[WARNING] +==== +The `[index]` will apply on the list of your most recent `list`, `list c\[classmate/teamate]` or `find` command. +Confirm the position of the contact in your most recent list to avoid editing other contact. +==== + +[NOTE] +==== +* At least one of the optional fields must be provided (`n\[name]`, `p\[phone number]`, +`e\[email address]` or `c\[classmate/teammate]`) +* The `[index]` must be a positive integer. (E.g. 1, 2, 3, ...) +* `[name]` and `[classmate/teammate]` are not case-sensitive. (E.g. "Jane doe" is the same as "jane Doe") +* The name you add will be automatically formatted. (E.g. "jane doe" will become "Jane Doe") +* You can only edit either `classmate` or `teammate` as category +==== + +Example: + +Suppose you want to edit the Alex Yeoh's phone number in your address book after using `list` as shown below, + +image::userguide/addressbook_list.PNG[width="690" align="center"] +[.text-center] +_[.small]#Fig 5.2.5.1: Example display message after entering `list`#_ + +enter the command: `edit i\1 p\88888888` + +NOVA will edit the phone number of Alex Yeoh to 88888888 as shown below. + +image::userguide/addressbook_edit.PNG[width="790" align="center"] +[.text-center] +_[.small]#Fig 5.2.5.2: Display message after entering `edit i\1 p\88888888`#_ -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. +==== Add, Edit or Delete Remark: `remark` +You can add remarks that are category specific, to a contact. -=== Locating persons by name: `find` +Format: `remark i\[index] r\[remark]` -Finds persons whose names contain any of the given keywords. + -Format: `find KEYWORD [MORE_KEYWORDS]` +[WARNING] +==== +The `[index]` will apply on the list of your most recent `list`, `list c\[classmate/teamate]` or `find` command. +Confirm the position of the contact in your most recent list to avoid editing other contact. +==== + +[NOTE] +==== +* The `[index]` must be a positive integer. (E.g. 1, 2, 3, ...) +* To remove any remarks, use `remark i\[index] r\` or `remark i\[index]` +==== -**** -* The search is case insensitive. e.g `hans` will match `Hans` -* The order of the keywords does not matter. e.g. `Hans Bo` will match `Bo Hans` -* Only the name is searched. -* Only full words will be matched e.g. `Han` will not match `Hans` -* Persons matching at least one keyword will be returned (i.e. `OR` search). e.g. `Hans Bo` will return `Hans Gruber`, `Bo Yang` -**** +Example: + +Suppose you want add remark to Bernice Yu in your address book after using `list` as seen in Fig 5.2.5.1, -Examples: +enter the command: `remark i\2 r\She\'s a nice teammate` -* `find John` + -Returns `john` and `John Doe` -* `find Betsy Tim John` + -Returns any person having names `Betsy`, `Tim`, or `John` +NOVA will add the remark "She's a nice teammate" to Bernice Yu in your address book as shown below. -// tag::delete[] -=== Deleting a person : `delete` +image::userguide/addressbook_remark.PNG[width="790" align="center"] +[.text-center] +_[.small]#Fig 5.2.6.2: Display message after entering `remark i\2 r\She's a nice teammate`#_ -Deletes the specified person from the address book. + -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, ... -**** +==== Delete Contact: `delete` +You can delete a contact that you have added. If the contact you try to delete does not exist, NOVA will let you know. -Examples: +Format: `delete i\[index]` -* `list` + -`delete 2` + -Deletes the 2nd person in the address book. -* `find Betsy` + -`delete 1` + -Deletes the 1st person in the results of the `find` command. +[WARNING] +==== +The `[index]` will apply on the list of your most recent `list`, `list c\[classmate/teamate]` or `find` command. +Confirm the position of the contact in your most recent list to avoid editing other contact. +==== + +[NOTE] +==== +* Deletes the contact at the specified `[index]` in the displayed contact list +* The `[index]` must be a positive integer. (E.g. 1, 2, 3, ...) +==== -// end::delete[] -=== Clearing all entries : `clear` +Example: + +Suppose you want to delete Alex Yeoh's contact after using the `list` as seen in Fig 5.2.5.1, + +enter the command: `delete i\1` + +NOVA will delete Alex Yeoh from your the address book. + +image::userguide/addressbook_delete.PNG[width="790" align="center"] +[.text-center] +_[.small]#Fig 5.2.7.2: Display message after entering `delete i\1`#_ + +<> + +==== Clear all Contacts: `clear` +You can clear all the contacts that you have added in your address book. -Clears all entries from the address book. + Format: `clear` -=== Exiting the program : `exit` +[WARNING] +Using `clear` command will **delete all** the contacts that you have saved. + +<> + +==== Undo Command: `undo` +You can undo a command that you have entered. If you cannot undo, NOVA will let you know. + +Format: `undo` + +<> + +==== Redo Command: `redo` + +You can redo undone commands. To use `redo`, you must first have used `undo`. If you cannot redo, NOVA will let you know. + +Format: `redo` + +[WARNING] +After you successfully entered a command (apart from undo) after an undo command, you will lose all undone commands. + +<> + +=== Schedule +Learn how to work with the schedule feature in NOVA. You can track important events by adding them into the schedule +and manage them easily. + +You need to be in schedule mode. Enter the schedule mode by entering the command `nav schedule`. +Your NOVA should look something like Figure 5.3 below. + +image::userguide/schedule_nav.png[width="790" align="center"] +[.text-center] +_[.small]#Figure 5.3: GUI of NOVA after user typed `nav schedule`#_ + +// tag::events[] + +==== Add a Meeting: `meeting`(Chua Huixian) +You can add a meeting as one of your events. +If there is already an event in the time slot, NOVA will inform you. + +Format: `meeting d\[description] v\[venue] t\[YYYY-MM-DD] [Start time (HH:MM)] [End time (HH:MM)]` + +[NOTE] +==== +* The `[End time (HH:MM)]` must be later than the `[Start time (HH:MM)]`. +* Events cannot span across more than a day (`00:00` is the start of a day, `23:59` is the end of a day). +* You can only add meetings to dates that fall within the semester. +* All prefixes (i.e. `d\`, `v\` and `t\`) must be present. +* `[description]` and/or `[venue]` can be left as blank spaces if you wish (i.e. " "). +==== + +Example: + +Suppose you wish to add a project meeting into your schedule, which is from 2pm to 3pm on 20 Feb 2020, + + +enter the command: `meeting d\CS2103T website set-up v\COM1 t\2020-02-20 14:00 15:00` + +NOVA will create an event for a team meeting at COM1 on 20 Feb 2020 to set up CS2103T website from 2pm to 3pm. + +image::userguide/EventMeeting.png[width="400" align="center"] +[.text-center] +_[.small]#Fig 5.3.1: Display message after entering + + `meeting d\CS2103T website set-up v\COM1 t\2020-02-20 14:00 15:00`#_ + +<> + +==== Add a Study Session: `study` (Chua Huixian) +You can add a study session as one of your events. +If there is already an event in the time slot, NOVA will inform you. + +Format: `study d\[description] v\[venue] t\[YYYY-MM-DD] [Start time (HH:MM)] [End time (HH:MM)]` + +[NOTE] +==== +* The `[End time (HH:MM)]` must be later than the `[Start time (HH:MM)]`. +* Events cannot span across more than a day (`00:00` is the start of a day, `23:59` is the end of a day). +* You can only add study sessions to dates that fall within the semester. +* All prefixes (i.e. `d\`, `v\` and `t\`) must be present. +* `[description]` and/or `[venue]` can be left as blank spaces if you wish (i.e. " "). +==== + +Example: + +Suppose you wish to add a group study session from 4pm to 5pm on 20 Feb 2020, + + +enter the command: `study d\cool peeps revision v\COM1 t\2020-02-20 16:00 17:00` + +NOVA will create an event for study session at COM1 on 20 Feb 2020 from 4pm to 5pm. + +image::userguide/EventStudy.png[width="400" align="center"] +[.text-center] +_[.small]#Fig 5.3.2: Display message after entering + + `study d\cool peeps revision v\COM1 t\2020-02-20 16:00 17:00`#_ + +<> + +==== Add a Consultation Session: `consultation` (Chua Huixian) +You can add a consultation session as one of your events. +If there is already an event in the time slot, NOVA will inform you. + +Format: `consultation d\[description] v\[venue] t\[YYYY-MM-DD] [Start time (HH:MM)] [End time (HH:MM)]` + +[NOTE] +==== +* The `[End time (HH:MM)]` must be later than the `[Start time (HH:MM)]`. +* Events cannot span across more than a day (`00:00` is the start of a day, `23:59` is the end of a day). +* You can only add consultation sessions to dates that fall within the semester. +* All prefixes (i.e. `d\`, `v\` and `t\`) must be present. +* `[description]` and/or `[venue]` can be left as blank spaces if you wish (i.e. " "). +==== + +Example: + +Suppose you wish to add a consultation session from 3pm to 4pm on 20 Feb 2020, + + +enter the command: `consultation d\clarify UML v\COM1 t\2020-02-20 15:00 16:00` + +NOVA will create an event for consultation at COM1 on 20 Feb 2020 to clarify UML from 3pm to 4pm. + +image::userguide/EventConsultation.png[width="400" align="center"] +[.text-center] +_[.small]#Fig 5.3.3: Display message after entering + + `consultation d\clarify UML v\COM1 t\2020-02-20 15:00 16:00`#_ + +<> + +==== Add a Lesson: `lesson` (Chua Huixian) +You can add weekly lessons as one of your events. +If there is already an event in the time slot, NOVA will inform you. + +Format: `lesson d\[description] v\[venue] t\[day] [Start time (HH:MM)] [End time (HH:MM)]` + +[NOTE] +==== +* The `[End time (HH:MM)]` must be later than the `[Start time (HH:MM)]`. +* Events cannot span across more than a day (`00:00` is the start of a day, `23:59` is the end of a day). +* The lesson will be repeated weekly from weeks 1 to 13. +* There will not be lessons in the recess week. +* All prefixes (i.e. `d\`, `v\` and `t\`) must be present. +* `[description]` and/or `[venue]` can be left as blank spaces if you wish (i.e. " "). +==== + +Example: + +Suppose you wish to add a weekly lesson from 3pm to 4pm on Friday, + + +enter the command: `lesson d\CS2103T tutorial v\COM1-B103 t\Friday 15:00 16:00` + +NOVA will create weekly events for CS2103T tutorial at COM1-B103 on Friday from 3pm to 4pm for the entire semester. + +image::userguide/EventLesson.png[width="400" align="center"] +[.text-center] +_[.small]#Fig 5.3.4: Display message after entering + + `lesson d\CS2103T tutorial v\COM1-B103 t\Friday 15:00 16:00`#_ + +<> + +==== Delete Event: `delete` (Chua Huixian) +You can delete an event that you no longer want. +If the event does not exist, NOVA will inform you. + +Format: `delete t\[YYYY-MM-DD] i\[index]` + +[NOTE] +==== +* `[index]` must be a positive integer. (E.g. 1, 2, 3, ...) +* All prefixes (i.e. `t\` and `i\`) must be present. +==== + +Example: + +Suppose you wish to remove the second event from the list of events on 20 Feb 2020, + + +enter the command: `delete t\2020-02-20 i\2` + +NOVA will delete the second event on 20 Feb 2020. + +image::userguide/EventDelete.png[width="400" align="center"] +[.text-center] +_[.small]#Fig 5.3.5: Display message after entering `delete t\2020-02-20 i\2`#_ + +<> + +==== Add Notes to Event: `note` (Chua Huixian) +You can add additional notes about an event. If the event does not exist, NOVA will inform you. + +Format: `note d\[description] t\[YYYY-MM-DD] i\[index]` + +[NOTE] +==== +* `[index]` must be a positive integer. (E.g. 1, 2, 3, ...) +* All prefixes (i.e. `d\`, `t\` and `i\`) must be present. +* `[description]` can be left as a blank space if you wish (i.e. " "). +==== + +Example: + +Suppose you wish to add a note that indicates the work allocation to a meeting event, + + +enter the command: `note d\Alice - Contact Us page, Bob - Readme Page t\2020-02-20 i\1` + +NOVA will add a note with the description "note d\Alice - Contact Us page, Bob - Readme Page t\2020-02-20 i\1" +to the first event on 20 Feb 2020. + +image::userguide/EventNote.png[width="400" align="center"] +[.text-center] + +_[.small]#Fig 5.3.6: Display message after entering + +`note d\Alice - Contact Us page, Bob - Readme Page t\2020-02-20 i\1`#_ + +// end::events[] + +<> -Exits the program. + -Format: `exit` +// tag::view[] +==== View Schedule of a Day: `view` (Terence) +You can view the schedule of a specified day. If there is nothing to do on that day, NOVA will tell you. -=== Saving the data +Format: + +`view t\[YYYY-MM-DD]` -Address book data are saved in the hard disk automatically after any command that changes the data. + -There is no need to save manually. +Example: + +Suppose you want to check out what events are happening on 20 Feb, + -// tag::dataencryption[] -=== Encrypting data files `[coming in v2.0]` +enter the command: `view t\2020-02-20` -_{explain how the user can enable/disable data encryption}_ -// end::dataencryption[] +NOVA will show your schedule on 20 Feb 2020 as seen below. +image::userguide/schedule_view_date.png[width="450" align="center"] +[.text-center] +_[.small]#Fig 5.3.7: Display message after entering `view t\2020-02-20`#_ + +<> + +==== View Schedule of a Week: `view` (Terence) +You can view the schedule of a specified week. If there is nothing to do on that week, NOVA will tell you. + +Format: + +`view week i\[week #]` + +[NOTE] +==== +* `[week #]` must be a positive integer. (E.g. 1, 2, 3, ...) +* Week number for special weeks are as such: +** Recess week is week 16 +** Reading week is week 14 +** Final examination week is week 15 +==== + +Example: + +Suppose you are interested in what is going to happen in week 6 of the semester, + + +enter the command: `view week i\6` + +NOVA will show your schedule in week 6 of the semester as seen below. + +image::userguide/schedule_view_week.png[width="450" align="center"] +[.text-center] +_[.small]#Fig 5.3.8: Display message after entering `view week i\6`#_ + +<> + +==== Find Free Slots: `freeslot` (Terence) +You can find free slots on a day easily within your schedule without looking through your schedule. + +Format: + +`freeslot t\[YYYY-MM-DD]` + +Example: + +Suppose you want to find pockets of free time on 20 Feb 2020, + + +enter the command: `freeslot t\2020-02-20` + +NOVA will show you your freeslots on 20 Feb 2020. + +<> + +// end::view[] +//tag::studyplanner[] +=== Daily Study Planner (Tan Wah Ken) +Learn how to work with the planner feature in NOVA. You can add daily/weeky tasks into your study plan and schedule it +on any day you like hassle-free. + +You need to be in planner mode. Enter the planner mode by entering the command +`nav planner`. + +image::userguide/planner.PNG[width="790" align="center"] +[.text-center] +_[.small]#Fig 5.4: GUI of NOVA after user typed contact `nav planner`#_ + +==== Create Routine Task: `routine` +You can create routine tasks in your study plan. When being scheduled, the tasks will lasts for 30 minutes weekly if +possible. + +Format: + +`routine p\[task name] f\[daily/weekly] d\[event duration in minutes]` + +[NOTE] +`[event duration in minutes]` must be between 0 and 1440 + +Example: + +Suppose you want to create a weekly routine task "read cs2103 textbook", + +enter the command: `routine p\read cs2103 textbook f\weekly d\30` + +NOVA will create a new task “read cs2103 textbook”. + +<> + +==== Create Flexible Day-Task: `flexible` +You can create flexible tasks in your study plan, which will create one event per day when being scheduled. When +being scheduled, these tasks will create events with duration as long as possible, from 10 minutes to 60 minutes. + +Format: + +`flexible p\[task name] t\[total minutes] mind\[maximum event duration in minutes] maxd\[maximum event duration in minutes]` + +[NOTE] +==== +* `[total minutes]` must be greater than `[minimum event duration in minutes]` +* `[maximum event duration in minutes]` must be greater or equal to `[minimum event duration in minutes]` +* `[minimum event duration in minutes]` and `[maximum event duration in minutes]` must be between 0 and 1440 +==== + +Example: + +Suppose you want to create a flexible task "study vocabulary", + + + +enter the command: `flexible p\study vocabulary t\100 mind\10 maxd\60` + +NOVA will create a new task “study vocabulary”. When being scheduled, this task will create one event “study vocabulary” +with duration as long as possible, from 10 minutes to 60 minutes. The total duration of all the event scheduled will +not exceed 100 minutes. + +<> + +==== Delete task: `delete` +You can delete a task, and all its related future events. + +Format: + +`delete p\[task name]` + +Example: + +Suppose you want to delete the task "study vocabulary", + +enter the command: `delete p\study vocabulary` + +NOVA will delete the task “study vocabulary” and all its related future events. + +<> + +==== View Statistics: `stats` +You can view the statistics of every tasks currently in study plan. The statistics are as follows: + +* For weekly routine task, statistics consists of: +** Number of weeks completed and incomplete since its creation +** All the events related to the task +* For daily routine task, statistics consists of: +** Number of days done and not done since its creation +** All the events related to the task + +* For flexible tasks, statistics consists of: +** Percentage done (Total duration of every event created / Total minutes) +** All the events related to the task + +Format: + +`stats` + +NOVA will display the statistics of all the tasks currently in study plan. + +<> + +==== Schedule Task: `schedule` +You can generate an event on a random spot on the specified day according to the requirements of the task if +possible. The event is generated on a random slot, you will need to manually modify the event generated if you wish. +If it is impossible to schedule an event for the task for that day, you will be notified. + +Format: + +`schedule p\[task name] t\[YYYY-MM-DD]` + + +Example: + +* Suppose you want to schedule the weekly routine task "read cs2103 textbook", ++ +enter the command: `schedule p\read cs2103 textbook t\2020-03-20` ++ +NOVA will finds a free slot bigger than 30 minutes as specified by the task, and creates an event “read cs2103 +textbook” that lasts for 30 minutes on a random spot within the free slot. + +* Suppose you want to schedule the flexible task "study vocabulary", ++ +enter the command: `schedule p\study vocabulary t\2020-03-20` + ++ +NOVA will finds a free slot bigger than 10 minutes as specified by the task, and creates an event “read cs2103 +textbook” on a random spot within the free slot. + +<> + +//end::studyplanner[] +//tag::ProgressTracker[] + +=== Progress Tracker (Yap Wen Jun Bryan) +Learn how to work with the progress tracker feature in NOVA. You can track important project tasks by adding them into the progress tracker +and manage them easily. + +You need to be in Progress Tracker mode. Enter the Progress Tracker mode by entering the +command `nav progresstracker`. Your NOVA should look something like Figure 5.5 below. + +image::userguide/progressTracker.png[width="790" align="center"] +[.text-center] +_[.small]#Fig 5.5: GUI of NOVA after typing command `nav progresstracker`#_ + +==== Add Project Task: `add` +You can add tasks under projects in the progress tracker. + +Format: + +`add p\[ip/tp] w\[week number] d\[task description]` + +[NOTE] +==== +* Only `ip` and `tp` projects are available and are not case-sensitive +* There are only weeks 1 to 13 in each project +* When multiple same tags are keyed, the last tag will be taken as the input +==== + +[WARNING] +==== +* `add` is case-sensitive +* `[week number]` must be a positive integer (E.g. 1, 2, 3, …) +* `[task description]` cannot be empty +==== + +Example: + +Suppose you want to add a task to week 3 of IP project to remind yourself to do up javaFx, + +enter the command: `add p\ip w\3 d\Do up javaFx` + +NOVA will add a task “Do up javaFx” to week 3 of IP in progress tracker. + +image::userguide/addPtTaskResult.png[width="790" align="center"] +[.text-center] +_[.small]#Fig 5.5.1: Display message after entering `add p\ip w\3 d\Do up javaFx`#_ + +<> + +==== View Project Task: `list` +You can view tasks under a certain week for projects in the progress tracker. + +Format: + +`list p\[ip/tp] w\[week number]` + +[NOTE] +==== +* Only `ip` and `tp` projects are available and are not case-sensitive +* There are only weeks 1 to 13 in each project +* When multiple same tags are keyed, the last tag will be taken as the input +==== + +[WARNING] +==== +* `list` is case-sensitive +* `[week number]` must be a positive integer (E.g. 1, 2, 3, …) +==== + +Example: + +Suppose you would like to view the tasks you have added to week 3 of IP project, + +enter the command: `list p\ip w\3` + +NOVA will list the tasks in week 3 of IP project. + +image::userguide/listPtTask.png[width="790" align="center"] +[.text-center] +_[.small]#Fig 5.5.2: Display message after entering `list p\ip w\3`#_ + +<> + +==== Delete Project Task: `delete` +You can remove tasks under projects in the progress tracker. + +Format: `delete p\[ip/tp] w\[week number] t\[task number]` + +[NOTE] +==== +* Only `ip` and `tp` projects are available and are not case-sensitive +* There are only weeks 1 to 13 in each project +* When multiple same tags are keyed, the last tag will be taken as the input +==== + +[WARNING] +==== +* `delete` is case-sensitive +* `[week number]` must be a positive integer (E.g. 1, 2, 3, …) +* `[task number]` must be a positive integer (E.g. 1, 2, 3, …) +==== + +Example: + +Suppose you wish to delete task number 1 in week 3 of IP project, + +enter the command: `delete p\ip w\3 t\1` + +NOVA will delete task 1 in week 3 of IP in progress tracker. + +image::userguide/deletePtTaskResult.png[width="790" align="center"] +[.text-center] +_[.small]#Fig 5.5.3: Display message after entering `delete p\ip w\3 t\1`#_ + +<> + +==== Complete Tasks: `done` +You can mark tasks in the progress tracker as done/ not done. + +Format: + +`done p\[ip/tp] w\[week number] t\[task number]` + +[NOTE] +==== +* Only `ip` and `tp` projects are available and are not case-sensitive +* There are only weeks 1 to 13 in each project +* When multiple same tags are keyed, the last tag will be taken as the input +* Calling `done` on a done task will set it to not done and vice versa +==== + +[WARNING] +==== +* `done` is case-sensitive +* `[week number]` must be a positive integer (E.g. 1, 2, 3, …) +* `[task number]` must be a positive integer (E.g. 1, 2, 3, …) +==== + +Example: + +Suppose you wish to set task number 1 in week 3 of IP project as done or undone, + +enter the command: `done p\ip w\3 t\1` + +NOVA will set task 1 in week 3 of IP in progress tracker as done/undone depending on it's status originally. + +image::userguide/setDonePtTaskResult.png[width="470" align="center"] +[.text-center] +_[.small]#Fig 5.5.4: Display message after entering `done p\ip w\3 t\1`#_ + +<> + +==== Add Notes: `addNote` +You can add notes to project tasks in the progress tracker. + +Format: + +`addNote p\[ip/tp] w\[week number] t\[task number] d\[note]` + +[NOTE] +==== +* Only `ip` and `tp` projects are available and are not case-sensitive +* There are only weeks 1 to 13 in each project +* When multiple same tags are keyed, the last tag will be taken as the input +==== + +[WARNING] +==== +* `addNote` is case-sensitive +* `[week number]` must be a positive integer (E.g. 1, 2, 3, …) +* `[task number]` must be a positive integer (E.g. 1, 2, 3, …) +* `[note]` cannot be empty +==== + +Example: + +Suppose you wish to add a note to remind yourself to create a branch and tag for task number 2 in week 3 of IP project, + +enter the command: `addNote p\ip w\3 t\1 d\Need create branch and tag` + +NOVA will add a note “Need create branch and tag” to task 1 in week 3 of IP. + +image::userguide/addPtNote.png[width="790" align="center"] +[.text-center] +_[.small]#Fig 5.5.5: Display message after entering `addNote p\ip w\3 t\1 d\Need create branch and tag`#_ + +<> + +==== Edit Note: `editNote` +You can edit the note that you have added to tasks in projects in the progress tracker. + +Format: + +`editNote p\[ip/tp] w\[week number] t\[task number] d\[new note]` + +[NOTE] +==== +* Only `ip` and `tp` projects are available and are not case-sensitive +* There are only weeks 1 to 13 in each project +* When multiple same tags are keyed, the last tag will be taken as the input. +==== + +[WARNING] +==== +* `editNote` is case-sensitive +* `[week number]` must be a positive integer (E.g. 1, 2, 3, …) +* `[task number]` must be a positive integer (E.g. 1, 2, 3, …) +* `[new note]` cannot be empty +==== + +Example: + +Suppose you wish to edit the note added in task 1 of week 3 in IP project, + +enter the command: `editNote p\ip w\3 t\1 d\consult tutor` + +NOVA will edit the note under task 1 of week 3 of IP. + +image::userguide/editPtNote.png[width="500" align="center"] +[.text-center] +_[.small]#Fig 5.5.6: Display message after entering `editNote p\ip w\3 t\1 d\consult tutor`#_ + +<> + +==== Delete Note: `deleteNote` +You can delete the note that you have added to tasks in projects in the progress tracker. + +Format: + +`deleteNote p\[ip/tp] w\[week number] t\[task number]` + +[NOTE] +==== +* Only `ip` and `tp` projects are available and are not case-sensitive +* There are only weeks 1 to 13 in each project +* When multiple same tags are keyed, the last tag will be taken as the input. +==== + +[WARNING] +==== +* `deleteNote` is case-sensitive +* `[week number]` must be a positive integer (E.g. 1, 2, 3, …) +* `[task number]` must be a positive integer (E.g. 1, 2, 3, …) +==== + +Example: + +Suppose you wish to delete the note added in task 1 of week 3 in IP project, + +enter the command: `deleteNote p\ip w\3 t\1` + +NOVA will delete the note under task 1 of week 3 of IP. + +image::userguide/deletePtNote.png[width="500" align="center"] +[.text-center] +_[.small]#Fig 5.5.7: Display message after entering `deleteNote p\ip w\3 t\1`#_ + +//end::ProgressTracker[] +<> + +[[Summary]] +== Command Summary (Terence and Bryan) + +[%autowidth,cols="<.^,<.^,<.^",options="header"] +|========================================================= +| Mode | Command | Description +.2+| Common +| exit | Exits NOVA +| nav [home/ ab/ schedule/ planner/ tracker] | Changes the mode of NOVA + +.10+| Address Book +| add n\[name] p\[phone number] e\[email address] c\[classmate/teammate] | Adds a contact into your address book +| clear | Deletes all contacts within your address book +| delete i\[index] | Deletes the contact at the specified index +| edit i\[index] n\[name] p\[phone number] e\[email address] c\[classmate/teammate] | Edits the contact at the +specified index to the specified fields +| find n\[name] | Finds a contact in your address book +| list | Lists all the contacts in your address book +| list c\[classmate/teammate] | Lists all the contacts of the specified category in your address book +| redo | Redoes an undone command +| remark i\[index] r\[remark] | Adds a category specific remark to the contact at the specified index +| undo | Undoes a command + +.9+| Schedule +| consultation d\[description] v\[venue] t\[YYYY-MM-DD] [Start time (HH:MM)] [End time (HH:MM)] | Adds a consultation +event to your schedule +| delete t\[YYYY-MM-DD] i\[index] | Deletes the event at the specified index on the specified day +| freeslot t\[YYYY-MM-DD] | Find pockets of free time you have on the specified day +| lesson d\[description] v\[venue] t\[day] [Start time (HH:MM)] [End time (HH:MM)] | Adds a lesson to your schedule +| meeting d\[description] v\[venue] t\[YYYY-MM-DD] [Start time (HH:MM)] [End time (HH:MM)] | Adds a meeting event to +your schedule +| note d\[description] t\[YYYY-MM-DD] i\[index] | Adds a note to the event at the specified index of the specified date +| study d\[description] v\[venue] t\[YYYY-MM-DD] [Start time (HH:MM)] [End time (HH:MM)] | Adds a study session event +into your schedule +| view t\[YYYY-MM-DD] | Shows the events happening on the specified date +| view week i\[week #] | Shows the events happening on the specified week + +.5+| Daily Study Planner +| delete p\[task name] | Deletes a task and all its events +| flexible | Creates a flexible task +| routine p\[task name] f\[daily/weekly] d\[event duration in minutes]| Creates a routine task +| schedule | Schedules a tasks in the schedule +| stats | Views the statistics of your planner + +.7+| Progress Tracker +| add p\[ip/tp] w\[week number] d\[task description] | Adds a task to the specified week and project +| list p\[ip/tp] w\[week number] | Lists the tasks that have been added to the specified week and project +| delete p\[ip/tp] w\[week number] t\[task number] | Deletes the specified task +| done p\[ip/tp] w\[week number] t\[task number] | Sets the status of the specified task to done/undone +| addNote p\[ip/tp] w\[week number] t\[task number] d\[note] | Adds a note to the specified task +| editNote p\[ip/tp] w\[week number] t\[task number] d\[new note] | Overwrites the existing note with the new note +| deleteNote p\[ip/tp] w\[week number] t\[task number] | Deletes the note in the specified task +|========================================================= + +<> + +[[FAQ]] == FAQ *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 Address Book folder. - -== Command Summary - -* *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` +*A*: You can install the app on the other computer and overwrite the empty data file it creates with the file that +contains the data of your previous NOVA folder. + +<> diff --git a/docs/UsingPlantUml.adoc b/docs/UsingPlantUml.adoc index cfe2533ea84..f7fbbb51d64 100644 --- a/docs/UsingPlantUml.adoc +++ b/docs/UsingPlantUml.adoc @@ -88,7 +88,7 @@ For example, `skinparam backgroundColor transparent` turns the background of the For a comprehensive list of ``skinparam``s head over to the https://plantuml-documentation.readthedocs.io/en/latest/[unofficial PlantUML skinparam documentation]. -*** +''' === Repositioning elements in PlantUML diagrams diff --git a/docs/diagrams/AbDeleteActivityDiagram.puml b/docs/diagrams/AbDeleteActivityDiagram.puml new file mode 100644 index 00000000000..8085782de6a --- /dev/null +++ b/docs/diagrams/AbDeleteActivityDiagram.puml @@ -0,0 +1,20 @@ +@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 format is valid]) + :NOVA parses the index; + if () then ([index is valid]) + :NOVA executes delete command; + :The person on the given index is deleted; + else ([else]) + :NOVA shows error; + endif +else ([else]) + :NOVA shows error; +endif +stop +@enduml diff --git a/docs/diagrams/AbDeleteSequenceDiagram.puml b/docs/diagrams/AbDeleteSequenceDiagram.puml new file mode 100644 index 00000000000..6658cd9d8fa --- /dev/null +++ b/docs/diagrams/AbDeleteSequenceDiagram.puml @@ -0,0 +1,93 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":LogicParser" as LogicParser LOGIC_COLOR +participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":AbDeleteCommandParser" as AbDeleteCommandParser LOGIC_COLOR +participant ":AbDeleteCommand" as AbDeleteCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +participant ":Mode" as Mode MODEL_COLOR +end box + +[-> LogicManager : execute("delete i\1") +activate LogicManager + +LogicManager -> LogicParser : parseCommand("delete i\1") +activate LogicParser + +LogicParser -> Model : getMode() +activate Model + +Model --> LogicParser : mode + +LogicParser -> Model : getModeEnum() + +Model -> Mode : getModeEnum() +activate Mode + +Mode --> Model : modeEnum +deactivate Mode + +Model --> LogicParser : modeEnum +deactivate Model + +LogicParser -> AddressBookParser : parseCommand("delete", "i\1") +activate AddressBookParser + +create AbDeleteCommandParser +AddressBookParser -> AbDeleteCommandParser +activate AbDeleteCommandParser + +AbDeleteCommandParser --> AddressBookParser +deactivate AbDeleteCommandParser + +AddressBookParser -> AbDeleteCommandParser : parse("i\1") +activate AbDeleteCommandParser + +create AbDeleteCommand +AbDeleteCommandParser -> AbDeleteCommand +activate AbDeleteCommand + +AbDeleteCommand --> AbDeleteCommandParser : command +deactivate AbDeleteCommand + +AbDeleteCommandParser --> AddressBookParser : command +deactivate AbDeleteCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +AbDeleteCommandParser -[hidden]-> AddressBookParser +destroy AbDeleteCommandParser + +AddressBookParser --> LogicParser : command +deactivate AddressBookParser + +LogicParser --> LogicManager : command +deactivate LogicParser + +LogicManager -> AbDeleteCommand : execute() +activate AbDeleteCommand + +AbDeleteCommand -> Model : deletePerson(1) +activate Model + +Model --> AbDeleteCommand +deactivate Model + +create CommandResult +AbDeleteCommand -> CommandResult +activate CommandResult + +CommandResult --> AbDeleteCommand +deactivate CommandResult + +AbDeleteCommand --> LogicManager : result +deactivate AbDeleteCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/ArchitectureSequenceDiagram.puml b/docs/diagrams/ArchitectureSequenceDiagram.puml index d1e2ae93675..55cf5ec69d7 100644 --- a/docs/diagrams/ArchitectureSequenceDiagram.puml +++ b/docs/diagrams/ArchitectureSequenceDiagram.puml @@ -7,10 +7,10 @@ Participant ":Logic" as logic LOGIC_COLOR Participant ":Model" as model MODEL_COLOR Participant ":Storage" as storage STORAGE_COLOR -user -[USER_COLOR]> ui : "delete 1" +user -[USER_COLOR]> ui : "delete i\1" activate ui UI_COLOR -ui -[UI_COLOR]> logic : execute("delete 1") +ui -[UI_COLOR]> logic : execute("delete i\1") activate logic LOGIC_COLOR logic -[LOGIC_COLOR]> model : deletePerson(p) @@ -19,7 +19,7 @@ activate model MODEL_COLOR model -[MODEL_COLOR]-> logic deactivate model -logic -[LOGIC_COLOR]> storage : saveAddressBook(addressBook) +logic -[LOGIC_COLOR]> storage : saveNova(nova) activate storage STORAGE_COLOR storage -[STORAGE_COLOR]> storage : Save to file diff --git a/docs/diagrams/PlannerScheduleTaskActivityDiagram.puml b/docs/diagrams/PlannerScheduleTaskActivityDiagram.puml new file mode 100644 index 00000000000..1c47794f4a4 --- /dev/null +++ b/docs/diagrams/PlannerScheduleTaskActivityDiagram.puml @@ -0,0 +1,21 @@ +@startuml +start +:User executes schedule task command; +if () then ([task exist in plan]) + if () then ([date is in schedule]) + :Get free slot for the day; + if () then ([task is schedulable within given free slot]) + :Task generates an event based on the + free slots and task requirement; + :Task adds the new event into schedule; + else ([else]) + :Ui shows error message; + endif + else ([date exceed schedule range]) + :Ui shows error message; + endif +else ([else]) + :Ui shows error message; +endif +stop +@enduml diff --git a/docs/diagrams/PlannerScheduleTaskSequenceDiagram.puml b/docs/diagrams/PlannerScheduleTaskSequenceDiagram.puml new file mode 100644 index 00000000000..aed93a102ed --- /dev/null +++ b/docs/diagrams/PlannerScheduleTaskSequenceDiagram.puml @@ -0,0 +1,98 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":LogicParser" as LogicParser LOGIC_COLOR +participant ":PlannerParser" as PlannerParser LOGIC_COLOR +participant ":ScheduleTaskCommandParser" as ScheduleTaskCommandParser LOGIC_COLOR +participant ":PlannerScheduleTaskCommand" as PlannerScheduleTaskCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +participant ":Mode" as Mode MODEL_COLOR +participant ":Plan" as Plan MODEL_COLOR +end box +[-> LogicManager : execute("schedule p\\task t\2020-02-20") +activate LogicManager + +LogicManager -> LogicParser : parseCommand("schedule p\\task t\2020-02-20") +activate LogicParser + +LogicParser -> Model :getMode() +activate Model + +Model --> LogicParser + +LogicParser -> Model :getModeEnum(mode) + +Model -> Mode: getModeEnum() +activate Mode + +Mode --> Model +deactivate Mode + +Model --> LogicParser +deactivate Model + +LogicParser -> PlannerParser : parseCommand("schedule p\\task t\2020-02-20") +activate PlannerParser + +create ScheduleTaskCommandParser +PlannerParser -> ScheduleTaskCommandParser +activate ScheduleTaskCommandParser + +ScheduleTaskCommandParser --> PlannerParser +deactivate ScheduleTaskCommandParser + +PlannerParser -> ScheduleTaskCommandParser: parse("p\\task t\2020-02-20") +activate ScheduleTaskCommandParser + +create PlannerScheduleTaskCommand +ScheduleTaskCommandParser -> PlannerScheduleTaskCommand +activate PlannerScheduleTaskCommand + +PlannerScheduleTaskCommand --> ScheduleTaskCommandParser: command +deactivate PlannerScheduleTaskCommand + +ScheduleTaskCommandParser --> PlannerParser: command +deactivate ScheduleTaskCommandParser +ScheduleTaskCommandParser -[hidden]-> PlannerParser: command +destroy ScheduleTaskCommandParser + +PlannerParser --> LogicParser: command +deactivate PlannerParser + +LogicParser --> LogicManager: command +deactivate LogicParser + +LogicManager -> PlannerScheduleTaskCommand : execute() +activate PlannerScheduleTaskCommand + +PlannerScheduleTaskCommand -> Model : generateTaskEvent(task, 2020-02-20) +activate Model + +Model -> Plan : generateTaskEvent(task, 2020-02-20, schedule) +activate Plan + +Plan --> Model +deactivate Plan + +Model --> PlannerScheduleTaskCommand +deactivate Model + +create CommandResult +PlannerScheduleTaskCommand -> CommandResult +activate CommandResult + +CommandResult --> PlannerScheduleTaskCommand +deactivate CommandResult + +PlannerScheduleTaskCommand --> LogicManager: result +deactivate PlannerScheduleTaskCommand + +[<--LogicManager: result +deactivate LogicManager +@enduml diff --git a/docs/diagrams/ScViewFreeSlotDiagram.puml b/docs/diagrams/ScViewFreeSlotDiagram.puml new file mode 100644 index 00000000000..5bc683c17a9 --- /dev/null +++ b/docs/diagrams/ScViewFreeSlotDiagram.puml @@ -0,0 +1,91 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":LogicParser" as LogicParser LOGIC_COLOR +participant ":ScheduleParser" as ScheduleParser LOGIC_COLOR +participant ":ScViewFreeSlotCommandParser" as ScViewFreeSlotCommandParser LOGIC_COLOR +participant ":ScViewFreeSlotCommand" as ScViewFreeSlotCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +participant ":Mode" as Mode MODEL_COLOR +end box +[-> LogicManager : execute("freeslot t\2020-02-20") +activate LogicManager + +LogicManager -> LogicParser : parseCommand("freeslot t\2020-02-20") +activate LogicParser + +LogicParser -> Model :getMode() +activate Model + +Model --> LogicParser + +LogicParser -> Model :getModeEnum(mode) + +Model -> Mode: getModeEnum() +activate Mode + +Mode --> Model +deactivate Mode + +Model --> LogicParser +deactivate Model + +LogicParser -> ScheduleParser : parseCommand("freeslot t\2020-02-20") +activate ScheduleParser + +create ScViewFreeSlotCommandParser +ScheduleParser -> ScViewFreeSlotCommandParser +activate ScViewFreeSlotCommandParser + +ScViewFreeSlotCommandParser --> ScheduleParser +deactivate ScViewFreeSlotCommandParser + +ScheduleParser -> ScViewFreeSlotCommandParser: parse("t\2020-02-20") +activate ScViewFreeSlotCommandParser + +create ScViewFreeSlotCommand +ScViewFreeSlotCommandParser -> ScViewFreeSlotCommand +activate ScViewFreeSlotCommand + +ScViewFreeSlotCommand --> ScViewFreeSlotCommandParser: command +deactivate ScViewFreeSlotCommand + +ScViewFreeSlotCommandParser --> ScheduleParser: command +deactivate ScViewFreeSlotCommandParser +ScViewFreeSlotCommandParser -[hidden]-> ScheduleParser: command +destroy ScViewFreeSlotCommandParser + +ScheduleParser --> LogicParser: command +deactivate ScheduleParser + +LogicParser --> LogicManager: command +deactivate LogicParser + +LogicManager -> ScViewFreeSlotCommand : execute() +activate ScViewFreeSlotCommand + +ScViewFreeSlotCommand -> Model : viewFreeSlot(2020-02-20) +activate Model + +Model --> ScViewFreeSlotCommand +deactivate Model + +create CommandResult +ScViewFreeSlotCommand -> CommandResult +activate CommandResult + +CommandResult --> ScViewFreeSlotCommand +deactivate CommandResult + +ScViewFreeSlotCommand --> LogicManager: result +deactivate ScViewFreeSlotCommand + +[<--LogicManager: result +deactivate LogicManager +@enduml diff --git a/docs/diagrams/StorageClassDiagram.puml b/docs/diagrams/StorageClassDiagram.puml index 6adb2e156bf..18dc8092fdb 100644 --- a/docs/diagrams/StorageClassDiagram.puml +++ b/docs/diagrams/StorageClassDiagram.puml @@ -6,19 +6,29 @@ skinparam classBackgroundColor STORAGE_COLOR Interface Storage <> Interface UserPrefsStorage <> -Interface AddressBookStorage <> +Interface NovaStorage <> Class StorageManager Class JsonUserPrefsStorage -Class JsonAddressBookStorage +Class JsonNovaStorage +Class JsonUtil +Class JsonSerializableNova -StorageManager .left.|> Storage +StorageManager .up.|> Storage StorageManager o-right-> UserPrefsStorage -StorageManager o--> AddressBookStorage +StorageManager o-left-> NovaStorage -JsonUserPrefsStorage .left.|> UserPrefsStorage -JsonAddressBookStorage .left.|> AddressBookStorage -JsonAddressBookStorage .down.> JsonSerializableAddressBookStorage -JsonSerializableAddressBookStorage .right.> JsonSerializablePerson -JsonSerializablePerson .right.> JsonAdaptedTag +JsonUserPrefsStorage .up.|>UserPrefsStorage +JsonUserPrefsStorage -left- JsonUtil: uses > +JsonNovaStorage .up.|> NovaStorage +JsonNovaStorage -right- JsonUtil: uses > +JsonNovaStorage .down.> JsonSerializableNova + + +JsonSerializableNova o-right-> JsonAdaptedPerson +JsonSerializableNova o-right-> JsonAdaptedPtTask +JsonSerializableNova o-down-> JsonAdaptedPlannerTask +JsonAdaptedPlannerTask o-right-> JsonAdaptedEvent +JsonSerializableNova o-down-> JsonAdaptedEvent +JsonAdaptedPerson o-right-> JsonAdaptedTag @enduml diff --git a/docs/diagrams/UndoRedoState1.puml b/docs/diagrams/UndoRedoState1.puml index 01fcb9b2b96..ac3e9cdda1a 100644 --- a/docs/diagrams/UndoRedoState1.puml +++ b/docs/diagrams/UndoRedoState1.puml @@ -3,7 +3,7 @@ skinparam ClassFontColor #000000 skinparam ClassBorderColor #000000 -title After command "delete 5" +title After command "delete i\5" package States <> { class State1 as "__ab0:AddressBook__" diff --git a/docs/diagrams/UndoRedoState2.puml b/docs/diagrams/UndoRedoState2.puml index bccc230a5d1..32e732cffe7 100644 --- a/docs/diagrams/UndoRedoState2.puml +++ b/docs/diagrams/UndoRedoState2.puml @@ -3,7 +3,7 @@ skinparam ClassFontColor #000000 skinparam ClassBorderColor #000000 -title After command "add n/David" +title After command "add n\David" package States <> { class State1 as "__ab0:AddressBook__" diff --git a/docs/diagrams/UndoSequenceDiagram.puml b/docs/diagrams/UndoSequenceDiagram.puml index 410aab4e412..00cf470b4a0 100644 --- a/docs/diagrams/UndoSequenceDiagram.puml +++ b/docs/diagrams/UndoSequenceDiagram.puml @@ -3,34 +3,60 @@ box Logic LOGIC_COLOR_T1 participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":LogicParser" as LogicParser LOGIC_COLOR participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR -participant "u:UndoCommand" as UndoCommand LOGIC_COLOR +participant "u:AbUndoCommand" as AbUndoCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR end box box Model MODEL_COLOR_T1 participant ":Model" as Model MODEL_COLOR +participant ":Mode" as Mode MODEL_COLOR participant ":VersionedAddressBook" as VersionedAddressBook MODEL_COLOR end box -[-> LogicManager : execute(undo) + +[-> LogicManager : execute("undo") activate LogicManager -LogicManager -> AddressBookParser : parseCommand(undo) +LogicManager -> LogicParser : parseCommand("undo") +activate LogicParser + +LogicParser -> Model : getMode() +activate Model + +Model --> LogicParser : mode + +LogicParser -> Model : getModeEnum() + +Model -> Mode : getModeEnum() +activate Mode + +Mode --> Model : modeEnum +deactivate Mode + +Model --> LogicParser : modeEnum +deactivate Model + +LogicParser -> AddressBookParser : parseCommand("undo", "") activate AddressBookParser -create UndoCommand -AddressBookParser -> UndoCommand -activate UndoCommand +create AbUndoCommand +AddressBookParser -> AbUndoCommand +activate AbUndoCommand -UndoCommand --> AddressBookParser -deactivate UndoCommand +AbUndoCommand --> AddressBookParser +deactivate AbUndoCommand -AddressBookParser --> LogicManager : u +AddressBookParser --> LogicParser : u deactivate AddressBookParser -LogicManager -> UndoCommand : execute() -activate UndoCommand +LogicParser --> LogicManager : u +deactivate LogicParser -UndoCommand -> Model : undoAddressBook() +LogicManager -> AbUndoCommand : execute() +activate AbUndoCommand + +AbUndoCommand -> Model : undoAddressBook() activate Model Model -> VersionedAddressBook : undo() @@ -40,13 +66,20 @@ VersionedAddressBook -> VersionedAddressBook :resetData(ReadOnlyAddressBook) VersionedAddressBook --> Model : deactivate VersionedAddressBook -Model --> UndoCommand +Model --> AbUndoCommand deactivate Model -UndoCommand --> LogicManager : result -deactivate UndoCommand -UndoCommand -[hidden]-> LogicManager : result -destroy UndoCommand +create CommandResult +AbUndoCommand -> CommandResult +activate CommandResult + +CommandResult --> AbUndoCommand +deactivate CommandResult + +AbUndoCommand --> LogicManager : result +deactivate AbUndoCommand +AbUndoCommand -[hidden]-> LogicManager : result +destroy AbUndoCommand [<--LogicManager deactivate LogicManager diff --git a/docs/diagrams/ViewWeekActivityDiagram.puml b/docs/diagrams/ViewWeekActivityDiagram.puml new file mode 100644 index 00000000000..705f10fcb33 --- /dev/null +++ b/docs/diagrams/ViewWeekActivityDiagram.puml @@ -0,0 +1,39 @@ +@startuml +start +:User executes command; + +if () then ([mode is schedule]) + : ScheduleParser + parse command; + if () then ([commandword is view]) + : ScViewCommandParser parse command; + if() then ([only one tag]) + if () then ([preamble is week]) + if () then ([tag is index]) + : Creates view + week command; + else ([else]) + : Invalid command format; + endif + else ([else]) + if () then ([tag is date]) + : Creates view + day command; + else ([else]) + : Invalid command format; + endif + endif + else ([else]) + : Invalid command format; + endif + else ([else]) + : Details + omitted; + endif + +else ([else]) + :Details + omitted; +endif +stop +@enduml diff --git a/docs/images/AbDeleteActivityDiagram.png b/docs/images/AbDeleteActivityDiagram.png new file mode 100644 index 00000000000..ff262548d68 Binary files /dev/null and b/docs/images/AbDeleteActivityDiagram.png differ diff --git a/docs/images/AbDeleteSequenceDiagram.png b/docs/images/AbDeleteSequenceDiagram.png new file mode 100644 index 00000000000..7379903de27 Binary files /dev/null and b/docs/images/AbDeleteSequenceDiagram.png differ diff --git a/docs/images/ArchitectureSequenceDiagram.png b/docs/images/ArchitectureSequenceDiagram.png index aa198138f8f..02fd6b2ecb8 100644 Binary files a/docs/images/ArchitectureSequenceDiagram.png and b/docs/images/ArchitectureSequenceDiagram.png differ diff --git a/docs/images/EventDeleteActDiagram.png b/docs/images/EventDeleteActDiagram.png new file mode 100644 index 00000000000..9b8e429da40 Binary files /dev/null and b/docs/images/EventDeleteActDiagram.png differ diff --git a/docs/images/EventDeleteSeqDiagram.png b/docs/images/EventDeleteSeqDiagram.png new file mode 100644 index 00000000000..24023cfae2f Binary files /dev/null and b/docs/images/EventDeleteSeqDiagram.png differ diff --git a/docs/images/GUI_ProgressTracker.png b/docs/images/GUI_ProgressTracker.png new file mode 100644 index 00000000000..a2cc05b9f52 Binary files /dev/null and b/docs/images/GUI_ProgressTracker.png differ diff --git a/docs/images/ListPtActivityDiagram.png b/docs/images/ListPtActivityDiagram.png new file mode 100644 index 00000000000..4b70e5bd95b Binary files /dev/null and b/docs/images/ListPtActivityDiagram.png differ diff --git a/docs/images/LogicArchitecture.png b/docs/images/LogicArchitecture.png new file mode 100644 index 00000000000..f9a44f06fad Binary files /dev/null and b/docs/images/LogicArchitecture.png differ diff --git a/docs/images/LogicClassDiagram.png b/docs/images/LogicClassDiagram.png index b9e853cef12..fb4b0b5f078 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 280064118cf..5cf00dd058b 100644 Binary files a/docs/images/ModelClassDiagram.png and b/docs/images/ModelClassDiagram.png differ diff --git a/docs/images/PlannerScheduleTaskActivityDiagram.png b/docs/images/PlannerScheduleTaskActivityDiagram.png new file mode 100644 index 00000000000..fec129908c7 Binary files /dev/null and b/docs/images/PlannerScheduleTaskActivityDiagram.png differ diff --git a/docs/images/PlannerScheduleTaskSequenceDiagram.png b/docs/images/PlannerScheduleTaskSequenceDiagram.png new file mode 100644 index 00000000000..eaac9d40fd9 Binary files /dev/null and b/docs/images/PlannerScheduleTaskSequenceDiagram.png differ diff --git a/docs/images/PtSeqDiagram.png b/docs/images/PtSeqDiagram.png new file mode 100644 index 00000000000..d47009824f8 Binary files /dev/null and b/docs/images/PtSeqDiagram.png differ diff --git a/docs/images/ScViewFreeSlotDiagram.png b/docs/images/ScViewFreeSlotDiagram.png new file mode 100644 index 00000000000..6c5d37e2e6a Binary files /dev/null and b/docs/images/ScViewFreeSlotDiagram.png differ diff --git a/docs/images/StorageClassDiagram.png b/docs/images/StorageClassDiagram.png index d87c1216820..7212b4aa906 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..c6ecc8d4aca 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 7b4b3dbea45..b1731f8a3c3 100644 Binary files a/docs/images/UiClassDiagram.png and b/docs/images/UiClassDiagram.png differ diff --git a/docs/images/UndoRedoState1.png b/docs/images/UndoRedoState1.png index df9908d0948..b1b2e7f2357 100644 Binary files a/docs/images/UndoRedoState1.png and b/docs/images/UndoRedoState1.png differ diff --git a/docs/images/UndoRedoState1edited.png b/docs/images/UndoRedoState1edited.png new file mode 100755 index 00000000000..14ffcf85776 Binary files /dev/null and b/docs/images/UndoRedoState1edited.png differ diff --git a/docs/images/UndoRedoState2.png b/docs/images/UndoRedoState2.png index 36519c1015b..7f072c032c0 100644 Binary files a/docs/images/UndoRedoState2.png and b/docs/images/UndoRedoState2.png differ diff --git a/docs/images/UndoRedoState2edited.png b/docs/images/UndoRedoState2edited.png new file mode 100755 index 00000000000..290f3d17844 Binary files /dev/null and b/docs/images/UndoRedoState2edited.png differ diff --git a/docs/images/UndoSequenceDiagram.png b/docs/images/UndoSequenceDiagram.png index 6addcd3a8d9..2cae189a5e2 100644 Binary files a/docs/images/UndoSequenceDiagram.png and b/docs/images/UndoSequenceDiagram.png differ diff --git a/docs/images/ViewCommandActivityDiagram.png b/docs/images/ViewCommandActivityDiagram.png new file mode 100644 index 00000000000..25fe7f1651c Binary files /dev/null and b/docs/images/ViewCommandActivityDiagram.png differ diff --git a/docs/images/bryanyap972.png b/docs/images/bryanyap972.png new file mode 100644 index 00000000000..9254b5fba41 Binary files /dev/null and b/docs/images/bryanyap972.png differ diff --git a/docs/images/huixianc.png b/docs/images/huixianc.png new file mode 100644 index 00000000000..a3b8c225791 Binary files /dev/null and b/docs/images/huixianc.png differ diff --git a/docs/images/lohszeying.png b/docs/images/lohszeying.png new file mode 100644 index 00000000000..15429dcad23 Binary files /dev/null and b/docs/images/lohszeying.png differ diff --git a/docs/images/mightyrabbit99.png b/docs/images/mightyrabbit99.png new file mode 100644 index 00000000000..20b0f0d82de Binary files /dev/null and b/docs/images/mightyrabbit99.png differ diff --git a/docs/images/userguide/EventConsultation.png b/docs/images/userguide/EventConsultation.png new file mode 100644 index 00000000000..dac7b211d3a Binary files /dev/null and b/docs/images/userguide/EventConsultation.png differ diff --git a/docs/images/userguide/EventDelete.png b/docs/images/userguide/EventDelete.png new file mode 100644 index 00000000000..a889cd0efaf Binary files /dev/null and b/docs/images/userguide/EventDelete.png differ diff --git a/docs/images/userguide/EventLesson.png b/docs/images/userguide/EventLesson.png new file mode 100644 index 00000000000..f8b4830de2e Binary files /dev/null and b/docs/images/userguide/EventLesson.png differ diff --git a/docs/images/userguide/EventMeeting.png b/docs/images/userguide/EventMeeting.png new file mode 100644 index 00000000000..c8149c54d8a Binary files /dev/null and b/docs/images/userguide/EventMeeting.png differ diff --git a/docs/images/userguide/EventNote.png b/docs/images/userguide/EventNote.png new file mode 100644 index 00000000000..45ce4a814f9 Binary files /dev/null and b/docs/images/userguide/EventNote.png differ diff --git a/docs/images/userguide/EventStudy.png b/docs/images/userguide/EventStudy.png new file mode 100644 index 00000000000..0c8cf16fae2 Binary files /dev/null and b/docs/images/userguide/EventStudy.png differ diff --git a/docs/images/userguide/addPtNote.png b/docs/images/userguide/addPtNote.png new file mode 100644 index 00000000000..2190b0579e2 Binary files /dev/null and b/docs/images/userguide/addPtNote.png differ diff --git a/docs/images/userguide/addPtTaskResult.png b/docs/images/userguide/addPtTaskResult.png new file mode 100644 index 00000000000..6b8f49f626a Binary files /dev/null and b/docs/images/userguide/addPtTaskResult.png differ diff --git a/docs/images/userguide/addressbook.png b/docs/images/userguide/addressbook.png new file mode 100644 index 00000000000..377f0083814 Binary files /dev/null and b/docs/images/userguide/addressbook.png differ diff --git a/docs/images/userguide/addressbook_add.PNG b/docs/images/userguide/addressbook_add.PNG new file mode 100644 index 00000000000..d4e9f53a25f Binary files /dev/null and b/docs/images/userguide/addressbook_add.PNG differ diff --git a/docs/images/userguide/addressbook_delete.PNG b/docs/images/userguide/addressbook_delete.PNG new file mode 100644 index 00000000000..605d5f490c4 Binary files /dev/null and b/docs/images/userguide/addressbook_delete.PNG differ diff --git a/docs/images/userguide/addressbook_edit.PNG b/docs/images/userguide/addressbook_edit.PNG new file mode 100644 index 00000000000..3ae931c7be2 Binary files /dev/null and b/docs/images/userguide/addressbook_edit.PNG differ diff --git a/docs/images/userguide/addressbook_list.PNG b/docs/images/userguide/addressbook_list.PNG new file mode 100644 index 00000000000..1e2d3b61839 Binary files /dev/null and b/docs/images/userguide/addressbook_list.PNG differ diff --git a/docs/images/userguide/addressbook_remark.PNG b/docs/images/userguide/addressbook_remark.PNG new file mode 100644 index 00000000000..5324d4bbaea Binary files /dev/null and b/docs/images/userguide/addressbook_remark.PNG differ diff --git a/docs/images/userguide/addressbooknew.png b/docs/images/userguide/addressbooknew.png new file mode 100644 index 00000000000..c963a8a2bc3 Binary files /dev/null and b/docs/images/userguide/addressbooknew.png differ diff --git a/docs/images/userguide/deletePtNote.png b/docs/images/userguide/deletePtNote.png new file mode 100644 index 00000000000..442c558bad9 Binary files /dev/null and b/docs/images/userguide/deletePtNote.png differ diff --git a/docs/images/userguide/deletePtTaskResult.png b/docs/images/userguide/deletePtTaskResult.png new file mode 100644 index 00000000000..428031fee44 Binary files /dev/null and b/docs/images/userguide/deletePtTaskResult.png differ diff --git a/docs/images/userguide/editPtNote.png b/docs/images/userguide/editPtNote.png new file mode 100644 index 00000000000..85218855064 Binary files /dev/null and b/docs/images/userguide/editPtNote.png differ diff --git a/docs/images/userguide/guiComponents.png b/docs/images/userguide/guiComponents.png new file mode 100644 index 00000000000..48f15a085d9 Binary files /dev/null and b/docs/images/userguide/guiComponents.png differ diff --git a/docs/images/userguide/listPtTask.png b/docs/images/userguide/listPtTask.png new file mode 100644 index 00000000000..f6a691e28fa Binary files /dev/null and b/docs/images/userguide/listPtTask.png differ diff --git a/docs/images/userguide/planner.PNG b/docs/images/userguide/planner.PNG new file mode 100644 index 00000000000..a437d4c3370 Binary files /dev/null and b/docs/images/userguide/planner.PNG differ diff --git a/docs/images/userguide/progresstracker.png b/docs/images/userguide/progresstracker.png new file mode 100644 index 00000000000..54bf1dea3bd Binary files /dev/null and b/docs/images/userguide/progresstracker.png differ diff --git a/docs/images/userguide/schedule_nav.png b/docs/images/userguide/schedule_nav.png new file mode 100644 index 00000000000..b1b02e51ae0 Binary files /dev/null and b/docs/images/userguide/schedule_nav.png differ diff --git a/docs/images/userguide/schedule_view_date.png b/docs/images/userguide/schedule_view_date.png new file mode 100644 index 00000000000..1756df1a6e6 Binary files /dev/null and b/docs/images/userguide/schedule_view_date.png differ diff --git a/docs/images/userguide/schedule_view_week.png b/docs/images/userguide/schedule_view_week.png new file mode 100644 index 00000000000..4eb620f038c Binary files /dev/null and b/docs/images/userguide/schedule_view_week.png differ diff --git a/docs/images/userguide/setDonePtTaskResult.png b/docs/images/userguide/setDonePtTaskResult.png new file mode 100644 index 00000000000..a6c240f3336 Binary files /dev/null and b/docs/images/userguide/setDonePtTaskResult.png differ diff --git a/docs/images/viewDaySequenceDiagram.png b/docs/images/viewDaySequenceDiagram.png new file mode 100644 index 00000000000..757a18bbe12 Binary files /dev/null and b/docs/images/viewDaySequenceDiagram.png differ diff --git a/docs/images/viewWeekSequenceDiagram.png b/docs/images/viewWeekSequenceDiagram.png new file mode 100644 index 00000000000..79e12d28519 Binary files /dev/null and b/docs/images/viewWeekSequenceDiagram.png differ diff --git a/docs/images/xcelestialphoenix.png b/docs/images/xcelestialphoenix.png new file mode 100644 index 00000000000..fe22695686f Binary files /dev/null and b/docs/images/xcelestialphoenix.png differ diff --git a/docs/stylesheets/gh-pages.css b/docs/stylesheets/gh-pages.css index 121cac3885f..e1d13f80de3 100644 --- a/docs/stylesheets/gh-pages.css +++ b/docs/stylesheets/gh-pages.css @@ -2,6 +2,11 @@ @import "asciidoctor.css"; /* Default asciidoc style framework - important */ /* Custom block: details */ +@media print { + a[href]:after { + content: none !important; + } +} .sidebarblock.details > .content { border-left: .25rem solid rgba(0, 0, 0, 0.1); @@ -18,6 +23,12 @@ padding-top: 0; } +@media print { + a[href]:after { + content: none !important; + } +} + /* Overrides for asciidoctor.css */ a { @@ -211,4 +222,8 @@ table.tableblock > .title, #site-header { display: none; } + + a[href]:after { + content: none !important; + } } diff --git a/docs/team/bryanyap972.adoc b/docs/team/bryanyap972.adoc new file mode 100644 index 00000000000..986b07e3fd3 --- /dev/null +++ b/docs/team/bryanyap972.adoc @@ -0,0 +1,136 @@ += Yap Wen Jun Bryan - Project Portfolio +:site-section: AboutUs +:imagesDir: ../images +:stylesDir: ../stylesheets + +== PROJECT: NOVA + +== Overview + +NOVA is a desktop application to help CS2103T with event planning, managing schedules and keeping track of their project tasks. + +== Summary of contributions + +I am in charge of the Progress Tracker and its features and is a maintainer for the team repo. + +* *Major enhancement*: +I added the ability to add project tasks into individual and team project. + +** What it does: Allows users to add tasks into specific weeks of projects. +** Justification: This enables users to keep track of the tasks that they need to complete each week for the projects. +** Highlights: The implementation required thinking of design restrictions such as having only 13 weeks since there are +only 13 weeks in a semester before project submission as well as having only two projects in the module. +** Credits: The parsers used for the commands were modified versions of the parsers from AddressBook 3. + +* *Major enhancement*: +I added the ability to navigate between the different features. + +** What it does: Allows users to navigate to the different features of the application. +** Justification: This enables the team to reuse command keywords since the parsers and commands can be separate now. +This also enables users to receive feature specific information such as specific error messages and helpbox at the side. +** Highlights: This enhancement affects the parsers and command keywords that is available to the team to use. +This also helped reduce the dependency of the different features and helped to link the different customisation and parsers +together. + +* *Minor enhancement*: +I added the ability to list tasks from a specific week of the projects. This allows the user to view the tasks that they +have added. + +* *Minor enhancement*: +I added the ability to delete tasks. This allows the user to delete the tasks that they +have added so that they can fix errors they made. + +* *Minor enhancement*: +I added the ability to edit tasks. This allows the user to edit the tasks that they +have added so that they can fix minor errors they made without having to delete and re-add tasks. + +* *Minor enhancement*: +I added the ability to add a note to tasks. This allows users to add additional information to a task. + +* *Minor enhancement*: +I added the ability to delete note to tasks. This allows user to delete note that have been wrongly added. + +* *Minor enhancement*: +I added the ability to edit note added to tasks. This allows user to make corrections to errors in notes without having +to delete and re-add a note. + +* *Code contributed*: (https://nus-cs2103-ay1920s2.github.io/tp-dashboard/#search=&sort=groupTitle&sortWithin=title&since=2020-02-14&timeframe=commit&mergegroup=false&groupSelect=groupByRepos&breakdown=false&tabOpen=true&tabType=authorship&tabAuthor=BryanYap972&tabRepo=AY1920S2-CS2103T-F10-3%2Fmain%5Bmaster%5D[Link to RepoSense]) (Link only works on windows pdf viewers. Will not work on safari or preview) + +* *Other contributions*: + +** Project management: +*** Acted as the main maintainer for team repository and ensured that code quality was up to standard before merging. + +** Enhancements to existing features: +*** Formatted GUI (added help box and the formatting of the components) and changed colour scheme of GUI +(https://github.com/AY1920S2-CS2103T-F10-3/main/pull/112[#112]) +*** Changed the team's website. Removed unnecessary links and headers (https://github.com/AY1920S2-CS2103T-F10-3/main/pull/57[#57]) +*** Refactored AB3 storage to better fit the team features (https://github.com/AY1920S2-CS2103T-F10-3/main/pull/238[#238]). +*** Added Home page and Help message for Home page (https://github.com/AY1920S2-CS2103T-F10-3/main/pull/190[#190], https://github.com/AY1920S2-CS2103T-F10-3/main/pull/401[#401]) +*** Implemented LogicParser class to integrate the different features' parsers (https://github.com/AY1920S2-CS2103T-F10-3/main/pull/118[#118]) + +** Documentation: +*** Updated the UI class diagram and description in the developer guide. + +** Community: +*** PRs reviewed (with non-trivial review comments): https://github.com/AY1920S2-CS2103T-F10-3/main/pull/150[#150], https://github.com/AY1920S2-CS2103T-F10-3/main/pull/235[#235] +*** Reported bugs and gave suggestions for other teams in the class (examples: https://github.com/nus-cs2103-AY1920S2/addressbook-level3/pull/14#pullrequestreview-382542427[1], https://github.com/nus-cs2103-AY1920S2/addressbook-level3/pull/14#discussion_r388685822[2], https://github.com/BryanYap972/ped/issues[3]) (Link 1 and 2 only works on windows pdf viewers. Will not work on safari or preview). + +** Tools: +*** Integrated travis CI into the project (https://github.com/AY1920S2-CS2103T-F10-3/main/pull/1[#1]). +*** Integrated Netlify into the project. + + +== Contributions to the User Guide +|_Given below are sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users._ + +include::../UserGuide.adoc[tag=ProgressTracker] + +[%autowidth,cols="<.^,<.^,<.^",options="header"] +|========================================================= +| Mode | Command | Description +.7+| Progress Tracker +| add p\[ip/tp] w\[week number] d\[task description] | Adds a task to the specified week and project +| list p\[ip/tp] w\[week number] | Lists the tasks that have been added to the specified week and project +| delete p\ip w\3 t\3 | Deletes the specified task +| done p\[ip/tp] w\[week number] t\[task number] | Sets the status of the specified task to done/undone +| addNote p\[ip/tp] w\[week number] t\[task number] d\note | Adds a note to the specified task +| editNote p\[ip/tp] w\[week number] t\[task number] d\[new note] | Overwrites the existing note with the new note +| deleteNote p\[ip/tp] w\[week number] t\[task number] | Deletes the note in the specified task +|========================================================= + +== Contributions to the Developer Guide +|_Given below are sections I contributed to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project._ + +include::../DeveloperGuide.adoc[tag=UI] +include::../DeveloperGuide.adoc[tag=listPt] + +[width="59%",cols="22%,<23%,<25%,<30%",options="header",] +|======================================================================= +|Priority |As a ... |I can ... |So that I ... + +|`* * *` |forgetful student |keep track of my project tasks |make sure all my project tasks are completed on time + +|`* * *` |student |mark tasks as done |track how many tasks I have finished + +|`* * *` |student |view tasks added |see the tasks I have to finish + +|`* * *` |student |add notes to project tasks |keep track of details regarding the tasks + +|`* * *` |student |delete project tasks |remove unwanted project tasks from the tracker + +|`* * *` |student |add project tasks |keep track of those project tasks + +|`* * *` |student |delete notes |remove unwanted notes from tasks + +|`* *` |lazy student |edit project tasks |can correct mistakes made to task descriptions with little effort + +|`* *` |lazy student |edit notes to project tasks |can correct mistakes made to notes with little effort + +|======================================================================= + +include::../DeveloperGuide.adoc[tag=UseCasesPt] + +include::../DeveloperGuide.adoc[tag=shutdown] + +include::../DeveloperGuide.adoc[tag=Pt] diff --git a/docs/team/huixianc.adoc b/docs/team/huixianc.adoc new file mode 100644 index 00000000000..33e8916a06c --- /dev/null +++ b/docs/team/huixianc.adoc @@ -0,0 +1,129 @@ += Chua Huixian - Project Portfolio +:site-section: AboutUs +:imagesDir: ../images +:stylesDir: ../stylesheets + +== PROJECT: NOVA + +== Overview +This portfolio is a summary of my contributions to Project NOVA. + +NOVA is a desktop application that acts as a one stop study aid platform, intended to help CS2103T students with event planning, managing schedules and tracking of their projects. + +The user interacts with it using a CLI, and it has a GUI created with JavaFX. +It is written in Java, and has about 17 kLoC. + +== Summary of contributions +This section shows a summary of my coding, documentation and other contributions to the project. + +* *Major enhancement*: +I added the ability to add events, namely consultations, meetings, study sessions and lessons. + +** What it does: Allows user to add events to their schedule. +** Justification: This enables the user to keep track of the various events they have to attend. +** Highlights: It was necessary to ensure that the user did not add an event with the same date and time slot +as an already existing event. Hence, implementation of the adding of events had to be done carefully. +The events also had to be sorted in chronological order. +** Credits: The parsing of the commands made use of the Address Book 3 code with some modifications. + +* *Minor enhancement*: +I added the ability to delete events. + +** What it does: Allows user to delete events from their schedule. +** Justification: This enables the user to remove any events that they no longer wish to have. +** Credits: The parsing of the command made use of the Address Book 3 code with some modifications. + +* *Minor enhancement*: +I added the ability to add notes to events. + +** What it does: Allows user to add notes to specific events in their schedule. + +** Justification: This enables the user to take down any important details e.g. significant points made in meetings +so that they do not forget them. + +** Credits: The parsing of the command made use of the Address Book 3 code with some modifications. + +* *Code contributed*: +Please click these links to see samples of my code: +https://nus-cs2103-ay1920s2.github.io/tp-dashboard/#search=f10-3&sort=groupTitle&sortWithin=title&since=2020-02-14&timeframe=commit&mergegroup=false&groupSelect=groupByRepos&breakdown=false&tabOpen=true&tabType=authorship&tabAuthor=huixianc&tabRepo=AY1920S2-CS2103T-F10-3%2Fmain%5Bmaster%5D[Code Dashboard] + +If the link does not work: +https://nus-cs2103-ay1920s2.github.io/tp-dashboard/#search=f10-3&sort=groupTitle&sortWithin=title&since=2020-02-14&timeframe=commit&mergegroup=false&groupSelect=groupByRepos&breakdown=false&tabOpen=true&tabType=authorship&tabAuthor=huixianc&tabRepo=AY1920S2-CS2103T-F10-3%2Fmain%5Bmaster%5D + +* *Other contributions*: + +** Project management: +*** Acted as the secondary maintainer - helped to maintain the team repository and facilitated merging of Pull Requests from Week 11 to 13. + +** Enhancements to existing features: +*** Updated the GUI layout to make the panels aligned (https://github.com/AY1920S2-CS2103T-F10-3/main/pull/344[#344]) + +** Documentation: +*** Helped to enable auto-publishing of documentation on GitHub Pages via Travis. +*** Updated Contact Us page. (https://github.com/AY1920S2-CS2103T-F10-3/main/pull/8[#8]) +*** Updated Logic Component of Developer Guide, as mentioned below. + +** Community: +*** Pull Request reviewed with non-trivial comments (https://github.com/AY1920S2-CS2103T-F10-3/main/pull/298[#298]) +*** Reported bugs and gave suggestions for other teams +(https://github.com/AY1920S2-CS2103T-T10-3/main/issues/131[1], +https://github.com/nus-cs2103-AY1920S2/addressbook-level3/pull/14#pullrequestreview-370050890[2], +https://github.com/nus-cs2103-AY1920S2/addressbook-level3/pull/14#pullrequestreview-382543444[3], +https://github.com/huixianc/ped/issues[4]) + +If Link 2 does not work: https://github.com/nus-cs2103-AY1920S2/addressbook-level3/pull/14#pullrequestreview-370050890 + +If Link 3 does not work: https://github.com/nus-cs2103-AY1920S2/addressbook-level3/pull/14#pullrequestreview-382543444 + + +== Contributions to the User Guide +|=== +|_Given below are sections I contributed to the User Guide. +They showcase my ability to write documentation targeting end-users._ +|=== + +include::../UserGuide.adoc[tag=events] + + +== Contributions to the Developer Guide + +|=== +|_Given below are sections I contributed to the Developer Guide. +They showcase my ability to write technical documentation and the technical depth of my contributions to the project._ +|=== + +|=== +|_1. The Logic component section under Design._ +|=== + +include::../DeveloperGuide.adoc[tag=logic] + +|=== +|_2. The Manage Events section under Implementation._ +|=== + +include::../DeveloperGuide.adoc[tag=events] + +|=== +|_3. The User Stories for events under Appendix B._ +|=== + +[width="59%",cols="22%,<23%,<25%,<30%",options="header",] +|======================================================================= +|Priority |As a ... |I can ... |So that I ... + +include::../DeveloperGuide.adoc[tag=eventuserstory] +|======================================================================= + + +|=== +|_4. The Use Cases for events under Appendix C._ +|=== + +include::../DeveloperGuide.adoc[tag=eventusecase] + +|=== +|_4. The Manual Testing for events under Appendix F._ +|=== + +include::../DeveloperGuide.adoc[tag=eventTest] diff --git a/docs/team/lohszeying.adoc b/docs/team/lohszeying.adoc new file mode 100644 index 00000000000..a3c53769f47 --- /dev/null +++ b/docs/team/lohszeying.adoc @@ -0,0 +1,98 @@ += Loh Sze Ying - Project Portfolio +:site-section: AboutUs +:imagesDir: ../images +:stylesDir: ../stylesheets + +== PROJECT: NOVA + +== Overview + +This portfolio sumarises my contribution to NOVA's development. +NOVA is a desktop application to help CS2103T with event planning and managing schedules, and is developed by a team of 5 members from NUS School of Computing. + + +NOVA is written in Java. Within NOVA's code, I am in charge of features related to Address Book + +== Summary of contributions + +* *Major enhancement*: Refactored and implemented address book feature +** What it does: Allows the user to do contacts management on their fellow CS2103T students within NOVA +** Justification: This feature improves a product as a student using NOVA for CS2103T management would want to do +contacts management on NOVA + +* *Minor enhancement*: added **the ability to sort and list contacts by category within Address Book mode** +** What it does: Allows the user to sort fellow CS2103T students by classmate or teammate, as well as viewing contacts by classmate or teammate +** Justification: User can easily see who are their fellow classmates or teammates +** Highlights: This enhancement affects the existing code from address book level 3, and needed some consideration to make category field compulsory rather than optional + +* *Minor enhancement*: added **the ability to view remarks only with list category command** +** What it does: Allows the user to only view fellow classmate's or teammate's remark with list category command +** Justification: When users want to view their contact list, they would want to only view their necessary contact information + +* *Minor enhancement*: added the **ability to undo or redo commands within Address Book mode** +** What it does: Allows the user to undo or redo whatever the commands that they typed within Address Book +** Justification: User can easily accidentally edit, delete or clear contacts within Address Book +** Highlights: This enhancement affects the existing code within Address Book mode +** Credits: Implementation of Undo/Redo from link:https://github.com/nus-cs2103-AY1920S2/addressbook-level3/blob/master/docs/DeveloperGuide.adoc[here] under Section 3.1. + +* *Minor enhancement*: added **sort contacts alphabetically automatically** +** What it does: Allows the user to have an easier time to find added contacts +** Justification: User can easily find their contacts after using list + +* *Minor enhancement*: added **the ability to format contact's name automatically** +** What it does: Allows the user to quickly add or edit a contact without worry about the format of name, eg. whether the name starts with capital letter, or if is there many spaces in-between the name +** Justification: This feature allows the user to quickly add a contact and NOVA will automatically format the person's name +** Highlights: There is a need to edit current implementation of `Name` class for this to work +** Example: User types `aLICe yeoH` into name parameter, and NOVA automatically converts it to `Alice Yeoh` + +* *Code contributed*: +** Please click https://nus-cs2103-ay1920s2.github.io/tp-dashboard/#search=f10-3&sort=totalCommits%20dsc&sortWithin=title&since=2020-02-14&timeframe=commit&mergegroup=false&groupSelect=groupByRepos&breakdown=false&tabOpen=true&tabType=authorship&tabAuthor=lohszeying&tabRepo=AY1920S2-CS2103T-F10-3%2Fmain%5Bmaster%5D[here] to view my Reposense. + +** Note: If the above link does not work, please copy and paste the URL manually: + +https://nus-cs2103-ay1920s2.github.io/tp-dashboard/#search=f10-3&sort=totalCommits%20dsc&sortWithin=title&since=2020-02-14&timeframe=commit&mergegroup=false&groupSelect=groupByRepos&breakdown=false&tabOpen=true&tabType=authorship&tabAuthor=lohszeying&tabRepo=AY1920S2-CS2103T-F10-3%2Fmain%5Bmaster%5D + +* *Other contributions*: + +** Project management: +*** Managed releases `v1.2.1` to `v1.3` (2 releases on GitHub) link:https://github.com/AY1920S2-CS2103T-F10-3/main/releases[here] +*** Refactored AddressBook commands (https://github.com/AY1920S2-CS2103T-F10-3/main/pull/86[#86]) + +** Enhancements to existing features: +*** Edited index tag within NOVA's Address Book from `[index]` to `i\[index]` (https://github.com/AY1920S2-CS2103T-F10-3/main/pull/244[#244]) +*** Sorted contact list alphabetically automatically (https://github.com/AY1920S2-CS2103T-F10-3/main/pull/347[#347]) + +** Documentation: +*** Updated About Us and team member's portfolio with template (https://github.com/AY1920S2-CS2103T-F10-3/main/pull/23[#23]) +*** Edited SettingUp.adoc, DevOps.adoc, Documentation.adoc, Testing.adoc to fit NOVA's product (https://github.com/AY1920S2-CS2103T-F10-3/main/pull/355[#355], https://github.com/AY1920S2-CS2103T-F10-3/main/pull/416[#416]) +*** Contributed and updated NOVA's User Guide and Developer Guide. Details below. + +** Community: +*** Gave suggestions to other team link:https://github.com/nus-cs2103-AY1920S2/addressbook-level3/pull/14#pullrequestreview-370052563[here] +**** Note: If the above link does not work, please copy and paste the URL manually: + +https://github.com/nus-cs2103-AY1920S2/addressbook-level3/pull/14#pullrequestreview-370052563 +*** Tested other team's application and reported bugs link:https://github.com/lohszeying/ped/issues[here] + +== Contributions to the User Guide + +|=== +|_Given below are sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users._ +|=== + +Written the User Guide https://github.com/AY1920S2-CS2103T-F10-3/main/blob/master/docs/UserGuide.adoc[here], most notably under the section Address Book (Section 5.2). + +Note: _Special thanks to https://github.com/xCelestialPhoenix[@xcelestialphoenix] for helping to put screenshot for Address Book feature, and managing of documents for User Guide and Developer Guide!_ + +== Contributions to the Developer Guide + +|=== +|_Given below are sections I contributed to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project._ +|=== + +Written the Developer Guide https://github.com/AY1920S2-CS2103T-F10-3/main/blob/master/docs/DeveloperGuide.adoc[here], most notably under Section 3.2's Address Book implementation. + +Also edited section https://github.com/AY1920S2-CS2103T-F10-3/main/pull/355[2.1]'s Figure 3, and edited section https://github.com/AY1920S2-CS2103T-F10-3/main/pull/207[2.6]. + +* Added and edited Address Book's user stories (https://github.com/AY1920S2-CS2103T-F10-3/main/pull/19[#19], https://github.com/AY1920S2-CS2103T-F10-3/main/pull/207[#207], https://github.com/AY1920S2-CS2103T-F10-3/main/pull/416[#416]) +* Added NFR (https://github.com/AY1920S2-CS2103T-F10-3/main/pull/19[#19], https://github.com/AY1920S2-CS2103T-F10-3/main/pull/355[#355]) +* Added use case (https://github.com/AY1920S2-CS2103T-F10-3/main/pull/355[#355], https://github.com/AY1920S2-CS2103T-F10-3/main/pull/416[#416]) +* Added glossary (https://github.com/AY1920S2-CS2103T-F10-3/main/pull/416[#416]) +* Edited Instructions for Manual Testing (https://github.com/AY1920S2-CS2103T-F10-3/main/pull/355[#355]) +* Removed product survey appendix (https://github.com/AY1920S2-CS2103T-F10-3/main/pull/416[#416]) diff --git a/docs/team/mightyrabbit99.adoc b/docs/team/mightyrabbit99.adoc new file mode 100644 index 00000000000..7a19cebabe7 --- /dev/null +++ b/docs/team/mightyrabbit99.adoc @@ -0,0 +1,61 @@ += Tan Wah Ken - Project Portfolio +:site-section: AboutUs +:imagesDir: ../images +:stylesDir: ../stylesheets + +== PROJECT: NOVA + +== Overview + +NOVA is a desktop application to help CS2103T with event planning and managing schedules. + +== Summary of contributions + +* *Major enhancement*: added *planner feature* +** What it does: Tracks the progress of user study plan and assists in planning study events based on the tasks inside study plan. +** Justification: This feature reminds the user of their study progress so that user are able to plan ahead their study events before being distracted by other things and lack behind on their study. +** Highlights: There's no need for user to think of the detail (e.g. start time, end time) of the event. +User only needs to specify the task and the date, planner will generate a study event on that day based on the task. +Due to time constraint of this module, the algorithm for choosing the free slot for generating event is first-fit algorithm for now. +More suitable algorithm needs to be developed in the future in order to make it more human-friendly. + +* *Major enhancement*: added *find free slot function* +** What it does: List the free slots in user's schedule. +** Justification: This feature is needed due to the fact that we don't have a schedule GUI. User are unable to grasp when they are free especially when there is many events in their schedule. + +* *Minor enhancement*: +** Make dialog window wrap text automatically whenever window is resized. reference: https://github.com/AY1920S2-CS2103T-F10-3/main/pull/345[22] + +* *Code contributed*: +** [https://github.com/AY1920S2-CS2103T-F10-3/main/issues?q=is%3Aclosed+author%3Amightyrabbit99[Functional code]][https://nus-cs2103-ay1920s2.github.io/tp-dashboard/#search=mightyrabbit99&sort=groupTitle&sortWithin=title&since=2020-02-14&timeframe=commit&mergegroup=false&groupSelect=groupByRepos&breakdown=false&tabOpen=true&tabType=authorship&tabAuthor=mightyrabbit99&tabRepo=AY1920S2-CS2103T-F10-3%2Fmain%5Bmaster%5D[RepoSense]] + +* *Other contributions*: + +//** Project management: + +//** Enhancements to existing features: + +//** Documentation: + +** Community: +*** Reported bugs and suggestions for other teams in the class: https://github.com/nus-cs2103-AY1920S2/addressbook-level3/pull/14#discussion_r388685822[12] + +//** Tools: + +== Contributions to the User Guide + +|=== +|_Given below are sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users._ +|=== + +include::../UserGuide.adoc[tag=studyplanner] + +== Contributions to the Developer Guide + +|=== +|_Given below are sections I contributed to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project._ +|=== + +include::../DeveloperGuide.adoc[tag=studyplannerfeature] +include::../DeveloperGuide.adoc[tag=studyplannerusecase] +include::../DeveloperGuide.adoc[tag=studyplannermss] diff --git a/docs/team/xcelestialphoenix.adoc b/docs/team/xcelestialphoenix.adoc new file mode 100644 index 00000000000..b56f42ff212 --- /dev/null +++ b/docs/team/xcelestialphoenix.adoc @@ -0,0 +1,116 @@ += Terence Ng - Project Portfolio +:site-section: AboutUs +:imagesDir: ../images +:stylesDir: ../stylesheets + +== PROJECT: NOVA + +Since: `Apr 2020` + +== Overview + +My team of 4 software engineering students and I were tasked to enhance a simple command line interface (CLI) desktop +address book application for our project to cater to users who are accustomed to typing. We expanded on the address +book into an organizer, hoping to aid future software engineering students taking this module in managing their activities. + +This portfolio documents the contributions I made to Project NOVA. It contains a section on the summary of contributions +towards the project as well as the user guide and developer guide. + +== Summary of contributions + +This section documents a summary of contributions towards the code base, documentation as well as other helpful +contributions. + +==== Major enhancement: `view` command + +I added the ability to view the activities in the schedule. + +* What it does: There are two levels of viewing (by date or by week). The `view` command allows the user to specify the +date which he/she wants to view. Alternatively, the user can use the `view week` command to view the activities +happening on that date. +* Justification: A schedule that cannot be viewed is useless. Users can choose whether to view the schedule at the +daily level or the weekly level. + +Pull requests: https://github.com/AY1920S2-CS2103T-F10-3/main/pull/143/files[#143] + +==== Minor enhancement: Schedule's framework + +I created the framework of the schedule in NOVA. + +* What it does: The framework offers a common platform for the other developers working on the schedule. +* Justification: Having a common framework reduces the conflict between different implementation of the schedule. It +also helps us integrate our features easily. +* Highlight: The implementation was challenging as a affects the implementation of events, view and study planner. A +common agreement needed to be reached to ensure the schedule will not hinder the implementation of those features. + +==== Code contributed: + +Here is the link to the code I have contributed: +https://nus-cs2103-ay1920s2.github.io/tp-dashboard/#search=f10-3&sort=groupTitle&sortWithin=title&since=2020-02-14&timeframe=commit&mergegroup=false&groupSelect=groupByRepos&breakdown=false&tabOpen=true&tabType=authorship&tabAuthor=xCelestialPhoenix&tabRepo=AY1920S2-CS2103T-F10-3%2Fmain%5Bmaster%5D[Code Dashboard] + +==== Other contributions: + +These are some additional contributions I made to the project. + +===== Project management: + +* Maintained the documentation of the project on GitHub. +** Ensured the team's documentation is appropriate and free of error +** Standardized the format for all to follow so the final document is cohesive. + +===== Documentation: +* Added the view commands under features (https://github.com/AY1920S2-CS2103T-F10-3/main/pull/31/files[#31]) +* Improved the introduction and about sections of the user guide (https://github.com/AY1920S2-CS2103T-F10-3/main/pull/287/files[#287]) +* Added a notations section to the user guide (https://github.com/AY1920S2-CS2103T-F10-3/main/pull/287/files[#287]) +* Added command summary (https://github.com/AY1920S2-CS2103T-F10-3/main/pull/287/files[#287]) +* Added a back to top link to improve navigability of the document (https://github.com/AY1920S2-CS2103T-F10-3/main/pull/287/files[#287]) + +===== Community: +* Shared problems faced with others on the forum so people encountering similar problems as me can discuss our +solutions (E.g. https://github.com/nus-cs2103-AY1920S2/forum/issues/20[1], https://github.com/nus-cs2103-AY1920S2/forum/issues/67[2]). + +== Contributions to the User Guide +Theses are my contributions towards the user guide. + +|=== +| _Theses are my contributions towards the front sections._ +|=== +include::../UserGuide.adoc[tag=intro] +include::../UserGuide.adoc[tag=about] +include::../UserGuide.adoc[tag=notation] + +{nbsp} + + +|=== +| _Theses are my contributions towards the features section._ +|=== +include::../UserGuide.adoc[tag=view] + +== Contributions to the Developer Guide +Theses are my contributions towards the developer guide. + +|=== +| _Theses are my contributions towards the model section._ +|=== +include::../DeveloperGuide.adoc[tag=model] + +{nbsp} + + +|=== +| _Theses are my contributions towards the user stories section._ +|=== +include::../DeveloperGuide.adoc[tag=viewScheduleUserStories] + +{nbsp} + + +|=== +| _Theses are my contributions towards the use cases section._ +|=== +include::../DeveloperGuide.adoc[tag=viewScheduleUseCases] + +{nbsp} + + +|=== +| _Theses are my contributions towards the manual testing section._ +|=== +include::../DeveloperGuide.adoc[tag=testView] diff --git a/docs/templates/_header.html.slim b/docs/templates/_header.html.slim index 3c2d5aed43c..8d840758c99 100644 --- a/docs/templates/_header.html.slim +++ b/docs/templates/_header.html.slim @@ -1,26 +1,4 @@ / NOTE: You must restart the gradle daemon after modifying any template file for the changes to take effect. -- if !(attr? 'no-site-header') && (attr? 'site-seedu') - #seedu-header - nav.navbar.navbar-lg.navbar-light.bg-lighter - .container - a.navbar-brand href='https://se-edu.github.io/' - img src=(site_url 'images/SeEduLogo.png') alt='SE-EDU' - ul.navbar-nav - li.nav-item - a.nav-link href='https://se-edu.github.io/addressbook-level1' AB-1 - li.nav-item - a.nav-link href='https://se-edu.github.io/addressbook-level2' AB-2 - li.nav-item - a.nav-link.active href=(site_url 'index.html') AB-3 - li.nav-item - a.nav-link href='https://se-edu.github.io/addressbook-level4' AB-4 - li.nav-item - a.nav-link href='https://se-edu.github.io/collate' Collate - li.nav-item - a.nav-link href='https://se-edu.github.io/se-book' Book - li.nav-item - a.nav-link href='https://se-edu.github.io/learningresources' Resources - - if !(attr? 'no-site-header') #site-header nav.navbar.navbar-light.bg-light diff --git a/docs/templates/helpers.rb b/docs/templates/helpers.rb index 7060efe223e..ebaa829b7a3 100644 --- a/docs/templates/helpers.rb +++ b/docs/templates/helpers.rb @@ -45,7 +45,7 @@ module Slim::Helpers ## # Creates an HTML tag with the given name and optionally attributes. Can take - # a block that will run between the opening and closing tags. + # a block that will run between the opening and closing categories. # # @param name [#to_s] the name of the tag. # @param attributes [Hash] @@ -207,11 +207,11 @@ def html_meta_if(name, content) %() if content end - # Returns formatted style/link and script tags for header. + # Returns formatted style/link and script categories for header. def styles_and_scripts scripts = [] styles = [] - tags = [] + categories = [] stylesheet = attr :stylesheet stylesdir = attr :stylesdir, '' @@ -280,21 +280,21 @@ def styles_and_scripts styles.each do |item| if item.key?(:text) - tags << html_tag(:style, {}, item[:text]) + categories << html_tag(:style, {}, item[:text]) else - tags << html_tag(:link, rel: 'stylesheet', href: urlize(*item[:href])) + categories << html_tag(:link, rel: 'stylesheet', href: urlize(*item[:href])) end end scripts.each do |item| if item.key? :text - tags << html_tag(:script, {type: item[:type]}, item[:text]) + categories << html_tag(:script, {type: item[:type]}, item[:text]) else - tags << html_tag(:script, type: item[:type], src: urlize(*item[:src])) + categories << html_tag(:script, type: item[:type], src: urlize(*item[:src])) end end - tags.join "\n" + categories.join "\n" end end diff --git a/docs/tutorials/AddRemark.adoc b/docs/tutorials/AddRemark.adoc index 51044c36494..f47a98b639a 100644 --- a/docs/tutorials/AddRemark.adoc +++ b/docs/tutorials/AddRemark.adoc @@ -34,11 +34,11 @@ For now, let's keep `RemarkCommand` as simple as possible and print some output. We accomplish that by returning a `CommandResult` with an accompanying message. .RemarkCommand.java -[source, java] +[source,java] ---- -package seedu.address.logic.commands; +package seedu.nova.logic.commands; -import seedu.address.model.Model; +import seedu.nova.model.Model; /** * Changes the remark of an existing person in the address book. @@ -108,9 +108,9 @@ We start by modifying the constructor of `RemarkCommand` to accept an `Index` an 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. -[source, java] +[source,java] ---- -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.nova.commons.util.CollectionUtil.requireAllNonNull; //... public class RemarkCommand extends Command { //... @@ -273,7 +273,7 @@ Simply add private Label remark; ``` -to link:https://github.com/nus-cs2103-AY1920S1/addressbook-level3/commit/2758455583f0101ed918a318fc75679270843a0d#diff-0c6b6abcfac8c205e075294f25e851fe[`seedu.address.ui.PersonCard`]. +to link:https://github.com/nus-cs2103-AY1920S1/addressbook-level3/commit/2758455583f0101ed918a318fc75679270843a0d#diff-0c6b6abcfac8c205e075294f25e851fe[`seedu.nova.ui.PersonCard`]. `@FXML` is an annotation that marks a private or protected field and makes it accessible to FXML. It might sound like Greek to you right now, don't worry -- we will get back to it later. diff --git a/docs/tutorials/RemovingFields.adoc b/docs/tutorials/RemovingFields.adoc index 5a50b6965a6..d2a78c8d05a 100644 --- a/docs/tutorials/RemovingFields.adoc +++ b/docs/tutorials/RemovingFields.adoc @@ -16,7 +16,7 @@ Perfection is achieved, not when there is nothing more to add, but when there is When working on Address Book, you will most likely find that some features and fields that are no longer necessary. In scenarios like this, you can consider refactoring the existing `Person` model to suit your use case. -In this tutorial, we'll do exactly just that and remove the `address` field from `Person`. +In this tutorial, we'll do exactly just that and remove the `nova` field from `Person`. == Safely deleting `Address` @@ -24,7 +24,7 @@ Fortunately, the IntelliJ IDEA provides a robust refactoring tool that can ident Let's try to use it as much as we can. === Assisted refactoring -The `address` field in `Person` is actually an instance of the `seedu.address.model.person.Address` class. +The `nova` 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. @@ -45,14 +45,14 @@ Other usages like in `EditPersonDescriptor` may require more careful inspection. Let's try removing references to `Address` in `EditPersonDescriptor`. -. Safe delete the field `address` in `EditPersonDescriptor` +. Safe delete the field `nova` in `EditPersonDescriptor` . Select `Yes` when prompted to remove getters and setters . Select `View Usages` again image:UnsafeDeleteOnField.png[width=1145px, height=583px] -. Remove the usages of `address` and select `Do refactor` when you are done. +. Remove the usages of `nova` and select `Do refactor` when you are done. + TIP: Removing usages may result in errors. Exercise discretion and fix them. -For example, removing the `address` field from the `Person` class will require you to modify its constructor. +For example, removing the `nova` field from the `Person` class will require you to modify its constructor. . Repeat the steps for the remaining usages of `Address` @@ -61,7 +61,7 @@ After you are done, verify that the application still works by compiling and run === Manual refactoring Unfortunately, there are usages of `Address` that IntelliJ IDEA cannot identify. -You can find them by searching for instances of the word `address` in your code (`Edit` > `Find` > `Find in path`). +You can find them by searching for instances of the word `nova` in your code (`Edit` > `Find` > `Find in path`). Places of interest to look out for would be resources used by the application. `main/resources` contains images and `fxml` files used by the application and `test/resources` contains test data. 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/LogicManager.java b/src/main/java/seedu/address/logic/LogicManager.java deleted file mode 100644 index d47ce874b1a..00000000000 --- a/src/main/java/seedu/address/logic/LogicManager.java +++ /dev/null @@ -1,78 +0,0 @@ -package seedu.address.logic; - -import java.io.IOException; -import java.nio.file.Path; -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; - -/** - * The main LogicManager of the app. - */ -public class LogicManager implements Logic { - public static final String FILE_OPS_ERROR_MESSAGE = "Could not save data to file: "; - private final Logger logger = LogsCenter.getLogger(LogicManager.class); - - private final Model model; - private final Storage storage; - private final AddressBookParser addressBookParser; - - public LogicManager(Model model, Storage storage) { - this.model = model; - this.storage = storage; - addressBookParser = new AddressBookParser(); - } - - @Override - public CommandResult execute(String commandText) throws CommandException, ParseException { - logger.info("----------------[USER COMMAND][" + commandText + "]"); - - CommandResult commandResult; - Command command = addressBookParser.parseCommand(commandText); - commandResult = command.execute(model); - - try { - storage.saveAddressBook(model.getAddressBook()); - } catch (IOException ioe) { - throw new CommandException(FILE_OPS_ERROR_MESSAGE + ioe, ioe); - } - - return commandResult; - } - - @Override - public ReadOnlyAddressBook getAddressBook() { - return model.getAddressBook(); - } - - @Override - public ObservableList getFilteredPersonList() { - return model.getFilteredPersonList(); - } - - @Override - public Path getAddressBookFilePath() { - return model.getAddressBookFilePath(); - } - - @Override - public GuiSettings getGuiSettings() { - return model.getGuiSettings(); - } - - @Override - public void setGuiSettings(GuiSettings guiSettings) { - model.setGuiSettings(guiSettings); - } -} 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/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/HelpCommand.java b/src/main/java/seedu/address/logic/commands/HelpCommand.java deleted file mode 100644 index bf824f91bd0..00000000000 --- a/src/main/java/seedu/address/logic/commands/HelpCommand.java +++ /dev/null @@ -1,21 +0,0 @@ -package seedu.address.logic.commands; - -import seedu.address.model.Model; - -/** - * Format full help instructions for every command for display. - */ -public class HelpCommand extends Command { - - public static final String COMMAND_WORD = "help"; - - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Shows program usage instructions.\n" - + "Example: " + COMMAND_WORD; - - public static final String SHOWING_HELP_MESSAGE = "Opened help window."; - - @Override - public CommandResult execute(Model model) { - return new CommandResult(SHOWING_HELP_MESSAGE, true, false); - } -} 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/DeleteCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java deleted file mode 100644 index 522b93081cc..00000000000 --- a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java +++ /dev/null @@ -1,29 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.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; - -/** - * Parses input arguments and creates a new DeleteCommand object - */ -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. - * @throws ParseException if the user input does not conform the expected format - */ - public DeleteCommand parse(String args) throws ParseException { - try { - Index index = ParserUtil.parseIndex(args); - return new DeleteCommand(index); - } catch (ParseException pe) { - throw new ParseException( - String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE), pe); - } - } - -} 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/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 0650c954f5c..00000000000 --- a/src/main/java/seedu/address/model/ModelManager.java +++ /dev/null @@ -1,151 +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) { - super(); - 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/ReadOnlyUserPrefs.java b/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java deleted file mode 100644 index befd58a4c73..00000000000 --- a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java +++ /dev/null @@ -1,16 +0,0 @@ -package seedu.address.model; - -import java.nio.file.Path; - -import seedu.address.commons.core.GuiSettings; - -/** - * Unmodifiable view of user prefs. - */ -public interface ReadOnlyUserPrefs { - - GuiSettings getGuiSettings(); - - Path getAddressBookFilePath(); - -} 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/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 e4f452b6cbf..00000000000 --- a/src/main/java/seedu/address/storage/StorageManager.java +++ /dev/null @@ -1,77 +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; - - - public StorageManager(AddressBookStorage addressBookStorage, UserPrefsStorage userPrefsStorage) { - super(); - 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/MainWindow.java b/src/main/java/seedu/address/ui/MainWindow.java deleted file mode 100644 index 90bbf11de97..00000000000 --- a/src/main/java/seedu/address/ui/MainWindow.java +++ /dev/null @@ -1,193 +0,0 @@ -package seedu.address.ui; - -import java.util.logging.Logger; - -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.scene.control.MenuItem; -import javafx.scene.control.TextInputControl; -import javafx.scene.input.KeyCombination; -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; - -/** - * The Main Window. Provides the basic application layout containing - * a menu bar and space where other JavaFX elements can be placed. - */ -public class MainWindow extends UiPart { - - private static final String FXML = "MainWindow.fxml"; - - private final Logger logger = LogsCenter.getLogger(getClass()); - - private Stage primaryStage; - private Logic logic; - - // Independent Ui parts residing in this Ui container - private PersonListPanel personListPanel; - private ResultDisplay resultDisplay; - private HelpWindow helpWindow; - - @FXML - private StackPane commandBoxPlaceholder; - - @FXML - private MenuItem helpMenuItem; - - @FXML - private StackPane personListPanelPlaceholder; - - @FXML - private StackPane resultDisplayPlaceholder; - - @FXML - private StackPane statusbarPlaceholder; - - public MainWindow(Stage primaryStage, Logic logic) { - super(FXML, primaryStage); - - // Set dependencies - this.primaryStage = primaryStage; - this.logic = logic; - - // Configure the UI - setWindowDefaultSize(logic.getGuiSettings()); - - setAccelerators(); - - helpWindow = new HelpWindow(); - } - - public Stage getPrimaryStage() { - return primaryStage; - } - - private void setAccelerators() { - setAccelerator(helpMenuItem, KeyCombination.valueOf("F1")); - } - - /** - * Sets the accelerator of a MenuItem. - * @param keyCombination the KeyCombination value of the accelerator - */ - private void setAccelerator(MenuItem menuItem, KeyCombination keyCombination) { - menuItem.setAccelerator(keyCombination); - - /* - * TODO: the code below can be removed once the bug reported here - * https://bugs.openjdk.java.net/browse/JDK-8131666 - * is fixed in later version of SDK. - * - * According to the bug report, TextInputControl (TextField, TextArea) will - * consume function-key events. Because CommandBox contains a TextField, and - * ResultDisplay contains a TextArea, thus some accelerators (e.g F1) will - * not work when the focus is in them because the key event is consumed by - * the TextInputControl(s). - * - * For now, we add following event filter to capture such key events and open - * help window purposely so to support accelerators even when focus is - * in CommandBox or ResultDisplay. - */ - getRoot().addEventFilter(KeyEvent.KEY_PRESSED, event -> { - if (event.getTarget() instanceof TextInputControl && keyCombination.match(event)) { - menuItem.getOnAction().handle(new ActionEvent()); - event.consume(); - } - }); - } - - /** - * Fills up all the placeholders of this window. - */ - void fillInnerParts() { - personListPanel = new PersonListPanel(logic.getFilteredPersonList()); - personListPanelPlaceholder.getChildren().add(personListPanel.getRoot()); - - resultDisplay = new ResultDisplay(); - resultDisplayPlaceholder.getChildren().add(resultDisplay.getRoot()); - - StatusBarFooter statusBarFooter = new StatusBarFooter(logic.getAddressBookFilePath()); - statusbarPlaceholder.getChildren().add(statusBarFooter.getRoot()); - - CommandBox commandBox = new CommandBox(this::executeCommand); - commandBoxPlaceholder.getChildren().add(commandBox.getRoot()); - } - - /** - * Sets the default size based on {@code guiSettings}. - */ - private void setWindowDefaultSize(GuiSettings guiSettings) { - primaryStage.setHeight(guiSettings.getWindowHeight()); - primaryStage.setWidth(guiSettings.getWindowWidth()); - if (guiSettings.getWindowCoordinates() != null) { - primaryStage.setX(guiSettings.getWindowCoordinates().getX()); - primaryStage.setY(guiSettings.getWindowCoordinates().getY()); - } - } - - /** - * Opens the help window or focuses on it if it's already opened. - */ - @FXML - public void handleHelp() { - if (!helpWindow.isShowing()) { - helpWindow.show(); - } else { - helpWindow.focus(); - } - } - - void show() { - primaryStage.show(); - } - - /** - * Closes the application. - */ - @FXML - private void handleExit() { - GuiSettings guiSettings = new GuiSettings(primaryStage.getWidth(), primaryStage.getHeight(), - (int) primaryStage.getX(), (int) primaryStage.getY()); - logic.setGuiSettings(guiSettings); - helpWindow.hide(); - primaryStage.hide(); - } - - public PersonListPanel getPersonListPanel() { - return personListPanel; - } - - /** - * Executes the command and returns the result. - * - * @see seedu.address.logic.Logic#execute(String) - */ - private CommandResult executeCommand(String commandText) throws CommandException, ParseException { - try { - CommandResult commandResult = logic.execute(commandText); - logger.info("Result: " + commandResult.getFeedbackToUser()); - resultDisplay.setFeedbackToUser(commandResult.getFeedbackToUser()); - - if (commandResult.isShowHelp()) { - handleHelp(); - } - - if (commandResult.isExit()) { - handleExit(); - } - - return commandResult; - } catch (CommandException | ParseException e) { - logger.info("Invalid command: " + commandText); - resultDisplay.setFeedbackToUser(e.getMessage()); - throw e; - } - } -} diff --git a/src/main/java/seedu/address/ui/ResultDisplay.java b/src/main/java/seedu/address/ui/ResultDisplay.java deleted file mode 100644 index 7d98e84eedf..00000000000 --- a/src/main/java/seedu/address/ui/ResultDisplay.java +++ /dev/null @@ -1,28 +0,0 @@ -package seedu.address.ui; - -import static java.util.Objects.requireNonNull; - -import javafx.fxml.FXML; -import javafx.scene.control.TextArea; -import javafx.scene.layout.Region; - -/** - * A ui for the status bar that is displayed at the header of the application. - */ -public class ResultDisplay extends UiPart { - - private static final String FXML = "ResultDisplay.fxml"; - - @FXML - private TextArea resultDisplay; - - public ResultDisplay() { - super(FXML); - } - - public void setFeedbackToUser(String feedbackToUser) { - requireNonNull(feedbackToUser); - resultDisplay.setText(feedbackToUser); - } - -} diff --git a/src/main/java/seedu/address/AppParameters.java b/src/main/java/seedu/nova/AppParameters.java similarity index 93% rename from src/main/java/seedu/address/AppParameters.java rename to src/main/java/seedu/nova/AppParameters.java index ab552c398f3..ae0ab0dd3c7 100644 --- a/src/main/java/seedu/address/AppParameters.java +++ b/src/main/java/seedu/nova/AppParameters.java @@ -1,4 +1,4 @@ -package seedu.address; +package seedu.nova; 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.nova.commons.core.LogsCenter; +import seedu.nova.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/nova/Main.java similarity index 97% rename from src/main/java/seedu/address/Main.java rename to src/main/java/seedu/nova/Main.java index 052a5068631..25990e77346 100644 --- a/src/main/java/seedu/address/Main.java +++ b/src/main/java/seedu/nova/Main.java @@ -1,4 +1,4 @@ -package seedu.address; +package seedu.nova; import javafx.application.Application; diff --git a/src/main/java/seedu/address/MainApp.java b/src/main/java/seedu/nova/MainApp.java similarity index 62% rename from src/main/java/seedu/address/MainApp.java rename to src/main/java/seedu/nova/MainApp.java index e5cfb161b73..b35b3fb309f 100644 --- a/src/main/java/seedu/address/MainApp.java +++ b/src/main/java/seedu/nova/MainApp.java @@ -1,42 +1,49 @@ -package seedu.address; +package seedu.nova; import java.io.IOException; import java.nio.file.Path; +import java.time.LocalDate; import java.util.Optional; import java.util.logging.Logger; 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.nova.commons.core.Config; +import seedu.nova.commons.core.LogsCenter; +import seedu.nova.commons.core.Version; +import seedu.nova.commons.exceptions.DataConversionException; +import seedu.nova.commons.util.ConfigUtil; +import seedu.nova.commons.util.StringUtil; +import seedu.nova.logic.Logic; +import seedu.nova.logic.LogicManager; +import seedu.nova.model.AddressBook; +import seedu.nova.model.Model; +import seedu.nova.model.ModelManager; +import seedu.nova.model.Nova; +import seedu.nova.model.ReadOnlyUserPrefs; +import seedu.nova.model.Schedule; +import seedu.nova.model.UserPrefs; +import seedu.nova.model.VersionedAddressBook; +import seedu.nova.model.plan.StudyPlan; +import seedu.nova.model.progresstracker.ProgressTracker; +import seedu.nova.model.util.SampleDataUtil; +import seedu.nova.storage.JsonNovaStorage; +import seedu.nova.storage.JsonUserPrefsStorage; +import seedu.nova.storage.NovaStorage; +import seedu.nova.storage.Storage; +import seedu.nova.storage.StorageManager; +import seedu.nova.storage.UserPrefsStorage; +import seedu.nova.ui.Ui; +import seedu.nova.ui.UiManager; + /** * Runs the application. */ public class MainApp extends Application { - public static final Version VERSION = new Version(0, 6, 0, true); + public static final Version VERSION = new Version(1, 4, 0, true); private static final Logger logger = LogsCenter.getLogger(MainApp.class); @@ -48,7 +55,7 @@ public class MainApp extends Application { @Override public void init() throws Exception { - logger.info("=============================[ Initializing AddressBook ]==========================="); + logger.info("=============================[ Initializing NOVA ]==========================="); super.init(); AppParameters appParameters = AppParameters.parse(getParameters()); @@ -56,8 +63,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); + NovaStorage novaStorage = new JsonNovaStorage(userPrefs.getNovaFilePath()); + storage = new StorageManager(novaStorage, userPrefsStorage); initLogging(config); @@ -69,25 +76,40 @@ 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 nova book and {@code userPrefs}.
+ * The data from the sample nova data will be used instead if {@code storage}'s nova data is not found, + * or an empty nova will be used instead if errors occur when reading {@code storage}'s nova data. */ private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) { - Optional addressBookOptional; - ReadOnlyAddressBook initialData; + Optional novaOptional; + Nova initialData; try { - addressBookOptional = storage.readAddressBook(); - if (!addressBookOptional.isPresent()) { - logger.info("Data file not found. Will be starting with a sample AddressBook"); + novaOptional = storage.readNova(); + if (!novaOptional.isPresent()) { + logger.info("Data file not found. Will be starting with a sample data file"); } - initialData = addressBookOptional.orElseGet(SampleDataUtil::getSampleAddressBook); + initialData = novaOptional.orElseGet(SampleDataUtil::getSampleNova); } catch (DataConversionException e) { - logger.warning("Data file not in the correct format. Will be starting with an empty AddressBook"); - initialData = new AddressBook(); + logger.warning("Data file not in the correct format. Will be starting with an empty data file"); + Nova nova = new Nova(); + nova.setAddressBookNova(new VersionedAddressBook(new AddressBook())); + nova.setProgressTrackerNova(new ProgressTracker()); + nova.setScheduleNova(new Schedule(LocalDate.of(2020, 1, 13), + LocalDate.of(2020, 5, 3))); + nova.setStudyPlan(new StudyPlan()); + + initialData = nova; } 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 data file"); + + Nova nova = new Nova(); + nova.setAddressBookNova(new VersionedAddressBook(new AddressBook())); + nova.setProgressTrackerNova(new ProgressTracker()); + nova.setScheduleNova(new Schedule(LocalDate.of(2020, 1, 13), + LocalDate.of(2020, 5, 3))); + nova.setStudyPlan(new StudyPlan()); + + initialData = nova; } return new ModelManager(initialData, userPrefs); @@ -167,13 +189,13 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) { @Override public void start(Stage primaryStage) { - logger.info("Starting AddressBook " + MainApp.VERSION); + logger.info("Starting NOVA " + MainApp.VERSION); ui.start(primaryStage); } @Override public void stop() { - logger.info("============================ [ Stopping Address Book ] ============================="); + logger.info("============================ [ Stopping NOVA ] ============================="); 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/nova/commons/core/Config.java similarity index 97% rename from src/main/java/seedu/address/commons/core/Config.java rename to src/main/java/seedu/nova/commons/core/Config.java index 91145745521..36613f5adb1 100644 --- a/src/main/java/seedu/address/commons/core/Config.java +++ b/src/main/java/seedu/nova/commons/core/Config.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.nova.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/nova/commons/core/GuiSettings.java similarity index 96% rename from src/main/java/seedu/address/commons/core/GuiSettings.java rename to src/main/java/seedu/nova/commons/core/GuiSettings.java index 5ace559ad15..8520325b20c 100644 --- a/src/main/java/seedu/address/commons/core/GuiSettings.java +++ b/src/main/java/seedu/nova/commons/core/GuiSettings.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.nova.commons.core; import java.awt.Point; import java.io.Serializable; @@ -6,7 +6,7 @@ /** * A Serializable class that contains the GUI settings. - * Guarantees: immutable. + * Guaranteed: immutable. */ public class GuiSettings implements Serializable { diff --git a/src/main/java/seedu/nova/commons/core/HelpMessages.java b/src/main/java/seedu/nova/commons/core/HelpMessages.java new file mode 100644 index 00000000000..319a2570df7 --- /dev/null +++ b/src/main/java/seedu/nova/commons/core/HelpMessages.java @@ -0,0 +1,91 @@ +package seedu.nova.commons.core; + +/** + * Container for help display messages. + */ +public class HelpMessages { + + //============== The help message for the home page ================== + public static final String HELP_HOME = "Help: Home Mode \n" + + "=================== \n" + + "Nav to addressbook: " + + "nav ab \n\n" + + "Nav to schedule: " + + "nav schedule \n\n" + + "Nav to ProgressTracker: " + + "nav progresstracker \n\n" + + "Nav to Planner: " + + "nav planner \n\n"; + + //============== The help message for the address book page ================== + public static final String HELP_ADDRESS_BOOK = "Help: Address Book Mode \n" + + "=================== \n" + + "Add contact: \n" + + "\u2022 add n\\[name] p\\[phone num] e\\[email addr] c\\[classmate/teammate] \n" + + "List contacts: \n" + + "\u2022 list \n" + + "List classmate/teammate: \n" + + "\u2022 list c\\[classmate/teammate] \n" + + "Find contact: \n" + + "find n\\[name] \n" + + "To undo, type undo \n" + + "To redo, type redo \n" + + "=================== \n" + + "NOTE: The following commands must be used after using list (including list category) and find command" + + "\n\n" + + "Edit contact: (After using list/find) \n" + + "\u2022 edit i\\[index] n\\[name] p\\[phone num] e\\[email addr] c\\[classmate/teammate] \n" + + "Delete contact: (After using list/find) \n" + + "\u2022 delete i\\[index]\n" + + "Add remark: (After using list/find) \n" + + "\u2022 remark i\\[index] r\\[remark]\n" + + "Remove remark: (After using list/find) \n" + + "\u2022 remark i\\[index] r\\ \n" + + "\u2022 remark i\\[index]"; + + + //============== The help message for the schedule page ================== + public static final String HELP_SCHEDULE = "Help: Schedule Mode \n" + + "=================== \n" + + "Add consultation: \n" + + "consultation d\\[description] v\\[venue] t\\[YYYY-MM-DD] [Start time (HH:MM)] [End time (HH:MM)] \n" + + "Add meeting: \n" + + "meeting d\\[description] v\\[venue] t\\[YYYY-MM-DD] [Start time (HH:MM)] [End time (HH:MM)] \n" + + "Add study session: \n" + + "study d\\[description] v\\[venue] t\\[YYYY-MM-DD] [Start time (HH:MM)] [End time (HH:MM)] \n" + + "Add lesson: \n" + + "lesson d\\[description] v\\[venue] t\\[day] [Start time (HH:MM)] [End time (HH:MM)] \n" + + "Add note to event: \n" + + "note d\\[description] t\\[YYYY-MM-DD] i\\[index] \n" + + "Delete event: \n" + + "delete t\\[YYYY-MM-DD] i\\[index] \n" + + "View schedule on date: \n" + + "view t\\[date] \n" + + "View schedule on week: \n" + + "view week i\\[week #]"; + + //============== The help message for the study planner page ================== + public static final String HELP_STUDY_PLANNER = ""; + + //============== The help message for the progress tracker page ================== + public static final String HELP_PROGRESS_TRACKER = "Help: ProgressTracker Mode \n" + + "=================== \n" + + "List tasks: \n" + + "list p\\[project] w\\[week] \n\n" + + "Add task: \n" + + "add p\\[project] w\\[week] d\\[task description] \n\n" + + "Delete task: \n" + + "delete p\\[project] w\\[week] t\\[task] \n\n" + + "Edit task: \n" + + "edit p\\[project] w\\[week] t\\[task] d\\[task description] \n\n" + + "Set task as done: \n" + + "done p\\[project] w\\[week] t\\[task] \n\n" + + "Add note to task \n" + + "addNote p\\[project] w\\[week] t\\[task] d\\[note] \n\n" + + "Delete note from task \n" + + "deleteNote p\\[project] w\\[week] t\\[task] \n\n" + + "Edit note in task \n" + + "editNote p\\[project] w\\[week] t\\[task] d\\[note] \n\n"; + + +} diff --git a/src/main/java/seedu/address/commons/core/LogsCenter.java b/src/main/java/seedu/nova/commons/core/LogsCenter.java similarity index 99% rename from src/main/java/seedu/address/commons/core/LogsCenter.java rename to src/main/java/seedu/nova/commons/core/LogsCenter.java index 431e7185e76..2abb0a4041f 100644 --- a/src/main/java/seedu/address/commons/core/LogsCenter.java +++ b/src/main/java/seedu/nova/commons/core/LogsCenter.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.nova.commons.core; import java.io.IOException; import java.util.Arrays; diff --git a/src/main/java/seedu/nova/commons/core/Messages.java b/src/main/java/seedu/nova/commons/core/Messages.java new file mode 100644 index 00000000000..cb621948ef0 --- /dev/null +++ b/src/main/java/seedu/nova/commons/core/Messages.java @@ -0,0 +1,20 @@ +package seedu.nova.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. " + + "Please only use edit, remark or delete command after using list, " + + "list c\\teammate, list c\\classmate or find command."; + public static final String MESSAGE_TOO_MANY_ARGUMENTS = "You entered to many arguments! \n%1$s"; + public static final String MESSAGE_INVALID_ARGUMENTS = "Invalid arguments! \n%1$s"; + public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!"; + public static final String MESSAGE_EMPTY_ARGUMENT = "Look at the help panel on the left to get started!"; + + public static final String MESSAGE_WRONG_TIME = "End time should be later than start time."; + +} diff --git a/src/main/java/seedu/address/commons/core/Version.java b/src/main/java/seedu/nova/commons/core/Version.java similarity index 98% rename from src/main/java/seedu/address/commons/core/Version.java rename to src/main/java/seedu/nova/commons/core/Version.java index e117f91b3b2..37ad76afa3e 100644 --- a/src/main/java/seedu/address/commons/core/Version.java +++ b/src/main/java/seedu/nova/commons/core/Version.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.nova.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/nova/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/nova/commons/core/index/Index.java index 19536439c09..695a1ea4ee8 100644 --- a/src/main/java/seedu/address/commons/core/index/Index.java +++ b/src/main/java/seedu/nova/commons/core/index/Index.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core.index; +package seedu.nova.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/nova/commons/exceptions/DataConversionException.java similarity index 84% rename from src/main/java/seedu/address/commons/exceptions/DataConversionException.java rename to src/main/java/seedu/nova/commons/exceptions/DataConversionException.java index 1f689bd8e3f..cdae9a53818 100644 --- a/src/main/java/seedu/address/commons/exceptions/DataConversionException.java +++ b/src/main/java/seedu/nova/commons/exceptions/DataConversionException.java @@ -1,4 +1,4 @@ -package seedu.address.commons.exceptions; +package seedu.nova.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/nova/commons/exceptions/IllegalValueException.java similarity index 93% rename from src/main/java/seedu/address/commons/exceptions/IllegalValueException.java rename to src/main/java/seedu/nova/commons/exceptions/IllegalValueException.java index 19124db485c..6edee285ef2 100644 --- a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java +++ b/src/main/java/seedu/nova/commons/exceptions/IllegalValueException.java @@ -1,4 +1,4 @@ -package seedu.address.commons.exceptions; +package seedu.nova.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/nova/commons/util/AppUtil.java similarity index 94% rename from src/main/java/seedu/address/commons/util/AppUtil.java rename to src/main/java/seedu/nova/commons/util/AppUtil.java index da90201dfd6..0b4c6908252 100644 --- a/src/main/java/seedu/address/commons/util/AppUtil.java +++ b/src/main/java/seedu/nova/commons/util/AppUtil.java @@ -1,9 +1,9 @@ -package seedu.address.commons.util; +package seedu.nova.commons.util; import static java.util.Objects.requireNonNull; import javafx.scene.image.Image; -import seedu.address.MainApp; +import seedu.nova.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/nova/commons/util/CollectionUtil.java similarity index 96% rename from src/main/java/seedu/address/commons/util/CollectionUtil.java rename to src/main/java/seedu/nova/commons/util/CollectionUtil.java index eafe4dfd681..876c6e1488c 100644 --- a/src/main/java/seedu/address/commons/util/CollectionUtil.java +++ b/src/main/java/seedu/nova/commons/util/CollectionUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.nova.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/nova/commons/util/ConfigUtil.java similarity index 77% rename from src/main/java/seedu/address/commons/util/ConfigUtil.java rename to src/main/java/seedu/nova/commons/util/ConfigUtil.java index f7f8a2bd44c..2ed4a7ebca2 100644 --- a/src/main/java/seedu/address/commons/util/ConfigUtil.java +++ b/src/main/java/seedu/nova/commons/util/ConfigUtil.java @@ -1,11 +1,11 @@ -package seedu.address.commons.util; +package seedu.nova.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.nova.commons.core.Config; +import seedu.nova.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/nova/commons/util/FileUtil.java similarity index 96% rename from src/main/java/seedu/address/commons/util/FileUtil.java rename to src/main/java/seedu/nova/commons/util/FileUtil.java index b1e2767cdd9..f652e401fa9 100644 --- a/src/main/java/seedu/address/commons/util/FileUtil.java +++ b/src/main/java/seedu/nova/commons/util/FileUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.nova.commons.util; import java.io.IOException; import java.nio.file.Files; @@ -18,7 +18,7 @@ public static boolean isFileExists(Path file) { } /** - * Returns true if {@code path} can be converted into a {@code Path} via {@link Paths#get(String)}, + * Returns true if {@code path} can be converted into a {@code Path} via {@link Paths#get(String, String...)}, * otherwise returns false. * @param path A string representing the file path. Cannot be null. */ diff --git a/src/main/java/seedu/address/commons/util/JsonUtil.java b/src/main/java/seedu/nova/commons/util/JsonUtil.java similarity index 97% rename from src/main/java/seedu/address/commons/util/JsonUtil.java rename to src/main/java/seedu/nova/commons/util/JsonUtil.java index 8ef609f055d..e5be0d2b23d 100644 --- a/src/main/java/seedu/address/commons/util/JsonUtil.java +++ b/src/main/java/seedu/nova/commons/util/JsonUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.nova.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.nova.commons.core.LogsCenter; +import seedu.nova.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/nova/commons/util/StringUtil.java similarity index 95% rename from src/main/java/seedu/address/commons/util/StringUtil.java rename to src/main/java/seedu/nova/commons/util/StringUtil.java index 61cc8c9a1cb..8a73b7b0d40 100644 --- a/src/main/java/seedu/address/commons/util/StringUtil.java +++ b/src/main/java/seedu/nova/commons/util/StringUtil.java @@ -1,7 +1,7 @@ -package seedu.address.commons.util; +package seedu.nova.commons.util; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; +import static seedu.nova.commons.util.AppUtil.checkArgument; import java.io.PrintWriter; import java.io.StringWriter; diff --git a/src/main/java/seedu/nova/logic/Logic.java b/src/main/java/seedu/nova/logic/Logic.java new file mode 100644 index 00000000000..838c74cf881 --- /dev/null +++ b/src/main/java/seedu/nova/logic/Logic.java @@ -0,0 +1,86 @@ +package seedu.nova.logic; + +import java.nio.file.Path; + +import javafx.collections.ObservableList; +import seedu.nova.commons.core.GuiSettings; +import seedu.nova.logic.commands.CommandResult; +import seedu.nova.logic.commands.exceptions.CommandException; +import seedu.nova.logic.parser.ModeEnum; +import seedu.nova.logic.parser.exceptions.ParseException; +import seedu.nova.model.Mode; +import seedu.nova.model.Model; +import seedu.nova.model.ReadOnlyAddressBook; +import seedu.nova.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 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 getNovaFilePath(); + + /** + * Returns the user prefs' GUI settings. + */ + GuiSettings getGuiSettings(); + + /** + * Set the user prefs' GUI settings. + */ + void setGuiSettings(GuiSettings guiSettings); + + /** + * Retrieves model object + * @return model object + */ + Model getModel(); + + /** + * Sets mode + * @param mode mode to set modeEnum + * @param modeEnum modeEnum to set to + */ + void setMode(Mode mode, ModeEnum modeEnum); + + /** + * Retrieves mode object + * @return mode object + */ + Mode getMode(); + + /** + * Retrieves modeEnum + * @param mode mode to retrieve modeEnum from + * @return modeEnum + */ + ModeEnum getModeEnum(Mode mode); + + /** + * Retrieves string for of modeEnum + * @param modeEnum modeENum to get name from + * @return string of modeENum name + */ + String getModeName(ModeEnum modeEnum); +} diff --git a/src/main/java/seedu/nova/logic/LogicManager.java b/src/main/java/seedu/nova/logic/LogicManager.java new file mode 100644 index 00000000000..679c360b3b8 --- /dev/null +++ b/src/main/java/seedu/nova/logic/LogicManager.java @@ -0,0 +1,110 @@ +package seedu.nova.logic; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.logging.Logger; + +import javafx.collections.ObservableList; + +import seedu.nova.commons.core.GuiSettings; +import seedu.nova.commons.core.LogsCenter; +import seedu.nova.logic.commands.Command; +import seedu.nova.logic.commands.CommandResult; +import seedu.nova.logic.commands.exceptions.CommandException; +import seedu.nova.logic.parser.LogicParser; +import seedu.nova.logic.parser.ModeEnum; +import seedu.nova.logic.parser.exceptions.ParseException; +import seedu.nova.model.Mode; +import seedu.nova.model.Model; +import seedu.nova.model.ReadOnlyAddressBook; +import seedu.nova.model.person.Person; +import seedu.nova.storage.Storage; + + +/** + * The main LogicManager of the app. + */ +public class LogicManager implements Logic { + public static final String FILE_OPS_ERROR_MESSAGE = "Could not save data to file: "; + private final Logger logger = LogsCenter.getLogger(LogicManager.class); + + private final Model model; + private final Storage storage; + private final LogicParser logicParser; + + public LogicManager(Model model, Storage storage) { + this.model = model; + this.storage = storage; + logicParser = new LogicParser(model); + } + + @Override + public CommandResult execute(String commandText) throws CommandException, ParseException { + //Logging, safe to ignore + logger.info("----------------[USER COMMAND][" + commandText + "]"); + + CommandResult commandResult; + Command command = logicParser.parseCommand(commandText); + commandResult = command.execute(model); + + try { + //We can deduce that the previous line of code modifies model in some way + // since it's being stored here. + storage.saveNova(model.getNova()); + } catch (IOException ioe) { + throw new CommandException(FILE_OPS_ERROR_MESSAGE + ioe, ioe); + } + + return commandResult; + } + + @Override + public ReadOnlyAddressBook getAddressBook() { + return model.getAddressBook(); + } + + @Override + public ObservableList getFilteredPersonList() { + return model.getFilteredPersonList(); + } + + @Override + public Path getNovaFilePath() { + return model.getNovaFilePath(); + } + + @Override + public GuiSettings getGuiSettings() { + return model.getGuiSettings(); + } + + @Override + public void setGuiSettings(GuiSettings guiSettings) { + model.setGuiSettings(guiSettings); + } + + @Override + public Model getModel() { + return this.model; + } + + @Override + public void setMode(Mode mode, ModeEnum modeEnum) { + mode.setModeEnum(modeEnum); + } + + @Override + public Mode getMode() { + return model.getMode(); + } + + @Override + public ModeEnum getModeEnum(Mode mode) { + return model.getModeEnum(mode); + } + + @Override + public String getModeName(ModeEnum modeEnum) { + return model.getModeName(modeEnum); + } +} diff --git a/src/main/java/seedu/address/logic/commands/Command.java b/src/main/java/seedu/nova/logic/commands/Command.java similarity index 78% rename from src/main/java/seedu/address/logic/commands/Command.java rename to src/main/java/seedu/nova/logic/commands/Command.java index 64f18992160..7022fbe2cbb 100644 --- a/src/main/java/seedu/address/logic/commands/Command.java +++ b/src/main/java/seedu/nova/logic/commands/Command.java @@ -1,7 +1,7 @@ -package seedu.address.logic.commands; +package seedu.nova.logic.commands; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; +import seedu.nova.logic.commands.exceptions.CommandException; +import seedu.nova.model.Model; /** * Represents a command with hidden internal logic and the ability to be executed. diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/seedu/nova/logic/commands/CommandResult.java similarity index 74% rename from src/main/java/seedu/address/logic/commands/CommandResult.java rename to src/main/java/seedu/nova/logic/commands/CommandResult.java index 92f900b7916..a1da2a82685 100644 --- a/src/main/java/seedu/address/logic/commands/CommandResult.java +++ b/src/main/java/seedu/nova/logic/commands/CommandResult.java @@ -1,4 +1,4 @@ -package seedu.address.logic.commands; +package seedu.nova.logic.commands; import static java.util.Objects.requireNonNull; @@ -12,7 +12,7 @@ public class CommandResult { private final String feedbackToUser; /** Help information should be shown to the user. */ - private final boolean showHelp; + private final boolean isChangeMode; /** The application should exit. */ private final boolean exit; @@ -20,9 +20,9 @@ public class CommandResult { /** * Constructs a {@code CommandResult} with the specified fields. */ - public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) { + public CommandResult(String feedbackToUser, boolean isChangeMode, boolean exit) { this.feedbackToUser = requireNonNull(feedbackToUser); - this.showHelp = showHelp; + this.isChangeMode = isChangeMode; this.exit = exit; } @@ -38,8 +38,8 @@ public String getFeedbackToUser() { return feedbackToUser; } - public boolean isShowHelp() { - return showHelp; + public boolean isChangeMode() { + return isChangeMode; } public boolean isExit() { @@ -59,13 +59,18 @@ public boolean equals(Object other) { CommandResult otherCommandResult = (CommandResult) other; return feedbackToUser.equals(otherCommandResult.feedbackToUser) - && showHelp == otherCommandResult.showHelp + && isChangeMode == otherCommandResult.isChangeMode && exit == otherCommandResult.exit; } @Override public int hashCode() { - return Objects.hash(feedbackToUser, showHelp, exit); + return Objects.hash(feedbackToUser, isChangeMode, exit); + } + + @Override + public String toString() { + return this.feedbackToUser; } } diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/nova/logic/commands/abcommands/AbAddCommand.java similarity index 50% rename from src/main/java/seedu/address/logic/commands/AddCommand.java rename to src/main/java/seedu/nova/logic/commands/abcommands/AbAddCommand.java index 71656d7c5c8..a32a6790a07 100644 --- a/src/main/java/seedu/address/logic/commands/AddCommand.java +++ b/src/main/java/seedu/nova/logic/commands/abcommands/AbAddCommand.java @@ -1,37 +1,36 @@ -package seedu.address.logic.commands; +package seedu.nova.logic.commands.abcommands; 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.nova.logic.parser.CliSyntax.PREFIX_CATEGORY; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_PHONE; + +import seedu.nova.logic.commands.Command; +import seedu.nova.logic.commands.CommandResult; +import seedu.nova.logic.commands.exceptions.CommandException; +import seedu.nova.model.Model; +import seedu.nova.model.person.Person; -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 class AbAddCommand extends Command { public static final String COMMAND_WORD = "add"; - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a person to the address book. " + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a person to the address book.\n" + "Parameters: " - + PREFIX_NAME + "NAME " - + PREFIX_PHONE + "PHONE " - + PREFIX_EMAIL + "EMAIL " - + PREFIX_ADDRESS + "ADDRESS " - + "[" + PREFIX_TAG + "TAG]...\n" + + PREFIX_NAME + "[name] " + + PREFIX_PHONE + "[phone] " + + PREFIX_EMAIL + "[email] " + + PREFIX_CATEGORY + "[category]\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"; + + PREFIX_CATEGORY + "classmate "; 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"; @@ -39,9 +38,9 @@ public class AddCommand extends Command { private final Person toAdd; /** - * Creates an AddCommand to add the specified {@code Person} + * Creates an AbAddCommand to add the specified {@code Person} */ - public AddCommand(Person person) { + public AbAddCommand(Person person) { requireNonNull(person); toAdd = person; } @@ -55,13 +54,14 @@ public CommandResult execute(Model model) throws CommandException { } model.addPerson(toAdd); + model.commitAddressBook(); 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)); + || (other instanceof AbAddCommand // instanceof handles nulls + && toAdd.equals(((AbAddCommand) other).toAdd)); } } diff --git a/src/main/java/seedu/nova/logic/commands/abcommands/AbClearCommand.java b/src/main/java/seedu/nova/logic/commands/abcommands/AbClearCommand.java new file mode 100644 index 00000000000..50c293310e7 --- /dev/null +++ b/src/main/java/seedu/nova/logic/commands/abcommands/AbClearCommand.java @@ -0,0 +1,28 @@ +package seedu.nova.logic.commands.abcommands; + +import static java.util.Objects.requireNonNull; + +import seedu.nova.logic.commands.Command; +import seedu.nova.logic.commands.CommandResult; +import seedu.nova.model.AddressBook; +import seedu.nova.model.Model; + +/** + * Clears the nova book. + */ +public class AbClearCommand extends Command { + + public static final String COMMAND_WORD = "clear"; + public static final String MESSAGE_SUCCESS = "Address book has been cleared!"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Clears the address book\n" + + "Example: clear"; + + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.setAddressBook(new AddressBook()); + model.commitAddressBook(); + return new CommandResult(MESSAGE_SUCCESS); + } +} diff --git a/src/main/java/seedu/nova/logic/commands/abcommands/AbDeleteCommand.java b/src/main/java/seedu/nova/logic/commands/abcommands/AbDeleteCommand.java new file mode 100644 index 00000000000..a51b7aac24c --- /dev/null +++ b/src/main/java/seedu/nova/logic/commands/abcommands/AbDeleteCommand.java @@ -0,0 +1,62 @@ +package seedu.nova.logic.commands.abcommands; + +import static java.util.Objects.requireNonNull; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_INDEX; + +import java.util.List; + +import seedu.nova.commons.core.Messages; +import seedu.nova.commons.core.index.Index; +import seedu.nova.logic.commands.Command; +import seedu.nova.logic.commands.CommandResult; +import seedu.nova.logic.commands.exceptions.CommandException; +import seedu.nova.model.Model; +import seedu.nova.model.person.Person; + + +/** + * Deletes a person identified using it's displayed index from the address book. + */ +public class AbDeleteCommand 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: " + PREFIX_INDEX + "[index] \n" + + "Example: " + COMMAND_WORD + " i\\1\n" + + "Note: Please only use delete command after using list, list c\\classmate, list c\\teammate or " + + "find command. You may wish to undo if you accidentally used delete command on the wrong person."; + + public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Deleted Person: %1$s\n\n" + + "Note: Please only use delete command after using list, list c\\classmate, list c\\teammate or " + + "find command. You may wish to undo if you accidentally used delete command on the wrong person."; + + private final Index targetIndex; + + public AbDeleteCommand(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); + model.commitAddressBook(); + 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 AbDeleteCommand // instanceof handles nulls + && targetIndex.equals(((AbDeleteCommand) other).targetIndex)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/EditCommand.java b/src/main/java/seedu/nova/logic/commands/abcommands/AbEditCommand.java similarity index 62% rename from src/main/java/seedu/address/logic/commands/EditCommand.java rename to src/main/java/seedu/nova/logic/commands/abcommands/AbEditCommand.java index 7e36114902f..0889c1dcdaa 100644 --- a/src/main/java/seedu/address/logic/commands/EditCommand.java +++ b/src/main/java/seedu/nova/logic/commands/abcommands/AbEditCommand.java @@ -1,12 +1,11 @@ -package seedu.address.logic.commands; +package seedu.nova.logic.commands.abcommands; 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 static seedu.nova.logic.parser.CliSyntax.PREFIX_CATEGORY; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_INDEX; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_PHONE; import java.util.Collections; import java.util.HashSet; @@ -14,39 +13,46 @@ 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; +import seedu.nova.commons.core.Messages; +import seedu.nova.commons.core.index.Index; +import seedu.nova.commons.util.CollectionUtil; +import seedu.nova.logic.commands.Command; +import seedu.nova.logic.commands.CommandResult; +import seedu.nova.logic.commands.exceptions.CommandException; +import seedu.nova.model.Model; +import seedu.nova.model.category.Category; +import seedu.nova.model.person.Email; +import seedu.nova.model.person.Name; +import seedu.nova.model.person.Person; +import seedu.nova.model.person.Phone; +import seedu.nova.model.person.Remark; + /** - * Edits the details of an existing person in the address book. + * Edits the details of an existing person in the nova book. */ -public class EditCommand extends Command { +public class AbEditCommand 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 " + + "Parameters: " + PREFIX_INDEX + "[index] " + + PREFIX_NAME + "[name] " + + PREFIX_PHONE + "[phone] " + + PREFIX_EMAIL + "[email] " + + PREFIX_CATEGORY + "[category]\n" + + "Example: " + COMMAND_WORD + " i\\1 " + PREFIX_PHONE + "91234567 " - + PREFIX_EMAIL + "johndoe@example.com"; - - public static final String MESSAGE_EDIT_PERSON_SUCCESS = "Edited Person: %1$s"; + + PREFIX_EMAIL + "johndoe@example.com " + + PREFIX_CATEGORY + "classmate\n" + + "Note: Please only use edit command after using list, list c\\classmate, list c\\teammate or " + + "find command. You may wish to undo if you accidentally used edit command on the wrong person."; + + public static final String MESSAGE_EDIT_PERSON_SUCCESS = "Edited Person: %1$s.\n\n" + + "Note: Please only use edit command after using list, list c\\classmate, list c\\teammate or " + + "find command. You may wish to undo if you accidentally used edit command on the wrong person."; 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."; @@ -57,7 +63,7 @@ public class EditCommand extends Command { * @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) { + public AbEditCommand(Index index, EditPersonDescriptor editPersonDescriptor) { requireNonNull(index); requireNonNull(editPersonDescriptor); @@ -82,7 +88,8 @@ public CommandResult execute(Model model) throws CommandException { } model.setPerson(personToEdit, editedPerson); - model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + model.updateFilteredPersonList(Model.PREDICATE_SHOW_ALL_PERSONS); + model.commitAddressBook(); return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, editedPerson)); } @@ -96,10 +103,10 @@ private static Person createEditedPerson(Person personToEdit, EditPersonDescript 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()); + Set updatedCategories = editPersonDescriptor.getCategories().orElse(personToEdit.getCategory()); + Remark updatedRemark = personToEdit.getRemark(); - return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedTags); + return new Person(updatedName, updatedPhone, updatedEmail, updatedCategories, updatedRemark); } @Override @@ -110,12 +117,12 @@ public boolean equals(Object other) { } // instanceof handles nulls - if (!(other instanceof EditCommand)) { + if (!(other instanceof AbEditCommand)) { return false; } // state check - EditCommand e = (EditCommand) other; + AbEditCommand e = (AbEditCommand) other; return index.equals(e.index) && editPersonDescriptor.equals(e.editPersonDescriptor); } @@ -128,28 +135,26 @@ public static class EditPersonDescriptor { private Name name; private Phone phone; private Email email; - private Address address; - private Set tags; + private Set categories; public EditPersonDescriptor() {} /** * Copy constructor. - * A defensive copy of {@code tags} is used internally. + * A defensive copy of {@code categories} is used internally. */ public EditPersonDescriptor(EditPersonDescriptor toCopy) { setName(toCopy.name); setPhone(toCopy.phone); setEmail(toCopy.email); - setAddress(toCopy.address); - setTags(toCopy.tags); + setCategories(toCopy.categories); } /** * Returns true if at least one field is edited. */ public boolean isAnyFieldEdited() { - return CollectionUtil.isAnyNonNull(name, phone, email, address, tags); + return CollectionUtil.isAnyNonNull(name, phone, email, categories); } public void setName(Name name) { @@ -176,29 +181,21 @@ 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. + * Sets {@code categories} to this object's {@code categories}. + * A defensive copy of {@code categories} is used internally. */ - public void setTags(Set tags) { - this.tags = (tags != null) ? new HashSet<>(tags) : null; + public void setCategories(Set categories) { + this.categories = (categories != null) ? new HashSet<>(categories) : null; } /** * Returns an unmodifiable tag set, which throws {@code UnsupportedOperationException} * if modification is attempted. - * Returns {@code Optional#empty()} if {@code tags} is null. + * Returns {@code Optional#empty()} if {@code categories} is null. */ - public Optional> getTags() { - return (tags != null) ? Optional.of(Collections.unmodifiableSet(tags)) : Optional.empty(); + public Optional> getCategories() { + return (categories != null) ? Optional.of(Collections.unmodifiableSet(categories)) : Optional.empty(); } @Override @@ -219,8 +216,7 @@ public boolean equals(Object other) { return getName().equals(e.getName()) && getPhone().equals(e.getPhone()) && getEmail().equals(e.getEmail()) - && getAddress().equals(e.getAddress()) - && getTags().equals(e.getTags()); + && getCategories().equals(e.getCategories()); } } } diff --git a/src/main/java/seedu/nova/logic/commands/abcommands/AbFindCommand.java b/src/main/java/seedu/nova/logic/commands/abcommands/AbFindCommand.java new file mode 100644 index 00000000000..b18bd265261 --- /dev/null +++ b/src/main/java/seedu/nova/logic/commands/abcommands/AbFindCommand.java @@ -0,0 +1,48 @@ +package seedu.nova.logic.commands.abcommands; + +import static java.util.Objects.requireNonNull; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_NAME; + +import seedu.nova.logic.commands.Command; +import seedu.nova.logic.commands.CommandResult; +import seedu.nova.model.Model; +import seedu.nova.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 AbFindCommand 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 n\\[more keywords]...\n" + + "Example: " + COMMAND_WORD + " " + PREFIX_NAME + "alice"; + + private final NameContainsKeywordsPredicate predicate; + + public AbFindCommand(NameContainsKeywordsPredicate predicate) { + this.predicate = predicate; + } + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.updateFilteredPersonList(predicate); + String listOfPeople = "Found the following: " + "\n"; + for (int i = 0; i < model.getFilteredPersonList().size(); i++) { + listOfPeople = listOfPeople + (i + 1) + ". " + model.getFilteredPersonList().get(i) + "\n"; + } + + return new CommandResult(listOfPeople); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof AbFindCommand // instanceof handles nulls + && predicate.equals(((AbFindCommand) other).predicate)); // state check + } +} diff --git a/src/main/java/seedu/nova/logic/commands/abcommands/AbListCategoryCommand.java b/src/main/java/seedu/nova/logic/commands/abcommands/AbListCategoryCommand.java new file mode 100644 index 00000000000..923d009725d --- /dev/null +++ b/src/main/java/seedu/nova/logic/commands/abcommands/AbListCategoryCommand.java @@ -0,0 +1,23 @@ +package seedu.nova.logic.commands.abcommands; + +import static seedu.nova.logic.parser.CliSyntax.PREFIX_CATEGORY; + +import seedu.nova.logic.commands.Command; +import seedu.nova.logic.commands.CommandResult; +import seedu.nova.model.Model; + +/** + * Lists all persons in the address book to the user. + */ +public abstract class AbListCategoryCommand extends Command { + public static final String COMMAND_WORD = "list"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": List contacts according to category. " + + "Parameters: " + + PREFIX_CATEGORY + "[classmate/teammate]\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_CATEGORY + "classmate "; + + @Override + public abstract CommandResult execute(Model model); +} diff --git a/src/main/java/seedu/nova/logic/commands/abcommands/AbListClassmateCommand.java b/src/main/java/seedu/nova/logic/commands/abcommands/AbListClassmateCommand.java new file mode 100644 index 00000000000..24011bc5d1e --- /dev/null +++ b/src/main/java/seedu/nova/logic/commands/abcommands/AbListClassmateCommand.java @@ -0,0 +1,35 @@ +package seedu.nova.logic.commands.abcommands; + +import static java.util.Objects.requireNonNull; + +import seedu.nova.logic.commands.CommandResult; +import seedu.nova.model.Model; +import seedu.nova.model.person.CategoryContainsKeywordsPredicate; + +/** + * Lists all persons in the address book to the user. + */ +public class AbListClassmateCommand extends AbListCategoryCommand { + + private final CategoryContainsKeywordsPredicate predicate; + + public AbListClassmateCommand(CategoryContainsKeywordsPredicate predicate) { + this.predicate = predicate; + } + + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.updateFilteredPersonList(predicate); + String listOfPeople = model.getFilteredPersonList().size() > 0 + ? "Listed classmate:\n" : "There is no classmate contact saved."; + for (int i = 0; i < model.getFilteredPersonList().size(); i++) { + listOfPeople = listOfPeople + (i + 1) + ". " + model.getFilteredPersonList().get(i) + ".\n" + + " Remark: " + model.getFilteredPersonList().get(i).getRemark() + + "\n"; + } + + return new CommandResult(listOfPeople); + } +} diff --git a/src/main/java/seedu/nova/logic/commands/abcommands/AbListCommand.java b/src/main/java/seedu/nova/logic/commands/abcommands/AbListCommand.java new file mode 100644 index 00000000000..c88de34edc2 --- /dev/null +++ b/src/main/java/seedu/nova/logic/commands/abcommands/AbListCommand.java @@ -0,0 +1,34 @@ +package seedu.nova.logic.commands.abcommands; + +import static java.util.Objects.requireNonNull; +import static seedu.nova.model.Model.PREDICATE_SHOW_ALL_PERSONS; + +import seedu.nova.logic.commands.Command; +import seedu.nova.logic.commands.CommandResult; +import seedu.nova.model.Model; + +/** + * Lists all persons in the nova book to the user. + */ +public class AbListCommand 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); + String listOfPeople = model.getAddressBook().getPersonList().size() > 0 + ? "Listed all contacts. Remarks will only appear with " + + "list c\\classmate or list c\\teammate command.\n\n" + : "There is no contact saved."; + for (int i = 0; i < model.getAddressBook().getPersonList().size(); i++) { + listOfPeople = listOfPeople + (i + 1) + ". " + model.getAddressBook().getPersonList().get(i) + "\n"; + } + + return new CommandResult(listOfPeople); + } +} diff --git a/src/main/java/seedu/nova/logic/commands/abcommands/AbListTeammateCommand.java b/src/main/java/seedu/nova/logic/commands/abcommands/AbListTeammateCommand.java new file mode 100644 index 00000000000..c38c6062d69 --- /dev/null +++ b/src/main/java/seedu/nova/logic/commands/abcommands/AbListTeammateCommand.java @@ -0,0 +1,35 @@ +package seedu.nova.logic.commands.abcommands; + +import static java.util.Objects.requireNonNull; + +import seedu.nova.logic.commands.CommandResult; +import seedu.nova.model.Model; +import seedu.nova.model.person.CategoryContainsKeywordsPredicate; + +/** + * Lists all persons in the address book to the user. + */ +public class AbListTeammateCommand extends AbListCategoryCommand { + + private final CategoryContainsKeywordsPredicate predicate; + + public AbListTeammateCommand(CategoryContainsKeywordsPredicate predicate) { + this.predicate = predicate; + } + + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.updateFilteredPersonList(predicate); + String listOfPeople = model.getFilteredPersonList().size() > 0 + ? "Listed teammate:\n" : "There is no teammate contact saved."; + for (int i = 0; i < model.getFilteredPersonList().size(); i++) { + listOfPeople = listOfPeople + (i + 1) + ". " + model.getFilteredPersonList().get(i) + ".\n" + + " Remark: " + model.getFilteredPersonList().get(i).getRemark() + + "\n"; + } + + return new CommandResult(listOfPeople); + } +} diff --git a/src/main/java/seedu/nova/logic/commands/abcommands/AbRedoCommand.java b/src/main/java/seedu/nova/logic/commands/abcommands/AbRedoCommand.java new file mode 100644 index 00000000000..91e6255b6a7 --- /dev/null +++ b/src/main/java/seedu/nova/logic/commands/abcommands/AbRedoCommand.java @@ -0,0 +1,32 @@ +package seedu.nova.logic.commands.abcommands; + +import static java.util.Objects.requireNonNull; + +import seedu.nova.logic.commands.Command; +import seedu.nova.logic.commands.CommandResult; +import seedu.nova.logic.commands.exceptions.CommandException; +import seedu.nova.model.Model; + +/** + * Clears the nova book. + */ +public class AbRedoCommand extends Command { + + public static final String COMMAND_WORD = "redo"; + public static final String MESSAGE_SUCCESS = "Address book has been redo-ed!"; + public static final String MESSAGE_FAILURE = "No more commands to redo!"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Redo the address book\n" + + "Example: redo"; + + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + if (model.canRedoAddressBook() == false) { + throw new CommandException(MESSAGE_FAILURE); + } + + model.redoAddressBook(); + return new CommandResult(MESSAGE_SUCCESS); + } +} diff --git a/src/main/java/seedu/nova/logic/commands/abcommands/AbRemarkCommand.java b/src/main/java/seedu/nova/logic/commands/abcommands/AbRemarkCommand.java new file mode 100644 index 00000000000..4fed0f51443 --- /dev/null +++ b/src/main/java/seedu/nova/logic/commands/abcommands/AbRemarkCommand.java @@ -0,0 +1,109 @@ +package seedu.nova.logic.commands.abcommands; + +import static seedu.nova.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_INDEX; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_REMARK; + +import java.util.List; + +import seedu.nova.commons.core.Messages; +import seedu.nova.commons.core.index.Index; +import seedu.nova.logic.commands.Command; +import seedu.nova.logic.commands.CommandResult; +import seedu.nova.logic.commands.exceptions.CommandException; +import seedu.nova.model.Model; +import seedu.nova.model.person.Person; +import seedu.nova.model.person.Remark; + + +/** + * Changes the remark of an existing person in the address book. + */ +public class AbRemarkCommand extends Command { + + public static final String COMMAND_WORD = "remark"; + public static final String MESSAGE_ARGUMENTS = "Index: %1$d, Remark: %2$s"; + 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. " + + "Existing remark will be overwritten by the input.\n" + + "Parameters: " + PREFIX_INDEX + "[index] " + + PREFIX_REMARK + "[remark]\n" + + "Example: " + COMMAND_WORD + " i\\1 " + + PREFIX_REMARK + "Likes to swim.\n" + + "Note: Please only use remark command after using list, list c\\classmate, list c\\teammate or " + + "find command. You may wish to undo if you accidentally used remark command on the wrong person."; + + public static final String MESSAGE_ADD_REMARK_SUCCESS = "Added remark to Person: %1$s."; + public static final String MESSAGE_DELETE_REMARK_SUCCESS = "Removed remark from Person: %1$s.\n\n" + + "Note: Please only use remark command after using list, list c\\classmate, list c\\teammate or " + + "find command. You may wish to undo if you accidentally used remark command on the wrong person."; + + private final Index index; + private final Remark remark; + + /** + * @param index of the person in the filtered person list to edit the remark + * @param remark of the person to be updated to + */ + public AbRemarkCommand(Index index, Remark remark) { + requireAllNonNull(index, remark); + + this.index = index; + this.remark = remark; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + List lastShownList = model.getFilteredPersonList(); + + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + } + + Person personToEdit = lastShownList.get(index.getZeroBased()); + Person editedPerson = new Person(personToEdit.getName(), personToEdit.getPhone(), personToEdit.getEmail(), + personToEdit.getCategory(), remark); + + model.setPerson(personToEdit, editedPerson); + //model.updateFilteredPersonList(Model.PREDICATE_SHOW_ALL_PERSONS); + model.commitAddressBook(); + return new CommandResult(generateSuccessMessage(editedPerson)); + } + + /** + * Generates a command execution success message based on whether the remark is added to or removed from + * {@code personToEdit}. + */ + private String generateSuccessMessage(Person personToEdit) { + //String message = !remark.value.isEmpty() ? MESSAGE_ADD_REMARK_SUCCESS : MESSAGE_DELETE_REMARK_SUCCESS; + String message = ""; + if (!remark.value.isEmpty()) { + message = MESSAGE_ADD_REMARK_SUCCESS; + message = message + "\nRemark: " + remark + "\n\n" + + "Note: Please only use remark command after using list, list c\\classmate, list c\\teammate or " + + "find command. You may wish to undo if you accidentally used remark command on the wrong person."; + } else { + message = MESSAGE_DELETE_REMARK_SUCCESS; + } + + return String.format(message, personToEdit); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof AbRemarkCommand)) { + return false; + } + + // state check + AbRemarkCommand e = (AbRemarkCommand) other; + return index.equals(e.index) + && remark.equals(e.remark); + } +} diff --git a/src/main/java/seedu/nova/logic/commands/abcommands/AbUndoCommand.java b/src/main/java/seedu/nova/logic/commands/abcommands/AbUndoCommand.java new file mode 100644 index 00000000000..01877e4f628 --- /dev/null +++ b/src/main/java/seedu/nova/logic/commands/abcommands/AbUndoCommand.java @@ -0,0 +1,32 @@ +package seedu.nova.logic.commands.abcommands; + +import static java.util.Objects.requireNonNull; + +import seedu.nova.logic.commands.Command; +import seedu.nova.logic.commands.CommandResult; +import seedu.nova.logic.commands.exceptions.CommandException; +import seedu.nova.model.Model; + +/** + * Clears the nova book. + */ +public class AbUndoCommand extends Command { + + public static final String COMMAND_WORD = "undo"; + public static final String MESSAGE_SUCCESS = "Address book has been undo-ed!"; + public static final String MESSAGE_FAILURE = "No more commands to undo!"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Undo the address book\n" + + "Example: undo"; + + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + if (model.canUndoAddressBook() == false) { + throw new CommandException(MESSAGE_FAILURE); + } + + model.undoAddressBook(); + return new CommandResult(MESSAGE_SUCCESS); + } +} diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/seedu/nova/logic/commands/commoncommands/ExitCommand.java similarity index 63% rename from src/main/java/seedu/address/logic/commands/ExitCommand.java rename to src/main/java/seedu/nova/logic/commands/commoncommands/ExitCommand.java index 3dd85a8ba90..a1c79d1dbb7 100644 --- a/src/main/java/seedu/address/logic/commands/ExitCommand.java +++ b/src/main/java/seedu/nova/logic/commands/commoncommands/ExitCommand.java @@ -1,6 +1,9 @@ -package seedu.address.logic.commands; +package seedu.nova.logic.commands.commoncommands; + +import seedu.nova.logic.commands.Command; +import seedu.nova.logic.commands.CommandResult; +import seedu.nova.model.Model; -import seedu.address.model.Model; /** * Terminates the program. @@ -9,7 +12,7 @@ public class ExitCommand extends Command { public static final String COMMAND_WORD = "exit"; - public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting Address Book as requested ..."; + public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting Nova as requested ..."; @Override public CommandResult execute(Model model) { diff --git a/src/main/java/seedu/nova/logic/commands/commoncommands/NavCommand.java b/src/main/java/seedu/nova/logic/commands/commoncommands/NavCommand.java new file mode 100644 index 00000000000..d2a57d45258 --- /dev/null +++ b/src/main/java/seedu/nova/logic/commands/commoncommands/NavCommand.java @@ -0,0 +1,74 @@ +package seedu.nova.logic.commands.commoncommands; + +import static java.util.Objects.requireNonNull; + +import java.util.logging.Logger; + +import seedu.nova.commons.core.LogsCenter; +import seedu.nova.logic.commands.Command; +import seedu.nova.logic.commands.CommandResult; +import seedu.nova.logic.parser.ModeEnum; +import seedu.nova.model.Mode; +import seedu.nova.model.Model; +import seedu.nova.model.progresstracker.Ip; +import seedu.nova.model.progresstracker.ProgressTracker; +import seedu.nova.model.progresstracker.Tp; + +/** + * Class for navigation command + */ +public class NavCommand extends Command { + public static final String COMMAND_WORD = "nav"; + public static final String MESSAGE_SUCCESS = "Changed mode to "; + public static final String MESSAGE_SAME_MODE = "You are already in %1$s mode"; + + private final Logger logger = LogsCenter.getLogger(getClass()); + + private ModeEnum modeEnum; + + public NavCommand(ModeEnum modeEnum) { + this.modeEnum = modeEnum; + } + + @Override + public CommandResult execute(Model model) { + logger.info("executing nav command to" + this.modeEnum.name()); + + requireNonNull(model); + + Mode mode = model.getMode(); + boolean isCurrentMode = mode.getModeEnum().equals(this.modeEnum); + boolean isProgressTrackerMode = this.modeEnum.equals(ModeEnum.PROGRESSTRACKER); + + if (isCurrentMode) { + String modeName = modeEnum.name().toLowerCase(); + + return new CommandResult(String.format(MESSAGE_SAME_MODE, modeName), false, false); + } else if (isProgressTrackerMode) { + mode.setModeEnum(this.modeEnum); + + ProgressTracker pt = model.getProgressTracker(); + Ip ip = pt.getIp(); + Tp tp = pt.getTp(); + double ipProgress = ip.getProgress(); + double tpProgress = tp.getProgress(); + + String modeName = mode.getModeEnum().name(); + String commandMessage = MESSAGE_SUCCESS + modeName + "\n"; + + //Get the progress for each project + String messageProgresstracker = "Projects: \n" + + " IP Project: " + ipProgress + "%\n" + + " TP Project: " + tpProgress + "%"; + + return new CommandResult(commandMessage + messageProgresstracker, true, false); + } else { + //Set mode and get modeEnum from new mode + model.setModeEnum(mode, this.modeEnum); + ModeEnum modeEnum = model.getModeEnum(mode); + String modeName = model.getModeName(modeEnum); + + return new CommandResult(MESSAGE_SUCCESS + modeName, true, false); + } + } +} diff --git a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java b/src/main/java/seedu/nova/logic/commands/exceptions/CommandException.java similarity index 83% rename from src/main/java/seedu/address/logic/commands/exceptions/CommandException.java rename to src/main/java/seedu/nova/logic/commands/exceptions/CommandException.java index a16bd14f2cd..2126bf3f0bd 100644 --- a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java +++ b/src/main/java/seedu/nova/logic/commands/exceptions/CommandException.java @@ -1,4 +1,6 @@ -package seedu.address.logic.commands.exceptions; +package seedu.nova.logic.commands.exceptions; + +import seedu.nova.logic.commands.Command; /** * Represents an error which occurs during execution of a {@link Command}. diff --git a/src/main/java/seedu/nova/logic/commands/plannercommands/DeleteTaskCommand.java b/src/main/java/seedu/nova/logic/commands/plannercommands/DeleteTaskCommand.java new file mode 100644 index 00000000000..bd726d0d899 --- /dev/null +++ b/src/main/java/seedu/nova/logic/commands/plannercommands/DeleteTaskCommand.java @@ -0,0 +1,50 @@ +package seedu.nova.logic.commands.plannercommands; + +import static java.util.Objects.requireNonNull; + +import java.time.LocalDate; + +import seedu.nova.logic.commands.Command; +import seedu.nova.logic.commands.CommandResult; +import seedu.nova.logic.commands.exceptions.CommandException; +import seedu.nova.model.Model; +import seedu.nova.model.plan.Task; +import seedu.nova.model.schedule.event.Event; + +/** + * delete a task + */ +public class DeleteTaskCommand extends Command { + public static final String COMMAND_WORD = "delete"; + private static final String MESSAGE_TASK_DELETE_SUCCESS = "Task deleted!"; + private static final String MESSAGE_TASK_NOT_EXIST = "Task does not exist!"; + private static final String MESSAGE_TASK_DELETE_FAILED = "Task NOT deleted :("; + + private String name; + + public DeleteTaskCommand(String name) { + this.name = name; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + Task task = model.searchTask(name); + if (task == null) { + return new CommandResult(MESSAGE_TASK_NOT_EXIST); + } + if (model.deleteTask(task)) { + for (Event e : task.getEventAfter(LocalDate.now())) { + try { + model.deleteEvent(e); + } catch (Exception ee) { + // do nothing + } + } + return new CommandResult(MESSAGE_TASK_DELETE_SUCCESS + "\n" + task); + } else { + return new CommandResult(MESSAGE_TASK_DELETE_FAILED); + } + } +} diff --git a/src/main/java/seedu/nova/logic/commands/plannercommands/PlannerAddFlexibleTaskCommand.java b/src/main/java/seedu/nova/logic/commands/plannercommands/PlannerAddFlexibleTaskCommand.java new file mode 100644 index 00000000000..34dd1c9d5a7 --- /dev/null +++ b/src/main/java/seedu/nova/logic/commands/plannercommands/PlannerAddFlexibleTaskCommand.java @@ -0,0 +1,55 @@ +package seedu.nova.logic.commands.plannercommands; + +import static java.util.Objects.requireNonNull; + +import java.time.Duration; + +import seedu.nova.logic.commands.Command; +import seedu.nova.logic.commands.CommandResult; +import seedu.nova.logic.commands.exceptions.CommandException; +import seedu.nova.model.Model; +import seedu.nova.model.plan.ImpossibleTaskException; +import seedu.nova.model.plan.WeakTask; + +/** + * Add flexible task command + */ +public class PlannerAddFlexibleTaskCommand extends Command { + public static final String COMMAND_WORD = "flexible"; + private static final String MESSAGE_TASK_ADY_EXIST = "The task already exist in your study plan"; + private static final String MESSAGE_TASK_ADD_SUCCESS = "Task added!"; + private static final String MESSAGE_IMPOSSIBLE_TASK = "You are asking for the impossible"; + private static final String MESSAGE_TASK_ADD_FAILED = "Task NOT added :("; + + private String name; + private Duration max; + private Duration min; + private Duration total; + + public PlannerAddFlexibleTaskCommand(String name, Duration min, Duration max, Duration total) { + this.name = name; + this.max = max; + this.min = min; + this.total = total; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + if (model.searchTask(name) != null) { + return new CommandResult(MESSAGE_TASK_ADY_EXIST); + } + WeakTask task; + try { + task = WeakTask.get(name, min, max, total); + } catch (ImpossibleTaskException ite) { + return new CommandResult(MESSAGE_IMPOSSIBLE_TASK); + } + if (model.addFlexibleTask(task)) { + return new CommandResult(MESSAGE_TASK_ADD_SUCCESS + "\n" + task); + } else { + return new CommandResult(MESSAGE_TASK_ADD_FAILED); + } + } +} diff --git a/src/main/java/seedu/nova/logic/commands/plannercommands/PlannerAddRoutineTaskCommand.java b/src/main/java/seedu/nova/logic/commands/plannercommands/PlannerAddRoutineTaskCommand.java new file mode 100644 index 00000000000..37ec3e56d6b --- /dev/null +++ b/src/main/java/seedu/nova/logic/commands/plannercommands/PlannerAddRoutineTaskCommand.java @@ -0,0 +1,47 @@ +package seedu.nova.logic.commands.plannercommands; + +import static java.util.Objects.requireNonNull; + +import java.time.Duration; + +import seedu.nova.logic.commands.Command; +import seedu.nova.logic.commands.CommandResult; +import seedu.nova.logic.commands.exceptions.CommandException; +import seedu.nova.model.Model; +import seedu.nova.model.plan.StrongTask; +import seedu.nova.model.plan.TaskFreq; + +/** + * Command for adding routine task to study plan + */ +public class PlannerAddRoutineTaskCommand extends Command { + public static final String COMMAND_WORD = "routine"; + private static final String MESSAGE_TASK_ADY_EXIST = "The task already exist in your study plan"; + private static final String MESSAGE_TASK_ADD_SUCCESS = "Task added!"; + private static final String MESSAGE_TASK_ADD_FAILED = "Task NOT added :("; + + private String name; + private Duration duration; + private TaskFreq freq; + + public PlannerAddRoutineTaskCommand(String name, Duration duration, TaskFreq freq) { + this.name = name; + this.duration = duration; + this.freq = freq; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + if (model.searchTask(name) != null) { + return new CommandResult(MESSAGE_TASK_ADY_EXIST); + } + StrongTask task = StrongTask.get(name, duration, freq); + if (model.addRoutineTask(task)) { + return new CommandResult(MESSAGE_TASK_ADD_SUCCESS + "\n" + task); + } else { + return new CommandResult(MESSAGE_TASK_ADD_FAILED); + } + } +} diff --git a/src/main/java/seedu/nova/logic/commands/plannercommands/PlannerScheduleTaskCommand.java b/src/main/java/seedu/nova/logic/commands/plannercommands/PlannerScheduleTaskCommand.java new file mode 100644 index 00000000000..82701c74c03 --- /dev/null +++ b/src/main/java/seedu/nova/logic/commands/plannercommands/PlannerScheduleTaskCommand.java @@ -0,0 +1,58 @@ +package seedu.nova.logic.commands.plannercommands; + +import static java.util.Objects.requireNonNull; + +import java.time.LocalDate; + +import seedu.nova.logic.commands.Command; +import seedu.nova.logic.commands.CommandResult; +import seedu.nova.logic.commands.exceptions.CommandException; +import seedu.nova.model.Model; +import seedu.nova.model.plan.Task; +import seedu.nova.model.schedule.event.Event; + +/** + * Schedule Task command + */ +public class PlannerScheduleTaskCommand extends Command { + public static final String COMMAND_WORD = "schedule"; + private static final String MESSAGE_EVENT_GEN_SUCCESS = "New event added: "; + private static final String MESSAGE_EVENT_GEN_FAILED = "Failed to add new event"; + private static final String MESSAGE_EVENT_ADY_EXIST = "There's already an existing event in the same day/week: "; + private static final String TASK_NOT_FOUND = "Cannot find task "; + + private String taskName; + private LocalDate date; + + public PlannerScheduleTaskCommand(String taskName, LocalDate date) { + this.taskName = taskName; + this.date = date; + } + + private String successMsg() { + return MESSAGE_EVENT_GEN_SUCCESS + date.toString(); + } + + private String taskNotFoundMsg() { + return TASK_NOT_FOUND + taskName; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + Task t = model.searchTask(taskName); + if (t == null) { + throw new CommandException(taskNotFoundMsg()); + } + if (t.hasEventOn(date)) { + return new CommandResult(MESSAGE_EVENT_ADY_EXIST + "\n" + t.getEventOn(date)); + } + Event event; + try { + event = model.generateTaskEvent(t, date); + } catch (Exception e) { + return new CommandResult(MESSAGE_EVENT_GEN_FAILED); + } + return new CommandResult(MESSAGE_EVENT_GEN_SUCCESS + "\n" + event); + } +} diff --git a/src/main/java/seedu/nova/logic/commands/plannercommands/PlannerStatsCommand.java b/src/main/java/seedu/nova/logic/commands/plannercommands/PlannerStatsCommand.java new file mode 100644 index 00000000000..cd43b43d828 --- /dev/null +++ b/src/main/java/seedu/nova/logic/commands/plannercommands/PlannerStatsCommand.java @@ -0,0 +1,39 @@ +package seedu.nova.logic.commands.plannercommands; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import seedu.nova.logic.commands.Command; +import seedu.nova.logic.commands.CommandResult; +import seedu.nova.logic.commands.exceptions.CommandException; +import seedu.nova.model.Model; +import seedu.nova.model.plan.Task; + +/** + * List task command + */ +public class PlannerStatsCommand extends Command { + public static final String COMMAND_WORD = "stats"; + private static final String listTitle = "Your tasks inside study plan: \n"; + + /** + * Generate task list string for user + * @param taskList task list + * @return string of list of task + */ + private String listString(List taskList) { + StringBuilder ans = new StringBuilder(listTitle); + for (Task t : taskList) { + ans.append(t.toString()).append("\n"); + } + return ans.toString(); + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + return new CommandResult(listString(model.getTaskList())); + } +} diff --git a/src/main/java/seedu/nova/logic/commands/ptcommands/PtAddCommand.java b/src/main/java/seedu/nova/logic/commands/ptcommands/PtAddCommand.java new file mode 100644 index 00000000000..117db973fd0 --- /dev/null +++ b/src/main/java/seedu/nova/logic/commands/ptcommands/PtAddCommand.java @@ -0,0 +1,119 @@ +package seedu.nova.logic.commands.ptcommands; + +import static java.util.Objects.requireNonNull; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_DESC; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_PROJECT; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_WEEK; + +import java.util.logging.Logger; + +import seedu.nova.commons.core.LogsCenter; +import seedu.nova.logic.commands.Command; +import seedu.nova.logic.commands.CommandResult; +import seedu.nova.logic.commands.exceptions.CommandException; +import seedu.nova.model.Model; +import seedu.nova.model.progresstracker.Project; +import seedu.nova.model.progresstracker.PtNote; +import seedu.nova.model.progresstracker.PtTask; +import seedu.nova.model.progresstracker.TaskDesc; + +/** + * Adds task to specified week + */ +public class PtAddCommand extends Command { + public static final String COMMAND_WORD = "add"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds task in the " + + "project in the specified week. " + + "Parameters: " + + PREFIX_PROJECT + "PROJECT " + + PREFIX_WEEK + "WEEK " + + PREFIX_DESC + "TASK DESCRIPTION \n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_PROJECT + "Ip " + + PREFIX_WEEK + "2 " + + PREFIX_DESC + "Implement javafx"; + + public static final String MESSAGE_NOWEEK = "No week beyond week 13"; + + public static final String MESSAGE_SUCCESS = "Added task to week %d of %s"; + + private final Logger logger = LogsCenter.getLogger(getClass()); + + private int weekNum; + private String project; + private String taskDesc; + + /** + * Creates an PtAddCommand object + * @param weekNum week to add PtTask to + * @param project project to add PtTask to + * @param taskDesc taskDesc for PtTask to be added + */ + public PtAddCommand(int weekNum, String project, String taskDesc) { + requireNonNull(project); + requireNonNull(taskDesc); + + this.weekNum = weekNum; + this.project = project.trim().toLowerCase(); + this.taskDesc = taskDesc; + } + + public int getWeekNum() { + return weekNum; + } + + public String getProject() { + return project; + } + + public String getTaskDesc() { + return taskDesc; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + logger.info("executing add PtTask command for: " + project + " week " + weekNum); + + requireNonNull(model); + boolean isOver13 = weekNum > 13; + + //if week is more than 13, throw no week error message + if (isOver13) { + throw new CommandException(MESSAGE_NOWEEK); + } else { + Project project; + boolean isIpProject = this.project.equals("ip"); + + if (isIpProject) { + project = model.getProgressTrackerIp(); + } else { + project = model.getProgressTrackerTp(); + } + + //Create new task + TaskDesc taskDesc = new TaskDesc(this.taskDesc); + PtTask newTask = new PtTask(taskDesc, project, new PtNote(""), this.weekNum, false); + + model.addPtTask(this.project, weekNum, newTask); + + String projectName = this.project.toUpperCase(); + String result = String.format(MESSAGE_SUCCESS, weekNum, projectName); + + return new CommandResult(result, false, false); + } + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof PtAddCommand)) { + return false; + } else { + boolean isSameProject = ((PtAddCommand) obj).getProject().equals(this.getProject()); + boolean isSameWeek = ((PtAddCommand) obj).getWeekNum() == this.getWeekNum(); + boolean isSameTaskDesc = ((PtAddCommand) obj).getTaskDesc().equals(this.getTaskDesc()); + + return isSameProject && isSameWeek && isSameTaskDesc; + } + } +} diff --git a/src/main/java/seedu/nova/logic/commands/ptcommands/PtAddNoteCommand.java b/src/main/java/seedu/nova/logic/commands/ptcommands/PtAddNoteCommand.java new file mode 100644 index 00000000000..3a6ac92f856 --- /dev/null +++ b/src/main/java/seedu/nova/logic/commands/ptcommands/PtAddNoteCommand.java @@ -0,0 +1,121 @@ +package seedu.nova.logic.commands.ptcommands; + +import static java.util.Objects.requireNonNull; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_DESC; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_PROJECT; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_TASK; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_WEEK; + +import java.util.logging.Logger; + +import seedu.nova.commons.core.LogsCenter; +import seedu.nova.logic.commands.Command; +import seedu.nova.logic.commands.CommandResult; +import seedu.nova.logic.commands.exceptions.CommandException; +import seedu.nova.model.Model; + +/** + * Adds note to specified task + */ +public class PtAddNoteCommand extends Command { + public static final String COMMAND_WORD = "addNote"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds note to specified task in the " + + "project in the specified week. " + + "Parameters: " + + PREFIX_PROJECT + "PROJECT " + + PREFIX_WEEK + "WEEK " + + PREFIX_TASK + "TASK " + + PREFIX_DESC + "NOTE \n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_PROJECT + "Ip " + + PREFIX_WEEK + "2 " + + PREFIX_TASK + "1 " + + PREFIX_DESC + "take note to do by 2359 Friday"; + + public static final String MESSAGE_NOWEEK = "No week beyond week 13"; + + public static final String MESSAGE_FAILURE = "Command failed. Please check that there is a task " + + "or that there isn't an existing note in the specified index"; + + public static final String MESSAGE_SUCCESS = "Added note to task %d in week %d of %s"; + + private final Logger logger = LogsCenter.getLogger(getClass()); + + private int weekNum; + private int taskNum; + private String project; + private String note; + + /** + * Creates a PtAddNoteCommand + * @param weekNum week of PtTask for note to be added to + * @param taskNum taskNum of PtTask for note to be added to + * @param project project of PtTask for note to be added to + * @param note note to be added to + */ + public PtAddNoteCommand(int weekNum, int taskNum, String project, String note) { + requireNonNull(project); + requireNonNull(note); + + this.weekNum = weekNum; + this.taskNum = taskNum; + this.project = project.trim().toLowerCase(); + this.note = note; + } + + public int getWeekNum() { + return weekNum; + } + + public int getTaskNum() { + return taskNum; + } + + public String getProject() { + return project; + } + + public String getNote() { + return note; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + logger.info("executing addNote command for: project " + project + ", week " + weekNum + ", task " + taskNum); + + requireNonNull(model); + boolean isOver13 = weekNum > 13; + + //if week is over 13, throw no week error message + if (isOver13) { + throw new CommandException(MESSAGE_NOWEEK); + } else { + boolean isAddSuccess = model.addPtNote(this.project, weekNum, taskNum, note); + + if (!isAddSuccess) { + throw new CommandException(MESSAGE_FAILURE); + } + + String projectName = this.project.toUpperCase(); + String result = String.format(MESSAGE_SUCCESS, taskNum, weekNum, projectName); + + return new CommandResult(result, false, false); + } + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof PtAddNoteCommand)) { + return false; + } else { + boolean isSameProject = ((PtAddNoteCommand) obj).getProject().equals(this.getProject()); + boolean isSameWeek = ((PtAddNoteCommand) obj).getWeekNum() == this.getWeekNum(); + boolean isSameTaskNum = ((PtAddNoteCommand) obj).getTaskNum() == (this.getTaskNum()); + boolean isSameNote = ((PtAddNoteCommand) obj).getNote().equals(this.getNote()); + + return isSameProject && isSameWeek && isSameNote && isSameTaskNum; + } + } +} + diff --git a/src/main/java/seedu/nova/logic/commands/ptcommands/PtDeleteCommand.java b/src/main/java/seedu/nova/logic/commands/ptcommands/PtDeleteCommand.java new file mode 100644 index 00000000000..287d096aec6 --- /dev/null +++ b/src/main/java/seedu/nova/logic/commands/ptcommands/PtDeleteCommand.java @@ -0,0 +1,108 @@ +package seedu.nova.logic.commands.ptcommands; + +import static java.util.Objects.requireNonNull; + +import static seedu.nova.logic.parser.CliSyntax.PREFIX_PROJECT; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_TASK; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_WEEK; + +import java.util.logging.Logger; + +import seedu.nova.commons.core.LogsCenter; +import seedu.nova.logic.commands.Command; +import seedu.nova.logic.commands.CommandResult; +import seedu.nova.logic.commands.exceptions.CommandException; +import seedu.nova.model.Model; + +/** + * Adds task to specified week + */ +public class PtDeleteCommand extends Command { + public static final String COMMAND_WORD = "delete"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Deletes task in the specified " + + "project in the specified week. " + + "Parameters: " + + PREFIX_PROJECT + "PROJECT " + + PREFIX_WEEK + "WEEK " + + PREFIX_TASK + "TASK \n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_PROJECT + "Ip " + + PREFIX_WEEK + "2 " + + PREFIX_TASK + "1"; + + public static final String MESSAGE_NOWEEK = "No week beyond week 13"; + + public static final String MESSAGE_FAILURE = "No task with that index"; + + public static final String MESSAGE_SUCCESS = "Deleted task %d in week %d of %s"; + + private final Logger logger = LogsCenter.getLogger(getClass()); + + private int weekNum; + private String project; + private int taskNum; + + /** + * Creates PtDeleteCommand object + * @param weekNum week of PtTask to be deleted + * @param project project of PtTask to be deleted + * @param taskNum task number of PtTask to be deleted + */ + public PtDeleteCommand(int weekNum, String project, int taskNum) { + requireNonNull(project); + + this.weekNum = weekNum; + this.project = project.trim().toLowerCase(); + this.taskNum = taskNum; + } + + public int getWeekNum() { + return weekNum; + } + + public String getProject() { + return project; + } + + public int getTaskNum() { + return taskNum; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + logger.info("executing delete command for: project " + project + ", week " + weekNum + ", task " + taskNum); + + requireNonNull(model); + boolean isOver13 = weekNum > 13; + + //if week is over 13, throw no week error message + if (isOver13) { + throw new CommandException(MESSAGE_NOWEEK); + } else { + boolean isDeleteSuccess = model.deletePtTask(this.project, weekNum, taskNum); + + if (!isDeleteSuccess) { + throw new CommandException(MESSAGE_FAILURE); + } + + String projectName = this.project.toUpperCase(); + String result = String.format(MESSAGE_SUCCESS, taskNum, weekNum, projectName); + + return new CommandResult(result, false, false); + } + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof PtDeleteCommand)) { + return false; + } else { + boolean isSameProject = ((PtDeleteCommand) obj).getProject().equals(this.getProject()); + boolean isSameWeek = ((PtDeleteCommand) obj).getWeekNum() == this.getWeekNum(); + boolean isSameTaskNum = ((PtDeleteCommand) obj).getTaskNum() == this.getTaskNum(); + + return isSameProject && isSameWeek && isSameTaskNum; + } + } +} diff --git a/src/main/java/seedu/nova/logic/commands/ptcommands/PtDeleteNoteCommand.java b/src/main/java/seedu/nova/logic/commands/ptcommands/PtDeleteNoteCommand.java new file mode 100644 index 00000000000..f5a2caaa4b3 --- /dev/null +++ b/src/main/java/seedu/nova/logic/commands/ptcommands/PtDeleteNoteCommand.java @@ -0,0 +1,111 @@ +package seedu.nova.logic.commands.ptcommands; + +import static java.util.Objects.requireNonNull; + +import static seedu.nova.logic.parser.CliSyntax.PREFIX_PROJECT; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_TASK; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_WEEK; + +import java.util.logging.Logger; + +import seedu.nova.commons.core.LogsCenter; +import seedu.nova.logic.commands.Command; +import seedu.nova.logic.commands.CommandResult; +import seedu.nova.logic.commands.exceptions.CommandException; +import seedu.nova.model.Model; + +/** + * Deletes note in specified task + */ +public class PtDeleteNoteCommand extends Command { + public static final String COMMAND_WORD = "deleteNote"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Deletes note in specified task in the " + + "project in the specified week. " + + "Parameters: " + + PREFIX_PROJECT + "PROJECT " + + PREFIX_WEEK + "WEEK " + + PREFIX_TASK + "TASK \n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_PROJECT + "Ip " + + PREFIX_WEEK + "2 " + + PREFIX_TASK + "1"; + + public static final String MESSAGE_NOWEEK = "No week beyond week 13"; + + public static final String MESSAGE_FAILURE = "Command failed. Please check that there is a task " + + "or note in the specified index"; + + public static final String MESSAGE_SUCCESS = "Deleted note to task %d in week %d of %s"; + + private final Logger logger = LogsCenter.getLogger(getClass()); + + private int weekNum; + private int taskNum; + private String project; + + /** + * Creates PtDeleteNoteCommand object + * @param weekNum week of PtTask with note to be deleted + * @param taskNum task number of PtTask with note to be deleted + * @param project project of PtTask with note to be deleted + */ + public PtDeleteNoteCommand(int weekNum, int taskNum, String project) { + requireNonNull(project); + + this.weekNum = weekNum; + this.taskNum = taskNum; + this.project = project.trim().toLowerCase(); + } + + public int getWeekNum() { + return weekNum; + } + + public int getTaskNum() { + return taskNum; + } + + public String getProject() { + return project; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + logger.info("executing deleteNote command for: project " + project + ", week " + weekNum + ", task " + taskNum); + + requireNonNull(model); + boolean isOver13 = weekNum > 13; + + //if week is over 13, throw no week error message + if (isOver13) { + throw new CommandException(MESSAGE_NOWEEK); + } else { + boolean isDeleteNoteSuccess = model.deletePtNote(this.project, weekNum, taskNum); + + if (!isDeleteNoteSuccess) { + throw new CommandException(MESSAGE_FAILURE); + } + + String projectName = this.project.toUpperCase(); + String result = String.format(MESSAGE_SUCCESS, taskNum, weekNum, projectName); + + return new CommandResult(result, false, false); + } + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof PtDeleteNoteCommand)) { + return false; + } else { + boolean isSameProject = ((PtDeleteNoteCommand) obj).getProject().equals(this.getProject()); + boolean isSameWeek = ((PtDeleteNoteCommand) obj).getWeekNum() == this.getWeekNum(); + boolean isSameTaskNum = ((PtDeleteNoteCommand) obj).getTaskNum() == (this.getTaskNum()); + + return isSameProject && isSameWeek && isSameTaskNum; + } + } +} + + diff --git a/src/main/java/seedu/nova/logic/commands/ptcommands/PtDoneCommand.java b/src/main/java/seedu/nova/logic/commands/ptcommands/PtDoneCommand.java new file mode 100644 index 00000000000..cdd3692fb7f --- /dev/null +++ b/src/main/java/seedu/nova/logic/commands/ptcommands/PtDoneCommand.java @@ -0,0 +1,108 @@ +package seedu.nova.logic.commands.ptcommands; + +import static java.util.Objects.requireNonNull; + +import static seedu.nova.logic.parser.CliSyntax.PREFIX_PROJECT; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_TASK; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_WEEK; + +import java.util.logging.Logger; + +import seedu.nova.commons.core.LogsCenter; +import seedu.nova.logic.commands.Command; +import seedu.nova.logic.commands.CommandResult; +import seedu.nova.logic.commands.exceptions.CommandException; +import seedu.nova.model.Model; + +/** + * Adds task to specified week + */ +public class PtDoneCommand extends Command { + public static final String COMMAND_WORD = "done"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Sets specified task as done " + + "Parameters: " + + PREFIX_PROJECT + "PROJECT " + + PREFIX_WEEK + "WEEK " + + PREFIX_TASK + "TASK \n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_PROJECT + "Ip " + + PREFIX_WEEK + "2 " + + PREFIX_TASK + "1"; + + public static final String MESSAGE_NOWEEK = "No week beyond week 13"; + + public static final String MESSAGE_NULLTASK = "No task with that index"; + + public static final String MESSAGE_SUCCESS = "Changed done status of task %d in week %d of %s"; + + private final Logger logger = LogsCenter.getLogger(getClass()); + + private int weekNum; + private String project; + private int taskNum; + + /** + * Creates PtDoneCommand object + * @param weekNum week of PtTask to set done/ undone + * @param project project of PtTask to set done/ undone + * @param taskNum task number of PtTask to set done/ undone + */ + public PtDoneCommand(int weekNum, String project, int taskNum) { + requireNonNull(project); + + this.weekNum = weekNum; + this.project = project.trim().toLowerCase(); + this.taskNum = taskNum; + } + + public int getWeekNum() { + return weekNum; + } + + public String getProject() { + return project; + } + + public int getTaskNum() { + return taskNum; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + logger.info("executing done command for: project " + project + ", week " + weekNum + ", task " + taskNum); + + requireNonNull(model); + boolean isOver13 = weekNum > 13; + + //if week is over 13, throw no week error message + if (isOver13) { + throw new CommandException(MESSAGE_NOWEEK); + } else { + boolean isSetDoneSuccessful = model.setDonePtTask(this.project, weekNum, taskNum); + + if (!isSetDoneSuccessful) { + throw new CommandException(MESSAGE_NULLTASK); + } + + String projectName = this.project.toUpperCase(); + String result = String.format(MESSAGE_SUCCESS, taskNum, weekNum, projectName); + + return new CommandResult(result, false, false); + } + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof PtDoneCommand)) { + return false; + } else { + boolean isSameProject = ((PtDoneCommand) obj).getProject().equals(this.getProject()); + boolean isSameWeek = ((PtDoneCommand) obj).getWeekNum() == this.getWeekNum(); + boolean isSameTaskNum = ((PtDoneCommand) obj).getTaskNum() == this.getTaskNum(); + + return isSameProject && isSameWeek && isSameTaskNum; + } + } +} + diff --git a/src/main/java/seedu/nova/logic/commands/ptcommands/PtEditCommand.java b/src/main/java/seedu/nova/logic/commands/ptcommands/PtEditCommand.java new file mode 100644 index 00000000000..7b059c7e826 --- /dev/null +++ b/src/main/java/seedu/nova/logic/commands/ptcommands/PtEditCommand.java @@ -0,0 +1,120 @@ +package seedu.nova.logic.commands.ptcommands; + +import static java.util.Objects.requireNonNull; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_DESC; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_PROJECT; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_TASK; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_WEEK; + +import java.util.logging.Logger; + +import seedu.nova.commons.core.LogsCenter; +import seedu.nova.logic.commands.Command; +import seedu.nova.logic.commands.CommandResult; +import seedu.nova.logic.commands.exceptions.CommandException; +import seedu.nova.model.Model; + +/** + * Adds task to specified week + */ +public class PtEditCommand extends Command { + public static final String COMMAND_WORD = "edit"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits task in the " + + "project in the specified week. " + + "Parameters: " + + PREFIX_PROJECT + "PROJECT " + + PREFIX_WEEK + "WEEK " + + PREFIX_TASK + "TASK " + + PREFIX_DESC + "TASK DESCRIPTION \n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_PROJECT + "Ip " + + PREFIX_WEEK + "2 " + + PREFIX_TASK + "1 " + + PREFIX_DESC + "Implement javafx"; + + public static final String MESSAGE_NOWEEK = "No week beyond week 13"; + + public static final String MESSAGE_FAILURE = "No task with that index"; + + public static final String MESSAGE_SUCCESS = "Edited task %d in week %d of %s"; + + private final Logger logger = LogsCenter.getLogger(getClass()); + + private int weekNum; + private String project; + private String taskDesc; + private int taskNum; + + /** + * Creates PtEditCommand object + * @param weekNum week of PtTask to be edited + * @param project project of PtTask to be edited + * @param taskDesc taskDesc of PtTask to be edited + * @param taskNum task number of PtTask to be edited + */ + public PtEditCommand(int weekNum, String project, String taskDesc, int taskNum) { + requireNonNull(project); + requireNonNull(taskDesc); + + this.weekNum = weekNum; + this.project = project.trim().toLowerCase(); + this.taskDesc = taskDesc; + this.taskNum = taskNum; + } + + public int getWeekNum() { + return weekNum; + } + + public String getProject() { + return project; + } + + public String getTaskDesc() { + return taskDesc; + } + + public int getTaskNum() { + return taskNum; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + logger.info("executing edit command for: project " + project + ", week " + weekNum + ", task " + taskNum); + + requireNonNull(model); + boolean isOver13 = weekNum > 13; + + //if week is over 13, throw no week error message + if (isOver13) { + throw new CommandException(MESSAGE_NOWEEK); + } else { + boolean isEditSuccess = model.editPtTask(this.project, weekNum, taskNum, taskDesc); + + if (!isEditSuccess) { + throw new CommandException(MESSAGE_FAILURE); + } + + String projectName = this.project.toUpperCase(); + String result = String.format(MESSAGE_SUCCESS, taskNum, weekNum, projectName); + + return new CommandResult(result, false, false); + } + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof PtEditCommand)) { + return false; + } else { + boolean isSameProject = ((PtEditCommand) obj).getProject().equals(this.getProject()); + boolean isSameWeek = ((PtEditCommand) obj).getWeekNum() == this.getWeekNum(); + boolean isSameTaskDesc = ((PtEditCommand) obj).getTaskDesc().equals(this.getTaskDesc()); + boolean isSameTaskNum = ((PtEditCommand) obj).getTaskNum() == this.getTaskNum(); + + return isSameProject && isSameWeek && isSameTaskNum && isSameTaskDesc; + } + } +} + diff --git a/src/main/java/seedu/nova/logic/commands/ptcommands/PtEditNoteCommand.java b/src/main/java/seedu/nova/logic/commands/ptcommands/PtEditNoteCommand.java new file mode 100644 index 00000000000..29f01f207d5 --- /dev/null +++ b/src/main/java/seedu/nova/logic/commands/ptcommands/PtEditNoteCommand.java @@ -0,0 +1,120 @@ +package seedu.nova.logic.commands.ptcommands; + +import static java.util.Objects.requireNonNull; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_DESC; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_PROJECT; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_TASK; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_WEEK; + +import java.util.logging.Logger; + +import seedu.nova.commons.core.LogsCenter; +import seedu.nova.logic.commands.Command; +import seedu.nova.logic.commands.CommandResult; +import seedu.nova.logic.commands.exceptions.CommandException; +import seedu.nova.model.Model; + +/** + * Edits task to specified week + */ +public class PtEditNoteCommand extends Command { + public static final String COMMAND_WORD = "editNote"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits note in specified task in the " + + "project in the specified week. " + + "Parameters: " + + PREFIX_PROJECT + "PROJECT " + + PREFIX_WEEK + "WEEK " + + PREFIX_TASK + "TASK " + + PREFIX_DESC + "NOTE \n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_PROJECT + "Ip " + + PREFIX_WEEK + "2 " + + PREFIX_TASK + "1 " + + PREFIX_DESC + "take note to do by 2359 Friday"; + + public static final String MESSAGE_NOWEEK = "No week beyond week 13"; + + public static final String MESSAGE_FAILURE = "Command failed. Please check that there is a task " + + "or note in the specified index"; + + public static final String MESSAGE_SUCCESS = "Edited note to task %d in week %d of %s"; + + private final Logger logger = LogsCenter.getLogger(getClass()); + + private int weekNum; + private int taskNum; + private String project; + private String note; + + /** + * Creates PtEditNoteCommand object + * @param weekNum week of PtTask with note to be edited + * @param taskNum task number of PtTask with note to be edited + * @param project project of PtTask with note to be edited + * @param note note to be edited + */ + public PtEditNoteCommand(int weekNum, int taskNum, String project, String note) { + requireNonNull(project); + requireNonNull(note); + + this.weekNum = weekNum; + this.taskNum = taskNum; + this.project = project.trim().toLowerCase(); + this.note = note; + } + + public int getWeekNum() { + return weekNum; + } + + public int getTaskNum() { + return taskNum; + } + + public String getProject() { + return project; + } + + public String getNote() { + return note; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + logger.info("executing editNote command for: project " + project + ", week " + weekNum + ", task " + taskNum); + + requireNonNull(model); + boolean isOver13 = weekNum > 13; + + //if week is over 13, throw no week error message + if (isOver13) { + throw new CommandException(MESSAGE_NOWEEK); + } else { + boolean isEditNoteSuccess = model.editPtNote(this.project, weekNum, taskNum, note); + + if (!isEditNoteSuccess) { + throw new CommandException(MESSAGE_FAILURE); + } + + String projectName = this.project.toUpperCase(); + String result = String.format(MESSAGE_SUCCESS, taskNum, weekNum, projectName); + + return new CommandResult(result, false, false); + } + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof PtEditNoteCommand)) { + return false; + } else { + boolean isSameProject = ((PtEditNoteCommand) obj).getProject().equals(this.getProject()); + boolean isSameWeek = ((PtEditNoteCommand) obj).getWeekNum() == this.getWeekNum(); + boolean isSameTaskNum = ((PtEditNoteCommand) obj).getTaskNum() == (this.getTaskNum()); + boolean isSameNote = ((PtEditNoteCommand) obj).getNote().equals(this.getNote()); + + return isSameProject && isSameWeek && isSameNote && isSameTaskNum; + } + } +} diff --git a/src/main/java/seedu/nova/logic/commands/ptcommands/PtListCommand.java b/src/main/java/seedu/nova/logic/commands/ptcommands/PtListCommand.java new file mode 100644 index 00000000000..a60ac3e27c4 --- /dev/null +++ b/src/main/java/seedu/nova/logic/commands/ptcommands/PtListCommand.java @@ -0,0 +1,98 @@ +package seedu.nova.logic.commands.ptcommands; + +import static java.util.Objects.requireNonNull; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_PROJECT; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_WEEK; + +import java.util.logging.Logger; + +import seedu.nova.commons.core.LogsCenter; +import seedu.nova.logic.commands.Command; +import seedu.nova.logic.commands.CommandResult; +import seedu.nova.logic.commands.exceptions.CommandException; +import seedu.nova.model.Model; + +/** + * Executes list command + */ +public class PtListCommand extends Command { + public static final String COMMAND_WORD = "list"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Lists tasks in the specified " + + "project in the specified week. " + + "Parameters: " + + PREFIX_PROJECT + "PROJECT " + + PREFIX_WEEK + "WEEK \n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_PROJECT + "Ip " + + PREFIX_WEEK + "2"; + + public static final String MESSAGE_NULLWEEK = "No task in specified week"; + + public static final String MESSAGE_NOWEEK = "No week beyond week 13"; + + public static final String MESSAGE_SUCCESS = "%s Project (Week %d):\n"; + + private final Logger logger = LogsCenter.getLogger(getClass()); + + private int weekNum; + private String project; + + /** + * Creates PtListCommand object + * @param weekNum week to be listed + * @param project project to be listed + */ + public PtListCommand(int weekNum, String project) { + requireNonNull(project); + + this.weekNum = weekNum; + this.project = project.trim().toLowerCase(); + } + + public int getWeekNum() { + return weekNum; + } + + public String getProject() { + return project; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + logger.info("executing list command for: project " + project + ", week " + weekNum); + + requireNonNull(model); + boolean isOver13 = weekNum > 13; + + //if week is over 13, throw no week error message + if (isOver13) { + throw new CommandException(MESSAGE_NOWEEK); + } else { + String listResult = model.listPtTask(this.project, weekNum); + + boolean noTask = listResult.equals(""); + + if (noTask) { + throw new CommandException(MESSAGE_NULLWEEK); + } + + String header = String.format(MESSAGE_SUCCESS, this.project.toUpperCase(), weekNum); + String result = header + listResult; + + return new CommandResult(result, false, false); + } + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof PtListCommand)) { + return false; + } else { + boolean isSameProject = ((PtListCommand) obj).getProject().equals(this.getProject()); + boolean isSameWeek = ((PtListCommand) obj).getWeekNum() == this.getWeekNum(); + + return isSameProject && isSameWeek; + } + } +} diff --git a/src/main/java/seedu/nova/logic/commands/sccommands/ScViewCommand.java b/src/main/java/seedu/nova/logic/commands/sccommands/ScViewCommand.java new file mode 100644 index 00000000000..0634de63da6 --- /dev/null +++ b/src/main/java/seedu/nova/logic/commands/sccommands/ScViewCommand.java @@ -0,0 +1,25 @@ +package seedu.nova.logic.commands.sccommands; + +import static seedu.nova.logic.parser.CliSyntax.PREFIX_DATE; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_INDEX; + +import seedu.nova.logic.commands.Command; + +/** + * The generic view command of schedule. + * A figurehead for the SvViewCommandParser. + */ +public abstract class ScViewCommand extends Command { + + /** + * The constant COMMAND_WORD. + */ + public static final String COMMAND_WORD = "view"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Views your schedule on a particular day. " + + "Parameters: " + + PREFIX_DATE + "[YYYY-MM-DD] \n" + + COMMAND_WORD + " week: Views your schedule on a particular week. " + + "Parameters: " + + PREFIX_INDEX + "[week #]"; + +} diff --git a/src/main/java/seedu/nova/logic/commands/sccommands/ScViewDayCommand.java b/src/main/java/seedu/nova/logic/commands/sccommands/ScViewDayCommand.java new file mode 100644 index 00000000000..0942d916534 --- /dev/null +++ b/src/main/java/seedu/nova/logic/commands/sccommands/ScViewDayCommand.java @@ -0,0 +1,67 @@ +package seedu.nova.logic.commands.sccommands; + +import static java.util.Objects.requireNonNull; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_DATE; + +import java.time.LocalDate; + +import seedu.nova.logic.commands.CommandResult; +import seedu.nova.logic.commands.exceptions.CommandException; +import seedu.nova.model.Model; + +/** + * The type Sc view day command. + */ +public class ScViewDayCommand extends ScViewCommand { + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Views your schedule on a particular day. " + + "Parameters: " + + PREFIX_DATE + "[YYYY-MM-DD]"; + + public static final String MESSAGE_DATE_OUT_OF_RANGE = "The date is not within the schedule"; + public static final String MESSAGE_NO_EVENT = "You have no event on that day"; + public static final String MESSAGE_NO_EVENT_TODAY = "You have no event today"; + + private final LocalDate date; + + /** + * Instantiates a new Sc view day command. + * + * @param date the date + */ + public ScViewDayCommand(LocalDate date) { + + requireNonNull(date); + this.date = date; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + + requireNonNull(model); + + if (!model.isWithinSem(date)) { + throw new CommandException(MESSAGE_DATE_OUT_OF_RANGE); + } + + String message = model.viewSchedule(date); + + if (message.equals("")) { + if (date.equals(LocalDate.now())) { + return new CommandResult(MESSAGE_NO_EVENT_TODAY); + } else { + return new CommandResult(MESSAGE_NO_EVENT); + } + } else { + return new CommandResult(message); + } + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof ScViewDayCommand // instanceof handles nulls + && date.equals(((ScViewDayCommand) other).date)); + } + +} diff --git a/src/main/java/seedu/nova/logic/commands/sccommands/ScViewFreeSlotCommand.java b/src/main/java/seedu/nova/logic/commands/sccommands/ScViewFreeSlotCommand.java new file mode 100644 index 00000000000..c986a9be842 --- /dev/null +++ b/src/main/java/seedu/nova/logic/commands/sccommands/ScViewFreeSlotCommand.java @@ -0,0 +1,52 @@ +package seedu.nova.logic.commands.sccommands; + +import static java.util.Objects.requireNonNull; + +import java.time.LocalDate; + +import seedu.nova.logic.commands.Command; +import seedu.nova.logic.commands.CommandResult; +import seedu.nova.logic.commands.exceptions.CommandException; +import seedu.nova.model.Model; + +/** + * Find free slot + */ +public class ScViewFreeSlotCommand extends Command { + /** + * The constant COMMAND_WORD. + */ + public static final String COMMAND_WORD = "freeslot"; + private static final String MESSAGE_DATE_OUT_OF_RANGE = "The date is not within the schedule"; + private static final String MESSAGE_NO_EVENT = "You have no event on that day"; + + private final LocalDate date; + + /** + * Instantiates a new Sc view day command. + * + * @param date the date + */ + public ScViewFreeSlotCommand(LocalDate date) { + + this.date = date; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + + requireNonNull(model); + + if (!model.isWithinSem(date)) { + throw new CommandException(MESSAGE_DATE_OUT_OF_RANGE); + } + + String freeSlotString = model.viewFreeSlot(date); + + if (freeSlotString.equals("")) { + return new CommandResult(MESSAGE_NO_EVENT); + } else { + return new CommandResult(freeSlotString); + } + } +} diff --git a/src/main/java/seedu/nova/logic/commands/sccommands/ScViewWeekCommand.java b/src/main/java/seedu/nova/logic/commands/sccommands/ScViewWeekCommand.java new file mode 100644 index 00000000000..428378f99ca --- /dev/null +++ b/src/main/java/seedu/nova/logic/commands/sccommands/ScViewWeekCommand.java @@ -0,0 +1,57 @@ +package seedu.nova.logic.commands.sccommands; + +import static java.util.Objects.requireNonNull; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_INDEX; + +import seedu.nova.logic.commands.CommandResult; +import seedu.nova.logic.commands.exceptions.CommandException; +import seedu.nova.model.Model; + +/** + * The view week command of schedule. + */ +public class ScViewWeekCommand extends ScViewCommand { + + public static final String MESSAGE_USAGE = COMMAND_WORD + " week : Views your schedule on a particular week. " + + "Parameters: " + + PREFIX_INDEX + "[week #]"; + + public static final String MESSAGE_WEEK_OUT_OF_RANGE = "That week is not within the schedule"; + public static final String MESSAGE_WEEK_NO_EVENT = "You have no events on that week"; + + private final int weekNumber; + + /** + * Instantiates a new ScViewWeekCommand object. + * + * @param weekNumber the week number to view + */ + public ScViewWeekCommand(int weekNumber) { + this.weekNumber = weekNumber; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + + requireNonNull(model); + + if (!model.isWithinSem(weekNumber)) { + throw new CommandException(MESSAGE_WEEK_OUT_OF_RANGE); + } + + String message = model.viewSchedule(weekNumber); + + if (message.equals("")) { + message = MESSAGE_WEEK_NO_EVENT; + } + return new CommandResult(message); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof ScViewWeekCommand // instanceof handles nulls + && weekNumber == ((ScViewWeekCommand) other).weekNumber); + } + +} diff --git a/src/main/java/seedu/nova/logic/commands/sccommands/eventcommands/EventAddConsultationCommand.java b/src/main/java/seedu/nova/logic/commands/sccommands/eventcommands/EventAddConsultationCommand.java new file mode 100644 index 00000000000..19c44ac4c77 --- /dev/null +++ b/src/main/java/seedu/nova/logic/commands/sccommands/eventcommands/EventAddConsultationCommand.java @@ -0,0 +1,75 @@ +package seedu.nova.logic.commands.sccommands.eventcommands; + +import static java.util.Objects.requireNonNull; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_DESC; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_TIME; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_VENUE; + +import java.util.logging.Logger; + +import seedu.nova.commons.core.LogsCenter; +import seedu.nova.logic.commands.Command; +import seedu.nova.logic.commands.CommandResult; +import seedu.nova.logic.commands.exceptions.CommandException; +import seedu.nova.model.Model; +import seedu.nova.model.schedule.event.Event; +import seedu.nova.model.schedule.event.exceptions.TimeOverlapException; + +/** + * Adds a Consultation into the Schedule. + */ +public class EventAddConsultationCommand extends Command { + public static final String COMMAND_WORD = "consultation"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a consultation to the schedule. \n" + + "Parameters: " + + PREFIX_DESC + "[description] " + + PREFIX_VENUE + "[venue] " + + PREFIX_TIME + "[YYYY-MM-DD] [Start time (HH:MM)] [End time (HH:MM)] " + + "\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_DESC + "clarify UML " + + PREFIX_VENUE + "COM1 " + + PREFIX_TIME + "2020-03-20 14:00 16:00 "; + + public static final String MESSAGE_SUCCESS = "New consultation has been added: \n%1$s\n"; + public static final String MESSAGE_TIME_OVERLAP = "You already have an event within that time frame."; + public static final String MESSAGE_INVALID_DATE = "That date does not fall within the semester."; + + private final Logger logger = LogsCenter.getLogger(getClass()); + + private Event toAdd; + + /** + * Creates an EventAddConsultationCommand to add the specified {@code Event} + */ + public EventAddConsultationCommand(Event consultation) { + requireNonNull(consultation); + this.toAdd = consultation; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + logger.info("executing add consultation command for: \n" + toAdd); + + requireNonNull(model); + + if (!model.isWithinSem(toAdd.getDate())) { + throw new CommandException(MESSAGE_INVALID_DATE); + } + + try { + model.addEvent(toAdd); + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); + } catch (TimeOverlapException e) { + throw new CommandException(MESSAGE_TIME_OVERLAP); + } + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof EventAddConsultationCommand // instanceof handles nulls + && toAdd.equals(((EventAddConsultationCommand) other).toAdd)); + } +} diff --git a/src/main/java/seedu/nova/logic/commands/sccommands/eventcommands/EventAddLessonCommand.java b/src/main/java/seedu/nova/logic/commands/sccommands/eventcommands/EventAddLessonCommand.java new file mode 100644 index 00000000000..10125f24fa9 --- /dev/null +++ b/src/main/java/seedu/nova/logic/commands/sccommands/eventcommands/EventAddLessonCommand.java @@ -0,0 +1,74 @@ +package seedu.nova.logic.commands.sccommands.eventcommands; + +import static java.util.Objects.requireNonNull; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_DESC; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_TIME; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_VENUE; + +import java.util.logging.Logger; + +import seedu.nova.commons.core.LogsCenter; +import seedu.nova.logic.commands.Command; +import seedu.nova.logic.commands.CommandResult; +import seedu.nova.logic.commands.exceptions.CommandException; +import seedu.nova.model.Model; +import seedu.nova.model.schedule.event.Event; +import seedu.nova.model.schedule.event.Lesson; +import seedu.nova.model.schedule.event.exceptions.TimeOverlapException; + + +/** + * Adds a Lesson into the Schedule. + */ +public class EventAddLessonCommand extends Command { + public static final String COMMAND_WORD = "lesson"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a lesson to the schedule. \n" + + "Parameters: " + + PREFIX_DESC + "[description] " + + PREFIX_VENUE + "[venue] " + + PREFIX_TIME + "[day] [Start time (HH:MM)] [End time (HH:MM)] " + + "\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_DESC + "CS2103T Tutorial " + + PREFIX_VENUE + "COM1 B1-03 " + + PREFIX_TIME + "Friday 10:00 11:00 "; + + public static final String MESSAGE_SUCCESS = "New lesson has been added: \n%1$s\n"; + public static final String MESSAGE_TIME_OVERLAP = "You already have an event within that time frame."; + + private final Logger logger = LogsCenter.getLogger(getClass()); + + private Event toAdd; + + /** + * Creates an EventAddLessonCommand to add the specified {@code Event} + */ + public EventAddLessonCommand(Event lesson) { + requireNonNull(lesson); + this.toAdd = lesson; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + logger.info("executing add lesson command for: \n" + toAdd); + + requireNonNull(model); + + try { + model.addAllLessons((Lesson) toAdd); + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); + } catch (TimeOverlapException e) { + throw new CommandException(MESSAGE_TIME_OVERLAP); + } + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof EventAddLessonCommand // instanceof handles nulls + && toAdd.equals(((EventAddLessonCommand) other).toAdd)); + } + + +} diff --git a/src/main/java/seedu/nova/logic/commands/sccommands/eventcommands/EventAddMeetingCommand.java b/src/main/java/seedu/nova/logic/commands/sccommands/eventcommands/EventAddMeetingCommand.java new file mode 100644 index 00000000000..81852df79d0 --- /dev/null +++ b/src/main/java/seedu/nova/logic/commands/sccommands/eventcommands/EventAddMeetingCommand.java @@ -0,0 +1,78 @@ +package seedu.nova.logic.commands.sccommands.eventcommands; + +import static java.util.Objects.requireNonNull; + +import static seedu.nova.logic.parser.CliSyntax.PREFIX_DESC; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_TIME; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_VENUE; + +import java.util.logging.Logger; + +import seedu.nova.commons.core.LogsCenter; +import seedu.nova.logic.commands.Command; +import seedu.nova.logic.commands.CommandResult; +import seedu.nova.logic.commands.exceptions.CommandException; +import seedu.nova.model.Model; +import seedu.nova.model.schedule.event.Event; +import seedu.nova.model.schedule.event.exceptions.TimeOverlapException; + +/** + * Adds a Meeting into the Schedule. + */ +public class EventAddMeetingCommand extends Command { + + public static final String COMMAND_WORD = "meeting"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a meeting to the schedule. \n" + + "Parameters: " + + PREFIX_DESC + "[description] " + + PREFIX_VENUE + "[venue] " + + PREFIX_TIME + "[YYYY-MM-DD] [Start time (HH:MM)] [End time (HH:MM)] " + + "\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_DESC + "CS2103T GUI refactoring " + + PREFIX_VENUE + "COM1 Basement " + + PREFIX_TIME + "2020-03-10 14:00 16:00 "; + + public static final String MESSAGE_SUCCESS = "New meeting has been added: \n%1$s\n"; + public static final String MESSAGE_TIME_OVERLAP = "You already have an event within that time frame."; + public static final String MESSAGE_INVALID_DATE = "That date does not fall within the semester."; + + private final Logger logger = LogsCenter.getLogger(getClass()); + + private Event toAdd; + + /** + * Creates an EventAddMeetingCommand to add the specified {@code Event} + */ + public EventAddMeetingCommand(Event meeting) { + requireNonNull(meeting); + this.toAdd = meeting; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + logger.info("executing add meeting command for: \n" + toAdd); + + requireNonNull(model); + + if (!model.isWithinSem(toAdd.getDate())) { + throw new CommandException(MESSAGE_INVALID_DATE); + } + + try { + model.addEvent(toAdd); + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); + } catch (TimeOverlapException e) { + throw new CommandException(MESSAGE_TIME_OVERLAP); + } + + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof EventAddMeetingCommand // instanceof handles nulls + && toAdd.equals(((EventAddMeetingCommand) other).toAdd)); + } +} diff --git a/src/main/java/seedu/nova/logic/commands/sccommands/eventcommands/EventAddNoteCommand.java b/src/main/java/seedu/nova/logic/commands/sccommands/eventcommands/EventAddNoteCommand.java new file mode 100644 index 00000000000..85e091d2954 --- /dev/null +++ b/src/main/java/seedu/nova/logic/commands/sccommands/eventcommands/EventAddNoteCommand.java @@ -0,0 +1,91 @@ +package seedu.nova.logic.commands.sccommands.eventcommands; + +import static java.util.Objects.requireNonNull; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_DESC; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_INDEX; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_TIME; + +import java.time.LocalDate; +import java.util.logging.Logger; + +import seedu.nova.commons.core.LogsCenter; +import seedu.nova.commons.core.index.Index; +import seedu.nova.logic.commands.Command; +import seedu.nova.logic.commands.CommandResult; +import seedu.nova.logic.commands.exceptions.CommandException; + +import seedu.nova.model.Model; +import seedu.nova.model.schedule.event.exceptions.DateNotFoundException; +import seedu.nova.model.schedule.event.exceptions.EventNotFoundException; + +/** + * Adds a note into an Event using its date and index in the list. + */ +public class EventAddNoteCommand extends Command { + public static final String COMMAND_WORD = "note"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a note to an event. \n" + + "Parameters: " + + PREFIX_DESC + "[description] " + + PREFIX_TIME + "[YYYY-MM-DD] " + + PREFIX_INDEX + "[index]" + + "\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_DESC + "Remember to bring charger! " + + PREFIX_TIME + "2020-03-10 " + + PREFIX_INDEX + "2"; + + public static final String MESSAGE_SUCCESS = "New note has been added: \n%1$s\n"; + public static final String MESSAGE_NO_EVENT = "Invalid date - you have no events that week."; + public static final String MESSAGE_INVALID_INDEX_DATE = "Invalid date or index - that event does not exist."; + public static final String MESSAGE_INVALID_DATE = "That date does not fall within the semester."; + + private final Logger logger = LogsCenter.getLogger(getClass()); + + private String desc; + private LocalDate date; + private Index index; + + public EventAddNoteCommand(String desc, LocalDate date, Index index) { + requireNonNull(desc); + requireNonNull(date); + requireNonNull(index); + + this.desc = desc; + this.date = date; + this.index = index; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + logger.info("executing add note command for event at: \n" + "date: " + date + ", index: " + index); + + requireNonNull(model); + + if (!model.isWithinSem(date)) { + throw new CommandException(MESSAGE_INVALID_DATE); + } + + try { + int i = index.getZeroBased(); + String response = model.addNote(desc, date, i); + return new CommandResult(String.format(MESSAGE_SUCCESS, response)); + + } catch (DateNotFoundException e) { + throw new CommandException(MESSAGE_NO_EVENT); + + } catch (EventNotFoundException e) { + throw new CommandException(MESSAGE_INVALID_INDEX_DATE); + } + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof EventAddNoteCommand // instanceof handles nulls + && index.equals(((EventAddNoteCommand) other).index) + && date.equals(((EventAddNoteCommand) other).date) + && desc.equals(((EventAddNoteCommand) other).desc)); // state check + } + +} diff --git a/src/main/java/seedu/nova/logic/commands/sccommands/eventcommands/EventAddStudyCommand.java b/src/main/java/seedu/nova/logic/commands/sccommands/eventcommands/EventAddStudyCommand.java new file mode 100644 index 00000000000..7d594229fba --- /dev/null +++ b/src/main/java/seedu/nova/logic/commands/sccommands/eventcommands/EventAddStudyCommand.java @@ -0,0 +1,76 @@ +package seedu.nova.logic.commands.sccommands.eventcommands; + +import static java.util.Objects.requireNonNull; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_DESC; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_TIME; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_VENUE; + +import java.util.logging.Logger; + +import seedu.nova.commons.core.LogsCenter; +import seedu.nova.logic.commands.Command; +import seedu.nova.logic.commands.CommandResult; +import seedu.nova.logic.commands.exceptions.CommandException; +import seedu.nova.model.Model; +import seedu.nova.model.schedule.event.Event; +import seedu.nova.model.schedule.event.exceptions.TimeOverlapException; + +/** + * Adds a StudySession into the Schedule. + */ +public class EventAddStudyCommand extends Command { + public static final String COMMAND_WORD = "study"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a study session to the schedule. \n" + + "Parameters: " + + PREFIX_DESC + "[description] " + + PREFIX_VENUE + "[venue] " + + PREFIX_TIME + "[YYYY-MM-DD] [Start time (HH:MM)] [End time (HH:MM)] " + + "\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_DESC + "cool peeps revision " + + PREFIX_VENUE + "COM1 Basement " + + PREFIX_TIME + "2020-03-19 14:00 15:00 "; + + public static final String MESSAGE_SUCCESS = "New study session has been added: \n%1$s\n"; + public static final String MESSAGE_TIME_OVERLAP = "You already have an event within that time frame."; + public static final String MESSAGE_INVALID_DATE = "That date does not fall within the semester."; + + private final Logger logger = LogsCenter.getLogger(getClass()); + + private Event toAdd; + + /** + * Creates an EventAddStudyCommand to add the specified {@code Event} + */ + public EventAddStudyCommand(Event study) { + requireNonNull(study); + this.toAdd = study; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + logger.info("executing add meeting command for: \n" + toAdd); + + requireNonNull(model); + + if (!model.isWithinSem(toAdd.getDate())) { + throw new CommandException(MESSAGE_INVALID_DATE); + } + + try { + model.addEvent(toAdd); + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); + } catch (TimeOverlapException e) { + throw new CommandException(MESSAGE_TIME_OVERLAP); + } + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof EventAddStudyCommand // instanceof handles nulls + && toAdd.equals(((EventAddStudyCommand) other).toAdd)); + } + +} diff --git a/src/main/java/seedu/nova/logic/commands/sccommands/eventcommands/EventDeleteCommand.java b/src/main/java/seedu/nova/logic/commands/sccommands/eventcommands/EventDeleteCommand.java new file mode 100644 index 00000000000..f815116b5b4 --- /dev/null +++ b/src/main/java/seedu/nova/logic/commands/sccommands/eventcommands/EventDeleteCommand.java @@ -0,0 +1,85 @@ +package seedu.nova.logic.commands.sccommands.eventcommands; + +import static java.util.Objects.requireNonNull; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_INDEX; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_TIME; + +import java.time.LocalDate; +import java.util.logging.Logger; + +import seedu.nova.commons.core.LogsCenter; +import seedu.nova.commons.core.index.Index; +import seedu.nova.logic.commands.Command; +import seedu.nova.logic.commands.CommandResult; +import seedu.nova.logic.commands.exceptions.CommandException; + +import seedu.nova.model.Model; +import seedu.nova.model.schedule.event.exceptions.DateNotFoundException; +import seedu.nova.model.schedule.event.exceptions.EventNotFoundException; + +/** + * Deletes an Event from the Schedule using its date and index in the list. + */ +public class EventDeleteCommand extends Command { + public static final String COMMAND_WORD = "delete"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Deletes an event from the schedule. \n" + + "Parameters: " + + PREFIX_TIME + "[YYYY-MM-DD] " + + PREFIX_INDEX + "[index] " + + "\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_TIME + "2020-03-20 " + + PREFIX_INDEX + "2 "; + + public static final String MESSAGE_SUCCESS = "Event has been deleted: \n%1$s\n"; + public static final String MESSAGE_NO_EVENT = "Invalid date - you have no events that week."; + public static final String MESSAGE_INVALID_INDEX_DATE = "Invalid date or index - that event does not exist."; + public static final String MESSAGE_INVALID_DATE = "That date does not fall within the semester."; + + private final Logger logger = LogsCenter.getLogger(getClass()); + + private LocalDate date; + private Index index; + + public EventDeleteCommand(LocalDate date, Index index) { + requireNonNull(date); + requireNonNull(index); + + this.date = date; + this.index = index; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + logger.info("executing delete command for event at: \n" + "date: " + date + ", index: " + index); + + requireNonNull(model); + + if (!model.isWithinSem(date)) { + throw new CommandException(MESSAGE_INVALID_DATE); + } + + try { + int i = index.getZeroBased(); + String response = model.deleteEvent(date, i); + return new CommandResult(String.format(MESSAGE_SUCCESS, response)); + + } catch (DateNotFoundException e) { + throw new CommandException(MESSAGE_NO_EVENT); + + } catch (EventNotFoundException e) { + throw new CommandException(MESSAGE_INVALID_INDEX_DATE); + } + + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof EventDeleteCommand // instanceof handles nulls + && index.equals(((EventDeleteCommand) other).index) + && date.equals(((EventDeleteCommand) other).date)); // state check + } + +} diff --git a/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java b/src/main/java/seedu/nova/logic/parser/ArgumentMultimap.java similarity index 98% rename from src/main/java/seedu/address/logic/parser/ArgumentMultimap.java rename to src/main/java/seedu/nova/logic/parser/ArgumentMultimap.java index 954c8e18f8e..89ea49fe20d 100644 --- a/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java +++ b/src/main/java/seedu/nova/logic/parser/ArgumentMultimap.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package seedu.nova.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/nova/logic/parser/ArgumentTokenizer.java similarity index 99% rename from src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java rename to src/main/java/seedu/nova/logic/parser/ArgumentTokenizer.java index 5c9aebfa488..490f9837efb 100644 --- a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java +++ b/src/main/java/seedu/nova/logic/parser/ArgumentTokenizer.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package seedu.nova.logic.parser; import java.util.ArrayList; import java.util.Arrays; diff --git a/src/main/java/seedu/nova/logic/parser/CliSyntax.java b/src/main/java/seedu/nova/logic/parser/CliSyntax.java new file mode 100644 index 00000000000..89bf7645df8 --- /dev/null +++ b/src/main/java/seedu/nova/logic/parser/CliSyntax.java @@ -0,0 +1,28 @@ +package seedu.nova.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_CATEGORY = new Prefix("c\\"); + public static final Prefix PREFIX_REMARK = new Prefix("r\\"); + public static final Prefix PREFIX_DATE = new Prefix("t\\"); + public static final Prefix PREFIX_DESC = new Prefix("d\\"); + public static final Prefix PREFIX_VENUE = new Prefix("v\\"); + public static final Prefix PREFIX_TIME = new Prefix("t\\"); + public static final Prefix PREFIX_TASK = new Prefix("t\\"); + public static final Prefix PREFIX_KEYWORD = new Prefix("k\\"); + public static final Prefix PREFIX_INDEX = new Prefix("i\\"); + public static final Prefix PREFIX_PROJECT = new Prefix("p\\"); + public static final Prefix PREFIX_TASK_NAME = new Prefix("p\\"); + public static final Prefix PREFIX_WEEK = new Prefix("w\\"); + public static final Prefix PREFIX_MIN_DURATION = new Prefix("mind\\"); + public static final Prefix PREFIX_MAX_DURATION = new Prefix("maxd\\"); + public static final Prefix PREFIX_FREQ = new Prefix("f\\"); + public static final Prefix PREFIX_DURATION = new Prefix("d\\"); +} diff --git a/src/main/java/seedu/nova/logic/parser/LogicParser.java b/src/main/java/seedu/nova/logic/parser/LogicParser.java new file mode 100644 index 00000000000..ad22a4f69e0 --- /dev/null +++ b/src/main/java/seedu/nova/logic/parser/LogicParser.java @@ -0,0 +1,99 @@ +package seedu.nova.logic.parser; + +import static seedu.nova.commons.core.Messages.MESSAGE_EMPTY_ARGUMENT; +import static seedu.nova.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import seedu.nova.logic.commands.Command; +import seedu.nova.logic.commands.commoncommands.ExitCommand; +import seedu.nova.logic.commands.commoncommands.NavCommand; +import seedu.nova.logic.parser.abparsers.AddressBookParser; +import seedu.nova.logic.parser.exceptions.ParseException; +import seedu.nova.logic.parser.plannerparser.PlannerParser; +import seedu.nova.logic.parser.ptparsers.ProgresstrackerParser; +import seedu.nova.logic.parser.scparser.ScheduleParser; +import seedu.nova.model.Mode; +import seedu.nova.model.Model; + +/** + * Class for logic parser + */ +public class LogicParser { + /** + * Used for initial separation of command word and args. + */ + private static final Pattern BASIC_COMMAND_FORMAT = Pattern.compile("(?\\S+)(?.*)"); + + private final NavCommandParser navCommandParser; + private final AddressBookParser addressBookParser; + private final seedu.nova.logic.parser.scparser.ScheduleParser scheduleParser; + private final ProgresstrackerParser progresstrackerParser; + private final PlannerParser plannerParser; + private Model model; + + public LogicParser(Model model) { + //Create parser objects for each page/ feature + navCommandParser = new NavCommandParser(); + addressBookParser = new AddressBookParser(); + scheduleParser = new ScheduleParser(); + progresstrackerParser = new ProgresstrackerParser(); + plannerParser = new PlannerParser(); + this.model = model; + } + + /** + * 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 { + Mode mode = model.getMode(); + ModeEnum modeEnum = model.getModeEnum(mode); + final Matcher matcher = BASIC_COMMAND_FORMAT.matcher(userInput.trim()); + boolean noSuchMode = (!modeEnum.equals(ModeEnum.ADDRESSBOOK)) && (!modeEnum.equals(ModeEnum.PROGRESSTRACKER)) + && (!modeEnum.equals(ModeEnum.SCHEDULE)) && (!modeEnum.equals(ModeEnum.HOME)) + && (!modeEnum.equals(ModeEnum.PLANNER)); + + if (!matcher.matches()) { + throw new ParseException(MESSAGE_EMPTY_ARGUMENT); + } + + if (noSuchMode) { + throw new ParseException("No such mode"); + } + + final String commandWord = matcher.group("commandWord"); + final String arguments = matcher.group("arguments"); + + if (commandWord.equals(NavCommand.COMMAND_WORD)) { + return navCommandParser.parse(arguments.trim()); + } else if (commandWord.equals(ExitCommand.COMMAND_WORD)) { + return new ExitCommand(); + } else { + //check mode + switch (modeEnum) { + + case HOME: + throw new ParseException(MESSAGE_UNKNOWN_COMMAND); + + case ADDRESSBOOK: + //return addressBookParser.parseCommand(userInput); + return addressBookParser.parseCommand(commandWord, arguments); + + case SCHEDULE: + return scheduleParser.parseCommand(commandWord, arguments); + + case PROGRESSTRACKER: + return progresstrackerParser.parseCommand(commandWord, arguments); + + case PLANNER: + return plannerParser.parseCommand(commandWord, arguments); + default: + throw new ParseException("No such mode"); + } + } + } +} diff --git a/src/main/java/seedu/nova/logic/parser/ModeEnum.java b/src/main/java/seedu/nova/logic/parser/ModeEnum.java new file mode 100644 index 00000000000..3f6b25e1f01 --- /dev/null +++ b/src/main/java/seedu/nova/logic/parser/ModeEnum.java @@ -0,0 +1,12 @@ +package seedu.nova.logic.parser; + +/** + * Enumeration for modes + */ +public enum ModeEnum { + HOME, + ADDRESSBOOK, + SCHEDULE, + PROGRESSTRACKER, + PLANNER +} diff --git a/src/main/java/seedu/nova/logic/parser/NavCommandParser.java b/src/main/java/seedu/nova/logic/parser/NavCommandParser.java new file mode 100644 index 00000000000..32f2c38dd93 --- /dev/null +++ b/src/main/java/seedu/nova/logic/parser/NavCommandParser.java @@ -0,0 +1,38 @@ +package seedu.nova.logic.parser; + +import static java.util.Objects.requireNonNull; + +import seedu.nova.logic.commands.commoncommands.NavCommand; +import seedu.nova.logic.parser.exceptions.ParseException; + +/** + * Parser to parse navigation related commands + */ +public class NavCommandParser implements Parser { + public static final String MESSAGE_UNKNOWN_MODE = "Please specify a mode to nav to or check that " + + "the mode exists in NOVA"; + + @Override + public NavCommand parse(String args) throws ParseException { + requireNonNull(args); + switch (args) { + case "home": + return new NavCommand(ModeEnum.HOME); + + case "ab": + return new NavCommand(ModeEnum.ADDRESSBOOK); + + case "schedule": + return new NavCommand(ModeEnum.SCHEDULE); + + case "progresstracker": + return new NavCommand(ModeEnum.PROGRESSTRACKER); + + case "planner": + return new NavCommand(ModeEnum.PLANNER); + + default: + throw new ParseException(MESSAGE_UNKNOWN_MODE); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/Parser.java b/src/main/java/seedu/nova/logic/parser/Parser.java similarity index 72% rename from src/main/java/seedu/address/logic/parser/Parser.java rename to src/main/java/seedu/nova/logic/parser/Parser.java index d6551ad8e3f..682e75dc9a4 100644 --- a/src/main/java/seedu/address/logic/parser/Parser.java +++ b/src/main/java/seedu/nova/logic/parser/Parser.java @@ -1,7 +1,7 @@ -package seedu.address.logic.parser; +package seedu.nova.logic.parser; -import seedu.address.logic.commands.Command; -import seedu.address.logic.parser.exceptions.ParseException; +import seedu.nova.logic.commands.Command; +import seedu.nova.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/nova/logic/parser/ParserUtil.java b/src/main/java/seedu/nova/logic/parser/ParserUtil.java new file mode 100644 index 00000000000..bb9ba40f294 --- /dev/null +++ b/src/main/java/seedu/nova/logic/parser/ParserUtil.java @@ -0,0 +1,303 @@ +package seedu.nova.logic.parser; + +import static java.util.Objects.requireNonNull; + +import java.time.DayOfWeek; +import java.time.Duration; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.format.DateTimeParseException; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import seedu.nova.commons.core.index.Index; +import seedu.nova.commons.util.StringUtil; +import seedu.nova.logic.parser.exceptions.ParseException; +import seedu.nova.model.category.Category; +import seedu.nova.model.person.Email; +import seedu.nova.model.person.Name; +import seedu.nova.model.person.Phone; +import seedu.nova.model.plan.TaskFreq; +import seedu.nova.model.progresstracker.Project; +import seedu.nova.model.progresstracker.PtNote; +import seedu.nova.model.progresstracker.TaskDesc; + +/** + * Contains utility methods used for parsing strings in the various *Parser classes. + */ +public class ParserUtil { + + /** + * The constant MESSAGE_INVALID_INDEX. + */ + public static final String MESSAGE_INVALID_INDEX = "Index is not a non-zero unsigned integer."; + public static final String MESSAGE_INVALID_WEEK = "Week is not a non-zero unsigned integer."; + public static final String MESSAGE_INVALID_TASK = "Task number is not a non-zero unsigned integer."; + public static final String MESSAGE_INVALID_DATE_FORMAT = "Date is invalid."; + public static final String MESSAGE_INVALID_TIME_FORMAT = "Time is invalid."; + public static final String MESSAGE_INVALID_DAY_FORMAT = "Day of week is invalid."; + + + + /** + * Parses {@code oneBasedIndex} into an {@code Index} and returns it. Leading and trailing whitespaces will be + * trimmed. + * + * @param oneBasedIndex the one based index + * @return the index + * @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. + * + * @param name the name + * @return the name + * @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. + * + * @param phone the phone + * @return the phone + * @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 email} into an {@code Email}. + * Leading and trailing whitespaces will be trimmed. + * + * @param email the email + * @return the email + * @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 Category}. + * Leading and trailing whitespaces will be trimmed. + * + * @param tag the tag + * @return the tag + * @throws ParseException if the given {@code tag} is invalid. + */ + public static Category parseTag(String tag) throws ParseException { + requireNonNull(tag); + String trimmedTag = tag.trim(); + if (!Category.isValidTagName(trimmedTag)) { + throw new ParseException(Category.MESSAGE_CONSTRAINTS); + } + return new Category(trimmedTag); + } + + /** + * Parses {@code Collection tags} into a {@code Set}. + * + * @param tags the tags + * @return the set + * @throws ParseException the parse exception + */ + public static Set parseTags(Collection tags) throws ParseException { + requireNonNull(tags); + final Set categorySet = new HashSet<>(); + for (String tagName : tags) { + categorySet.add(parseTag(tagName)); + } + return categorySet; + } + + /** + * parse the time into a LocalTime + * @param time String value of time + * @return LocalDate object + * @throws ParseException if time is invalid + */ + public static LocalTime parseTime(String time) throws ParseException { + requireNonNull(time); + + String trimmedTime = time.trim(); + + try { + return LocalTime.parse(trimmedTime); + } catch (DateTimeParseException e) { + throw new ParseException(MESSAGE_INVALID_TIME_FORMAT); + } + } + + /** + * Parses {@code String day} into a {@code DayOfWeek}. + */ + public static DayOfWeek parseDay(String day) throws ParseException { + requireNonNull(day); + + day = day.toUpperCase().trim(); + try { + DayOfWeek d = DayOfWeek.valueOf(day); + return d; + } catch (IllegalArgumentException e) { + throw new ParseException(MESSAGE_INVALID_DAY_FORMAT); + } + } + + /** + * Parsers week number into an int + * + * @param week string containing number to specify the week + * @return int of the week input + * @throws ParseException if is non-zero throw exception + */ + public static int parseWeek(String week) throws ParseException { + requireNonNull(week); + String trimmedWeek = week.trim(); + if (!StringUtil.isNonZeroUnsignedInteger(trimmedWeek)) { + throw new ParseException(MESSAGE_INVALID_WEEK); + } + return Integer.parseInt(trimmedWeek); + } + + /** + * Parsers task number into an int + * @param taskNum string containing number to specify the task + * @return int of the task input + * @throws ParseException if is non-zero throw exception + */ + public static int parseTask(String taskNum) throws ParseException { + requireNonNull(taskNum); + String trimmedNum = taskNum.trim(); + if (!StringUtil.isNonZeroUnsignedInteger(trimmedNum)) { + throw new ParseException(MESSAGE_INVALID_TASK); + } + return Integer.parseInt(trimmedNum); + } + + /** + * Checks if project name is correct + * + * @param project project name + * @return project name + * @throws ParseException if project name is wrong + */ + public static String parseProject(String project) throws ParseException { + requireNonNull(project); + String trimmedProject = project.trim(); + + if (!Project.isValidProject(trimmedProject)) { + throw new ParseException(Project.MESSAGE_CONSTRAINTS); + } + + return trimmedProject; + } + + /** + * Checks if taskDesc is blank + * @param taskDesc task description + * @return task description + * @throws ParseException if task description is blank + */ + public static String parseTaskDesc(String taskDesc) throws ParseException { + requireNonNull(taskDesc); + String trimmedTaskDesc = taskDesc.trim(); + + if (!TaskDesc.isValidTaskDesc(trimmedTaskDesc)) { + throw new ParseException(TaskDesc.MESSAGE_CONSTRAINTS); + } + + return trimmedTaskDesc; + } + + /** + * Checks if note is blank + * @param note note + * @return note + * @throws ParseException if note is blank + */ + public static String parseNote(String note) throws ParseException { + requireNonNull(note); + String trimmedNote = note.trim(); + + if (!PtNote.isValidNote(trimmedNote)) { + throw new ParseException(PtNote.MESSAGE_CONSTRAINTS); + } + + return trimmedNote; + } + + + /** + * Parse date local date. + * + * @param date the date + * @return the local date + */ + public static LocalDate parseDate(String date) throws ParseException { + requireNonNull(date); + String trimmedDate = date.trim(); + + try { + return LocalDate.parse(trimmedDate); + } catch (DateTimeParseException e) { + throw new ParseException(MESSAGE_INVALID_DATE_FORMAT); + } + + } + + /** + * Parse minutes into Duration + * + * @param min minutes + * @return Duration + * @throws ParseException + */ + public static Duration parseMinuteDuration(String min) throws ParseException { + requireNonNull(min); + String trimmedMin = min.trim(); + return Duration.ofMinutes(Long.parseLong(trimmedMin)); + } + + /** + * Parse task frequency to TaskFreq + * + * @param freq frequency + * @return TaskFreq + * @throws ParseException + */ + public static TaskFreq parseTaskFreq(String freq) throws ParseException { + requireNonNull(freq); + String trimmedFreq = freq.trim().toUpperCase(); + return TaskFreq.valueOf(trimmedFreq); + } +} diff --git a/src/main/java/seedu/address/logic/parser/Prefix.java b/src/main/java/seedu/nova/logic/parser/Prefix.java similarity index 95% rename from src/main/java/seedu/address/logic/parser/Prefix.java rename to src/main/java/seedu/nova/logic/parser/Prefix.java index c859d5fa5db..994a2a92ff8 100644 --- a/src/main/java/seedu/address/logic/parser/Prefix.java +++ b/src/main/java/seedu/nova/logic/parser/Prefix.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package seedu.nova.logic.parser; /** * A prefix that marks the beginning of an argument in an arguments string. diff --git a/src/main/java/seedu/nova/logic/parser/abparsers/AbAddCommandParser.java b/src/main/java/seedu/nova/logic/parser/abparsers/AbAddCommandParser.java new file mode 100644 index 00000000000..90278b293f7 --- /dev/null +++ b/src/main/java/seedu/nova/logic/parser/abparsers/AbAddCommandParser.java @@ -0,0 +1,70 @@ +package seedu.nova.logic.parser.abparsers; + +import static seedu.nova.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_CATEGORY; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_PHONE; + +import java.util.Set; +import java.util.stream.Stream; + +import seedu.nova.logic.commands.abcommands.AbAddCommand; +import seedu.nova.logic.parser.ArgumentMultimap; +import seedu.nova.logic.parser.ArgumentTokenizer; +import seedu.nova.logic.parser.Parser; +import seedu.nova.logic.parser.ParserUtil; +import seedu.nova.logic.parser.Prefix; +import seedu.nova.logic.parser.exceptions.ParseException; +import seedu.nova.model.category.Category; +import seedu.nova.model.person.Email; +import seedu.nova.model.person.Name; +import seedu.nova.model.person.Person; +import seedu.nova.model.person.Phone; +import seedu.nova.model.person.Remark; + +/** + * Parses input arguments and creates a new AbAddCommand object + */ +public class AbAddCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the AbAddCommand + * and returns an AbAddCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public AbAddCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_CATEGORY); + + if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_CATEGORY) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AbAddCommand.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()); + Set categoryList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_CATEGORY)); + if (ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_CATEGORY)).size() == 1) { + categoryList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_CATEGORY)); + } else if (argMultimap.getAllValues(PREFIX_CATEGORY).size() > 1) { + throw new ParseException("Please only provide 1 category"); + } + + Remark remark = new Remark(""); + + Person person = new Person(name, phone, email, categoryList, remark); + + return new AbAddCommand(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/nova/logic/parser/abparsers/AbDeleteCommandParser.java b/src/main/java/seedu/nova/logic/parser/abparsers/AbDeleteCommandParser.java new file mode 100644 index 00000000000..fd31e16ab7d --- /dev/null +++ b/src/main/java/seedu/nova/logic/parser/abparsers/AbDeleteCommandParser.java @@ -0,0 +1,38 @@ +package seedu.nova.logic.parser.abparsers; + +import static seedu.nova.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_INDEX; + +import seedu.nova.commons.core.index.Index; +import seedu.nova.logic.commands.abcommands.AbDeleteCommand; +import seedu.nova.logic.parser.ArgumentMultimap; +import seedu.nova.logic.parser.ArgumentTokenizer; +import seedu.nova.logic.parser.CliSyntax; +import seedu.nova.logic.parser.Parser; +import seedu.nova.logic.parser.ParserUtil; +import seedu.nova.logic.parser.exceptions.ParseException; + + +/** + * Parses input arguments and creates a new AbDeleteCommand object + */ +public class AbDeleteCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the AbDeleteCommand + * and returns a AbDeleteCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public AbDeleteCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_INDEX); + + if (argMultimap.getValue(CliSyntax.PREFIX_INDEX).isPresent()) { + Index index = ParserUtil.parseIndex(argMultimap.getValue(CliSyntax.PREFIX_INDEX).get()); + return new AbDeleteCommand(index); + } else { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AbDeleteCommand.MESSAGE_USAGE)); + } + } + +} diff --git a/src/main/java/seedu/nova/logic/parser/abparsers/AbEditCommandParser.java b/src/main/java/seedu/nova/logic/parser/abparsers/AbEditCommandParser.java new file mode 100644 index 00000000000..2badc572dbe --- /dev/null +++ b/src/main/java/seedu/nova/logic/parser/abparsers/AbEditCommandParser.java @@ -0,0 +1,101 @@ +package seedu.nova.logic.parser.abparsers; + +import static java.util.Objects.requireNonNull; +import static seedu.nova.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_CATEGORY; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_INDEX; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_PHONE; + +import java.util.Collection; +import java.util.Set; + +import seedu.nova.commons.core.index.Index; +import seedu.nova.logic.commands.abcommands.AbEditCommand; +import seedu.nova.logic.commands.abcommands.AbEditCommand.EditPersonDescriptor; +import seedu.nova.logic.parser.ArgumentMultimap; +import seedu.nova.logic.parser.ArgumentTokenizer; +import seedu.nova.logic.parser.CliSyntax; +import seedu.nova.logic.parser.Parser; +import seedu.nova.logic.parser.ParserUtil; +import seedu.nova.logic.parser.exceptions.ParseException; +import seedu.nova.model.category.Category; + +/** + * Parses input arguments and creates a new AbEditCommand object + */ +public class AbEditCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the AbEditCommand + * and returns an AbEditCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public AbEditCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_INDEX, PREFIX_NAME, + PREFIX_PHONE, PREFIX_EMAIL, PREFIX_CATEGORY); + + Index index; + + if (argMultimap.getValue(CliSyntax.PREFIX_INDEX).isPresent()) { + index = ParserUtil.parseIndex(argMultimap.getValue(CliSyntax.PREFIX_INDEX).get()); + } else { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AbEditCommand.MESSAGE_USAGE)); + } + + EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor(); + if (argMultimap.getValue(CliSyntax.PREFIX_NAME).isPresent()) { + editPersonDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(CliSyntax.PREFIX_NAME).get())); + } + if (argMultimap.getValue(CliSyntax.PREFIX_PHONE).isPresent()) { + editPersonDescriptor.setPhone(ParserUtil.parsePhone(argMultimap.getValue(CliSyntax.PREFIX_PHONE).get())); + } + if (argMultimap.getValue(CliSyntax.PREFIX_EMAIL).isPresent()) { + editPersonDescriptor.setEmail(ParserUtil.parseEmail(argMultimap.getValue(CliSyntax.PREFIX_EMAIL).get())); + } + + if (argMultimap.getAllValues(PREFIX_CATEGORY).size() == 1) { + editPersonDescriptor.setCategories(ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_CATEGORY))); + } else if (argMultimap.getAllValues(PREFIX_CATEGORY).size() > 1) { + throw new ParseException("Please only provide 1 category"); + } + + if (!editPersonDescriptor.isAnyFieldEdited()) { + throw new ParseException(AbEditCommand.MESSAGE_NOT_EDITED); + } + + return new AbEditCommand(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)); + } */ + + /** + * 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 Set parseTagsForEdit(Collection tags) throws ParseException { + assert tags != null; + + //Collection tagSet = tags.size() == 1 && tags.contains("") ? Collections.emptySet() : tags; + Collection tagSet = tags; + return ParserUtil.parseTags(tagSet); + } + +} diff --git a/src/main/java/seedu/nova/logic/parser/abparsers/AbFindCommandParser.java b/src/main/java/seedu/nova/logic/parser/abparsers/AbFindCommandParser.java new file mode 100644 index 00000000000..74e0e3b0a49 --- /dev/null +++ b/src/main/java/seedu/nova/logic/parser/abparsers/AbFindCommandParser.java @@ -0,0 +1,44 @@ +package seedu.nova.logic.parser.abparsers; + +import static seedu.nova.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_NAME; + +import java.util.Arrays; + +import seedu.nova.logic.commands.abcommands.AbFindCommand; +import seedu.nova.logic.parser.ArgumentMultimap; +import seedu.nova.logic.parser.ArgumentTokenizer; +import seedu.nova.logic.parser.Parser; +import seedu.nova.logic.parser.ParserUtil; +import seedu.nova.logic.parser.exceptions.ParseException; +import seedu.nova.model.person.Name; +import seedu.nova.model.person.NameContainsKeywordsPredicate; + +/** + * Parses input arguments and creates a new AbFindCommand object + */ +public class AbFindCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the AbFindCommand + * and returns a AbFindCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public AbFindCommand parse(String args) throws ParseException { + String trimmedArgs = args.trim(); + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_NAME); + Name name; + + if (argMultimap.getValue(PREFIX_NAME).isPresent()) { + name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()); + } else { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, + seedu.nova.logic.commands.abcommands.AbFindCommand.MESSAGE_USAGE)); + } + + String[] nameKeywords = name.toString().split("\\s+"); + return new AbFindCommand(new NameContainsKeywordsPredicate(Arrays.asList(nameKeywords))); + } + +} diff --git a/src/main/java/seedu/nova/logic/parser/abparsers/AbListCategoryCommandParser.java b/src/main/java/seedu/nova/logic/parser/abparsers/AbListCategoryCommandParser.java new file mode 100644 index 00000000000..dbe96d26e8b --- /dev/null +++ b/src/main/java/seedu/nova/logic/parser/abparsers/AbListCategoryCommandParser.java @@ -0,0 +1,51 @@ +package seedu.nova.logic.parser.abparsers; + +import static seedu.nova.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_CATEGORY; + +import java.util.Arrays; +import java.util.Set; + +import seedu.nova.logic.commands.abcommands.AbListCategoryCommand; +import seedu.nova.logic.commands.abcommands.AbListClassmateCommand; +import seedu.nova.logic.commands.abcommands.AbListTeammateCommand; +import seedu.nova.logic.parser.ArgumentMultimap; +import seedu.nova.logic.parser.ArgumentTokenizer; +import seedu.nova.logic.parser.Parser; +import seedu.nova.logic.parser.ParserUtil; +import seedu.nova.logic.parser.exceptions.ParseException; +import seedu.nova.model.category.Category; +import seedu.nova.model.person.CategoryContainsKeywordsPredicate; + +/** + * Parses input arguments and creates a new AbFindCommand object + */ +public class AbListCategoryCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the AbFindCommand + * and returns a AbFindCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public AbListCategoryCommand parse(String args) throws ParseException { + String trimmedArgs = args.trim(); + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_CATEGORY); + Set categoryList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_CATEGORY)); + + if (categoryList.size() == 0) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + AbListCategoryCommand.MESSAGE_USAGE)); + } else if (categoryList.size() > 1) { + throw new ParseException("Please only provide 1 category."); + } + + if (categoryList.iterator().next().toString().equals("classmate")) { + String[] classmateList = {"classmate"}; + return new AbListClassmateCommand(new CategoryContainsKeywordsPredicate(Arrays.asList(classmateList))); + } else { + String[] teammateList = {"teammate"}; + return new AbListTeammateCommand(new CategoryContainsKeywordsPredicate(Arrays.asList(teammateList))); + } + } + +} diff --git a/src/main/java/seedu/nova/logic/parser/abparsers/AbRemarkCommandParser.java b/src/main/java/seedu/nova/logic/parser/abparsers/AbRemarkCommandParser.java new file mode 100644 index 00000000000..58c7bf02b1e --- /dev/null +++ b/src/main/java/seedu/nova/logic/parser/abparsers/AbRemarkCommandParser.java @@ -0,0 +1,44 @@ +package seedu.nova.logic.parser.abparsers; + +import static java.util.Objects.requireNonNull; +import static seedu.nova.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.nova.commons.core.index.Index; +import seedu.nova.logic.commands.abcommands.AbRemarkCommand; +import seedu.nova.logic.parser.ArgumentMultimap; +import seedu.nova.logic.parser.ArgumentTokenizer; +import seedu.nova.logic.parser.CliSyntax; +import seedu.nova.logic.parser.Parser; +import seedu.nova.logic.parser.ParserUtil; +import seedu.nova.logic.parser.exceptions.ParseException; +import seedu.nova.model.person.Remark; + +/** + * Parses input arguments and creates a new {@code AbRemarkCommand} object + */ +public class AbRemarkCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the {@code AbRemarkCommand} + * and returns a {@code AbRemarkCommand} object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public AbRemarkCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, + CliSyntax.PREFIX_INDEX, CliSyntax.PREFIX_REMARK); + + Index index; + + if (argMultimap.getValue(CliSyntax.PREFIX_INDEX).isPresent()) { + index = ParserUtil.parseIndex(argMultimap.getValue(CliSyntax.PREFIX_INDEX).get()); + } else { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AbRemarkCommand.MESSAGE_USAGE)); + } + + String value = argMultimap.getValue(CliSyntax.PREFIX_REMARK).orElse(""); + Remark remark = new Remark(value); + + return new AbRemarkCommand(index, remark); + } +} diff --git a/src/main/java/seedu/nova/logic/parser/abparsers/AddressBookParser.java b/src/main/java/seedu/nova/logic/parser/abparsers/AddressBookParser.java new file mode 100644 index 00000000000..6494565ce67 --- /dev/null +++ b/src/main/java/seedu/nova/logic/parser/abparsers/AddressBookParser.java @@ -0,0 +1,95 @@ +package seedu.nova.logic.parser.abparsers; + +import static seedu.nova.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.nova.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; + +import java.util.regex.Pattern; + +import seedu.nova.logic.commands.Command; +import seedu.nova.logic.commands.abcommands.AbAddCommand; +import seedu.nova.logic.commands.abcommands.AbClearCommand; +import seedu.nova.logic.commands.abcommands.AbDeleteCommand; +import seedu.nova.logic.commands.abcommands.AbEditCommand; +import seedu.nova.logic.commands.abcommands.AbFindCommand; +import seedu.nova.logic.commands.abcommands.AbListCommand; +import seedu.nova.logic.commands.abcommands.AbRedoCommand; +import seedu.nova.logic.commands.abcommands.AbRemarkCommand; +import seedu.nova.logic.commands.abcommands.AbUndoCommand; +import seedu.nova.logic.parser.exceptions.ParseException; + +/** + * Parses user input for address book feature. + */ +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 commandWord command under address book feature + * @param arguments arguments under address book feature + * @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 { + public Command parseCommand(String commandWord, String arguments) throws ParseException { + switch (commandWord) { + + case AbAddCommand.COMMAND_WORD: + return new AbAddCommandParser().parse(arguments); + + case AbEditCommand.COMMAND_WORD: + return new AbEditCommandParser().parse(arguments); + + case AbDeleteCommand.COMMAND_WORD: + return new AbDeleteCommandParser().parse(arguments); + + case AbClearCommand.COMMAND_WORD: + if (arguments.trim().equals("")) { + return new AbClearCommand(); + } else { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + AbClearCommand.MESSAGE_USAGE)); + } + + case AbFindCommand.COMMAND_WORD: + return new AbFindCommandParser().parse(arguments); + + case AbListCommand.COMMAND_WORD: + String[] classmateList = {"classmate"}; + String[] teammateList = {"teammate"}; + if (arguments.trim().equals("")) { + return new AbListCommand(); + } else { + return new AbListCategoryCommandParser().parse(arguments); + } + + case AbUndoCommand.COMMAND_WORD: + if (arguments.trim().equals("")) { + return new AbUndoCommand(); + } else { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + AbUndoCommand.MESSAGE_USAGE)); + } + + case AbRedoCommand.COMMAND_WORD: + if (arguments.trim().equals("")) { + return new AbRedoCommand(); + } else { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + AbRedoCommand.MESSAGE_USAGE)); + } + + case AbRemarkCommand.COMMAND_WORD: + return new AbRemarkCommandParser().parse(arguments); + + default: + throw new ParseException(MESSAGE_UNKNOWN_COMMAND); + } + } + +} diff --git a/src/main/java/seedu/address/logic/parser/exceptions/ParseException.java b/src/main/java/seedu/nova/logic/parser/exceptions/ParseException.java similarity index 73% rename from src/main/java/seedu/address/logic/parser/exceptions/ParseException.java rename to src/main/java/seedu/nova/logic/parser/exceptions/ParseException.java index 158a1a54c1c..545bba77a88 100644 --- a/src/main/java/seedu/address/logic/parser/exceptions/ParseException.java +++ b/src/main/java/seedu/nova/logic/parser/exceptions/ParseException.java @@ -1,6 +1,6 @@ -package seedu.address.logic.parser.exceptions; +package seedu.nova.logic.parser.exceptions; -import seedu.address.commons.exceptions.IllegalValueException; +import seedu.nova.commons.exceptions.IllegalValueException; /** * Represents a parse error encountered by a parser. diff --git a/src/main/java/seedu/nova/logic/parser/plannerparser/AddFlexibleTaskCommandParser.java b/src/main/java/seedu/nova/logic/parser/plannerparser/AddFlexibleTaskCommandParser.java new file mode 100644 index 00000000000..becc17bdc72 --- /dev/null +++ b/src/main/java/seedu/nova/logic/parser/plannerparser/AddFlexibleTaskCommandParser.java @@ -0,0 +1,48 @@ +package seedu.nova.logic.parser.plannerparser; + +import static seedu.nova.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_MAX_DURATION; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_MIN_DURATION; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_TASK_NAME; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_TIME; + +import java.time.Duration; +import java.util.stream.Stream; + +import seedu.nova.logic.commands.plannercommands.PlannerAddFlexibleTaskCommand; +import seedu.nova.logic.parser.ArgumentMultimap; +import seedu.nova.logic.parser.ArgumentTokenizer; +import seedu.nova.logic.parser.Parser; +import seedu.nova.logic.parser.ParserUtil; +import seedu.nova.logic.parser.Prefix; +import seedu.nova.logic.parser.exceptions.ParseException; + +/** + * Parser for flexible command + */ +public class AddFlexibleTaskCommandParser implements Parser { + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + + @Override + public PlannerAddFlexibleTaskCommand parse(String args) throws ParseException { + + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_TASK_NAME, PREFIX_TIME, + PREFIX_MIN_DURATION, PREFIX_MAX_DURATION); + + if (!arePrefixesPresent(argMultimap, PREFIX_TASK_NAME, PREFIX_TIME, PREFIX_MIN_DURATION, PREFIX_MAX_DURATION) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(MESSAGE_INVALID_COMMAND_FORMAT); + } + + String taskName = argMultimap.getValue(PREFIX_TASK_NAME).get(); + Duration totalMinute = ParserUtil.parseMinuteDuration(argMultimap.getValue(PREFIX_TIME).get()); + Duration minD = ParserUtil.parseMinuteDuration(argMultimap.getValue(PREFIX_MIN_DURATION).get()); + Duration maxD = ParserUtil.parseMinuteDuration(argMultimap.getValue(PREFIX_MAX_DURATION).get()); + + + return new PlannerAddFlexibleTaskCommand(taskName, minD, maxD, totalMinute); + } +} diff --git a/src/main/java/seedu/nova/logic/parser/plannerparser/AddRoutineTaskCommandParser.java b/src/main/java/seedu/nova/logic/parser/plannerparser/AddRoutineTaskCommandParser.java new file mode 100644 index 00000000000..91d4921edfa --- /dev/null +++ b/src/main/java/seedu/nova/logic/parser/plannerparser/AddRoutineTaskCommandParser.java @@ -0,0 +1,46 @@ +package seedu.nova.logic.parser.plannerparser; + +import static seedu.nova.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_DURATION; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_FREQ; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_TASK_NAME; + +import java.time.Duration; +import java.util.stream.Stream; + +import seedu.nova.logic.commands.plannercommands.PlannerAddRoutineTaskCommand; +import seedu.nova.logic.parser.ArgumentMultimap; +import seedu.nova.logic.parser.ArgumentTokenizer; +import seedu.nova.logic.parser.Parser; +import seedu.nova.logic.parser.ParserUtil; +import seedu.nova.logic.parser.Prefix; +import seedu.nova.logic.parser.exceptions.ParseException; +import seedu.nova.model.plan.TaskFreq; + +/** + * Add routine task command parser + */ +public class AddRoutineTaskCommandParser implements Parser { + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + + @Override + public PlannerAddRoutineTaskCommand parse(String args) throws ParseException { + + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_TASK_NAME, PREFIX_FREQ, PREFIX_DURATION); + + if (!arePrefixesPresent(argMultimap, PREFIX_TASK_NAME, PREFIX_FREQ, PREFIX_DURATION) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(MESSAGE_INVALID_COMMAND_FORMAT); + } + + String taskName = argMultimap.getValue(PREFIX_TASK_NAME).get(); + TaskFreq freq = ParserUtil.parseTaskFreq(argMultimap.getValue(PREFIX_FREQ).get()); + Duration d = ParserUtil.parseMinuteDuration(argMultimap.getValue(PREFIX_DURATION).get()); + + + return new PlannerAddRoutineTaskCommand(taskName, d, freq); + } +} diff --git a/src/main/java/seedu/nova/logic/parser/plannerparser/DeleteTaskCommandParser.java b/src/main/java/seedu/nova/logic/parser/plannerparser/DeleteTaskCommandParser.java new file mode 100644 index 00000000000..6bf1e5bc38c --- /dev/null +++ b/src/main/java/seedu/nova/logic/parser/plannerparser/DeleteTaskCommandParser.java @@ -0,0 +1,36 @@ +package seedu.nova.logic.parser.plannerparser; + +import static seedu.nova.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_TASK_NAME; + +import java.util.stream.Stream; + +import seedu.nova.logic.commands.plannercommands.DeleteTaskCommand; +import seedu.nova.logic.parser.ArgumentMultimap; +import seedu.nova.logic.parser.ArgumentTokenizer; +import seedu.nova.logic.parser.Parser; +import seedu.nova.logic.parser.Prefix; +import seedu.nova.logic.parser.exceptions.ParseException; + +/** + * DeleteTaskCommandParser + */ +public class DeleteTaskCommandParser implements Parser { + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + + @Override + public DeleteTaskCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_TASK_NAME); + + if (!arePrefixesPresent(argMultimap, PREFIX_TASK_NAME) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(MESSAGE_INVALID_COMMAND_FORMAT); + } + + String taskName = argMultimap.getValue(PREFIX_TASK_NAME).get(); + return new DeleteTaskCommand(taskName); + } +} diff --git a/src/main/java/seedu/nova/logic/parser/plannerparser/PlannerParser.java b/src/main/java/seedu/nova/logic/parser/plannerparser/PlannerParser.java new file mode 100644 index 00000000000..d73e901383e --- /dev/null +++ b/src/main/java/seedu/nova/logic/parser/plannerparser/PlannerParser.java @@ -0,0 +1,42 @@ +package seedu.nova.logic.parser.plannerparser; + +import static seedu.nova.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; + +import seedu.nova.logic.commands.Command; +import seedu.nova.logic.commands.plannercommands.DeleteTaskCommand; +import seedu.nova.logic.commands.plannercommands.PlannerAddFlexibleTaskCommand; +import seedu.nova.logic.commands.plannercommands.PlannerAddRoutineTaskCommand; +import seedu.nova.logic.commands.plannercommands.PlannerScheduleTaskCommand; +import seedu.nova.logic.commands.plannercommands.PlannerStatsCommand; +import seedu.nova.logic.parser.exceptions.ParseException; + +/** + * Parser for planner related commands + */ +public class PlannerParser { + /** + * Parses command. + * + * @param commandWord the command word + * @param arguments the arguments + * @return the command + * @throws ParseException the parse exception + */ + public Command parseCommand(String commandWord, String arguments) throws ParseException { + + switch (commandWord) { + case PlannerAddFlexibleTaskCommand.COMMAND_WORD: + return new AddFlexibleTaskCommandParser().parse(arguments); + case PlannerAddRoutineTaskCommand.COMMAND_WORD: + return new AddRoutineTaskCommandParser().parse(arguments); + case PlannerStatsCommand.COMMAND_WORD: + return new StatsCommandParser().parse(arguments); + case PlannerScheduleTaskCommand.COMMAND_WORD: + return new ScheduleTaskCommandParser().parse(arguments); + case DeleteTaskCommand.COMMAND_WORD: + return new DeleteTaskCommandParser().parse(arguments); + default: + throw new ParseException(MESSAGE_UNKNOWN_COMMAND); + } + } +} diff --git a/src/main/java/seedu/nova/logic/parser/plannerparser/ScheduleTaskCommandParser.java b/src/main/java/seedu/nova/logic/parser/plannerparser/ScheduleTaskCommandParser.java new file mode 100644 index 00000000000..40e9c6c549b --- /dev/null +++ b/src/main/java/seedu/nova/logic/parser/plannerparser/ScheduleTaskCommandParser.java @@ -0,0 +1,43 @@ +package seedu.nova.logic.parser.plannerparser; + +import static seedu.nova.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_DATE; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_TASK_NAME; + +import java.time.LocalDate; +import java.util.stream.Stream; + +import seedu.nova.logic.commands.plannercommands.PlannerScheduleTaskCommand; +import seedu.nova.logic.parser.ArgumentMultimap; +import seedu.nova.logic.parser.ArgumentTokenizer; +import seedu.nova.logic.parser.Parser; +import seedu.nova.logic.parser.ParserUtil; +import seedu.nova.logic.parser.Prefix; +import seedu.nova.logic.parser.exceptions.ParseException; + +/** + * Schedule Task Command Parser + */ +public class ScheduleTaskCommandParser implements Parser { + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + + @Override + public PlannerScheduleTaskCommand parse(String args) throws ParseException { + + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_TASK_NAME, PREFIX_DATE); + + if (!arePrefixesPresent(argMultimap, PREFIX_TASK_NAME, PREFIX_DATE) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(MESSAGE_INVALID_COMMAND_FORMAT); + } + + String taskName = argMultimap.getValue(PREFIX_TASK_NAME).get(); + LocalDate d = ParserUtil.parseDate(argMultimap.getValue(PREFIX_DATE).get()); + + + return new PlannerScheduleTaskCommand(taskName, d); + } +} diff --git a/src/main/java/seedu/nova/logic/parser/plannerparser/StatsCommandParser.java b/src/main/java/seedu/nova/logic/parser/plannerparser/StatsCommandParser.java new file mode 100644 index 00000000000..76bf9ed077e --- /dev/null +++ b/src/main/java/seedu/nova/logic/parser/plannerparser/StatsCommandParser.java @@ -0,0 +1,16 @@ +package seedu.nova.logic.parser.plannerparser; + +import seedu.nova.logic.commands.plannercommands.PlannerStatsCommand; +import seedu.nova.logic.parser.Parser; +import seedu.nova.logic.parser.exceptions.ParseException; + +/** + * List command parser + */ +public class StatsCommandParser implements Parser { + + @Override + public PlannerStatsCommand parse(String args) throws ParseException { + return new PlannerStatsCommand(); + } +} diff --git a/src/main/java/seedu/nova/logic/parser/ptparsers/ProgresstrackerParser.java b/src/main/java/seedu/nova/logic/parser/ptparsers/ProgresstrackerParser.java new file mode 100644 index 00000000000..1841e5d340a --- /dev/null +++ b/src/main/java/seedu/nova/logic/parser/ptparsers/ProgresstrackerParser.java @@ -0,0 +1,59 @@ +package seedu.nova.logic.parser.ptparsers; + +import static seedu.nova.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; + +import seedu.nova.logic.commands.Command; +import seedu.nova.logic.commands.ptcommands.PtAddCommand; +import seedu.nova.logic.commands.ptcommands.PtAddNoteCommand; +import seedu.nova.logic.commands.ptcommands.PtDeleteCommand; +import seedu.nova.logic.commands.ptcommands.PtDeleteNoteCommand; +import seedu.nova.logic.commands.ptcommands.PtDoneCommand; +import seedu.nova.logic.commands.ptcommands.PtEditCommand; +import seedu.nova.logic.commands.ptcommands.PtEditNoteCommand; +import seedu.nova.logic.commands.ptcommands.PtListCommand; +import seedu.nova.logic.parser.exceptions.ParseException; + +/** + * Parses user input for progress tracker feature. + */ +public class ProgresstrackerParser { + + /** + * Parses user input into command for execution. + * @return the command based on the user input + * @throws ParseException if the user input does not conform the expected format + */ + public Command parseCommand(String commandWord, String arguments) throws ParseException { + switch (commandWord) { + + case PtListCommand.COMMAND_WORD: + return new PtListCommandParser().parse(arguments); + + case PtAddCommand.COMMAND_WORD: + return new PtAddCommandParser().parse(arguments); + + case PtDeleteCommand.COMMAND_WORD: + return new PtDeleteCommandParser().parse(arguments); + + case PtDoneCommand.COMMAND_WORD: + return new PtDoneCommandParser().parse(arguments); + + case PtEditCommand.COMMAND_WORD: + return new PtEditCommandParser().parse(arguments); + + case PtAddNoteCommand.COMMAND_WORD: + return new PtAddNoteCommandParser().parse(arguments); + + case PtDeleteNoteCommand.COMMAND_WORD: + return new PtDeleteNoteCommandParser().parse(arguments); + + case PtEditNoteCommand.COMMAND_WORD: + return new PtEditNoteCommandParser().parse(arguments); + + default: + throw new ParseException(MESSAGE_UNKNOWN_COMMAND); + } + } +} + + diff --git a/src/main/java/seedu/nova/logic/parser/ptparsers/PtAddCommandParser.java b/src/main/java/seedu/nova/logic/parser/ptparsers/PtAddCommandParser.java new file mode 100644 index 00000000000..ead9be1d6b6 --- /dev/null +++ b/src/main/java/seedu/nova/logic/parser/ptparsers/PtAddCommandParser.java @@ -0,0 +1,49 @@ +package seedu.nova.logic.parser.ptparsers; + +import static java.util.Objects.requireNonNull; +import static seedu.nova.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.util.stream.Stream; + +import seedu.nova.logic.commands.ptcommands.PtAddCommand; +import seedu.nova.logic.parser.ArgumentMultimap; +import seedu.nova.logic.parser.ArgumentTokenizer; +import seedu.nova.logic.parser.CliSyntax; +import seedu.nova.logic.parser.Parser; +import seedu.nova.logic.parser.ParserUtil; +import seedu.nova.logic.parser.Prefix; +import seedu.nova.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new PtAddCommand object + */ +public class PtAddCommandParser implements Parser { + + @Override + public PtAddCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, CliSyntax.PREFIX_PROJECT, CliSyntax.PREFIX_WEEK, + CliSyntax.PREFIX_DESC); + + if (!arePrefixesPresent(argMultimap, CliSyntax.PREFIX_PROJECT, CliSyntax.PREFIX_WEEK, CliSyntax.PREFIX_DESC) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, PtAddCommand.MESSAGE_USAGE)); + } + + String project = ParserUtil.parseProject(argMultimap.getValue(CliSyntax.PREFIX_PROJECT).get()); + int week = ParserUtil.parseWeek(argMultimap.getValue(CliSyntax.PREFIX_WEEK).get()); + String taskDesc = ParserUtil.parseTaskDesc(argMultimap.getValue(CliSyntax.PREFIX_DESC).get()); + + return new PtAddCommand(week, project, taskDesc); + } + + /** + * 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/nova/logic/parser/ptparsers/PtAddNoteCommandParser.java b/src/main/java/seedu/nova/logic/parser/ptparsers/PtAddNoteCommandParser.java new file mode 100644 index 00000000000..a1c038897c2 --- /dev/null +++ b/src/main/java/seedu/nova/logic/parser/ptparsers/PtAddNoteCommandParser.java @@ -0,0 +1,51 @@ +package seedu.nova.logic.parser.ptparsers; + +import static java.util.Objects.requireNonNull; +import static seedu.nova.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.util.stream.Stream; + +import seedu.nova.logic.commands.ptcommands.PtAddNoteCommand; +import seedu.nova.logic.parser.ArgumentMultimap; +import seedu.nova.logic.parser.ArgumentTokenizer; +import seedu.nova.logic.parser.CliSyntax; +import seedu.nova.logic.parser.Parser; +import seedu.nova.logic.parser.ParserUtil; +import seedu.nova.logic.parser.Prefix; +import seedu.nova.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new PtAddNoteCommand object + */ +public class PtAddNoteCommandParser implements Parser { + + @Override + public PtAddNoteCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, CliSyntax.PREFIX_PROJECT, CliSyntax.PREFIX_WEEK, CliSyntax.PREFIX_TASK, + CliSyntax.PREFIX_DESC); + + if (!arePrefixesPresent(argMultimap, CliSyntax.PREFIX_PROJECT, CliSyntax.PREFIX_WEEK, CliSyntax.PREFIX_TASK, + CliSyntax.PREFIX_DESC) || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, PtAddNoteCommand.MESSAGE_USAGE)); + } + + String project = ParserUtil.parseProject(argMultimap.getValue(CliSyntax.PREFIX_PROJECT).get()); + int week = ParserUtil.parseWeek(argMultimap.getValue(CliSyntax.PREFIX_WEEK).get()); + int taskNum = ParserUtil.parseTask(argMultimap.getValue(CliSyntax.PREFIX_TASK).get()); + String note = ParserUtil.parseNote(argMultimap.getValue(CliSyntax.PREFIX_DESC).get()); + + return new PtAddNoteCommand(week, taskNum, project, note); + } + + /** + * 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/nova/logic/parser/ptparsers/PtDeleteCommandParser.java b/src/main/java/seedu/nova/logic/parser/ptparsers/PtDeleteCommandParser.java new file mode 100644 index 00000000000..5afaa24e8b2 --- /dev/null +++ b/src/main/java/seedu/nova/logic/parser/ptparsers/PtDeleteCommandParser.java @@ -0,0 +1,49 @@ +package seedu.nova.logic.parser.ptparsers; + +import static java.util.Objects.requireNonNull; +import static seedu.nova.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.util.stream.Stream; + +import seedu.nova.logic.commands.ptcommands.PtDeleteCommand; +import seedu.nova.logic.parser.ArgumentMultimap; +import seedu.nova.logic.parser.ArgumentTokenizer; +import seedu.nova.logic.parser.CliSyntax; +import seedu.nova.logic.parser.Parser; +import seedu.nova.logic.parser.ParserUtil; +import seedu.nova.logic.parser.Prefix; +import seedu.nova.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new PtDeleteCommand object + */ +public class PtDeleteCommandParser implements Parser { + + @Override + public PtDeleteCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, CliSyntax.PREFIX_PROJECT, CliSyntax.PREFIX_WEEK, + CliSyntax.PREFIX_TASK); + + if (!arePrefixesPresent(argMultimap, CliSyntax.PREFIX_PROJECT, CliSyntax.PREFIX_WEEK, CliSyntax.PREFIX_TASK) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, PtDeleteCommand.MESSAGE_USAGE)); + } + + String project = ParserUtil.parseProject(argMultimap.getValue(CliSyntax.PREFIX_PROJECT).get()); + int week = ParserUtil.parseWeek(argMultimap.getValue(CliSyntax.PREFIX_WEEK).get()); + int taskNum = ParserUtil.parseTask(argMultimap.getValue(CliSyntax.PREFIX_TASK).get()); + + return new PtDeleteCommand(week, project, taskNum); + } + + /** + * 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/nova/logic/parser/ptparsers/PtDeleteNoteCommandParser.java b/src/main/java/seedu/nova/logic/parser/ptparsers/PtDeleteNoteCommandParser.java new file mode 100644 index 00000000000..6ca27688c46 --- /dev/null +++ b/src/main/java/seedu/nova/logic/parser/ptparsers/PtDeleteNoteCommandParser.java @@ -0,0 +1,48 @@ +package seedu.nova.logic.parser.ptparsers; + +import static java.util.Objects.requireNonNull; +import static seedu.nova.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.util.stream.Stream; + +import seedu.nova.logic.commands.ptcommands.PtDeleteNoteCommand; +import seedu.nova.logic.parser.ArgumentMultimap; +import seedu.nova.logic.parser.ArgumentTokenizer; +import seedu.nova.logic.parser.CliSyntax; +import seedu.nova.logic.parser.Parser; +import seedu.nova.logic.parser.ParserUtil; +import seedu.nova.logic.parser.Prefix; +import seedu.nova.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new PtDeleteNoteCommand object + */ +public class PtDeleteNoteCommandParser implements Parser { + + @Override + public PtDeleteNoteCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, CliSyntax.PREFIX_PROJECT, CliSyntax.PREFIX_WEEK, + CliSyntax.PREFIX_TASK); + + if (!arePrefixesPresent(argMultimap, CliSyntax.PREFIX_PROJECT, CliSyntax.PREFIX_WEEK, CliSyntax.PREFIX_TASK) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, PtDeleteNoteCommand.MESSAGE_USAGE)); + } + + String project = ParserUtil.parseProject(argMultimap.getValue(CliSyntax.PREFIX_PROJECT).get()); + int week = ParserUtil.parseWeek(argMultimap.getValue(CliSyntax.PREFIX_WEEK).get()); + int taskNum = ParserUtil.parseTask(argMultimap.getValue(CliSyntax.PREFIX_TASK).get()); + + return new PtDeleteNoteCommand(week, taskNum, project); + } + + /** + * 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/nova/logic/parser/ptparsers/PtDoneCommandParser.java b/src/main/java/seedu/nova/logic/parser/ptparsers/PtDoneCommandParser.java new file mode 100644 index 00000000000..747f46f69f4 --- /dev/null +++ b/src/main/java/seedu/nova/logic/parser/ptparsers/PtDoneCommandParser.java @@ -0,0 +1,49 @@ +package seedu.nova.logic.parser.ptparsers; + +import static java.util.Objects.requireNonNull; +import static seedu.nova.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.util.stream.Stream; + +import seedu.nova.logic.commands.ptcommands.PtDoneCommand; +import seedu.nova.logic.parser.ArgumentMultimap; +import seedu.nova.logic.parser.ArgumentTokenizer; +import seedu.nova.logic.parser.CliSyntax; +import seedu.nova.logic.parser.Parser; +import seedu.nova.logic.parser.ParserUtil; +import seedu.nova.logic.parser.Prefix; +import seedu.nova.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new PtDoneCommand object + */ +public class PtDoneCommandParser implements Parser { + + @Override + public PtDoneCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, CliSyntax.PREFIX_PROJECT, CliSyntax.PREFIX_WEEK, + CliSyntax.PREFIX_TASK); + + if (!arePrefixesPresent(argMultimap, CliSyntax.PREFIX_PROJECT, CliSyntax.PREFIX_WEEK, CliSyntax.PREFIX_TASK) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, PtDoneCommand.MESSAGE_USAGE)); + } + + String project = ParserUtil.parseProject(argMultimap.getValue(CliSyntax.PREFIX_PROJECT).get()); + int week = ParserUtil.parseWeek(argMultimap.getValue(CliSyntax.PREFIX_WEEK).get()); + int taskNum = ParserUtil.parseTask(argMultimap.getValue(CliSyntax.PREFIX_TASK).get()); + + return new PtDoneCommand(week, project, taskNum); + } + + /** + * 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/nova/logic/parser/ptparsers/PtEditCommandParser.java b/src/main/java/seedu/nova/logic/parser/ptparsers/PtEditCommandParser.java new file mode 100644 index 00000000000..6930ac9fe65 --- /dev/null +++ b/src/main/java/seedu/nova/logic/parser/ptparsers/PtEditCommandParser.java @@ -0,0 +1,52 @@ +package seedu.nova.logic.parser.ptparsers; + +import static java.util.Objects.requireNonNull; +import static seedu.nova.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.util.stream.Stream; + +import seedu.nova.logic.commands.ptcommands.PtEditCommand; +import seedu.nova.logic.parser.ArgumentMultimap; +import seedu.nova.logic.parser.ArgumentTokenizer; +import seedu.nova.logic.parser.CliSyntax; +import seedu.nova.logic.parser.Parser; +import seedu.nova.logic.parser.ParserUtil; +import seedu.nova.logic.parser.Prefix; +import seedu.nova.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new PtEditCommand object + */ +public class PtEditCommandParser implements Parser { + + @Override + public PtEditCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, CliSyntax.PREFIX_PROJECT, CliSyntax.PREFIX_WEEK, + CliSyntax.PREFIX_TASK, CliSyntax.PREFIX_DESC); + + if (!arePrefixesPresent(argMultimap, CliSyntax.PREFIX_PROJECT, CliSyntax.PREFIX_WEEK, + CliSyntax.PREFIX_TASK, CliSyntax.PREFIX_DESC) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, PtEditCommand.MESSAGE_USAGE)); + } + + String project = ParserUtil.parseProject(argMultimap.getValue(CliSyntax.PREFIX_PROJECT).get()); + int week = ParserUtil.parseWeek(argMultimap.getValue(CliSyntax.PREFIX_WEEK).get()); + int taskNum = ParserUtil.parseTask(argMultimap.getValue(CliSyntax.PREFIX_TASK).get()); + String taskDesc = ParserUtil.parseTaskDesc(argMultimap.getValue(CliSyntax.PREFIX_DESC).get()); + + return new PtEditCommand(week, project, taskDesc, taskNum); + } + + /** + * 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/nova/logic/parser/ptparsers/PtEditNoteCommandParser.java b/src/main/java/seedu/nova/logic/parser/ptparsers/PtEditNoteCommandParser.java new file mode 100644 index 00000000000..a8cd2375f6d --- /dev/null +++ b/src/main/java/seedu/nova/logic/parser/ptparsers/PtEditNoteCommandParser.java @@ -0,0 +1,49 @@ +package seedu.nova.logic.parser.ptparsers; + +import static java.util.Objects.requireNonNull; +import static seedu.nova.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.util.stream.Stream; + +import seedu.nova.logic.commands.ptcommands.PtEditNoteCommand; +import seedu.nova.logic.parser.ArgumentMultimap; +import seedu.nova.logic.parser.ArgumentTokenizer; +import seedu.nova.logic.parser.CliSyntax; +import seedu.nova.logic.parser.Parser; +import seedu.nova.logic.parser.ParserUtil; +import seedu.nova.logic.parser.Prefix; +import seedu.nova.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new PtEditNoteCommand object + */ +public class PtEditNoteCommandParser implements Parser { + + @Override + public PtEditNoteCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, CliSyntax.PREFIX_PROJECT, CliSyntax.PREFIX_WEEK, CliSyntax.PREFIX_TASK, + CliSyntax.PREFIX_DESC); + + if (!arePrefixesPresent(argMultimap, CliSyntax.PREFIX_PROJECT, CliSyntax.PREFIX_WEEK, CliSyntax.PREFIX_TASK, + CliSyntax.PREFIX_DESC) || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, PtEditNoteCommand.MESSAGE_USAGE)); + } + + String project = ParserUtil.parseProject(argMultimap.getValue(CliSyntax.PREFIX_PROJECT).get()); + int week = ParserUtil.parseWeek(argMultimap.getValue(CliSyntax.PREFIX_WEEK).get()); + int taskNum = ParserUtil.parseTask(argMultimap.getValue(CliSyntax.PREFIX_TASK).get()); + String note = ParserUtil.parseNote(argMultimap.getValue(CliSyntax.PREFIX_DESC).get()); + + return new PtEditNoteCommand(week, taskNum, project, note); + } + + /** + * 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/nova/logic/parser/ptparsers/PtListCommandParser.java b/src/main/java/seedu/nova/logic/parser/ptparsers/PtListCommandParser.java new file mode 100644 index 00000000000..ae23e3906b4 --- /dev/null +++ b/src/main/java/seedu/nova/logic/parser/ptparsers/PtListCommandParser.java @@ -0,0 +1,47 @@ +package seedu.nova.logic.parser.ptparsers; + +import static java.util.Objects.requireNonNull; +import static seedu.nova.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.util.stream.Stream; + +import seedu.nova.logic.commands.ptcommands.PtListCommand; +import seedu.nova.logic.parser.ArgumentMultimap; +import seedu.nova.logic.parser.ArgumentTokenizer; +import seedu.nova.logic.parser.CliSyntax; +import seedu.nova.logic.parser.Parser; +import seedu.nova.logic.parser.ParserUtil; +import seedu.nova.logic.parser.Prefix; +import seedu.nova.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new PtListCommand object + */ +public class PtListCommandParser implements Parser { + + @Override + public PtListCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, CliSyntax.PREFIX_PROJECT, CliSyntax.PREFIX_WEEK); + + if (!arePrefixesPresent(argMultimap, CliSyntax.PREFIX_PROJECT, CliSyntax.PREFIX_WEEK) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, PtListCommand.MESSAGE_USAGE)); + } + + String project = ParserUtil.parseProject(argMultimap.getValue(CliSyntax.PREFIX_PROJECT).get()); + int week = ParserUtil.parseWeek(argMultimap.getValue(CliSyntax.PREFIX_WEEK).get()); + + return new PtListCommand(week, project); + } + + /** + * 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/nova/logic/parser/scparser/ScViewCommandParser.java b/src/main/java/seedu/nova/logic/parser/scparser/ScViewCommandParser.java new file mode 100644 index 00000000000..27ab46f715d --- /dev/null +++ b/src/main/java/seedu/nova/logic/parser/scparser/ScViewCommandParser.java @@ -0,0 +1,75 @@ +package seedu.nova.logic.parser.scparser; + +import static seedu.nova.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_DATE; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_INDEX; + +import java.time.LocalDate; +import java.util.stream.Stream; + +import seedu.nova.commons.core.index.Index; +import seedu.nova.logic.commands.sccommands.ScViewCommand; +import seedu.nova.logic.commands.sccommands.ScViewDayCommand; +import seedu.nova.logic.commands.sccommands.ScViewWeekCommand; +import seedu.nova.logic.parser.ArgumentMultimap; +import seedu.nova.logic.parser.ArgumentTokenizer; +import seedu.nova.logic.parser.Parser; +import seedu.nova.logic.parser.ParserUtil; +import seedu.nova.logic.parser.Prefix; +import seedu.nova.logic.parser.exceptions.ParseException; + +/** + * The parser for the view command of schedule. + */ +public class ScViewCommandParser implements Parser { + + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + + @Override + public ScViewCommand parse(String args) throws ParseException { + + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_DATE, PREFIX_INDEX); + + if ((arePrefixesPresent(argMultimap, PREFIX_DATE) && arePrefixesPresent(argMultimap, PREFIX_INDEX)) + || (!arePrefixesPresent(argMultimap, PREFIX_DATE) && !arePrefixesPresent(argMultimap, PREFIX_INDEX))) { + + if (argMultimap.getPreamble().isEmpty() || !argMultimap.getPreamble().equals("week")) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ScViewCommand.MESSAGE_USAGE)); + } else { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + ScViewWeekCommand.MESSAGE_USAGE)); + } + } + + if (argMultimap.getPreamble().isEmpty()) { + //View date command + + if (!arePrefixesPresent(argMultimap, PREFIX_DATE)) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ScViewCommand.MESSAGE_USAGE)); + } + + LocalDate date = ParserUtil.parseDate(argMultimap.getValue(PREFIX_DATE).get()); + return new ScViewDayCommand(date); + + } else { + //View with preamble + if (!argMultimap.getPreamble().equals("week")) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ScViewCommand.MESSAGE_USAGE)); + } + + if (!arePrefixesPresent(argMultimap, PREFIX_INDEX)) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + ScViewWeekCommand.MESSAGE_USAGE)); + } + + //view week command + Index weekNumber = ParserUtil.parseIndex(argMultimap.getValue(PREFIX_INDEX).get()); + return new ScViewWeekCommand(weekNumber.getOneBased()); + } + + } + +} diff --git a/src/main/java/seedu/nova/logic/parser/scparser/ScViewFreeSlotCommandParser.java b/src/main/java/seedu/nova/logic/parser/scparser/ScViewFreeSlotCommandParser.java new file mode 100644 index 00000000000..7b10986fedf --- /dev/null +++ b/src/main/java/seedu/nova/logic/parser/scparser/ScViewFreeSlotCommandParser.java @@ -0,0 +1,42 @@ +package seedu.nova.logic.parser.scparser; + +import static seedu.nova.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.nova.logic.parser.CliSyntax.PREFIX_DATE; + +import java.time.LocalDate; +import java.util.stream.Stream; + +import seedu.nova.logic.commands.sccommands.ScViewFreeSlotCommand; +import seedu.nova.logic.parser.ArgumentMultimap; +import seedu.nova.logic.parser.ArgumentTokenizer; +import seedu.nova.logic.parser.Parser; +import seedu.nova.logic.parser.ParserUtil; +import seedu.nova.logic.parser.Prefix; +import seedu.nova.logic.parser.exceptions.ParseException; + +/** + * View free slot + */ +public class ScViewFreeSlotCommandParser implements Parser { + + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + + @Override + public ScViewFreeSlotCommand parse(String args) throws ParseException { + + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_DATE); + + if (!arePrefixesPresent(argMultimap, PREFIX_DATE) || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(MESSAGE_INVALID_COMMAND_FORMAT); + } + + LocalDate date = ParserUtil.parseDate(argMultimap.getValue(PREFIX_DATE).get()); + + + return new ScViewFreeSlotCommand(date); + } + +} diff --git a/src/main/java/seedu/nova/logic/parser/scparser/ScheduleParser.java b/src/main/java/seedu/nova/logic/parser/scparser/ScheduleParser.java new file mode 100644 index 00000000000..76de38663a6 --- /dev/null +++ b/src/main/java/seedu/nova/logic/parser/scparser/ScheduleParser.java @@ -0,0 +1,65 @@ +package seedu.nova.logic.parser.scparser; + +import static seedu.nova.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; + +import seedu.nova.logic.commands.Command; +import seedu.nova.logic.commands.sccommands.ScViewCommand; +import seedu.nova.logic.commands.sccommands.ScViewFreeSlotCommand; +import seedu.nova.logic.commands.sccommands.eventcommands.EventAddConsultationCommand; +import seedu.nova.logic.commands.sccommands.eventcommands.EventAddLessonCommand; +import seedu.nova.logic.commands.sccommands.eventcommands.EventAddMeetingCommand; +import seedu.nova.logic.commands.sccommands.eventcommands.EventAddNoteCommand; +import seedu.nova.logic.commands.sccommands.eventcommands.EventAddStudyCommand; +import seedu.nova.logic.commands.sccommands.eventcommands.EventDeleteCommand; +import seedu.nova.logic.parser.exceptions.ParseException; +import seedu.nova.logic.parser.scparser.eventparsers.EventAddConsultationCommandParser; +import seedu.nova.logic.parser.scparser.eventparsers.EventAddLessonCommandParser; +import seedu.nova.logic.parser.scparser.eventparsers.EventAddMeetingCommandParser; +import seedu.nova.logic.parser.scparser.eventparsers.EventAddNoteCommandParser; +import seedu.nova.logic.parser.scparser.eventparsers.EventAddStudyCommandParser; +import seedu.nova.logic.parser.scparser.eventparsers.EventDeleteCommandParser; + +/** + * The type Schedule parser. + */ +public class ScheduleParser { + + /** + * Parses command. + * + * @param commandWord the command word + * @param arguments the arguments + * @return the command + * @throws ParseException the parse exception + */ + public Command parseCommand(String commandWord, String arguments) throws ParseException { + + switch (commandWord) { + case ScViewCommand.COMMAND_WORD: + return new ScViewCommandParser().parse(arguments); + case ScViewFreeSlotCommand.COMMAND_WORD: + return new ScViewFreeSlotCommandParser().parse(arguments); + + case EventAddMeetingCommand.COMMAND_WORD: + return new EventAddMeetingCommandParser().parse(arguments); + + case EventAddConsultationCommand.COMMAND_WORD: + return new EventAddConsultationCommandParser().parse(arguments); + + case EventAddStudyCommand.COMMAND_WORD: + return new EventAddStudyCommandParser().parse(arguments); + + case EventAddLessonCommand.COMMAND_WORD: + return new EventAddLessonCommandParser().parse(arguments); + + case EventDeleteCommand.COMMAND_WORD: + return new EventDeleteCommandParser().parse(arguments); + + case EventAddNoteCommand.COMMAND_WORD: + return new EventAddNoteCommandParser().parse(arguments); + default: + throw new ParseException(MESSAGE_UNKNOWN_COMMAND); + } + } + +} diff --git a/src/main/java/seedu/nova/logic/parser/scparser/eventparsers/EventAddConsultationCommandParser.java b/src/main/java/seedu/nova/logic/parser/scparser/eventparsers/EventAddConsultationCommandParser.java new file mode 100644 index 00000000000..7f37424cf76 --- /dev/null +++ b/src/main/java/seedu/nova/logic/parser/scparser/eventparsers/EventAddConsultationCommandParser.java @@ -0,0 +1,72 @@ +package seedu.nova.logic.parser.scparser.eventparsers; + +import static seedu.nova.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.nova.commons.core.Messages.MESSAGE_WRONG_TIME; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.stream.Stream; + +import seedu.nova.logic.commands.sccommands.eventcommands.EventAddConsultationCommand; +import seedu.nova.logic.parser.ArgumentMultimap; +import seedu.nova.logic.parser.ArgumentTokenizer; +import seedu.nova.logic.parser.CliSyntax; +import seedu.nova.logic.parser.Parser; +import seedu.nova.logic.parser.ParserUtil; +import seedu.nova.logic.parser.Prefix; +import seedu.nova.logic.parser.exceptions.ParseException; +import seedu.nova.model.schedule.event.Consultation; +import seedu.nova.model.schedule.event.Event; + +/** + * Parses input arguments and creates a new EventAddConsultationCommand object. + */ +public class EventAddConsultationCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the EventAddMeetingCommand + * and returns an EventAddMeetingCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public EventAddConsultationCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, CliSyntax.PREFIX_DESC, CliSyntax.PREFIX_VENUE, CliSyntax.PREFIX_TIME); + + if (!arePrefixesPresent(argMultimap, CliSyntax.PREFIX_DESC, CliSyntax.PREFIX_VENUE, CliSyntax.PREFIX_TIME) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + EventAddConsultationCommand.MESSAGE_USAGE)); + } + + String desc = argMultimap.getValue(CliSyntax.PREFIX_DESC).get(); + String venue = argMultimap.getValue(CliSyntax.PREFIX_VENUE).get(); + + String dateTime = argMultimap.getValue(CliSyntax.PREFIX_TIME).get(); + String[] dateTimeArr = dateTime.split(" "); + + if (dateTimeArr.length != 3) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + EventAddConsultationCommand.MESSAGE_USAGE)); + } + + LocalDate date = ParserUtil.parseDate(dateTimeArr[0]); + LocalTime startTime = ParserUtil.parseTime(dateTimeArr[1]); + LocalTime endTime = ParserUtil.parseTime(dateTimeArr[2]); + + if (startTime.compareTo(endTime) >= 0) { + throw new ParseException(MESSAGE_WRONG_TIME); + } + + Event consultation = new Consultation(desc, venue, startTime, endTime, date); + return new EventAddConsultationCommand(consultation); + } + + /** + * 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/nova/logic/parser/scparser/eventparsers/EventAddLessonCommandParser.java b/src/main/java/seedu/nova/logic/parser/scparser/eventparsers/EventAddLessonCommandParser.java new file mode 100644 index 00000000000..af9f2071a4c --- /dev/null +++ b/src/main/java/seedu/nova/logic/parser/scparser/eventparsers/EventAddLessonCommandParser.java @@ -0,0 +1,73 @@ +package seedu.nova.logic.parser.scparser.eventparsers; + +import static seedu.nova.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.nova.commons.core.Messages.MESSAGE_WRONG_TIME; + +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.stream.Stream; + +import seedu.nova.logic.commands.sccommands.eventcommands.EventAddLessonCommand; +import seedu.nova.logic.parser.ArgumentMultimap; +import seedu.nova.logic.parser.ArgumentTokenizer; +import seedu.nova.logic.parser.CliSyntax; +import seedu.nova.logic.parser.Parser; +import seedu.nova.logic.parser.ParserUtil; +import seedu.nova.logic.parser.Prefix; +import seedu.nova.logic.parser.exceptions.ParseException; +import seedu.nova.model.schedule.event.Event; +import seedu.nova.model.schedule.event.Lesson; + +/** + * Parses input arguments and creates a new EventAddLessonCommand object. + */ +public class EventAddLessonCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the EventAddStudyCommand + * and returns an EventAddLessonCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public EventAddLessonCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, CliSyntax.PREFIX_DESC, CliSyntax.PREFIX_VENUE, CliSyntax.PREFIX_TIME); + + if (!arePrefixesPresent(argMultimap, CliSyntax.PREFIX_DESC, CliSyntax.PREFIX_VENUE, CliSyntax.PREFIX_TIME) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + EventAddLessonCommand.MESSAGE_USAGE)); + } + + String desc = argMultimap.getValue(CliSyntax.PREFIX_DESC).get(); + String venue = argMultimap.getValue(CliSyntax.PREFIX_VENUE).get(); + + String dateTime = argMultimap.getValue(CliSyntax.PREFIX_TIME).get(); + String[] dateTimeArr = dateTime.split(" "); + + if (dateTimeArr.length != 3) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + EventAddLessonCommand.MESSAGE_USAGE)); + } + + DayOfWeek day = ParserUtil.parseDay(dateTimeArr[0]); + LocalTime startTime = ParserUtil.parseTime(dateTimeArr[1]); + LocalTime endTime = ParserUtil.parseTime(dateTimeArr[2]); + LocalDate placeholder = LocalDate.parse("2020-01-17"); + + if (startTime.compareTo(endTime) >= 0) { + throw new ParseException(MESSAGE_WRONG_TIME); + } + + Event lesson = new Lesson(desc, venue, startTime, endTime, placeholder, day); + return new EventAddLessonCommand(lesson); + } + + /** + * 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/nova/logic/parser/scparser/eventparsers/EventAddMeetingCommandParser.java b/src/main/java/seedu/nova/logic/parser/scparser/eventparsers/EventAddMeetingCommandParser.java new file mode 100644 index 00000000000..b0dcaed691d --- /dev/null +++ b/src/main/java/seedu/nova/logic/parser/scparser/eventparsers/EventAddMeetingCommandParser.java @@ -0,0 +1,71 @@ +package seedu.nova.logic.parser.scparser.eventparsers; + +import static seedu.nova.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.nova.commons.core.Messages.MESSAGE_WRONG_TIME; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.stream.Stream; + +import seedu.nova.logic.commands.sccommands.eventcommands.EventAddMeetingCommand; +import seedu.nova.logic.parser.ArgumentMultimap; +import seedu.nova.logic.parser.ArgumentTokenizer; +import seedu.nova.logic.parser.CliSyntax; +import seedu.nova.logic.parser.Parser; +import seedu.nova.logic.parser.ParserUtil; +import seedu.nova.logic.parser.Prefix; +import seedu.nova.logic.parser.exceptions.ParseException; +import seedu.nova.model.schedule.event.Event; +import seedu.nova.model.schedule.event.Meeting; + +/** + * Parses input arguments and creates a new EventAddMeetingCommand object. + */ +public class EventAddMeetingCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the EventAddMeetingCommand + * and returns an EventAddMeetingCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public EventAddMeetingCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, CliSyntax.PREFIX_DESC, CliSyntax.PREFIX_VENUE, CliSyntax.PREFIX_TIME); + + if (!arePrefixesPresent(argMultimap, CliSyntax.PREFIX_DESC, CliSyntax.PREFIX_VENUE, CliSyntax.PREFIX_TIME) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + EventAddMeetingCommand.MESSAGE_USAGE)); + } + + String desc = argMultimap.getValue(CliSyntax.PREFIX_DESC).get(); + String venue = argMultimap.getValue(CliSyntax.PREFIX_VENUE).get(); + + String dateTime = argMultimap.getValue(CliSyntax.PREFIX_TIME).get(); + String[] dateTimeArr = dateTime.split(" "); + + if (dateTimeArr.length != 3) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + EventAddMeetingCommand.MESSAGE_USAGE)); + } + + LocalDate date = ParserUtil.parseDate(dateTimeArr[0]); + LocalTime startTime = ParserUtil.parseTime(dateTimeArr[1]); + LocalTime endTime = ParserUtil.parseTime(dateTimeArr[2]); + + if (startTime.compareTo(endTime) >= 0) { + throw new ParseException(MESSAGE_WRONG_TIME); + } + + Event meeting = new Meeting(desc, venue, startTime, endTime, date); + return new EventAddMeetingCommand(meeting); + } + + /** + * 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/nova/logic/parser/scparser/eventparsers/EventAddNoteCommandParser.java b/src/main/java/seedu/nova/logic/parser/scparser/eventparsers/EventAddNoteCommandParser.java new file mode 100644 index 00000000000..05f4691d4bb --- /dev/null +++ b/src/main/java/seedu/nova/logic/parser/scparser/eventparsers/EventAddNoteCommandParser.java @@ -0,0 +1,54 @@ +package seedu.nova.logic.parser.scparser.eventparsers; + +import static seedu.nova.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.time.LocalDate; +import java.util.stream.Stream; + +import seedu.nova.commons.core.index.Index; +import seedu.nova.logic.commands.sccommands.eventcommands.EventAddNoteCommand; +import seedu.nova.logic.parser.ArgumentMultimap; +import seedu.nova.logic.parser.ArgumentTokenizer; +import seedu.nova.logic.parser.CliSyntax; +import seedu.nova.logic.parser.Parser; +import seedu.nova.logic.parser.ParserUtil; +import seedu.nova.logic.parser.Prefix; +import seedu.nova.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new EventAddNoteCommand object. + */ +public class EventAddNoteCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the EventAddNoteCommand + * and returns an EventAddNoteCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public EventAddNoteCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, CliSyntax.PREFIX_DESC, CliSyntax.PREFIX_TIME, CliSyntax.PREFIX_INDEX); + + if (!arePrefixesPresent(argMultimap, CliSyntax.PREFIX_DESC, CliSyntax.PREFIX_TIME, CliSyntax.PREFIX_INDEX) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + EventAddNoteCommand.MESSAGE_USAGE)); + } + + String desc = argMultimap.getValue(CliSyntax.PREFIX_DESC).get(); + String date = argMultimap.getValue(CliSyntax.PREFIX_TIME).get(); + String index = argMultimap.getValue(CliSyntax.PREFIX_INDEX).get(); + + LocalDate localDate = ParserUtil.parseDate(date); + Index i = ParserUtil.parseIndex(index); + + return new EventAddNoteCommand(desc, localDate, i); + } + + /** + * 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/nova/logic/parser/scparser/eventparsers/EventAddStudyCommandParser.java b/src/main/java/seedu/nova/logic/parser/scparser/eventparsers/EventAddStudyCommandParser.java new file mode 100644 index 00000000000..7c08692a6de --- /dev/null +++ b/src/main/java/seedu/nova/logic/parser/scparser/eventparsers/EventAddStudyCommandParser.java @@ -0,0 +1,72 @@ +package seedu.nova.logic.parser.scparser.eventparsers; + +import static seedu.nova.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.nova.commons.core.Messages.MESSAGE_WRONG_TIME; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.stream.Stream; + +import seedu.nova.logic.commands.sccommands.eventcommands.EventAddStudyCommand; +import seedu.nova.logic.parser.ArgumentMultimap; +import seedu.nova.logic.parser.ArgumentTokenizer; +import seedu.nova.logic.parser.CliSyntax; +import seedu.nova.logic.parser.Parser; +import seedu.nova.logic.parser.ParserUtil; +import seedu.nova.logic.parser.Prefix; +import seedu.nova.logic.parser.exceptions.ParseException; +import seedu.nova.model.schedule.event.Event; +import seedu.nova.model.schedule.event.StudySession; + +/** + * Parses input arguments and creates a new EventAddStudyCommand object. + */ +public class EventAddStudyCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the EventAddStudyCommand + * and returns an EventAddStudyCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public EventAddStudyCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, CliSyntax.PREFIX_DESC, CliSyntax.PREFIX_VENUE, CliSyntax.PREFIX_TIME); + + if (!arePrefixesPresent(argMultimap, CliSyntax.PREFIX_DESC, CliSyntax.PREFIX_VENUE, CliSyntax.PREFIX_TIME) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + EventAddStudyCommand.MESSAGE_USAGE)); + } + + String desc = argMultimap.getValue(CliSyntax.PREFIX_DESC).get(); + String venue = argMultimap.getValue(CliSyntax.PREFIX_VENUE).get(); + + String dateTime = argMultimap.getValue(CliSyntax.PREFIX_TIME).get(); + String[] dateTimeArr = dateTime.split(" "); + + if (dateTimeArr.length != 3) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + EventAddStudyCommand.MESSAGE_USAGE)); + } + + LocalDate date = ParserUtil.parseDate(dateTimeArr[0]); + LocalTime startTime = ParserUtil.parseTime(dateTimeArr[1]); + LocalTime endTime = ParserUtil.parseTime(dateTimeArr[2]); + + if (startTime.compareTo(endTime) >= 0) { + throw new ParseException(MESSAGE_WRONG_TIME); + } + + Event study = new StudySession(desc, venue, startTime, endTime, date); + return new EventAddStudyCommand(study); + } + + /** + * 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/nova/logic/parser/scparser/eventparsers/EventDeleteCommandParser.java b/src/main/java/seedu/nova/logic/parser/scparser/eventparsers/EventDeleteCommandParser.java new file mode 100644 index 00000000000..e826a95b45e --- /dev/null +++ b/src/main/java/seedu/nova/logic/parser/scparser/eventparsers/EventDeleteCommandParser.java @@ -0,0 +1,54 @@ +package seedu.nova.logic.parser.scparser.eventparsers; + +import static seedu.nova.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.time.LocalDate; +import java.util.stream.Stream; + +import seedu.nova.commons.core.index.Index; +import seedu.nova.logic.commands.sccommands.eventcommands.EventDeleteCommand; +import seedu.nova.logic.parser.ArgumentMultimap; +import seedu.nova.logic.parser.ArgumentTokenizer; +import seedu.nova.logic.parser.CliSyntax; +import seedu.nova.logic.parser.Parser; +import seedu.nova.logic.parser.ParserUtil; +import seedu.nova.logic.parser.Prefix; +import seedu.nova.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new EventDeleteCommand object. + */ +public class EventDeleteCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the EventDeleteCommand + * and returns an EventDeleteCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public EventDeleteCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, CliSyntax.PREFIX_TIME, CliSyntax.PREFIX_INDEX); + + if (!arePrefixesPresent(argMultimap, CliSyntax.PREFIX_TIME, CliSyntax.PREFIX_INDEX) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + EventDeleteCommand.MESSAGE_USAGE)); + } + + String date = argMultimap.getValue(CliSyntax.PREFIX_TIME).get(); + String index = argMultimap.getValue(CliSyntax.PREFIX_INDEX).get(); + + LocalDate localDate = ParserUtil.parseDate(date); + Index i = ParserUtil.parseIndex(index); + + return new EventDeleteCommand(localDate, i); + } + + /** + * 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/model/AddressBook.java b/src/main/java/seedu/nova/model/AddressBook.java similarity index 96% rename from src/main/java/seedu/address/model/AddressBook.java rename to src/main/java/seedu/nova/model/AddressBook.java index 1a943a0781a..9f239f8f550 100644 --- a/src/main/java/seedu/address/model/AddressBook.java +++ b/src/main/java/seedu/nova/model/AddressBook.java @@ -1,12 +1,12 @@ -package seedu.address.model; +package seedu.nova.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; +import seedu.nova.model.person.Person; +import seedu.nova.model.person.UniquePersonList; /** * Wraps all data at the address-book level diff --git a/src/main/java/seedu/nova/model/Mode.java b/src/main/java/seedu/nova/model/Mode.java new file mode 100644 index 00000000000..1f5cfab7de8 --- /dev/null +++ b/src/main/java/seedu/nova/model/Mode.java @@ -0,0 +1,22 @@ +package seedu.nova.model; + +import seedu.nova.logic.parser.ModeEnum; + +/** + * Class for mode + */ +public class Mode { + private ModeEnum mode; + + public Mode(ModeEnum mode) { + this.mode = mode; + } + + public void setModeEnum (ModeEnum mode) { + this.mode = mode; + } + + public ModeEnum getModeEnum() { + return mode; + } +} diff --git a/src/main/java/seedu/nova/model/Model.java b/src/main/java/seedu/nova/model/Model.java new file mode 100644 index 00000000000..771b9281687 --- /dev/null +++ b/src/main/java/seedu/nova/model/Model.java @@ -0,0 +1,281 @@ +package seedu.nova.model; + +import java.nio.file.Path; +import java.time.LocalDate; +import java.util.List; +import java.util.function.Predicate; + +import javafx.collections.ObservableList; +import seedu.nova.commons.core.GuiSettings; +import seedu.nova.logic.parser.ModeEnum; +import seedu.nova.model.person.Person; +import seedu.nova.model.plan.StrongTask; +import seedu.nova.model.plan.Task; +import seedu.nova.model.plan.WeakTask; +import seedu.nova.model.progresstracker.Ip; +import seedu.nova.model.progresstracker.ProgressTracker; +import seedu.nova.model.progresstracker.PtTask; +import seedu.nova.model.progresstracker.Tp; +import seedu.nova.model.schedule.event.Event; +import seedu.nova.model.schedule.event.Lesson; +import seedu.nova.model.util.time.slotlist.DateTimeSlotList; + +/** + * The API of the Model component. + */ +public interface Model { + /** + * {@code Predicate} that always evaluate to true + */ + Predicate PREDICATE_SHOW_ALL_PERSONS = unused -> true; + + /** + * Returns the user prefs. + */ + ReadOnlyUserPrefs getUserPrefs(); + + /** + * Replaces user prefs data with the data in {@code userPrefs}. + */ + void setUserPrefs(ReadOnlyUserPrefs userPrefs); + + /** + * Returns the user prefs' GUI settings. + */ + GuiSettings getGuiSettings(); + + /** + * Sets the user prefs' GUI settings. + */ + void setGuiSettings(GuiSettings guiSettings); + + /** + * Returns the user prefs' nova book file path. + */ + Path getNovaFilePath(); + + /** + * Sets the user prefs' nova book file path. + */ + void setNovaFilePath(Path addressBookFilePath); + + /** + * Retrieves Nova object + * @return Nova object + */ + Nova getNova(); + + /** + * Retrieves mode + * @return mode + */ + Mode getMode(); + + /** + * Retrieves ModeEnum in mode + * @param mode mode to get ModeEnum from + * @return ModeEnum in mode + */ + ModeEnum getModeEnum(Mode mode); + + /** + * Gets the string form of the modeEnum + * @param modeEnum modeEnum to get name from + * @return name of modeEnum + */ + String getModeName(ModeEnum modeEnum); + + /** + * Sets modeEnum in mode + * @param mode mode to set modeEnum + * @param modeEnum modeEnum to be set + */ + void setModeEnum(Mode mode, ModeEnum modeEnum); + + /** + * Returns the AddressBook + */ + ReadOnlyAddressBook getAddressBook(); + + /** + * Replaces nova book data with the data in {@code addressBook}. + */ + void setAddressBook(ReadOnlyAddressBook addressBook); + + /** + * Returns true if a person with the same identity as {@code person} exists in the nova book. + */ + boolean hasPerson(Person person); + + /** + * Deletes the given person. + * The person must exist in the nova book. + */ + void deletePerson(Person target); + + /** + * Adds the given person. + * {@code person} must not already exist in the nova book. + */ + void addPerson(Person person); + + /** + * Replaces the given person {@code target} with {@code editedPerson}. + * {@code target} must exist in the nova book. + * The person identity of {@code editedPerson} must not be the same as another existing person in the nova 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); + + void commitAddressBook(); + + void undoAddressBook(); + + boolean canUndoAddressBook(); + + boolean canRedoAddressBook(); + + void redoAddressBook(); + + String viewSchedule(LocalDate date); + + String viewSchedule(int weekNumber); + + boolean isWithinSem(LocalDate date); + + boolean isWithinSem(int weekNumber); + + void addEvent(Event e); + + void addAllLessons(Lesson l); + + DateTimeSlotList getFreeSlotOn(LocalDate date); + + String viewFreeSlot(LocalDate date); + + String deleteEvent(LocalDate date, int index); + + boolean deleteEvent(Event event); + + String addNote(String desc, LocalDate date, int index); + + //==============studyplanner============= + + void resetPlan(); + + boolean addRoutineTask(StrongTask st); + + boolean addFlexibleTask(WeakTask wt); + + List getTaskList(); + + Task searchTask(String name); + + boolean deleteTask(Task task); + + Event generateTaskEvent(Task task, LocalDate date) throws Exception; + + //============== Progress Tracker ============= + + /** + * Retrieves ProgressTracker object + * @return ProgressTracker object + */ + ProgressTracker getProgressTracker(); + + /** + * Retrieves Ip object + * @return Ip object + */ + Ip getProgressTrackerIp(); + + /** + * Retrieves Tp object + * @return Tp object + */ + Tp getProgressTrackerTp(); + + /** + * Lists PtTasks in specified week of project + * @param projectName project to list tasks in + * @param weekNum week to list tasks in + * @return String of lists of tasks + */ + String listPtTask(String projectName, int weekNum); + + /** + * Adds PtTask + * @param projectName Project to add PtTask to + * @param weekNum week to add PtTask to + * @param task PtTask to be added + */ + void addPtTask(String projectName, int weekNum, PtTask task); + + /** + * Deletes PtTask + * @param projectName Project the PtTask in + * @param weekNum week the PtTask in + * @param taskNum task number of PtTask to be deleted + * @return boolean on whether execution was successful + */ + boolean deletePtTask(String projectName, int weekNum, int taskNum); + + /** + * Edits PtTask + * @param projectName Project the PtTask in + * @param weekNum week the PtTask in + * @param taskNum task number of PtTask to be deleted + * @param taskDesc new task description + * @return boolean on whether execution was successful + */ + boolean editPtTask(String projectName, int weekNum, int taskNum, String taskDesc); + + /** + * Changes done status of PtTask + * @param projectName Project the PtTask in + * @param weekNum week the PtTask in + * @param taskNum task number of PtTask to change done + * @return boolean on whether execution was successful + */ + boolean setDonePtTask(String projectName, int weekNum, int taskNum); + + /** + * Adds note to PtTask + * @param projectName Project the PtTask in + * @param weekNum week the PtTask in + * @param taskNum task number of PtTask to add note to + * @param note note to be added + * @return boolean on whether execution was successful + */ + boolean addPtNote(String projectName, int weekNum, int taskNum, String note); + + /** + * Deletes note from PtTask + * @param projectName Project the PtTask in + * @param weekNum week the PtTask in + * @param taskNum task number of PtTask to delete note + * @return boolean on whether execution was successful + */ + boolean deletePtNote(String projectName, int weekNum, int taskNum); + + /** + * Edits note of PtTask + * @param projectName Project the PtTask in + * @param weekNum week the PtTask in + * @param taskNum task number of PtTask to edit note + * @param note new note + * @return boolean on whether execution was successful + */ + boolean editPtNote(String projectName, int weekNum, int taskNum, String note); +} diff --git a/src/main/java/seedu/nova/model/ModelManager.java b/src/main/java/seedu/nova/model/ModelManager.java new file mode 100644 index 00000000000..6905f805baa --- /dev/null +++ b/src/main/java/seedu/nova/model/ModelManager.java @@ -0,0 +1,389 @@ +package seedu.nova.model; + +import static java.util.Objects.requireNonNull; +import static seedu.nova.commons.util.CollectionUtil.requireAllNonNull; + +import java.nio.file.Path; +import java.time.LocalDate; +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.nova.commons.core.GuiSettings; +import seedu.nova.commons.core.LogsCenter; +import seedu.nova.logic.parser.ModeEnum; +import seedu.nova.model.person.Person; +import seedu.nova.model.plan.Plan; +import seedu.nova.model.plan.StrongTask; +import seedu.nova.model.plan.Task; +import seedu.nova.model.plan.WeakTask; +import seedu.nova.model.progresstracker.Ip; +import seedu.nova.model.progresstracker.ProgressTracker; +import seedu.nova.model.progresstracker.PtTask; +import seedu.nova.model.progresstracker.Tp; +import seedu.nova.model.schedule.event.Event; +import seedu.nova.model.schedule.event.Lesson; +import seedu.nova.model.util.time.slotlist.DateTimeSlotList; + + +/** + * Represents the in-memory model of the data. + */ +public class ModelManager implements Model { + private static final Logger logger = LogsCenter.getLogger(ModelManager.class); + + private final Nova nova; + private final VersionedAddressBook addressBook; + private final UserPrefs userPrefs; + private final FilteredList filteredPersons; + private final Schedule schedule; + private final Plan plan; + private final ProgressTracker progressTracker; + private Mode mode; + + /** + * Initializes a ModelManager with the given addressBook and userPrefs. + */ + public ModelManager(Nova nova, ReadOnlyUserPrefs userPrefs) { + super(); + requireAllNonNull(nova, userPrefs); + + logger.fine("Initializing with NOVA: " + nova + " and user prefs " + userPrefs); + + this.nova = nova; + this.addressBook = nova.getAddressBookNova(); + this.userPrefs = new UserPrefs(userPrefs); + this.progressTracker = nova.getProgressTracker(); + filteredPersons = new FilteredList<>(this.addressBook.getPersonList()); + this.schedule = nova.getScheduleNova(); + // this.schedule = new Schedule(LocalDate.of(2020, 1, 13), LocalDate.of(2020, 5, 3)); + this.plan = nova.getStudyPlan(); + this.mode = new Mode(ModeEnum.HOME); + } + + public ModelManager() { + this(new Nova(), new UserPrefs()); + } + + //=========== UserPrefs ================================================================================== + + @Override + public ReadOnlyUserPrefs getUserPrefs() { + return userPrefs; + } + + @Override + public void setUserPrefs(ReadOnlyUserPrefs userPrefs) { + requireNonNull(userPrefs); + this.userPrefs.resetData(userPrefs); + } + + @Override + public GuiSettings getGuiSettings() { + return userPrefs.getGuiSettings(); + } + + @Override + public void setGuiSettings(GuiSettings guiSettings) { + requireNonNull(guiSettings); + userPrefs.setGuiSettings(guiSettings); + } + + @Override + public Path getNovaFilePath() { + return userPrefs.getNovaFilePath(); + } + + @Override + public void setNovaFilePath(Path novaFilePath) { + requireNonNull(novaFilePath); + userPrefs.setNovaFilePath(novaFilePath); + } + + @Override + public Nova getNova() { + nova.setScheduleNova(schedule); + return this.nova; + } + + //=========== Mode ================================================================================== + @Override + public Mode getMode() { + return mode; + } + + @Override + public ModeEnum getModeEnum(Mode mode) { + return mode.getModeEnum(); + } + + @Override + public String getModeName(ModeEnum modeEnum) { + return modeEnum.name(); + } + + @Override + public void setModeEnum(Mode mode, ModeEnum modeEnum) { + mode.setModeEnum(modeEnum); + } + + //=========== AddressBook ================================================================================ + @Override + public ReadOnlyAddressBook getAddressBook() { + return addressBook; + } + + @Override + public void setAddressBook(ReadOnlyAddressBook addressBook) { + this.addressBook.resetData(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); + } + + //=========== Address Book ============================================================= + //=========== AB: 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); + } + + //=========== AB: Undo/Redo ============================================================= + + @Override + public void commitAddressBook() { + addressBook.commit(); + } + + @Override + public void undoAddressBook() { + addressBook.undo(); + } + + @Override + public boolean canUndoAddressBook() { + return addressBook.canUndo(); + } + + @Override + public boolean canRedoAddressBook() { + return addressBook.canRedo(); + } + + @Override + public void redoAddressBook() { + addressBook.redo(); + } + + @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); + } + + //=========== Scheduler Methods ============================================================= + + @Override + public String viewSchedule(LocalDate date) { + + return schedule.view(date); + + } + + @Override + public String viewSchedule(int weekNumber) { + + return schedule.view(weekNumber); + + } + + @Override + public boolean isWithinSem(LocalDate date) { + + return schedule.checkDateValidity(date); + + } + + @Override + public boolean isWithinSem(int weekNumber) { + + return schedule.checkWeekValidity(weekNumber); + + } + + //=========== Event and Schedule ============================================================= + @Override + public void addEvent(Event e) { + schedule.addEvent(e); + } + + @Override + public void addAllLessons(Lesson l) { + schedule.addAllLessons(l); + } + + @Override + public DateTimeSlotList getFreeSlotOn(LocalDate date) { + return schedule.getDay(date).getFreeSlotList(); + } + + @Override + public String viewFreeSlot(LocalDate date) { + return getFreeSlotOn(date).toString(); + } + + @Override + public String deleteEvent(LocalDate date, int index) { + return schedule.deleteEvent(date, index).toString(); + } + + @Override + public boolean deleteEvent(Event e) { + return schedule.deleteEvent(e); + } + + @Override + public String addNote(String desc, LocalDate date, int index) { + return schedule.addNote(desc, date, index); + } + + //=========== Study Planner ============================================================= + @Override + public void resetPlan() { + plan.resetPlan(); + } + + @Override + public boolean addRoutineTask(StrongTask task) { + return plan.addTask(task); + } + + @Override + public boolean addFlexibleTask(WeakTask task) { + return plan.addTask(task); + } + + @Override + public List getTaskList() { + return plan.getTaskList(); + } + + @Override + public Task searchTask(String name) { + return plan.searchTask(name); + } + + @Override + public boolean deleteTask(Task task) { + task.getEventAfter(LocalDate.now()).forEach(schedule::deleteEvent); + return plan.deleteTask(task); + } + + @Override + public Event generateTaskEvent(Task task, LocalDate date) throws Exception { + return plan.generateTaskEvent(task, date, schedule); + } + + //=========== Progress Tracker ============================================================= + @Override + public ProgressTracker getProgressTracker() { + return progressTracker; + } + + @Override + public Ip getProgressTrackerIp() { + return progressTracker.getIp(); + } + + @Override + public Tp getProgressTrackerTp() { + return progressTracker.getTp(); + } + + @Override + public String listPtTask(String projectName, int weekNum) { + return progressTracker.listPtTask(projectName, weekNum); + } + + @Override + public void addPtTask(String projectName, int weekNum, PtTask task) { + progressTracker.addPtTask(projectName, weekNum, task); + } + + @Override + public boolean deletePtTask(String projectName, int weekNum, int taskNum) { + return progressTracker.deletePtTask(projectName, weekNum, taskNum); + } + + @Override + public boolean editPtTask(String projectName, int weekNum, int taskNum, String taskDesc) { + return progressTracker.editPtTask(projectName, weekNum, taskNum, taskDesc); + } + + @Override + public boolean setDonePtTask(String projectName, int weekNum, int taskNum) { + return progressTracker.setDonePtTask(projectName, weekNum, taskNum); + } + + @Override + public boolean addPtNote(String projectName, int weekNum, int taskNum, String note) { + return progressTracker.addPtNote(projectName, weekNum, taskNum, note); + } + + @Override + public boolean deletePtNote(String projectName, int weekNum, int taskNum) { + return progressTracker.deletePtNote(projectName, weekNum, taskNum); + } + + @Override + public boolean editPtNote(String projectName, int weekNum, int taskNum, String note) { + return progressTracker.editPtNote(projectName, weekNum, taskNum, note); + } +} diff --git a/src/main/java/seedu/nova/model/Nova.java b/src/main/java/seedu/nova/model/Nova.java new file mode 100644 index 00000000000..d9d35250fd7 --- /dev/null +++ b/src/main/java/seedu/nova/model/Nova.java @@ -0,0 +1,50 @@ +package seedu.nova.model; + +import static java.util.Objects.requireNonNull; + +import seedu.nova.model.plan.Plan; +import seedu.nova.model.progresstracker.ProgressTracker; + +/** + * Nova class + */ +public class Nova { + + private VersionedAddressBook addressBook; + private Schedule schedule; + private ProgressTracker progressTracker; + private Plan studyPlan; + + public VersionedAddressBook getAddressBookNova() { + return this.addressBook; + } + + public void setAddressBookNova(VersionedAddressBook addressBook) { + this.addressBook = addressBook; + } + + public Schedule getScheduleNova() { + return this.schedule; + } + + public void setScheduleNova(Schedule schedule) { + this.schedule = schedule; + } + + public ProgressTracker getProgressTracker() { + return this.progressTracker; + } + + public Plan getStudyPlan() { + return this.studyPlan; + } + + public void setStudyPlan(Plan plan) { + this.studyPlan = plan; + } + + public void setProgressTrackerNova(ProgressTracker progressTracker) { + requireNonNull(progressTracker); + this.progressTracker = progressTracker; + } +} diff --git a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java b/src/main/java/seedu/nova/model/ReadOnlyAddressBook.java similarity index 81% rename from src/main/java/seedu/address/model/ReadOnlyAddressBook.java rename to src/main/java/seedu/nova/model/ReadOnlyAddressBook.java index 6ddc2cd9a29..4f7d06b6472 100644 --- a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java +++ b/src/main/java/seedu/nova/model/ReadOnlyAddressBook.java @@ -1,7 +1,7 @@ -package seedu.address.model; +package seedu.nova.model; import javafx.collections.ObservableList; -import seedu.address.model.person.Person; +import seedu.nova.model.person.Person; /** * Unmodifiable view of an address book diff --git a/src/main/java/seedu/nova/model/ReadOnlyUserPrefs.java b/src/main/java/seedu/nova/model/ReadOnlyUserPrefs.java new file mode 100644 index 00000000000..012a196aa7e --- /dev/null +++ b/src/main/java/seedu/nova/model/ReadOnlyUserPrefs.java @@ -0,0 +1,21 @@ +package seedu.nova.model; + +import java.nio.file.Path; +import java.time.LocalDate; + +import seedu.nova.commons.core.GuiSettings; + +/** + * Unmodifiable view of user prefs. + */ +public interface ReadOnlyUserPrefs { + + GuiSettings getGuiSettings(); + + Path getNovaFilePath(); + + LocalDate getScheduleStartDate(); + + LocalDate getScheduleEndDate(); + +} diff --git a/src/main/java/seedu/nova/model/Schedule.java b/src/main/java/seedu/nova/model/Schedule.java new file mode 100644 index 00000000000..febc4355581 --- /dev/null +++ b/src/main/java/seedu/nova/model/Schedule.java @@ -0,0 +1,317 @@ +package seedu.nova.model; + +import static java.time.temporal.ChronoUnit.DAYS; + +import java.time.LocalDate; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +import seedu.nova.model.schedule.Day; +import seedu.nova.model.schedule.Week; +import seedu.nova.model.schedule.event.Event; +import seedu.nova.model.schedule.event.Lesson; +import seedu.nova.model.schedule.event.exceptions.DateNotFoundException; +import seedu.nova.model.util.Copyable; + +/** + * The type Schedule. + */ +public class Schedule implements Copyable { + + private static final int DAYS_IN_WEEK = 7; + private static final int ACTUAL_RECESS_WEEK = 7; + private static final int SCHEDULE_RECESS_WEEK = 16; + private static final int WEEK_OFFSET = 1; + + private final LocalDate startDate; + private final LocalDate endDate; + + private Week[] weeks; + + /** + * Instantiates a new Schedule. + * + * @param startDate the start date + * @param endDate the end date + */ + public Schedule(LocalDate startDate, LocalDate endDate) { + + this.startDate = startDate; + this.endDate = endDate; + + weeks = new Week[17]; + } + + private Schedule(LocalDate startDate, LocalDate endDate, Week[] weeks) { + this.startDate = startDate; + this.endDate = endDate; + this.weeks = weeks; + } + + /** + * Adds a single event to the schedule. + * + * @param event the event + */ + public void addEvent(Event event) { + + LocalDate date = event.getDate(); + + int weekNumber = calWeekNumber(date); + + if (weeks[weekNumber] == null) { + weeks[weekNumber] = new Week(startDate.plusWeeks(weekNumber - WEEK_OFFSET)); + } + weeks[weekNumber].addEvent(event); + + } + + /** + * Adds all weekly lessons to the schedule for the semester. + * + * @param lesson the lesson + */ + public void addAllLessons(Lesson lesson) { + for (int i = 1; i <= 14; i++) { + if (i == ACTUAL_RECESS_WEEK) { + //No lesson on recess week + continue; + } + + if (weeks[i] == null) { + weeks[i] = new Week(startDate.plusWeeks(i - 1)); + } + + weeks[i].addLesson(lesson); + } + } + + public Day getDay(LocalDate date) { + int weekNumber = calWeekNumber(date); + if (weeks[weekNumber] == null) { + return null; + } + return weeks[weekNumber].getDay(date.getDayOfWeek()); + } + + /** + * Deletes an event + * @param date the date of the event + * @param index the position of event in list + */ + public Event deleteEvent(LocalDate date, int index) throws DateNotFoundException { + int weekNumber = calWeekNumber(date); + + if (weeks[weekNumber] == null) { + throw new DateNotFoundException(); + } + + return weeks[weekNumber].deleteEvent(date, index); + } + + /** + * Deletes an event + * @param event the event + */ + public boolean deleteEvent(Event event) throws DateNotFoundException { + int weekNumber = calWeekNumber(event.getDate()); + + if (weeks[weekNumber] == null) { + throw new DateNotFoundException(); + } + + return weeks[weekNumber].deleteEvent(event); + } + + + /** + * Adds a note to an Event. + * @param desc description of the note + * @param date the date of the event + * @param index the position of event in list + * @return String representing the event with added note + */ + public String addNote(String desc, LocalDate date, int index) { + int weekNumber = calWeekNumber(date); + + if (weeks[weekNumber] == null) { + throw new DateNotFoundException(); + } + + return weeks[weekNumber].addNote(desc, date, index); + } + + /** + * Views the schedule on specified date. + * + * @param date the date of interest. + * @return the return message to print on the app. + */ + public String view(LocalDate date) { + + int weekNumber = calWeekNumber(date); + + if (weeks[weekNumber] == null) { + return ""; + } + + return weeks[weekNumber].view(date); + } + + /** + * Views the schedule of a particular week. + * + * @param weekNumber the week of interest. + * @return the return message to print on the app. + */ + public String view(int weekNumber) { + + weekNumber = adjustWeekNumber(weekNumber); + + if (weeks[weekNumber] == null) { + return ""; + } + + return weeks[weekNumber].view(); + } + + /** + * Checks if a date lies within the schedule's period. + * + * @param date the date of interest. + * @return if the date lies within the schedule. + */ + public boolean checkDateValidity(LocalDate date) { + + return !isBeforeStart(date) && !isAfterEnd(date); + } + + /** + * Check if the week number lies within the schedule. + * + * @param weekNumber the week number of interest. + * @return if the week lies within the schedule. + */ + public boolean checkWeekValidity(int weekNumber) { + + return !isBeforeStart(weekNumber) && !isAfterEnd(weekNumber); + } + + /** + * Adjusts the user-entered week number into the corresponding week index in schedule. + * + * @param weekNumber the user-entered week number. + * @return the corresponding week number in schedule + */ + private int adjustWeekNumber(int weekNumber) { + + if (weekNumber == SCHEDULE_RECESS_WEEK) { + + return ACTUAL_RECESS_WEEK; + + } else if (weekNumber >= ACTUAL_RECESS_WEEK) { + + return weekNumber + 1; + + } else { + + return weekNumber; + + } + } + + /** + * An encapsulation to check if a date occurs before the start date of the schedule. + * + * @param date the date in question. + * @return if date is before the schedule's start date. + */ + + private boolean isBeforeStart(LocalDate date) { + + return date.compareTo(startDate) < 0; + } + + /** + * An encapsulation to check if the user-entered week number is before the schedule's starting week. + * + * @param weekNumber the week number in question. + * @return if the week number is before the first week of schedule. + */ + + private boolean isBeforeStart(int weekNumber) { + + return weekNumber < 1; + } + + /** + * An encapsulation to check if a date occurs after the end date of the schedule. + * + * @param date the date in question. + * @return if date is after the schedule's end date. + */ + + private boolean isAfterEnd(LocalDate date) { + + return date.compareTo(endDate) > 0; + } + + /** + * An encapsulation to check if the user-entered week number is after the schedule's ending week. + * + * @param weekNumber the week number in question. + * @return if the week number is after the last week of schedule. + */ + + private boolean isAfterEnd(int weekNumber) { + + return weekNumber > 16; + } + + /** + * Calculates the week number of a given date within the schedule. + * + * @param date the date of interest. + * @return the corresponding week number. + */ + + private int calWeekNumber(LocalDate date) { + + return (int) (DAYS.between(startDate, date) / DAYS_IN_WEEK) + WEEK_OFFSET; + } + + /** + * Check if event is in + * @param event event + * @return event is inside? + */ + public boolean hasEvent(Event event) { + int weekNumber = calWeekNumber(event.getDate()); + + if (weeks[weekNumber] == null) { + return false; + } + + return weeks[weekNumber].hasEvent(event); + } + + public List getEventList() { + List events = new LinkedList<>(); + + for (Week w: weeks) { + + if (w != null) { + events.addAll(w.getEventList()); + } + } + + return events; + } + + @Override + public Schedule getCopy() { + return new Schedule(startDate, endDate, (Week[]) Arrays.stream(weeks).map(Week::getCopy).toArray()); + } + +} diff --git a/src/main/java/seedu/address/model/UserPrefs.java b/src/main/java/seedu/nova/model/UserPrefs.java similarity index 55% rename from src/main/java/seedu/address/model/UserPrefs.java rename to src/main/java/seedu/nova/model/UserPrefs.java index 25a5fd6eab9..6f4bcc721bd 100644 --- a/src/main/java/seedu/address/model/UserPrefs.java +++ b/src/main/java/seedu/nova/model/UserPrefs.java @@ -1,12 +1,13 @@ -package seedu.address.model; +package seedu.nova.model; import static java.util.Objects.requireNonNull; import java.nio.file.Path; import java.nio.file.Paths; +import java.time.LocalDate; import java.util.Objects; -import seedu.address.commons.core.GuiSettings; +import seedu.nova.commons.core.GuiSettings; /** * Represents User's preferences. @@ -14,7 +15,9 @@ public class UserPrefs implements ReadOnlyUserPrefs { private GuiSettings guiSettings = new GuiSettings(); - private Path addressBookFilePath = Paths.get("data" , "addressbook.json"); + private Path novaFilePath = Paths.get("data" , "Nova.json"); + private LocalDate scheduleStartDate = LocalDate.of(2020, 1, 13); + private LocalDate scheduleEndDate = LocalDate.of(2020, 5, 3); /** * Creates a {@code UserPrefs} with default values. @@ -35,7 +38,9 @@ public UserPrefs(ReadOnlyUserPrefs userPrefs) { public void resetData(ReadOnlyUserPrefs newUserPrefs) { requireNonNull(newUserPrefs); setGuiSettings(newUserPrefs.getGuiSettings()); - setAddressBookFilePath(newUserPrefs.getAddressBookFilePath()); + setNovaFilePath(newUserPrefs.getNovaFilePath()); + setScheduleStartDate(newUserPrefs.getScheduleStartDate()); + setScheduleEndDate(newUserPrefs.getScheduleEndDate()); } public GuiSettings getGuiSettings() { @@ -47,13 +52,31 @@ public void setGuiSettings(GuiSettings guiSettings) { this.guiSettings = guiSettings; } - public Path getAddressBookFilePath() { - return addressBookFilePath; + public Path getNovaFilePath() { + return novaFilePath; } - public void setAddressBookFilePath(Path addressBookFilePath) { + public void setNovaFilePath(Path addressBookFilePath) { requireNonNull(addressBookFilePath); - this.addressBookFilePath = addressBookFilePath; + this.novaFilePath = addressBookFilePath; + } + + public LocalDate getScheduleStartDate() { + return scheduleStartDate; + } + + public void setScheduleStartDate(LocalDate scheduleStartDate) { + requireNonNull(scheduleStartDate); + this.scheduleStartDate = scheduleStartDate; + } + + public LocalDate getScheduleEndDate() { + return scheduleEndDate; + } + + public void setScheduleEndDate(LocalDate scheduleEndDate) { + requireNonNull(scheduleEndDate); + this.scheduleEndDate = scheduleEndDate; } @Override @@ -68,19 +91,19 @@ public boolean equals(Object other) { UserPrefs o = (UserPrefs) other; return guiSettings.equals(o.guiSettings) - && addressBookFilePath.equals(o.addressBookFilePath); + && novaFilePath.equals(o.novaFilePath); } @Override public int hashCode() { - return Objects.hash(guiSettings, addressBookFilePath); + return Objects.hash(guiSettings, novaFilePath); } @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 : " + novaFilePath); return sb.toString(); } diff --git a/src/main/java/seedu/nova/model/VersionedAddressBook.java b/src/main/java/seedu/nova/model/VersionedAddressBook.java new file mode 100644 index 00000000000..3ff0a2c7918 --- /dev/null +++ b/src/main/java/seedu/nova/model/VersionedAddressBook.java @@ -0,0 +1,79 @@ +package seedu.nova.model; + +import java.util.ArrayList; +import java.util.List; + +/** + * For use in redo and undo command for Address Book feature. + * Save the state of AddressBook every time certain command is used. + */ +public class VersionedAddressBook extends AddressBook { + + private final List addressBookStateList; + private int currStatePointer; + + public VersionedAddressBook(ReadOnlyAddressBook initialState) { + super(initialState); + + addressBookStateList = new ArrayList<>(); + addressBookStateList.add(new AddressBook(initialState)); + currStatePointer = 0; + } + + /** + * Save and add current state of address book. + */ + public void commit() { + addressBookStateList.subList(currStatePointer + 1, addressBookStateList.size()).clear(); + addressBookStateList.add(new AddressBook(this)); + currStatePointer++; + } + + public boolean canUndo() { + return currStatePointer > 0; + } + + public boolean canRedo() { + return currStatePointer < addressBookStateList.size() - 1; + } + + /** + * Undo the command. + */ + public void undo() { + if (canUndo()) { + currStatePointer--; + resetData(addressBookStateList.get(currStatePointer)); + } + } + + /** + * Redo the command. + */ + public void redo() { + if (canRedo()) { + currStatePointer++; + resetData(addressBookStateList.get(currStatePointer)); + } + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof VersionedAddressBook)) { + return false; + } + + VersionedAddressBook otherVersionedAddressBook = (VersionedAddressBook) other; + + // state check + return super.equals(otherVersionedAddressBook) + && addressBookStateList.equals(otherVersionedAddressBook.addressBookStateList) + && currStatePointer == otherVersionedAddressBook.currStatePointer; + } +} diff --git a/src/main/java/seedu/nova/model/category/Category.java b/src/main/java/seedu/nova/model/category/Category.java new file mode 100644 index 00000000000..6cd5b2f5e57 --- /dev/null +++ b/src/main/java/seedu/nova/model/category/Category.java @@ -0,0 +1,60 @@ +package seedu.nova.model.category; + +import static java.util.Objects.requireNonNull; +import static seedu.nova.commons.util.AppUtil.checkArgument; + +/** + * Represents a Category in the address book. + * Guarantees: immutable; name is valid as declared in {@link #isValidTagName(String)} + */ +public class Category { + + public static final String MESSAGE_CONSTRAINTS = "Category should be either classmate or teammate only"; + public static final String VALIDATION_REGEX = "\\p{Alnum}+"; + + public final String categoryName; + + /** + * Constructs a {@code Category}. + * + * @param categoryName A valid tag name. + */ + public Category(String categoryName) { + requireNonNull(categoryName); + checkArgument(isValidTagName(categoryName), MESSAGE_CONSTRAINTS); + this.categoryName = categoryName.toLowerCase(); + } + + /** + * Returns true if a given string is a valid tag name. + */ + public static boolean isValidTagName(String test) { + if (test.matches(VALIDATION_REGEX) + && (test.toLowerCase().equals("classmate") || test.toLowerCase().equals("teammate"))) { + return true; + } else { + return false; + } + //return test.matches(VALIDATION_REGEX); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Category // instanceof handles nulls + && categoryName.equals(((Category) other).categoryName)); // state check + } + + @Override + public int hashCode() { + return categoryName.hashCode(); + } + + /** + * Format state as text for viewing. + */ + public String toString() { + return categoryName; + } + +} diff --git a/src/main/java/seedu/nova/model/note/NotesList.java b/src/main/java/seedu/nova/model/note/NotesList.java new file mode 100644 index 00000000000..70e5f6feddf --- /dev/null +++ b/src/main/java/seedu/nova/model/note/NotesList.java @@ -0,0 +1,7 @@ +package seedu.nova.model.note; + +/** + * Represents a note list + */ +public class NotesList { +} diff --git a/src/main/java/seedu/address/model/person/Address.java b/src/main/java/seedu/nova/model/person/Address.java similarity index 93% rename from src/main/java/seedu/address/model/person/Address.java rename to src/main/java/seedu/nova/model/person/Address.java index 60472ca22a0..ed6b81ccb72 100644 --- a/src/main/java/seedu/address/model/person/Address.java +++ b/src/main/java/seedu/nova/model/person/Address.java @@ -1,7 +1,7 @@ -package seedu.address.model.person; +package seedu.nova.model.person; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; +import static seedu.nova.commons.util.AppUtil.checkArgument; /** * Represents a Person's address in the address book. diff --git a/src/main/java/seedu/nova/model/person/CategoryContainsKeywordsPredicate.java b/src/main/java/seedu/nova/model/person/CategoryContainsKeywordsPredicate.java new file mode 100644 index 00000000000..734b40f62ca --- /dev/null +++ b/src/main/java/seedu/nova/model/person/CategoryContainsKeywordsPredicate.java @@ -0,0 +1,30 @@ +package seedu.nova.model.person; + +import java.util.List; +import java.util.function.Predicate; + +/** + * Tests that a {@code Person}'s {@code Category} matches any of the keywords given. + */ +public class CategoryContainsKeywordsPredicate implements Predicate { + private final List keywords; + + public CategoryContainsKeywordsPredicate(List keywords) { + this.keywords = keywords; + } + + @Override + public boolean test(Person person) { + return keywords.stream() + .anyMatch(keywords -> person.getCategory().iterator().next().toString().contains(keywords)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof CategoryContainsKeywordsPredicate // instanceof handles nulls + && keywords.equals(((CategoryContainsKeywordsPredicate) other).keywords)); // state check + } + +} + diff --git a/src/main/java/seedu/address/model/person/Email.java b/src/main/java/seedu/nova/model/person/Email.java similarity index 96% rename from src/main/java/seedu/address/model/person/Email.java rename to src/main/java/seedu/nova/model/person/Email.java index a5bbe0b6a5f..a400e52d3e1 100644 --- a/src/main/java/seedu/address/model/person/Email.java +++ b/src/main/java/seedu/nova/model/person/Email.java @@ -1,7 +1,7 @@ -package seedu.address.model.person; +package seedu.nova.model.person; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; +import static seedu.nova.commons.util.AppUtil.checkArgument; /** * Represents a Person's email in the address book. diff --git a/src/main/java/seedu/address/model/person/Name.java b/src/main/java/seedu/nova/model/person/Name.java similarity index 74% rename from src/main/java/seedu/address/model/person/Name.java rename to src/main/java/seedu/nova/model/person/Name.java index 79244d71cf7..266c13c8325 100644 --- a/src/main/java/seedu/address/model/person/Name.java +++ b/src/main/java/seedu/nova/model/person/Name.java @@ -1,7 +1,7 @@ -package seedu.address.model.person; +package seedu.nova.model.person; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; +import static seedu.nova.commons.util.AppUtil.checkArgument; /** * Represents a Person's name in the address book. @@ -13,7 +13,7 @@ public class Name { "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} ]*"; @@ -28,7 +28,13 @@ public class Name { public Name(String name) { requireNonNull(name); checkArgument(isValidName(name), MESSAGE_CONSTRAINTS); - fullName = name; + String[] splitBySpace = name.split("\\s+"); + String newName = ""; + for (int i = 0; i < splitBySpace.length; i++) { + newName = newName + Character.toUpperCase(splitBySpace[i].charAt(0)) + + splitBySpace[i].substring(1).toLowerCase() + " "; + } + fullName = newName; } /** diff --git a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java b/src/main/java/seedu/nova/model/person/NameContainsKeywordsPredicate.java similarity index 91% rename from src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java rename to src/main/java/seedu/nova/model/person/NameContainsKeywordsPredicate.java index c9b5868427c..281c90c6c2d 100644 --- a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java +++ b/src/main/java/seedu/nova/model/person/NameContainsKeywordsPredicate.java @@ -1,9 +1,9 @@ -package seedu.address.model.person; +package seedu.nova.model.person; import java.util.List; import java.util.function.Predicate; -import seedu.address.commons.util.StringUtil; +import seedu.nova.commons.util.StringUtil; /** * Tests that a {@code Person}'s {@code Name} matches any of the keywords given. diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/nova/model/person/Person.java similarity index 69% rename from src/main/java/seedu/address/model/person/Person.java rename to src/main/java/seedu/nova/model/person/Person.java index 557a7a60cd5..ce1b963fd31 100644 --- a/src/main/java/seedu/address/model/person/Person.java +++ b/src/main/java/seedu/nova/model/person/Person.java @@ -1,13 +1,13 @@ -package seedu.address.model.person; +package seedu.nova.model.person; -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.nova.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; +import seedu.nova.model.category.Category; /** * Represents a Person in the address book. @@ -20,20 +20,21 @@ public class Person { private final Phone phone; private final Email email; + // Data fields - private final Address address; - private final Set tags = new HashSet<>(); + private final Remark remark; + private final Set category = 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); + public Person(Name name, Phone phone, Email email, Set category, Remark remark) { + requireAllNonNull(name, phone, email, category); this.name = name; this.phone = phone; this.email = email; - this.address = address; - this.tags.addAll(tags); + this.category.addAll(category); + this.remark = remark; } public Name getName() { @@ -48,16 +49,16 @@ public Email getEmail() { return email; } - public Address getAddress() { - return address; + public Remark getRemark() { + return remark; } /** * Returns an immutable tag set, which throws {@code UnsupportedOperationException} * if modification is attempted. */ - public Set getTags() { - return Collections.unmodifiableSet(tags); + public Set getCategory() { + return Collections.unmodifiableSet(category); } /** @@ -92,28 +93,26 @@ public boolean equals(Object other) { return otherPerson.getName().equals(getName()) && otherPerson.getPhone().equals(getPhone()) && otherPerson.getEmail().equals(getEmail()) - && otherPerson.getAddress().equals(getAddress()) - && otherPerson.getTags().equals(getTags()); + && otherPerson.getCategory().equals(getCategory()); } @Override public int hashCode() { // use this method for custom fields hashing instead of implementing your own - return Objects.hash(name, phone, email, address, tags); + return Objects.hash(name, phone, email, category); } @Override public String toString() { final StringBuilder builder = new StringBuilder(); - builder.append(getName()) - .append(" Phone: ") + String name = getName().toString().trim(); + builder.append(name) + .append(", Phone: ") .append(getPhone()) - .append(" Email: ") + .append(", Email: ") .append(getEmail()) - .append(" Address: ") - .append(getAddress()) - .append(" Tags: "); - getTags().forEach(builder::append); + .append(", Category: "); + getCategory().forEach(builder::append); return builder.toString(); } diff --git a/src/main/java/seedu/nova/model/person/PersonNameComparator.java b/src/main/java/seedu/nova/model/person/PersonNameComparator.java new file mode 100644 index 00000000000..c57c1d3deb5 --- /dev/null +++ b/src/main/java/seedu/nova/model/person/PersonNameComparator.java @@ -0,0 +1,14 @@ +package seedu.nova.model.person; + +import java.util.Comparator; + +/** + * Comparator to compare a person's name alphabetically and sort them in alphabetical order. + */ +public class PersonNameComparator implements Comparator { + + @Override + public int compare(Person p1, Person p2) { + return p1.getName().toString().compareToIgnoreCase(p2.getName().toString()); + } +} diff --git a/src/main/java/seedu/address/model/person/Phone.java b/src/main/java/seedu/nova/model/person/Phone.java similarity index 93% rename from src/main/java/seedu/address/model/person/Phone.java rename to src/main/java/seedu/nova/model/person/Phone.java index 872c76b382f..fafcb726360 100644 --- a/src/main/java/seedu/address/model/person/Phone.java +++ b/src/main/java/seedu/nova/model/person/Phone.java @@ -1,7 +1,7 @@ -package seedu.address.model.person; +package seedu.nova.model.person; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; +import static seedu.nova.commons.util.AppUtil.checkArgument; /** * Represents a Person's phone number in the address book. diff --git a/src/main/java/seedu/nova/model/person/Remark.java b/src/main/java/seedu/nova/model/person/Remark.java new file mode 100644 index 00000000000..1e7a4350ab0 --- /dev/null +++ b/src/main/java/seedu/nova/model/person/Remark.java @@ -0,0 +1,33 @@ +package seedu.nova.model.person; + +import static java.util.Objects.requireNonNull; + +/** + * Represents a Person's remark in the address book. + * Guarantees: immutable; is always valid + */ +public class Remark { + public final String value; + + public Remark(String remark) { + requireNonNull(remark); + value = remark; + } + + @Override + public String toString() { + return value.equals("") ? "[You have not added any remark for this person]" : value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Remark // instanceof handles nulls + && value.equals(((Remark) 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/nova/model/person/UniquePersonList.java similarity index 87% rename from src/main/java/seedu/address/model/person/UniquePersonList.java rename to src/main/java/seedu/nova/model/person/UniquePersonList.java index 0fee4fe57e6..f8c9d9238e7 100644 --- a/src/main/java/seedu/address/model/person/UniquePersonList.java +++ b/src/main/java/seedu/nova/model/person/UniquePersonList.java @@ -1,15 +1,15 @@ -package seedu.address.model.person; +package seedu.nova.model.person; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.nova.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; +import seedu.nova.model.person.exceptions.DuplicatePersonException; +import seedu.nova.model.person.exceptions.PersonNotFoundException; /** * A list of persons that enforces uniqueness between its elements and does not allow nulls. @@ -46,6 +46,7 @@ public void add(Person toAdd) { throw new DuplicatePersonException(); } internalList.add(toAdd); + FXCollections.sort(internalList, new PersonNameComparator()); } /** @@ -66,6 +67,7 @@ public void setPerson(Person target, Person editedPerson) { } internalList.set(index, editedPerson); + FXCollections.sort(internalList, new PersonNameComparator()); } /** @@ -77,11 +79,13 @@ public void remove(Person toRemove) { if (!internalList.remove(toRemove)) { throw new PersonNotFoundException(); } + FXCollections.sort(internalList, new PersonNameComparator()); } public void setPersons(UniquePersonList replacement) { requireNonNull(replacement); internalList.setAll(replacement.internalList); + FXCollections.sort(internalList, new PersonNameComparator()); } /** @@ -95,6 +99,7 @@ public void setPersons(List persons) { } internalList.setAll(persons); + FXCollections.sort(internalList, new PersonNameComparator()); } /** diff --git a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java b/src/main/java/seedu/nova/model/person/exceptions/DuplicatePersonException.java similarity index 87% rename from src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java rename to src/main/java/seedu/nova/model/person/exceptions/DuplicatePersonException.java index d7290f59442..b851c31f216 100644 --- a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java +++ b/src/main/java/seedu/nova/model/person/exceptions/DuplicatePersonException.java @@ -1,4 +1,4 @@ -package seedu.address.model.person.exceptions; +package seedu.nova.model.person.exceptions; /** * Signals that the operation will result in duplicate Persons (Persons are considered duplicates if they have the same diff --git a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java b/src/main/java/seedu/nova/model/person/exceptions/PersonNotFoundException.java similarity index 75% rename from src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java rename to src/main/java/seedu/nova/model/person/exceptions/PersonNotFoundException.java index fa764426ca7..1f7db466a12 100644 --- a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java +++ b/src/main/java/seedu/nova/model/person/exceptions/PersonNotFoundException.java @@ -1,4 +1,4 @@ -package seedu.address.model.person.exceptions; +package seedu.nova.model.person.exceptions; /** * Signals that the operation is unable to find the specified person. diff --git a/src/main/java/seedu/nova/model/plan/ImpossibleTaskException.java b/src/main/java/seedu/nova/model/plan/ImpossibleTaskException.java new file mode 100644 index 00000000000..ffb21b5bdd7 --- /dev/null +++ b/src/main/java/seedu/nova/model/plan/ImpossibleTaskException.java @@ -0,0 +1,7 @@ +package seedu.nova.model.plan; + +/** + * Throw when cannot generate event for task + */ +public class ImpossibleTaskException extends Exception { +} diff --git a/src/main/java/seedu/nova/model/plan/Plan.java b/src/main/java/seedu/nova/model/plan/Plan.java new file mode 100644 index 00000000000..678d4163421 --- /dev/null +++ b/src/main/java/seedu/nova/model/plan/Plan.java @@ -0,0 +1,24 @@ +package seedu.nova.model.plan; + +import java.time.LocalDate; +import java.util.List; + +import seedu.nova.model.Schedule; +import seedu.nova.model.schedule.event.Event; + +/** + * task container + */ +public interface Plan { + void resetPlan(); + + List getTaskList(); + + boolean addTask(Task task); + + boolean deleteTask(Task task); + + Task searchTask(String name); + + Event generateTaskEvent(Task task, LocalDate date, Schedule sc) throws ImpossibleTaskException; +} diff --git a/src/main/java/seedu/nova/model/plan/StrongTask.java b/src/main/java/seedu/nova/model/plan/StrongTask.java new file mode 100644 index 00000000000..3796a3d91d3 --- /dev/null +++ b/src/main/java/seedu/nova/model/plan/StrongTask.java @@ -0,0 +1,65 @@ +package seedu.nova.model.plan; + +import java.time.Duration; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import seedu.nova.model.Schedule; +import seedu.nova.model.schedule.Day; +import seedu.nova.model.schedule.event.Event; +import seedu.nova.model.schedule.event.WeakEvent; +import seedu.nova.model.util.time.duration.DateTimeDuration; + +/** + * Task which generates definite events + */ +public class StrongTask extends Task { + + private StrongTask(TaskDetails details) { + super(details); + } + + public static StrongTask get(String name, Duration duration, TaskFreq freq) { + return new StrongTask(new TaskDetails(name, duration, freq)); + } + + @Override + public Event generateEventOnDay(LocalDate date, Schedule sc) throws ImpossibleTaskException { + if (hasEventOn(date) && sc.hasEvent(getEventOn(date))) { + return null; + } else { + Day d = sc.getDay(date); + List dtdLst; + if (d == null) { + dtdLst = new Day(date).getFreeSlotList().getSlotList(getBaseDuration()); + } else { + dtdLst = d.getFreeSlotList().getSlotList(getBaseDuration()); + } + if (dtdLst.isEmpty()) { + throw new ImpossibleTaskException(); + } else { + DateTimeDuration dtd = getBestTimeframe(dtdLst); + Event newEvent = new WeakEvent(getName(), dtd, this); + addEvent(newEvent); + sc.addEvent(newEvent); + return newEvent; + } + } + } + + private DateTimeDuration getBestTimeframe(List freeSlots) { + assert !freeSlots.isEmpty() : "no free slot to choose"; + List lst = new ArrayList<>(freeSlots); + Collections.shuffle(lst); + DateTimeDuration dtd = lst.get(0); + return new DateTimeDuration(dtd.getStartDateTime(), dtd.getStartDateTime().plus(getBaseDuration())); + } + + @Override + public String toString() { + return String.format("%s\nNo. of weeks done: %d\nEvents scheduled:\n %s\n", super.toString(), size(), + listEvents()); + } +} diff --git a/src/main/java/seedu/nova/model/plan/StudyPlan.java b/src/main/java/seedu/nova/model/plan/StudyPlan.java new file mode 100644 index 00000000000..6a7367297f0 --- /dev/null +++ b/src/main/java/seedu/nova/model/plan/StudyPlan.java @@ -0,0 +1,61 @@ +package seedu.nova.model.plan; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; + +import seedu.nova.model.Schedule; +import seedu.nova.model.schedule.event.Event; + +/** + * Plan with definite tasks + */ +public class StudyPlan implements Plan { + private HashMap map = new HashMap<>(); + + public StudyPlan() { + resetPlan(); + } + + public StudyPlan(Collection tasks) { + for (Task t : tasks) { + addTask(t); + } + } + + @Override + public void resetPlan() { + this.map = new HashMap<>(); + } + + @Override + public List getTaskList() { + return new ArrayList<>(map.values()); + } + + @Override + public boolean addTask(Task task) { + if (map.get(task.details) != null) { + return false; + } else { + return map.put(task.details, task) == null; + } + } + + @Override + public boolean deleteTask(Task task) { + return map.remove(task.details, task); + } + + @Override + public Task searchTask(String name) { + return map.get(TaskDetails.ofName(name)); + } + + @Override + public Event generateTaskEvent(Task task, LocalDate date, Schedule sc) throws ImpossibleTaskException { + return task.generateEventOnDay(date, sc); + } +} diff --git a/src/main/java/seedu/nova/model/plan/Task.java b/src/main/java/seedu/nova/model/plan/Task.java new file mode 100644 index 00000000000..c835c8dfaec --- /dev/null +++ b/src/main/java/seedu/nova/model/plan/Task.java @@ -0,0 +1,172 @@ +package seedu.nova.model.plan; + +import java.io.Serializable; +import java.time.Duration; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; +import java.util.TreeMap; +import java.util.stream.IntStream; + +import seedu.nova.model.Schedule; +import seedu.nova.model.schedule.event.Event; +import seedu.nova.model.util.time.TimeUtil; + + +/** + * task inside plan, represents a soon-to-be-defined event for each day. + */ +public abstract class Task implements Serializable { + protected TaskDetails details; + protected TreeMap dayEventMap; + protected Duration totalEventDuration; + + protected Task(TaskDetails details) { + this.details = details; + this.dayEventMap = new TreeMap<>(); + this.totalEventDuration = Duration.ZERO; + } + + public TaskDetails getDetails() { + return details; + } + + public String getName() { + return details.getName(); + } + + public int size() { + return dayEventMap.size(); + } + + public List getEvents() { + return new ArrayList<>(dayEventMap.values()); + } + + public Duration getBaseDuration() { + return details.getDuration(); + } + + public TaskFreq getTaskFreq() { + return details.getTaskFreq(); + } + + public TreeMap getDayEventMap() { + return dayEventMap; + } + + public Event getEventOn(LocalDate date) { + switch (getTaskFreq()) { + case WEEKLY: + return dayEventMap.get(TimeUtil.getMondayOfWeek(date)); + case DAILY: + default: + return dayEventMap.get(date); + } + } + + public List getEventAfter(LocalDate date) { + return new ArrayList<>(dayEventMap.tailMap(date).values()); + } + + /** + * check if it is appropriate to generate an event on the date specified according to the task frequency + * + * @param date date + * @return good? + */ + public boolean hasEventOn(LocalDate date) { + switch (getTaskFreq()) { + case WEEKLY: + return dayEventMap.containsKey(TimeUtil.getMondayOfWeek(date)); + case DAILY: + default: + return dayEventMap.containsKey(date); + } + } + + /** + * Generate an event based on the day's schedule and specification of this task. + * event is not added into day. + * + * @param date date to generate event on + * @param sc the schedule + * @return event that is added. null if event not added + * @throws ImpossibleTaskException when cannot generate event + */ + public abstract Event generateEventOnDay(LocalDate date, Schedule sc) throws ImpossibleTaskException; + + /** + * Add event that is related to the task. + * For scheduler use only + * + * @param event event generated by this task + * @return deleted? + */ + public Event addEvent(Event event) { + this.totalEventDuration = this.totalEventDuration.plus(event.getDtd().getDuration()); + switch (getTaskFreq()) { + case WEEKLY: + return this.dayEventMap.put(TimeUtil.getMondayOfWeek(event.getDate()), event); + case DAILY: + default: + return this.dayEventMap.put(event.getDate(), event); + } + } + + /** + * Delete event generated by the task. + * For scheduler use only + * + * @param event event generated by this task + * @return deleted? + */ + public boolean deleteEvent(Event event) { + if (deleteEventFromMap(event)) { + this.totalEventDuration = this.totalEventDuration.minus(event.getDtd().getDuration()); + return true; + } else { + return false; + } + } + + /** + * @param event + * @return + */ + private boolean deleteEventFromMap(Event event) { + switch (getTaskFreq()) { + case WEEKLY: + return this.dayEventMap.remove(TimeUtil.getMondayOfWeek(event.getDate()), event); + case DAILY: + default: + return this.dayEventMap.remove(event.getDate(), event); + } + } + + /** + * List out all the events + * + * @return string of list of events + */ + protected String listEvents() { + final List lst = getEvents(); + return IntStream.range(0, lst.size()) + .mapToObj(i -> "(" + i + ")\n" + lst.get(i).toString() + "\n") + .reduce("", (x, y) -> x + y); + } + + @Override + public String toString() { + return details.toString(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Task) { + return details.equals(((Task) obj).details); + } else { + return super.equals(obj); + } + } +} diff --git a/src/main/java/seedu/nova/model/plan/TaskDetails.java b/src/main/java/seedu/nova/model/plan/TaskDetails.java new file mode 100644 index 00000000000..507f7cbdf31 --- /dev/null +++ b/src/main/java/seedu/nova/model/plan/TaskDetails.java @@ -0,0 +1,61 @@ +package seedu.nova.model.plan; + +import java.io.Serializable; +import java.time.Duration; + +/** + * Task details + */ +public class TaskDetails implements Serializable { + private final String name; + private final Duration duration; + private final TaskFreq freq; + + public TaskDetails(String name, Duration duration, TaskFreq freq) { + this.name = name; + this.duration = duration; + this.freq = freq; + } + + static TaskDetails ofName(String name) { + return new TaskDetails(name, null, null); + } + + String getName() { + return name; + } + + Duration getDuration() { + return duration; + } + + TaskFreq getTaskFreq() { + return freq; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof TaskDetails) { + return name.equals(((TaskDetails) obj).name); + } else { + return super.equals(obj); + } + } + + @Override + public String toString() { + String sb = "Name: " + + name + + "Duration: " + + duration + + "Frequency: " + + freq.toString(); + return sb; + } + + @Override + public int hashCode() { + return name.hashCode(); + } + +} diff --git a/src/main/java/seedu/nova/model/plan/TaskFreq.java b/src/main/java/seedu/nova/model/plan/TaskFreq.java new file mode 100644 index 00000000000..26e69368a9e --- /dev/null +++ b/src/main/java/seedu/nova/model/plan/TaskFreq.java @@ -0,0 +1,8 @@ +package seedu.nova.model.plan; + +/** + * Task frequency + */ +public enum TaskFreq { + WEEKLY, DAILY +} diff --git a/src/main/java/seedu/nova/model/plan/WeakTask.java b/src/main/java/seedu/nova/model/plan/WeakTask.java new file mode 100644 index 00000000000..95065143d22 --- /dev/null +++ b/src/main/java/seedu/nova/model/plan/WeakTask.java @@ -0,0 +1,94 @@ +package seedu.nova.model.plan; + +import java.time.Duration; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import seedu.nova.model.Schedule; +import seedu.nova.model.schedule.Day; +import seedu.nova.model.schedule.event.Event; +import seedu.nova.model.schedule.event.WeakEvent; +import seedu.nova.model.util.time.duration.DateTimeDuration; + +/** + * Task which generates definite events + */ +public class WeakTask extends Task { + private Duration maxDuration; + private Duration total; + + private WeakTask(TaskDetails details, Duration maxDuration, Duration total) { + super(details); + this.maxDuration = maxDuration; + this.total = total; + } + + public static WeakTask get(String name, Duration mind, Duration maxd, Duration total) + throws ImpossibleTaskException { + if (mind.compareTo(total) > 0) { + throw new ImpossibleTaskException(); + } + return new WeakTask(new TaskDetails(name, mind, TaskFreq.DAILY), maxd, total); + } + + public Duration getMaxDuration() { + if (total.minus(totalEventDuration).compareTo(maxDuration) > 0) { + return maxDuration; + } else { + return total.minus(totalEventDuration); + } + } + + @Override + public Event generateEventOnDay(LocalDate date, Schedule sc) throws ImpossibleTaskException { + if (hasEventOn(date) && sc.hasEvent(getEventOn(date))) { + return null; + } else { + Day d = sc.getDay(date); + List dtdLst; + if (d == null) { + dtdLst = new Day(date).getFreeSlotList().getSlotList(getBaseDuration()); + } else { + dtdLst = d.getFreeSlotList().getSlotList(getBaseDuration()); + } + if (dtdLst.isEmpty()) { + throw new ImpossibleTaskException(); + } else { + DateTimeDuration dtd = getBestTimeframe(dtdLst); + Event newEvent = new WeakEvent(getName(), dtd, this); + addEvent(newEvent); + sc.addEvent(newEvent); + return newEvent; + } + } + } + + private DateTimeDuration getBestTimeframe(List freeSlots) throws ImpossibleTaskException { + assert !freeSlots.isEmpty() : "no free slot to choose"; + if (getMaxDuration().isZero()) { + throw new ImpossibleTaskException(); + } + List lst = new ArrayList<>(freeSlots); + Collections.sort(lst); + Collections.reverse(lst); + for (DateTimeDuration dtd : lst) { + if (dtd.getDuration().compareTo(getMaxDuration()) < 0 + && dtd.getDuration().compareTo(getBaseDuration()) > 0) { + return dtd; + } else { + return new DateTimeDuration(dtd.getStartDateTime(), dtd.getStartDateTime().plus(getMaxDuration())); + } + } + throw new ImpossibleTaskException(); + } + + @Override + public String toString() { + return String.format("%s\nPercentage done: %f\nEvents scheduled:\n %s\n", + super.toString(), ( + totalEventDuration.getSeconds() + 0.0) / total.getSeconds(), + listEvents()); + } +} diff --git a/src/main/java/seedu/nova/model/progresstracker/Ip.java b/src/main/java/seedu/nova/model/progresstracker/Ip.java new file mode 100644 index 00000000000..8964fb3f718 --- /dev/null +++ b/src/main/java/seedu/nova/model/progresstracker/Ip.java @@ -0,0 +1,30 @@ +package seedu.nova.model.progresstracker; + +/** + * Represents individual project + */ +public class Ip extends Project { + private PtWeekList weekList; + private String projectName; + + /** + * Creates Ip object + */ + public Ip() { + weekList = new PtWeekList(); + projectName = "ip"; + } + + public String getProjectName() { + return projectName; + } + + public PtWeekList getWeekList() { + return weekList; + } + + public double getProgress() { + double progress = Math.round(weekList.getProgressProject() * 100d) / 100d; + return progress * 100; + } +} diff --git a/src/main/java/seedu/nova/model/progresstracker/ProgressTracker.java b/src/main/java/seedu/nova/model/progresstracker/ProgressTracker.java new file mode 100644 index 00000000000..7aaeb301db2 --- /dev/null +++ b/src/main/java/seedu/nova/model/progresstracker/ProgressTracker.java @@ -0,0 +1,453 @@ +package seedu.nova.model.progresstracker; + +import static java.util.Objects.requireNonNull; + +import java.util.ArrayList; + +/** + * Represents a progress tracker + */ +public class ProgressTracker { + private Ip ip; + private Tp tp; + + /** + * Creates a ProgressTracker object + */ + public ProgressTracker() { + this.ip = new Ip(); + this.tp = new Tp(); + } + + public Ip getIp() { + return ip; + } + + public Tp getTp() { + return tp; + } + + public PtWeekList getPtWeekList(Project project) { + return project.getWeekList(); + } + + public ArrayList getArrayListFromWeekList(PtWeekList ptWeekList) { + return ptWeekList.getWeekList(); + } + + public PtWeek getPtWeek(PtWeekList ptWeekList, int weekNum) { + return ptWeekList.getWeek(weekNum); + } + + public int getNumFromWeek(PtWeek week) { + return week.getWeekNum(); + } + + public PtTaskList getPtTaskList(PtWeek ptWeek) { + return ptWeek.getTaskList(); + } + + public ArrayList getArrayListFromTaskList(PtTaskList ptTaskList) { + return ptTaskList.getList(); + } + + public int getNumTask(PtTaskList ptTaskList) { + return ptTaskList.getNumTask(); + } + + public PtTask getTaskFromList(PtTaskList ptTaskList, int taskNum) { + return ptTaskList.getTask(taskNum); + } + + public PtNote getNoteFromTask(PtTask task) { + return task.getNote(); + } + + public String listTaskFromList(PtTaskList ptTaskList) { + return ptTaskList.listTasks(); + } + + public void addTaskToList(PtTaskList ptTaskList, PtTask task) { + ptTaskList.addTask(task); + } + + public void deleteTaskFromList(PtTaskList ptTaskList, int taskNum) { + ptTaskList.deleteTask(taskNum); + } + + public void editTaskFromList(PtTask task, String taskDesc) { + task.setTaskDesc(taskDesc); + } + + public void setDoneInList(PtTask task) { + task.setDone(); + } + + public void addNoteToTask(PtTask task, String note) { + task.setNote(note); + } + + /** + * Lists tasks in specified project and week + * + * @param projectName specified project + * @param weekNum specified week + * @return listing of the tasks in the specified week and project + */ + public String listPtTask(String projectName, int weekNum) { + requireNonNull(projectName); + + Project project; + boolean isIpProject = projectName.toLowerCase().equals("ip"); + + if (isIpProject) { + project = getIp(); + } else { + project = getTp(); + } + + PtWeekList ptWeekList = getPtWeekList(project); + PtWeek ptWeek = getPtWeek(ptWeekList, weekNum); + PtTaskList ptTaskList = getPtTaskList(ptWeek); + + return listTaskFromList(ptTaskList); + } + + /** + * adds a pttask to the specified project and week + * + * @param projectName specified project + * @param weekNum specified week + * @param task to be added + */ + public void addPtTask(String projectName, int weekNum, PtTask task) { + requireNonNull(projectName); + requireNonNull(task); + + Project project; + boolean isIpProject = projectName.toLowerCase().equals("ip"); + + if (isIpProject) { + project = getIp(); + } else { + project = getTp(); + } + + PtWeekList ptWeekList = getPtWeekList(project); + PtWeek ptWeek = getPtWeek(ptWeekList, weekNum); + PtTaskList ptTaskList = getPtTaskList(ptWeek); + + addTaskToList(ptTaskList, task); + } + + /** + * deletes task from specified project and week + * + * @param projectName specified project + * @param weekNum specified week + * @param taskNum specified task + * @return true if successful, false if no task + */ + public boolean deletePtTask(String projectName, int weekNum, int taskNum) { + requireNonNull(projectName); + + Project project; + boolean isIpProject = projectName.toLowerCase().equals("ip"); + + if (isIpProject) { + project = getIp(); + } else { + project = getTp(); + } + + PtWeekList ptWeekList = getPtWeekList(project); + PtWeek ptWeek = getPtWeek(ptWeekList, weekNum); + PtTaskList ptTaskList = getPtTaskList(ptWeek); + boolean isOutOfBound = getNumTask(ptTaskList) < taskNum; + + if (isOutOfBound) { + return false; + } else { + deleteTaskFromList(ptTaskList, taskNum); + return true; + } + } + + /** + * edits task from specified project and week + * + * @param projectName specified project + * @param weekNum specified week + * @param taskNum specified task + * @return true if successful, false if no task + */ + public boolean editPtTask(String projectName, int weekNum, int taskNum, String taskDesc) { + requireNonNull(projectName); + requireNonNull(taskDesc); + + Project project; + boolean isIpProject = projectName.toLowerCase().equals("ip"); + + if (isIpProject) { + project = getIp(); + } else { + project = getTp(); + } + + PtWeekList ptWeekList = getPtWeekList(project); + PtWeek ptWeek = getPtWeek(ptWeekList, weekNum); + PtTaskList ptTaskList = getPtTaskList(ptWeek); + boolean isOutOfBound = getNumTask(ptTaskList) < taskNum; + + if (isOutOfBound) { + return false; + } else { + PtTask taskEdit = getTaskFromList(ptTaskList, taskNum); + + editTaskFromList(taskEdit, taskDesc); + return true; + } + } + + /** + * sets done for specified task + * + * @param projectName specified project + * @param weekNum specified week + * @param taskNum specified task + * @return true if successful, false if no task + */ + public boolean setDonePtTask(String projectName, int weekNum, int taskNum) { + requireNonNull(projectName); + + Project project; + boolean isIpProject = projectName.toLowerCase().equals("ip"); + + if (isIpProject) { + project = getIp(); + } else { + project = getTp(); + } + + PtWeekList ptWeekList = getPtWeekList(project); + PtWeek ptWeek = getPtWeek(ptWeekList, weekNum); + PtTaskList ptTaskList = getPtTaskList(ptWeek); + boolean isOutOfBound = getNumTask(ptTaskList) < taskNum; + + if (isOutOfBound) { + return false; + } else { + PtTask taskToSetDone = ptTaskList.getTask(taskNum); + + setDoneInList(taskToSetDone); + return true; + } + } + + /** + * adds ptNote to a specified task + * + * @param projectName specified project + * @param weekNum specified week + * @param taskNum specified task + * @param note ptNote to add + * @return true if successful, false if no task + */ + public boolean addPtNote(String projectName, int weekNum, int taskNum, String note) { + requireNonNull(projectName); + requireNonNull(note); + + Project project; + boolean isIpProject = projectName.toLowerCase().equals("ip"); + + if (isIpProject) { + project = getIp(); + } else { + project = getTp(); + } + + PtWeekList ptWeekList = getPtWeekList(project); + PtWeek ptWeek = getPtWeek(ptWeekList, weekNum); + PtTaskList ptTaskList = getPtTaskList(ptWeek); + boolean isOutOfBound = getNumTask(ptTaskList) < taskNum; + + if (isOutOfBound) { + return false; + } + + PtTask taskToAddNote = getTaskFromList(ptTaskList, taskNum); + boolean hasNote = !getNoteFromTask(taskToAddNote).toString().equals(""); + + if (hasNote) { + return false; + } else { + addNoteToTask(taskToAddNote, note); + return true; + } + } + + /** + * deletes the note of the specified task + * + * @param projectName specified project + * @param weekNum specified week + * @param taskNum specified task + * @return true if successful, false if not successful + */ + public boolean deletePtNote(String projectName, int weekNum, int taskNum) { + requireNonNull(projectName); + + Project project; + boolean isIpProject = projectName.toLowerCase().equals("ip"); + + if (isIpProject) { + project = getIp(); + } else { + project = getTp(); + } + + PtWeekList ptWeekList = getPtWeekList(project); + PtWeek ptWeek = getPtWeek(ptWeekList, weekNum); + PtTaskList ptTaskList = getPtTaskList(ptWeek); + boolean isOutOfBound = getNumTask(ptTaskList) < taskNum; + + if (isOutOfBound) { + return false; + } + + PtTask taskToDeleteNote = getTaskFromList(ptTaskList, taskNum); + PtNote noteDelete = getNoteFromTask(taskToDeleteNote); + boolean isEmptyNote = noteDelete.toString().equals(""); + + if (isEmptyNote) { + return false; + } else { + addNoteToTask(taskToDeleteNote, ""); + return true; + } + } + + /** + * edits the note of the specified task + * + * @param projectName specified project + * @param weekNum specified week + * @param taskNum specified task + * @return true if successful, false if not successful + */ + public boolean editPtNote(String projectName, int weekNum, int taskNum, String note) { + requireNonNull(projectName); + requireNonNull(note); + + Project project; + boolean isIpProject = projectName.toLowerCase().equals("ip"); + + if (isIpProject) { + project = getIp(); + } else { + project = getTp(); + } + + PtWeekList ptWeekList = getPtWeekList(project); + PtWeek ptWeek = getPtWeek(ptWeekList, weekNum); + PtTaskList ptTaskList = getPtTaskList(ptWeek); + boolean isOutOfBound = getNumTask(ptTaskList) < taskNum; + + if (isOutOfBound) { + return false; + } + + PtTask taskToEditNote = getTaskFromList(ptTaskList, taskNum); + PtNote noteEdit = getNoteFromTask(taskToEditNote); + boolean isEmptyNote = noteEdit.toString().equals(""); + + if (isEmptyNote) { + return false; + } else { + addNoteToTask(taskToEditNote, note); + return true; + } + } + + /** + * Returns a list of ptTasks in Ip project + */ + public ArrayList toIpPtTaskList() { + ArrayList ipTasks = new ArrayList<>(); + Ip ip = getIp(); + PtWeekList ipWeekList = getPtWeekList(ip); + + for (PtWeek week : getArrayListFromWeekList(ipWeekList)) { + int weekNum = getNumFromWeek(week); + PtTaskList ptTaskList = getPtTaskList(week); + + for (PtTask task : getArrayListFromTaskList(ptTaskList)) { + ipTasks.add(task); + } + } + return ipTasks; + } + + /** + * Returns a list of ptTasks in Tp project + */ + public ArrayList toTpPtTaskList() { + ArrayList tpTasks = new ArrayList<>(); + Tp tp = getTp(); + PtWeekList tpWeekList = getPtWeekList(tp); + + for (PtWeek week : getArrayListFromWeekList(tpWeekList)) { + int weekNum = getNumFromWeek(week); + PtTaskList ptTaskList = getPtTaskList(week); + + for (PtTask task : getArrayListFromTaskList(ptTaskList)) { + tpTasks.add(task); + } + } + + return tpTasks; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } else if (other instanceof ProgressTracker) { + boolean ipTaskEqual = true; + boolean tpTaskEqual = true; + + ArrayList ipTasks = toIpPtTaskList(); + ArrayList tpTasks = toTpPtTaskList(); + ArrayList otherIpTasks = ((ProgressTracker) other).toIpPtTaskList(); + ArrayList otherTpTasks = ((ProgressTracker) other).toTpPtTaskList(); + + if (ipTasks.size() != otherIpTasks.size() || tpTasks.size() != otherTpTasks.size()) { + ipTaskEqual = false; + tpTaskEqual = false; + } else { + for (int i = 0; i < ipTasks.size(); i++) { + PtTask ipTaskCurrent = ipTasks.get(i); + PtTask otherIpTaskCurrent = otherIpTasks.get(i); + + if (!ipTaskCurrent.equals(otherIpTaskCurrent)) { + ipTaskEqual = false; + break; + } + } + + for (int i = 0; i < tpTasks.size(); i++) { + PtTask tpTaskCurrent = tpTasks.get(i); + PtTask otherTpTaskCurrent = otherIpTasks.get(i); + + if (!tpTaskCurrent.equals(otherTpTaskCurrent)) { + tpTaskEqual = false; + break; + } + } + } + return ipTaskEqual && tpTaskEqual; + } else { + return false; + } + } +} diff --git a/src/main/java/seedu/nova/model/progresstracker/Project.java b/src/main/java/seedu/nova/model/progresstracker/Project.java new file mode 100644 index 00000000000..ad2df2bc827 --- /dev/null +++ b/src/main/java/seedu/nova/model/progresstracker/Project.java @@ -0,0 +1,28 @@ +package seedu.nova.model.progresstracker; + +/** + * Represents a project + */ +public abstract class Project { + public static final String MESSAGE_CONSTRAINTS = "Project name must be ip or tp only and should not be blank"; + + public abstract PtWeekList getWeekList(); + + public abstract String getProjectName(); + + /** + * Checks if is Ip or Tp + * @param projectName name of project + * @return true if is Ip or Tp but false if it's other projects + */ + public static boolean isValidProject(String projectName) { + projectName = projectName.toLowerCase(); + + if (projectName.equals("ip") || projectName.equals("tp")) { + return true; + } else { + return false; + } + } + +} diff --git a/src/main/java/seedu/nova/model/progresstracker/PtNote.java b/src/main/java/seedu/nova/model/progresstracker/PtNote.java new file mode 100644 index 00000000000..abba692043d --- /dev/null +++ b/src/main/java/seedu/nova/model/progresstracker/PtNote.java @@ -0,0 +1,43 @@ +package seedu.nova.model.progresstracker; + +import static java.util.Objects.requireNonNull; + +/** + * Represents a note + */ +public class PtNote { + public static final String MESSAGE_CONSTRAINTS = "Note 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].*"; + + private String note; + + /** + * Creates a PtNote object + * @param note note in PtNote + */ + public PtNote(String note) { + requireNonNull(note); + this.note = note; + } + + public String getNote() { + return note; + } + + /** + * Returns true if a given string is a valid note. + */ + public static boolean isValidNote(String test) { + return test.matches(VALIDATION_REGEX); + } + + @Override + public String toString() { + return note; + } +} diff --git a/src/main/java/seedu/nova/model/progresstracker/PtTask.java b/src/main/java/seedu/nova/model/progresstracker/PtTask.java new file mode 100644 index 00000000000..4f35c69b70d --- /dev/null +++ b/src/main/java/seedu/nova/model/progresstracker/PtTask.java @@ -0,0 +1,138 @@ +package seedu.nova.model.progresstracker; + +import static java.util.Objects.requireNonNull; + +/** + * Represents a project task + */ +public class PtTask { + private Project project; + private TaskDesc taskDesc; + private PtNote note; + private int ptWeek; + private boolean isDone; + + /** + * Creates PtTask object + * @param taskDesc taskDesc of task + * @param project project of task + * @param note note of task + * @param ptWeek week of task + * @param isDone done status of task + */ + public PtTask(TaskDesc taskDesc, Project project, PtNote note, int ptWeek, boolean isDone) { + requireNonNull(taskDesc); + requireNonNull(project); + requireNonNull(note); + + this.taskDesc = taskDesc; + this.project = project; + this.ptWeek = ptWeek; + this.isDone = isDone; + this.note = note; + } + + public boolean isDone() { + return isDone; + } + + /** + * return string of boolean isDone + * @return true or false + */ + public String isDoneString() { + if (isDone) { + return "true"; + } else { + return "false"; + } + } + + public void setDone() { + if (isDone) { + isDone = false; + } else { + isDone = true; + } + } + + public Project getProject() { + return project; + } + + public String getProjectName() { + return project.getProjectName(); + } + + public void setProject(Project project) { + requireNonNull(project); + this.project = project; + } + + public TaskDesc getTaskDesc() { + return taskDesc; + } + + public String getTaskDescString() { + return taskDesc.toString(); + } + + public void setTaskDesc(String taskDesc) { + this.taskDesc = new TaskDesc(taskDesc); + } + + public PtNote getNote() { + return note; + } + + public String getNoteString() { + return note.toString(); + } + + public void setNote(String noteDesc) { + this.note = new PtNote(noteDesc); + } + + public int getPtWeek() { + return ptWeek; + } + + /** + * Gets icon for task status. + * @return icon in string + */ + public String getStatusIcon() { + if (isDone) { + return "\u2713"; + } else { + return "\u2718"; + } + } + + /** + * Returns true if both ptTasks are the same and data fields. + * This defines a stronger notion of equality between two ptTasks. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof PtTask)) { + return false; + } + + PtTask otherPtTask = (PtTask) other; + return otherPtTask.getProject().getProjectName().equals(getProject().getProjectName()) + && otherPtTask.getNote().toString().equals(getNote().toString()) + && otherPtTask.getTaskDesc().toString().equals(getTaskDesc().toString()) + && (otherPtTask.getPtWeek() == getPtWeek()) && (otherPtTask.isDone() == isDone()); + } + + + @Override + public String toString() { + return "[" + getStatusIcon() + "] " + getTaskDesc(); + } +} diff --git a/src/main/java/seedu/nova/model/progresstracker/PtTaskList.java b/src/main/java/seedu/nova/model/progresstracker/PtTaskList.java new file mode 100644 index 00000000000..3dd628cfe31 --- /dev/null +++ b/src/main/java/seedu/nova/model/progresstracker/PtTaskList.java @@ -0,0 +1,78 @@ +package seedu.nova.model.progresstracker; + +import static java.util.Objects.requireNonNull; + +import java.util.ArrayList; + +/** + * Handles list related methods for tasks + */ +public class PtTaskList { + private final String listHeader = "Tasks for the week: \n"; + + private ArrayList list; + + /** + * Creates PtTaskList object + */ + public PtTaskList() { + list = new ArrayList<>(); + } + + public int getNumTask() { + return list.size(); + } + + public ArrayList getList() { + return list; + } + + public PtTask getTask(int taskNum) { + return list.get(taskNum - 1); + } + + public double getNumDone() { + double counter = 0; + + for (int i = 0; i < list.size(); i++) { + PtTask task = list.get(i); + boolean isDone = task.isDone(); + + if (isDone) { + counter++; + } + } + return counter; + } + + /** + * Lists the tasks in the task list + * @return String listing out the tasks + */ + public String listTasks() { + String str = ""; + + for (int i = 0; i < list.size(); i++) { + PtTask task = list.get(i); + String note = task.getNote().toString() + "\n"; + + str = str + " " + (i + 1) + ") " + task.toString() + "\n" + + " Note: " + note; + } + + return str; + } + + /** + * Adds task to the list + * @param task to be added + */ + public void addTask(PtTask task) { + requireNonNull(task); + list.add(task); + } + + public void deleteTask(int taskNum) { + list.remove(taskNum - 1); + } +} diff --git a/src/main/java/seedu/nova/model/progresstracker/PtWeek.java b/src/main/java/seedu/nova/model/progresstracker/PtWeek.java new file mode 100644 index 00000000000..075388e69bb --- /dev/null +++ b/src/main/java/seedu/nova/model/progresstracker/PtWeek.java @@ -0,0 +1,50 @@ +package seedu.nova.model.progresstracker; + +/** + * Represents a week + */ +public class PtWeek { + public static final String MESSAGE_CONSTRAINTS = "Week must be a number and 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 = "\\d+"; + + private int weekNum; + private PtTaskList taskList; + + /** + * Creates PtWeek object + * @param num week number + */ + public PtWeek(int num) { + weekNum = num; + taskList = new PtTaskList(); + } + + public int getWeekNum() { + return weekNum; + } + + public PtTaskList getTaskList() { + return taskList; + } + + /** + * Returns true if a given string is a valid email. + */ + public static boolean isValidWeek(String test) { + if (test.matches(VALIDATION_REGEX) && Integer.valueOf(test) > 0 && Integer.valueOf(test) <= 13) { + return true; + } else { + return false; + } + } + + @Override + public String toString() { + return Integer.toString(weekNum); + } +} diff --git a/src/main/java/seedu/nova/model/progresstracker/PtWeekList.java b/src/main/java/seedu/nova/model/progresstracker/PtWeekList.java new file mode 100644 index 00000000000..916ccd376a5 --- /dev/null +++ b/src/main/java/seedu/nova/model/progresstracker/PtWeekList.java @@ -0,0 +1,71 @@ +package seedu.nova.model.progresstracker; + +import java.util.ArrayList; + +/** + * Handles week objects + */ +public class PtWeekList { + private ArrayList weekList; + + /** + * Creates PtWeekList object + */ + public PtWeekList() { + weekList = new ArrayList<>(); + + //create 13 weeks since one semester only has 13 weeks + for (int i = 0; i < 13; i++) { + PtWeek week = new PtWeek(i + 1); + weekList.add(week); + } + } + + public ArrayList getWeekList() { + return weekList; + } + + public int getNumWeeks() { + return weekList.size(); + } + + public PtWeek getWeek(int num) { + if (weekList.size() >= num) { + return weekList.get(num - 1); + } else { + return null; + } + } + + public double totalTasks(PtTaskList ptTaskList) { + return ptTaskList.getNumTask(); + } + + public double totalDone(PtTaskList ptTaskList) { + return ptTaskList.getNumDone(); + } + + /** + * Calculates the progress for the project + * @return a double representing the percentage of tasks done in the project + */ + public double getProgressProject() { + double totalTaskDone = 0; + double totalTasks = 0; + + //Get progress of each week + for (int i = 0; i < weekList.size(); i++) { + PtWeek current = weekList.get(i); + PtTaskList taskList = current.getTaskList(); + + totalTasks = totalTasks + totalTasks(taskList); + totalTaskDone = totalTaskDone + totalDone(taskList); + } + + if (totalTasks == 0) { + return 0; + } else { + return totalTaskDone / totalTasks; + } + } +} diff --git a/src/main/java/seedu/nova/model/progresstracker/TaskDesc.java b/src/main/java/seedu/nova/model/progresstracker/TaskDesc.java new file mode 100644 index 00000000000..357fbc7623e --- /dev/null +++ b/src/main/java/seedu/nova/model/progresstracker/TaskDesc.java @@ -0,0 +1,49 @@ +package seedu.nova.model.progresstracker; + +import static java.util.Objects.requireNonNull; +import static seedu.nova.commons.util.AppUtil.checkArgument; + +/** + * Creates an object encapsulating the task description + */ +public class TaskDesc { + public static final String MESSAGE_CONSTRAINTS = "Task description 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].*"; + + private String desc; + + /** + * Creates TaskDesc object + * @param desc task description + */ + public TaskDesc(String desc) { + requireNonNull(desc); + checkArgument(isValidTaskDesc(desc), MESSAGE_CONSTRAINTS); + this.desc = desc; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + /** + * Returns true if a given string is a valid email. + */ + public static boolean isValidTaskDesc(String test) { + return test.matches(VALIDATION_REGEX); + } + + @Override + public String toString() { + return desc; + } +} diff --git a/src/main/java/seedu/nova/model/progresstracker/Tp.java b/src/main/java/seedu/nova/model/progresstracker/Tp.java new file mode 100644 index 00000000000..f343fa5496f --- /dev/null +++ b/src/main/java/seedu/nova/model/progresstracker/Tp.java @@ -0,0 +1,31 @@ +package seedu.nova.model.progresstracker; + +/** + * Represents team project + */ +public class Tp extends Project { + private PtWeekList weekList; + private String projectName; + + /** + * Creates Tp object + */ + public Tp() { + weekList = new PtWeekList(); + projectName = "tp"; + } + + public String getProjectName() { + return projectName; + } + + public PtWeekList getWeekList() { + return weekList; + } + + public double getProgress() { + double progress = Math.round(weekList.getProgressProject() * 100d) / 100d; + return progress * 100; + } +} + diff --git a/src/main/java/seedu/nova/model/schedule/Day.java b/src/main/java/seedu/nova/model/schedule/Day.java new file mode 100644 index 00000000000..7cc61324056 --- /dev/null +++ b/src/main/java/seedu/nova/model/schedule/Day.java @@ -0,0 +1,207 @@ +package seedu.nova.model.schedule; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +import seedu.nova.model.schedule.event.Event; +import seedu.nova.model.schedule.event.Lesson; +import seedu.nova.model.schedule.event.WeakEvent; +import seedu.nova.model.schedule.event.exceptions.EventNotFoundException; +import seedu.nova.model.schedule.event.exceptions.TimeOverlapException; +import seedu.nova.model.util.Copyable; +import seedu.nova.model.util.time.slotlist.DateTimeSlotList; + +/** + * The type Day. + */ +public class Day implements Copyable { + + private List events; + private LocalDate date; + private DateTimeSlotList freeSlots; + + /** + * Instantiates a new Day. + * + * @param date the date + */ + public Day(LocalDate date) { + events = new LinkedList<>(); + this.date = date; + freeSlots = DateTimeSlotList.ofDay(date); + } + + private Day(List events, LocalDate date, DateTimeSlotList freeSlots) { + this.events = events; + this.date = date; + this.freeSlots = freeSlots; + } + + + /** + * Adds event. + * + * @param event the event + */ + public void addEvent(Event event) { + boolean canAdd = true; + + for (Event curr: events) { + canAdd = checkCanAdd(event, curr); + + if (!canAdd) { + break; + } + } + + if (canAdd) { + events.add(event); + freeSlots.excludeDuration(event.getDtd()); + Collections.sort(events); + } else { + throw new TimeOverlapException(); + } + } + + /** + * Checks if a given event overlaps with an existing event. + * @param event the given event + * @param curr the existing event + * @return boolean that determines if there is no overlap + */ + boolean checkCanAdd(Event event, Event curr) { + boolean canAdd = true; + + LocalTime currStart = curr.getStartTime(); + LocalTime currEnd = curr.getEndTime(); + + boolean haveSameStart = currStart.equals(event.getStartTime()); + boolean haveSameEnd = currEnd.equals(event.getEndTime()); + + + boolean eventStartIsBetween = isBetween(event.getStartTime(), currStart, currEnd); + boolean eventEndIsBetween = isBetween(event.getEndTime(), currStart, currEnd); + + if (haveSameStart || haveSameEnd || eventStartIsBetween || eventEndIsBetween) { + canAdd = false; + } + + return canAdd; + } + + /** + * determines if a given time is in between 2 other times + * @param time the given time + * @param start the start time + * @param end the end time + * @return a boolean + */ + boolean isBetween(LocalTime time, LocalTime start, LocalTime end) { + int isAfterStart = time.compareTo(start); + int isBeforeEnd = time.compareTo(end); + + return isAfterStart > 0 && isBeforeEnd < 0; + } + + + /** + * Add lesson. + * + * @param lesson the lesson + */ + public void addLesson(Lesson lesson) { + Lesson tmp = new Lesson(lesson); + tmp.setDate(date); + addEvent(tmp); + } + + /** + * Removes an event. + * + * @param index index of event in the LinkedList + */ + Event deleteEvent(int index) { + if (index >= events.size()) { + throw new EventNotFoundException(); + } + Event deleted = events.remove(index); + + freeSlots.includeDuration(deleted.getDtd()); + if (deleted instanceof WeakEvent) { + WeakEvent wkE = (WeakEvent) deleted; + wkE.destroy(); + } + return deleted; + } + + /** + * Removes an event. + * + * @param event event to be removed + */ + boolean deleteEvent(Event event) { + events.remove(event); + freeSlots.includeDuration(event.getDtd()); + if (event instanceof WeakEvent) { + WeakEvent wkE = (WeakEvent) event; + wkE.destroy(); + } + return true; + } + + /** + * Adds a note to an event. + * + * @param index index of event in the LinkedList + * @return String representing the event with added note + */ + public String addNote(String desc, int index) { + if (index >= events.size()) { + throw new EventNotFoundException(); + } + events.get(index).addNote(desc); + return events.get(index).toString(); + } + + public List getEventList() { + return events; + } + + + /** + * View the events for the day. + * + * @return the string of events + */ + public String view() { + + StringBuilder sb = new StringBuilder(); + int index = 0; + + for (Event event : events) { + sb.append(++index); + sb.append(". "); + sb.append(event); + sb.append("\n"); + } + + return sb.toString(); + } + + /** + * Get free slots in the form of DateTimeDuration + * + * @return List of DateTimeDuration + */ + public DateTimeSlotList getFreeSlotList() { + return freeSlots; + } + + @Override + public Day getCopy() { + return new Day(new LinkedList<>(events), date, freeSlots.getCopy()); + } +} diff --git a/src/main/java/seedu/nova/model/schedule/Week.java b/src/main/java/seedu/nova/model/schedule/Week.java new file mode 100644 index 00000000000..561aec3fb04 --- /dev/null +++ b/src/main/java/seedu/nova/model/schedule/Week.java @@ -0,0 +1,204 @@ +package seedu.nova.model.schedule; + +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +import seedu.nova.model.schedule.event.Event; +import seedu.nova.model.schedule.event.Lesson; +import seedu.nova.model.schedule.event.exceptions.DateNotFoundException; +import seedu.nova.model.util.Copyable; +import seedu.nova.model.util.time.TimeUtil; + +/** + * The week class of schedule. + */ +public class Week implements Copyable { + + private final Day[] days; + private final LocalDate startDate; + private Set events; + + /** + * Instantiates a new Week. + * + * @param date the date + */ + public Week(LocalDate date) { + date = TimeUtil.getMondayOfWeek(date); + days = new Day[7]; + for (int i = 0; i < 7; i++) { + days[i] = new Day(date.plusDays(i)); + } + startDate = date; + events = new TreeSet<>(); + } + + private Week(Day[] days, LocalDate startDate, Set events) { + this.days = days; + this.startDate = startDate; + this.events = events; + } + + /** + * Adds event. + * + * @param event the event + */ + public void addEvent(Event event) { + LocalDate date = event.getDate(); + int day = date.getDayOfWeek().getValue() - 1; + + if (days[day] == null) { + days[day] = new Day(date); + } + + events.add(event); + days[day].addEvent(event); + } + + /** + * Adds lesson. + * + * @param lesson the lesson + */ + public void addLesson(Lesson lesson) { + int day = lesson.getDay().getValue() - 1; + + if (days[day] == null) { + days[day] = new Day(startDate.plusDays(day)); + } + + LocalDate d = startDate.plusDays(day); + lesson.setDate(d); + days[day].addLesson(lesson); + } + + /** + * gets a particular day + * + * @param dow day of week of the day + * @return the day + */ + public Day getDay(DayOfWeek dow) { + return days[dow.getValue() - 1]; + } + + /** + * Deletes an event + * + * @param date the date of the event + * @param index the position of event in list + */ + public Event deleteEvent(LocalDate date, int index) { + int day = date.getDayOfWeek().getValue() - 1; + + if (days[day] == null) { + throw new DateNotFoundException(); + } + Event removed = days[day].deleteEvent(index); + events.remove(removed); + return removed; + } + + /** + * deletes an event + * @param event event to delete + * @return successfully deleted? + */ + public boolean deleteEvent(Event event) { + int day = event.getDate().getDayOfWeek().getValue() - 1; + + if (days[day] == null) { + throw new DateNotFoundException(); + } + events.remove(event); + return days[day].deleteEvent(event); + } + + /** + * Adds a note to an Event. + * + * @param desc description of the note + * @param date the date of the event + * @param index the position of event in list + * @return String representing the event with added note + */ + public String addNote(String desc, LocalDate date, int index) { + int day = date.getDayOfWeek().getValue() - 1; + + if (days[day] == null) { + throw new DateNotFoundException(); + } + + return days[day].addNote(desc, index); + } + + public List getEventList() { + List list = new LinkedList<>(); + + for (Day day: days) { + list.addAll(day.getEventList()); + } + + return list; + } + + /** + * View the schedule of a particular day. + * + * @param date the date + * @return the string of events + */ + public String view(LocalDate date) { + + int day = date.getDayOfWeek().getValue() - 1; + return days[day].view(); + } + + /** + * View the schedule of the week. + * + * @return the string of events + */ + public String view() { + + StringBuilder sb = new StringBuilder(); + int index = 0; + for (Day day : days) { + + index++; + + if (day == null) { + + continue; + } + + String result = day.view(); + + if (!result.equals("")) { + + sb.append(DayOfWeek.of(index)); + sb.append(": \n"); + sb.append(result); + sb.append("\n"); + + } + } + return sb.toString(); + } + + public boolean hasEvent(Event event) { + return events.contains(event); + } + + @Override + public Week getCopy() { + return new Week((Day[]) Arrays.stream(days).map(Day::getCopy).toArray(), startDate, new TreeSet<>(events)); + } + +} diff --git a/src/main/java/seedu/nova/model/schedule/event/Consultation.java b/src/main/java/seedu/nova/model/schedule/event/Consultation.java new file mode 100644 index 00000000000..31551f4b33a --- /dev/null +++ b/src/main/java/seedu/nova/model/schedule/event/Consultation.java @@ -0,0 +1,56 @@ +package seedu.nova.model.schedule.event; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.time.format.FormatStyle; + +/** + * Represents the consultation type of Event. + */ +public class Consultation extends Event { + private static final String EVENT_TYPE = "consultation"; + + /** + * Constructs a consultation event. + * @param desc description of consultation + * @param venue venue of consultation + * @param startTime time at which consultation starts + * @param endTime time at which consultation ends + * @param date date of consultation + */ + public Consultation(String desc, String venue, LocalTime startTime, LocalTime endTime, LocalDate date) { + super(desc, venue, startTime, endTime, date); + } + + /** + * Constructs a consultation event with a note. + * @param desc description of consultation + * @param venue venue of consultation + * @param startTime time at which consultation starts + * @param endTime time at which consultation ends + * @param date date of consultation + * @param note note regarding the consultation + */ + public Consultation(String desc, String venue, LocalTime startTime, + LocalTime endTime, LocalDate date, String note) { + super(desc, venue, startTime, endTime, date); + this.note = note; + } + + @Override + public String getEventType() { + return EVENT_TYPE; + } + + @Override + public String toString() { + return "Description: " + desc + "\n" + + "Venue: " + venue + "\n" + + "Date/Time: " + date.format(DateTimeFormatter.ofPattern("MMM d yyyy")) + + ", " + startTime.format(DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)) + + " - " + endTime.format(DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)) + "\n" + + "Note: " + note; + } + +} diff --git a/src/main/java/seedu/nova/model/schedule/event/Event.java b/src/main/java/seedu/nova/model/schedule/event/Event.java new file mode 100644 index 00000000000..f9a6957968e --- /dev/null +++ b/src/main/java/seedu/nova/model/schedule/event/Event.java @@ -0,0 +1,117 @@ +package seedu.nova.model.schedule.event; + +import java.io.Serializable; +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.Objects; + +import seedu.nova.model.util.time.duration.DateTimeDuration; +import seedu.nova.model.util.time.duration.TimedDuration; +import seedu.nova.model.util.time.duration.WeekDayDuration; + +/** + * Represents an Event that can be managed. + */ +public abstract class Event implements Comparable, Serializable { + protected String desc; + protected String venue; + protected LocalTime startTime; + protected LocalTime endTime; + protected LocalDate date; + protected String note = "NIL"; + protected TimedDuration dtd; + + protected Event(String desc, String venue, LocalTime startTime, LocalTime endTime, LocalDate date) { + this.desc = desc; + this.venue = venue; + this.startTime = startTime; + this.endTime = endTime; + this.date = date; + this.dtd = new DateTimeDuration(date, startTime, endTime); + } + + protected Event(String desc, String venue, LocalTime startTime, LocalTime endTime, DayOfWeek dow) { + this.desc = desc; + this.venue = venue; + this.startTime = startTime; + this.endTime = endTime; + this.date = null; + this.dtd = new WeekDayDuration(dow, startTime, endTime); + } + + public abstract String getEventType(); + + public String getDesc() { + return desc; + } + + public String getVenue() { + return venue; + } + + public String getNote() { + return note; + } + + public LocalDate getDate() { + return date; + } + + public void setDate(LocalDate date) { + this.date = date; + } + + public LocalTime getStartTime() { + return startTime; + } + + public LocalDateTime getStartDateTime() { + return LocalDateTime.of(getDate(), getStartTime()); + } + + public LocalTime getEndTime() { + return endTime; + } + + public TimedDuration getDtd() { + return dtd; + } + + public DayOfWeek getDayOfWeek() { + return dtd.getStartDay(); + } + + public void addNote(String note) { + this.note = note; + } + + @Override + public int compareTo(Event o) { + return getStartDateTime().compareTo(o.getStartDateTime()); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Event) { + return desc.equals(((Event) obj).desc) + && date.equals(((Event) obj).date) + && startTime.equals(((Event) obj).startTime) + && endTime.equals(((Event) obj).endTime); + } else { + return super.equals(obj); + } + } + + @Override + public String toString() { + return "name: " + desc + "\n" + "date: " + date + "\n" + "start time: " + + startTime + "\n" + "end time: " + endTime; + } + + @Override + public int hashCode() { + return Objects.hash(desc, date, startTime, endTime); + } +} diff --git a/src/main/java/seedu/nova/model/schedule/event/Lesson.java b/src/main/java/seedu/nova/model/schedule/event/Lesson.java new file mode 100644 index 00000000000..bbbcb391b60 --- /dev/null +++ b/src/main/java/seedu/nova/model/schedule/event/Lesson.java @@ -0,0 +1,88 @@ +package seedu.nova.model.schedule.event; + +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.time.format.FormatStyle; + +/** + * Represents the lesson type of Event. + */ +public class Lesson extends Event { + private static final String EVENT_TYPE = "lesson"; + private DayOfWeek day; + + + /** + * Constructs a lesson event. + * @param description description of lesson + * @param venue venue of lesson + * @param startTime time at which lesson starts + * @param endTime time at which lesson ends + * @param date date of lesson + * @param day day of week of lesson + */ + public Lesson(String description, String venue, LocalTime startTime, LocalTime endTime, LocalDate date, + DayOfWeek day) { + super(description, venue, startTime, endTime, date); + this.day = day; + } + + /** + * Constructs a lesson event with a note. + * @param description description of lesson + * @param venue venue of lesson + * @param startTime time at which lesson starts + * @param endTime time at which lesson ends + * @param date date of lesson + * @param day day of week of lesson + * @param note note related to lesson + */ + public Lesson(String description, String venue, LocalTime startTime, LocalTime endTime, + DayOfWeek day, LocalDate date, String note) { + super(description, venue, startTime, endTime, date); + this.day = day; + this.note = note; + } + + /** + * Constructs a copy of the Lesson. + * @param lesson lesson object to be copied + */ + public Lesson(Lesson lesson) { + this(lesson.desc, lesson.venue, lesson.startTime, lesson.endTime, lesson.date, lesson.day); + } + + @Override + public String getEventType() { + return EVENT_TYPE; + } + + public DayOfWeek getDay() { + return day; + } + + @Override + public String toString() { + return "Description: " + desc + "\n" + + "Venue: " + venue + "\n" + + "Day/Time: " + day.toString() + ", " + + startTime.format(DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)) + + " - " + endTime.format(DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)) + "\n" + + "Note: " + note; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Event) { + return desc.equals(((Event) obj).desc) + && startTime.equals(((Event) obj).startTime) + && endTime.equals(((Event) obj).endTime) + && date.equals(((Event) obj).date) + && day.equals(((Lesson) obj).day); + } else { + return super.equals(obj); + } + } +} diff --git a/src/main/java/seedu/nova/model/schedule/event/Meeting.java b/src/main/java/seedu/nova/model/schedule/event/Meeting.java new file mode 100644 index 00000000000..d8cf53fe2fb --- /dev/null +++ b/src/main/java/seedu/nova/model/schedule/event/Meeting.java @@ -0,0 +1,54 @@ +package seedu.nova.model.schedule.event; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.time.format.FormatStyle; + +/** + * Represents the meeting type of Event. + */ +public class Meeting extends Event { + private static final String EVENT_TYPE = "meeting"; + + /** + * Constructs a meeting event. + * @param desc description of meeting + * @param venue venue of meeting + * @param startTime time at which meeting starts + * @param endTime time at which meeting ends + * @param date date of meeting + */ + public Meeting(String desc, String venue, LocalTime startTime, LocalTime endTime, LocalDate date) { + super(desc, venue, startTime, endTime, date); + } + + /** + * Constructs a meeting event with a note. + * @param desc description of meeting + * @param venue venue of meeting + * @param startTime time at which meeting starts + * @param endTime time at which meeting ends + * @param date date of meeting + * @param note note regarding the meeting + */ + public Meeting(String desc, String venue, LocalTime startTime, LocalTime endTime, LocalDate date, String note) { + super(desc, venue, startTime, endTime, date); + this.note = note; + } + + @Override + public String getEventType() { + return EVENT_TYPE; + } + + @Override + public String toString() { + return "Description: " + desc + "\n" + + "Venue: " + venue + "\n" + + "Date/Time: " + date.format(DateTimeFormatter.ofPattern("MMM d yyyy")) + + ", " + startTime.format(DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)) + + " - " + endTime.format(DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)) + "\n" + + "Note: " + note; + } +} diff --git a/src/main/java/seedu/nova/model/schedule/event/StudySession.java b/src/main/java/seedu/nova/model/schedule/event/StudySession.java new file mode 100644 index 00000000000..61d08fd0d07 --- /dev/null +++ b/src/main/java/seedu/nova/model/schedule/event/StudySession.java @@ -0,0 +1,57 @@ +package seedu.nova.model.schedule.event; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.time.format.FormatStyle; + +/** + * Represents the study session type of Event. + */ +public class StudySession extends Event { + private static final String EVENT_TYPE = "study"; + + /** + * Constructs a study session event. + * @param desc description of study session + * @param venue venue of study session + * @param startTime time at which study session starts + * @param endTime time at which study session ends + * @param date date of study session + */ + public StudySession(String desc, String venue, LocalTime startTime, LocalTime endTime, LocalDate date) { + super(desc, venue, startTime, endTime, date); + } + + /** + * Constructs a study session event with a note. + * @param desc description of study session + * @param venue venue of study session + * @param startTime time at which study session starts + * @param endTime time at which study session ends + * @param date date of study session + * @param note note regarding the study session + */ + public StudySession(String desc, String venue, LocalTime startTime, + LocalTime endTime, LocalDate date, String note) { + super(desc, venue, startTime, endTime, date); + this.note = note; + } + + @Override + public String getEventType() { + return EVENT_TYPE; + } + + @Override + public String toString() { + return "Description: " + desc + "\n" + + "Venue: " + venue + "\n" + + "Date/Time: " + date.format(DateTimeFormatter.ofPattern("MMM d yyyy")) + + ", " + startTime.format(DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)) + + " - " + endTime.format(DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)) + "\n" + + "Note: " + note; + } + + +} diff --git a/src/main/java/seedu/nova/model/schedule/event/WeakEvent.java b/src/main/java/seedu/nova/model/schedule/event/WeakEvent.java new file mode 100644 index 00000000000..3541281a079 --- /dev/null +++ b/src/main/java/seedu/nova/model/schedule/event/WeakEvent.java @@ -0,0 +1,83 @@ +package seedu.nova.model.schedule.event; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.format.FormatStyle; +import java.util.Objects; + +import seedu.nova.model.plan.Task; +import seedu.nova.model.plan.TaskFreq; +import seedu.nova.model.util.time.TimeUtil; +import seedu.nova.model.util.time.duration.DateTimeDuration; + +/** + * Event generated by task + */ +public class WeakEvent extends Event { + private static final String EVENT_TYPE = "weak"; + private Task origin; + + public WeakEvent(String name, DateTimeDuration dtd, Task origin) { + super(name, null, dtd.getStartTime(), dtd.getEndTime(), dtd.getStartDate()); + this.origin = origin; + } + + public Task getOriginTask() { + return origin; + } + + public void destroy() { + origin.deleteEvent(this); + } + + @Override + public String getEventType() { + return EVENT_TYPE; + } + + /** + * Sets date. + * + * @param date the date + */ + @Override + public void setDate(LocalDate date) { + origin.deleteEvent(this); + this.date = date; + origin.addEvent(this); + } + + @Override + public String toString() { + return "Description: " + desc + "\n" + + "Venue: " + venue + "\n" + + "Time: " + getDayOfWeek() + ", " + + startTime.format(DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)) + + " - " + endTime.format(DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)); + } + + @Override + public boolean equals(Object o) { + if (o instanceof WeakEvent) { + if (origin.getTaskFreq() == TaskFreq.WEEKLY) { + return TimeUtil.getMondayOfWeek(date).equals(TimeUtil.getMondayOfWeek(((WeakEvent) o).getDate())) + && getStartTime().equals(((WeakEvent) o).getStartTime()) + && getEndTime().equals(((WeakEvent) o).getEndTime()) + && desc.equals(((WeakEvent) o).desc); + } else { + return super.equals(o); + } + } else { + return super.equals(o); + } + } + + @Override + public int hashCode() { + if (origin.getTaskFreq() == TaskFreq.WEEKLY) { + return Objects.hash(desc, TimeUtil.getMondayOfWeek(date), startTime, endTime); + } else { + return Objects.hash(desc, date, startTime, endTime); + } + } +} diff --git a/src/main/java/seedu/nova/model/schedule/event/exceptions/DateNotFoundException.java b/src/main/java/seedu/nova/model/schedule/event/exceptions/DateNotFoundException.java new file mode 100644 index 00000000000..24171265530 --- /dev/null +++ b/src/main/java/seedu/nova/model/schedule/event/exceptions/DateNotFoundException.java @@ -0,0 +1,8 @@ +package seedu.nova.model.schedule.event.exceptions; + +/** + * Signals that the operation is not able to find the date. + */ +public class DateNotFoundException extends RuntimeException { + +} diff --git a/src/main/java/seedu/nova/model/schedule/event/exceptions/EventNotFoundException.java b/src/main/java/seedu/nova/model/schedule/event/exceptions/EventNotFoundException.java new file mode 100644 index 00000000000..d850fdffc2c --- /dev/null +++ b/src/main/java/seedu/nova/model/schedule/event/exceptions/EventNotFoundException.java @@ -0,0 +1,7 @@ +package seedu.nova.model.schedule.event.exceptions; + +/** + * Signals that the operation is not able to find the Event. + */ +public class EventNotFoundException extends RuntimeException { +} diff --git a/src/main/java/seedu/nova/model/schedule/event/exceptions/TimeOverlapException.java b/src/main/java/seedu/nova/model/schedule/event/exceptions/TimeOverlapException.java new file mode 100644 index 00000000000..b0bd65b3439 --- /dev/null +++ b/src/main/java/seedu/nova/model/schedule/event/exceptions/TimeOverlapException.java @@ -0,0 +1,8 @@ +package seedu.nova.model.schedule.event.exceptions; + +/** + * Signals that the operation is trying to add an event that has a timing that overlaps with another. + */ +public class TimeOverlapException extends RuntimeException { + +} diff --git a/src/main/java/seedu/nova/model/util/Copyable.java b/src/main/java/seedu/nova/model/util/Copyable.java new file mode 100644 index 00000000000..2143609469f --- /dev/null +++ b/src/main/java/seedu/nova/model/util/Copyable.java @@ -0,0 +1,9 @@ +package seedu.nova.model.util; + +/** + * Objects that can be deep copied + * @param the object + */ +public interface Copyable { + public T getCopy(); +} diff --git a/src/main/java/seedu/nova/model/util/SampleDataUtil.java b/src/main/java/seedu/nova/model/util/SampleDataUtil.java new file mode 100644 index 00000000000..66d570cfd91 --- /dev/null +++ b/src/main/java/seedu/nova/model/util/SampleDataUtil.java @@ -0,0 +1,164 @@ +package seedu.nova.model.util; + +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; + +import seedu.nova.model.AddressBook; +import seedu.nova.model.Nova; +import seedu.nova.model.Schedule; +import seedu.nova.model.VersionedAddressBook; +import seedu.nova.model.category.Category; +import seedu.nova.model.person.Email; +import seedu.nova.model.person.Name; +import seedu.nova.model.person.Person; +import seedu.nova.model.person.Phone; +import seedu.nova.model.person.Remark; +import seedu.nova.model.plan.Plan; +import seedu.nova.model.plan.StudyPlan; +import seedu.nova.model.plan.Task; +import seedu.nova.model.progresstracker.Ip; +import seedu.nova.model.progresstracker.ProgressTracker; +import seedu.nova.model.progresstracker.PtNote; +import seedu.nova.model.progresstracker.PtTask; +import seedu.nova.model.progresstracker.TaskDesc; +import seedu.nova.model.progresstracker.Tp; +import seedu.nova.model.schedule.event.Consultation; +import seedu.nova.model.schedule.event.Event; +import seedu.nova.model.schedule.event.Lesson; +import seedu.nova.model.schedule.event.Meeting; +import seedu.nova.model.schedule.event.StudySession; + +/** + * Contains utility methods for populating {@code AddressBook} with sample data. + */ +public class SampleDataUtil { + + public static final Remark EMPTY_REMARK = new Remark(""); + public static final LocalDate SAMPLE_START_DATE = LocalDate.of(2020, 1, 13); + public static final LocalDate SAMPLE_END_DATE = LocalDate.of(2020, 5, 3); + + public static Person[] getSamplePersons() { + return new Person[] { + new Person(new Name("Alex Yeoh"), new Phone("87438807"), new Email("alexyeoh@example.com"), + getTagSet("teammate"), new Remark("He's actually very nice!")), + new Person(new Name("Bernice Yu"), new Phone("99272758"), new Email("berniceyu@example.com"), + getTagSet("classmate"), EMPTY_REMARK), + new Person(new Name("Charlotte Oliveiro"), new Phone("93210283"), new Email("charlotte@example.com"), + getTagSet("teammate"), EMPTY_REMARK), + new Person(new Name("David Li"), new Phone("91031282"), new Email("lidavid@example.com"), + getTagSet("teammate"), EMPTY_REMARK), + new Person(new Name("Irfan Ibrahim"), new Phone("92492021"), new Email("irfan@example.com"), + getTagSet("classmate"), new Remark("Very nice person who helps me with iP!")) + }; + } + + public static PtTask[] getSamplePtTasks() { + return new PtTask[] { + new PtTask(new TaskDesc("task 1"), new Ip(), new PtNote("note 1"), 1, false), + new PtTask(new TaskDesc("task 2"), new Ip(), new PtNote("note 2"), 1, false), + new PtTask(new TaskDesc("task 1"), new Tp(), new PtNote("note 1"), 1, true), + new PtTask(new TaskDesc("task 2"), new Tp(), new PtNote("note 2"), 1, true), + }; + } + + public static Event[] getSampleEvents() { + return new Event[] { + new Meeting("CS2103T DG Meeting", "COM1 Basement", LocalTime.parse("13:00"), + LocalTime.parse("14:00"), LocalDate.parse("2020-04-06"), "to do Logic portion"), + new Consultation("CS2103T UML Diagram", "COM1 B01-03", LocalTime.parse("11:00"), + LocalTime.parse("12:00"), LocalDate.parse("2020-04-06"), "ask about sequence diagrams"), + new StudySession("cool peeps study", "on zoom", LocalTime.parse("14:00"), + LocalTime.parse("16:00"), LocalDate.parse("2020-04-05"), "NIL"), + new Lesson("CS2103T Tutorial", "COM1 B1-03", LocalTime.parse("10:00"), + LocalTime.parse("11:00"), DayOfWeek.FRIDAY, + LocalDate.parse("2020-01-17"), "NIL"), + new Lesson("CS2103T Tutorial", "COM1 B1-03", LocalTime.parse("10:00"), + LocalTime.parse("11:00"), DayOfWeek.FRIDAY, + LocalDate.parse("2020-01-24"), "NIL"), + new Lesson("CS2103T Tutorial", "COM1 B1-03", LocalTime.parse("10:00"), + LocalTime.parse("11:00"), DayOfWeek.FRIDAY, + LocalDate.parse("2020-01-31"), "NIL"), + new Lesson("CS2103T Tutorial", "COM1 B1-03", LocalTime.parse("10:00"), + LocalTime.parse("11:00"), DayOfWeek.FRIDAY, + LocalDate.parse("2020-02-07"), "NIL"), + new Lesson("CS2103T Tutorial", "COM1 B1-03", LocalTime.parse("10:00"), + LocalTime.parse("11:00"), DayOfWeek.FRIDAY, + LocalDate.parse("2020-02-14"), "NIL"), + new Lesson("CS2103T Tutorial", "COM1 B1-03", LocalTime.parse("10:00"), + LocalTime.parse("11:00"), DayOfWeek.FRIDAY, + LocalDate.parse("2020-02-21"), "NIL"), + new Lesson("CS2103T Tutorial", "COM1 B1-03", LocalTime.parse("10:00"), + LocalTime.parse("11:00"), DayOfWeek.FRIDAY, + LocalDate.parse("2020-03-06"), "NIL"), + new Lesson("CS2103T Tutorial", "COM1 B1-03", LocalTime.parse("10:00"), + LocalTime.parse("11:00"), DayOfWeek.FRIDAY, + LocalDate.parse("2020-03-13"), "NIL"), + new Lesson("CS2103T Tutorial", "COM1 B1-03", LocalTime.parse("10:00"), + LocalTime.parse("11:00"), DayOfWeek.FRIDAY, + LocalDate.parse("2020-03-20"), "NIL"), + new Lesson("CS2103T Tutorial", "COM1 B1-03", LocalTime.parse("10:00"), + LocalTime.parse("11:00"), DayOfWeek.FRIDAY, + LocalDate.parse("2020-03-27"), "NIL"), + new Lesson("CS2103T Tutorial", "COM1 B1-03", LocalTime.parse("10:00"), + LocalTime.parse("11:00"), DayOfWeek.FRIDAY, + LocalDate.parse("2020-04-03"), "finding bugs"), + new Lesson("CS2103T Tutorial", "COM1 B1-03", LocalTime.parse("10:00"), + LocalTime.parse("11:00"), DayOfWeek.FRIDAY, + LocalDate.parse("2020-04-10"), "product demo rehearsal!"), + new Lesson("CS2103T Tutorial", "COM1 B1-03", LocalTime.parse("10:00"), + LocalTime.parse("11:00"), DayOfWeek.FRIDAY, + LocalDate.parse("2020-04-17"), "product demo!") + }; + } + + public static Task[] getSampleTasks() { + return new Task[]{}; + } + + public static Nova getSampleNova() { + AddressBook ab = new AddressBook(); + Schedule sampleSchedule = new Schedule(SAMPLE_START_DATE, SAMPLE_END_DATE); + ProgressTracker sampleProgressTracker = new ProgressTracker(); + Plan samplePlan = new StudyPlan(); + + for (Person samplePerson : getSamplePersons()) { + ab.addPerson(samplePerson); + } + + for (PtTask samplePtTask : getSamplePtTasks()) { + sampleProgressTracker.addPtTask(samplePtTask.getProject().getProjectName(), + samplePtTask.getPtWeek(), samplePtTask); + } + + for (Event sampleEvent : getSampleEvents()) { + sampleSchedule.addEvent(sampleEvent); + } + + for (Task sampleTask : getSampleTasks()) { + samplePlan.addTask(sampleTask); + } + + Nova nova = new Nova(); + VersionedAddressBook addressBook = new VersionedAddressBook(ab); + nova.setAddressBookNova(addressBook); + nova.setProgressTrackerNova(sampleProgressTracker); + nova.setScheduleNova(sampleSchedule); + nova.setStudyPlan(samplePlan); + + return nova; + } + + /** + * Returns a tag set containing the list of strings given. + */ + public static Set getTagSet(String... strings) { + return Arrays.stream(strings) + .map(Category::new) + .collect(Collectors.toSet()); + } + +} diff --git a/src/main/java/seedu/nova/model/util/time/TimeUtil.java b/src/main/java/seedu/nova/model/util/time/TimeUtil.java new file mode 100644 index 00000000000..8aa0bd25066 --- /dev/null +++ b/src/main/java/seedu/nova/model/util/time/TimeUtil.java @@ -0,0 +1,102 @@ +package seedu.nova.model.util.time; + +import java.time.DayOfWeek; +import java.time.Duration; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; + +/** + * Useful functions for LocalTime + */ +public interface TimeUtil { + DateTimeFormatter[] DEFAULT_DATE_F = new DateTimeFormatter[]{ + DateTimeFormatter.ofPattern("yyyy-mm-dd") + }; + DateTimeFormatter[] DEFAULT_TIME_F = new DateTimeFormatter[]{ + DateTimeFormatter.ofPattern("hh:mm a") + }; + LocalTime BEGIN_DAY_TIME = LocalTime.of(0, 0, 0); + LocalTime END_DAY_TIME = LocalTime.of(23, 59, 59); + LocalDate EX_DATE = LocalDate.of(1970, 1, 1); + + /** + * Parse date string to LocalDate + * @param date date string + * @return LocalDate + * @throws DateTimeParseException if format not recognised + */ + static LocalDate parseDate(String date) throws DateTimeParseException { + for (DateTimeFormatter f : DEFAULT_DATE_F) { + try { + return LocalDate.parse(date, f); + } catch (DateTimeParseException ignored) { + continue; + } + } + throw new DateTimeParseException("date format wrong", date, 0); + } + + /** + * Parse time string to LocalTime + * @param time time string + * @return LocalTime + * @throws DateTimeParseException if format not recognised + */ + static LocalTime parseTime(String time) throws DateTimeParseException { + for (DateTimeFormatter f : DEFAULT_TIME_F) { + try { + return LocalTime.parse(time, f); + } catch (DateTimeParseException ignored) { + continue; + } + } + throw new DateTimeParseException("time format wrong", time, 0); + } + + /** + * Number of weeks the two date spans, including the weeks that both dates are in. + * @param start start date + * @param end end date + * @return number of weeks + */ + static int noOfWeeks(LocalDate start, LocalDate end) { + LocalDate monStart = getMondayOfWeek(start); + LocalDate monEnd = getMondayOfWeek(end); + return 2 + (int) Duration.between(LocalDateTime.of(monStart, BEGIN_DAY_TIME), + LocalDateTime.of(monEnd, BEGIN_DAY_TIME)).toDays() / 7; + } + + /** + * Make LocalDate + * @param dow day of week + * @param sameWeekWith week of + * @return LocalDate + */ + static LocalDate dateOfSameWeek(DayOfWeek dow, LocalDate sameWeekWith) { + int offset = dow.getValue() - sameWeekWith.getDayOfWeek().getValue(); + return sameWeekWith.plusDays(offset); + } + + /** + * Get Monday of Week + * @param weekOf same week with this + * @return LocalDate of monday of week + */ + static LocalDate getMondayOfWeek(LocalDate weekOf) { + return weekOf.minusDays(weekOf.getDayOfWeek().getValue() - 1); + } + + /** + * Get LocalDateTime + * @param dow day of week + * @param sameWeekWith + * @param time + * @return + */ + static LocalDateTime toDateTime(DayOfWeek dow, LocalDate sameWeekWith, LocalTime time) { + return LocalDateTime.of(dateOfSameWeek(dow, sameWeekWith), time); + } +} diff --git a/src/main/java/seedu/nova/model/util/time/duration/DateTimeDuration.java b/src/main/java/seedu/nova/model/util/time/duration/DateTimeDuration.java new file mode 100644 index 00000000000..10907d56730 --- /dev/null +++ b/src/main/java/seedu/nova/model/util/time/duration/DateTimeDuration.java @@ -0,0 +1,329 @@ +package seedu.nova.model.util.time.duration; + +import java.time.DayOfWeek; +import java.time.Duration; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.ArrayList; +import java.util.List; + +import seedu.nova.model.util.time.TimeUtil; + +/** + * Combining LocalDateTime and Duration + */ +public class DateTimeDuration implements TimedDuration { + + public static final TimedDuration ZERO = new DateTimeDuration(Duration.ZERO); + + private LocalDateTime startDateTime; + private LocalDateTime endDateTime; + private Duration duration; + + public DateTimeDuration(LocalDateTime start, LocalDateTime stop) { + this.startDateTime = start; + this.endDateTime = stop; + if (start.compareTo(stop) < 0) { + this.duration = Duration.between(start, stop); + } else { + this.duration = Duration.ZERO; + } + } + + public DateTimeDuration(LocalDate date, LocalTime start, LocalTime end) { + if (start.compareTo(end) < 0) { + this.startDateTime = LocalDateTime.of(date, start); + this.endDateTime = LocalDateTime.of(date, end); + this.duration = Duration.between(this.startDateTime, this.endDateTime); + } else { + this.startDateTime = LocalDateTime.of(date, start); + this.endDateTime = LocalDateTime.of(date.plusDays(1), end); + this.duration = Duration.between(this.startDateTime, this.endDateTime); + } + } + + public DateTimeDuration(LocalDate date, LocalTime start, Duration duration) { + this.startDateTime = LocalDateTime.of(date, start); + this.duration = duration; + this.endDateTime = this.startDateTime.plus(duration); + } + + private DateTimeDuration(Duration duration) { + this.duration = duration; + } + + private DateTimeDuration(LocalDateTime start, LocalDateTime end, Duration duration) { + this.startDateTime = start; + this.endDateTime = end; + this.duration = duration; + } + + /** + * DateTimeDuration representation of a duration + * + * @param duration + * @return DateTimeDuration + */ + public static DateTimeDuration parseDuration(Duration duration) { + return new DateTimeDuration(duration); + } + + /** + * Parse DateTimeDuration of a day + * + * @param lDate date of the day + * @return DateTimeDuration representation of the day + */ + public static DateTimeDuration parseDayFromDate(LocalDate lDate) { + return new DateTimeDuration( + LocalDateTime.of(LocalDate.of(lDate.getYear(), lDate.getMonth(), lDate.getDayOfMonth()), + TimeUtil.BEGIN_DAY_TIME), + LocalDateTime.of(lDate.plusDays(1), TimeUtil.BEGIN_DAY_TIME) + ); + } + + /** + * Parse DateTimeDuration + * + * @param monDate a day from the week + * @return DateTimeDuration of the week + */ + public static DateTimeDuration parseWeekFromDate(LocalDate monDate) { + return new DateTimeDuration( + LocalDateTime.of(TimeUtil.getMondayOfWeek(monDate), TimeUtil.BEGIN_DAY_TIME), + LocalDateTime.of(TimeUtil.getMondayOfWeek(monDate).plusDays(1), TimeUtil.BEGIN_DAY_TIME) + ); + } + + /** + * Parse DateTimeDuration + * + * @param startDateTime startDateTime + * @param duration duration + * @return DateTimeDuration + */ + public static DateTimeDuration parseFromDateTime(LocalDateTime startDateTime, Duration duration) { + return new DateTimeDuration( + startDateTime, + startDateTime.plusMinutes(duration.toMinutes()) + ); + } + + public boolean isZero() { + return this.duration.isZero(); + } + + //-----getters + + public LocalDateTime getStartDateTime() { + return this.startDateTime; + } + + public LocalDate getStartDate() { + return this.startDateTime.toLocalDate(); + } + + public void setStartDate(LocalDate date) { + this.startDateTime = LocalDateTime.of(date, this.startDateTime.toLocalTime()); + this.endDateTime = this.startDateTime.plus(this.duration); + } + + public LocalTime getStartTime() { + return this.startDateTime.toLocalTime(); + } + + public DayOfWeek getStartDay() { + return this.startDateTime.getDayOfWeek(); + } + + public LocalDateTime getEndDateTime() { + return this.endDateTime; + } + + public LocalDate getEndDate() { + return this.endDateTime.toLocalDate(); + } + + public LocalTime getEndTime() { + return this.endDateTime.toLocalTime(); + } + + public DayOfWeek getEndDay() { + return this.endDateTime.getDayOfWeek(); + } + + public Duration getDuration() { + return this.duration; + } + + public long toDays() { + return this.duration.toDays(); + } + + public long toWeeks() { + return (long) Math.ceil((toDays() + 0.0) / 7); + } + + public List getWeekStartList() { + List lst = new ArrayList<>(); + LocalDate d = this.startDateTime.toLocalDate(); + LocalDate dd = this.endDateTime.toLocalDate(); + while (d.compareTo(dd) <= 0) { + lst.add(d); + d = d.plusDays(7); + } + return lst; + } + + /** + * Shift to days days after + * + * @param days num of days + * @return DateTimeDuration + */ + public DateTimeDuration plusDays(long days) { + DateTimeDuration d = cast(getCopy()); + d.startDateTime = d.startDateTime.plusDays(days); + d.endDateTime = d.endDateTime.plusDays(days); + return d; + } + + /** + * cast to dateTimeDuration + * + * @param another timedDuration + * @return dateTimeDuration + */ + private DateTimeDuration cast(TimedDuration another) { + if (another instanceof DateTimeDuration) { + return (DateTimeDuration) another; + } else { + return ((WeekDayDuration) another).toDateTimeDuration(getStartDate()); + } + } + + /** + * Check if another is overlapped with this duration + * + * @param another another timedDuration + * @return isOverlapped? + */ + public boolean isOverlapping(TimedDuration another) { + DateTimeDuration d = cast(another); + return this.startDateTime.compareTo(d.endDateTime) < 0 + && this.endDateTime.compareTo(d.startDateTime) > 0; + } + + /** + * Check if another is subset of this + * + * @param another another timedDuration + * @return is subset? + */ + public boolean isSubsetOf(TimedDuration another) { + DateTimeDuration d = cast(another); + return this.startDateTime.compareTo(d.startDateTime) >= 0 + && this.endDateTime.compareTo(d.endDateTime) <= 0; + } + + /** + * Check if another is connected with this + * + * @param another TimedDuration + * @return is connected? + */ + public boolean isConnected(TimedDuration another) { + DateTimeDuration d = cast(another); + return this.startDateTime.compareTo(d.endDateTime) <= 0 + && this.endDateTime.compareTo(d.startDateTime) >= 0; + } + + /** + * get relative complement of another + * + * @param another timed duration + * @return relative compliment + */ + public List relativeComplementOf(TimedDuration another) { + DateTimeDuration d = cast(another); + List lst = new ArrayList<>(); + if (isOverlapping(d)) { + if (d.startDateTime.compareTo(this.startDateTime) > 0) { + lst.add(DateTimeDuration.parseFromDateTime(this.startDateTime, + Duration.between(this.startDateTime, d.startDateTime))); + } + if (d.endDateTime.compareTo(this.endDateTime) < 0) { + lst.add(DateTimeDuration.parseFromDateTime(d.endDateTime, + Duration.between(d.endDateTime, this.endDateTime))); + } + } else { + lst.add(this); + } + return lst; + } + + /** + * intersection with + * + * @param another another + * @return timed duration + */ + public TimedDuration intersectWith(TimedDuration another) { + DateTimeDuration d = cast(another); + LocalDateTime start; + LocalDateTime end; + if (d.startDateTime.compareTo(this.startDateTime) > 0) { + start = d.startDateTime; + } else { + start = this.startDateTime; + } + if (d.endDateTime.compareTo(this.endDateTime) < 0) { + end = d.endDateTime; + } else { + end = this.endDateTime; + } + if (end.compareTo(start) > 0) { + return new DateTimeDuration(start, end); + } else { + return ZERO; + } + } + + /** + * convert this to WeekDauDuration. If duration more than a week, return a whole week + * @return WeekDayDuration + */ + public WeekDayDuration toWeekDayDuration() { + if (this.duration.compareTo(new WeekDayDuration().getDuration()) >= 0) { + return new WeekDayDuration(); + } else { + return new WeekDayDuration(getStartDay(), getStartTime(), getEndDay(), getEndTime()); + } + } + + @Override + public int compareTo(TimedDuration o) { + return this.duration.compareTo(o.getDuration()); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof DateTimeDuration) { + return this.duration.equals(((DateTimeDuration) obj).duration) + && this.startDateTime.equals(((DateTimeDuration) obj).startDateTime); + } else { + return super.equals(obj); + } + } + + @Override + public String toString() { + return this.startDateTime.toString() + " - " + this.endDateTime.toString(); + } + + @Override + public DateTimeDuration getCopy() { + return new DateTimeDuration(this.startDateTime, this.endDateTime, this.duration); + } +} diff --git a/src/main/java/seedu/nova/model/util/time/duration/TimedDuration.java b/src/main/java/seedu/nova/model/util/time/duration/TimedDuration.java new file mode 100644 index 00000000000..8f67c0712b7 --- /dev/null +++ b/src/main/java/seedu/nova/model/util/time/duration/TimedDuration.java @@ -0,0 +1,40 @@ +package seedu.nova.model.util.time.duration; + +import java.io.Serializable; +import java.time.DayOfWeek; +import java.time.Duration; +import java.time.LocalTime; +import java.util.List; + +import seedu.nova.model.util.Copyable; + +/** + * Combination of duration with some time data structure + */ +public interface TimedDuration extends Comparable, Copyable, Serializable { + static int getDayTimeValue(DayOfWeek dow, LocalTime lt) { + return 86400 * dow.getValue() + (int) (lt.toNanoOfDay() / 1000000000); + } + + boolean isZero(); + + DayOfWeek getStartDay(); + + DayOfWeek getEndDay(); + + LocalTime getStartTime(); + + LocalTime getEndTime(); + + Duration getDuration(); + + boolean isConnected(TimedDuration another); // isOverlapping && !isSubsetOf + + boolean isOverlapping(TimedDuration another); + + boolean isSubsetOf(TimedDuration another); + + List relativeComplementOf(TimedDuration another); + + TimedDuration intersectWith(TimedDuration another); +} diff --git a/src/main/java/seedu/nova/model/util/time/duration/WeekDayDuration.java b/src/main/java/seedu/nova/model/util/time/duration/WeekDayDuration.java new file mode 100644 index 00000000000..d549c696fe2 --- /dev/null +++ b/src/main/java/seedu/nova/model/util/time/duration/WeekDayDuration.java @@ -0,0 +1,201 @@ +package seedu.nova.model.util.time.duration; + +import java.time.DayOfWeek; +import java.time.Duration; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.List; +import java.util.stream.Collectors; + +import seedu.nova.model.util.time.TimeUtil; + +/** + * Combination of Day of week, time and duration + */ +public class WeekDayDuration implements TimedDuration { + private LocalDateTime start; + private LocalDateTime end; + private Duration duration; + + public WeekDayDuration() { + this.start = TimeUtil.toDateTime(DayOfWeek.MONDAY, TimeUtil.EX_DATE, TimeUtil.BEGIN_DAY_TIME); + this.end = TimeUtil.toDateTime(DayOfWeek.SUNDAY, TimeUtil.EX_DATE, TimeUtil.END_DAY_TIME); + this.duration = Duration.between(this.start, this.end); + } + + public WeekDayDuration(DayOfWeek startDow, LocalTime startTime, LocalTime endTime) { + this.start = TimeUtil.toDateTime(startDow, TimeUtil.EX_DATE, startTime); + this.end = TimeUtil.toDateTime(startDow, TimeUtil.EX_DATE, endTime); + if (this.start.compareTo(this.end) > 0) { + this.end = this.end.plusDays(1); + } + this.duration = Duration.between(this.start, this.end); + } + + public WeekDayDuration(DayOfWeek startDow, LocalTime startTime, DayOfWeek endDow, LocalTime endTime) { + this.start = TimeUtil.toDateTime(startDow, TimeUtil.EX_DATE, startTime); + this.end = TimeUtil.toDateTime(endDow, TimeUtil.EX_DATE, endTime); + if (this.start.compareTo(this.end) > 0) { + this.end = this.end.plusDays(7); + } + this.duration = Duration.between(this.start, this.end); + } + + private WeekDayDuration(LocalDateTime start, LocalDateTime end, Duration duration) { + this.start = start; + this.end = end; + this.duration = duration; + } + + /** + * Make a WeekDayDuration representation of duration for comparison purpose only. + * @param duration = duration + * @return WeekDayDurationc + */ + public static WeekDayDuration parseDuration(Duration duration) { + return new WeekDayDuration(null, null, duration); + } + + public static WeekDayDuration parseDay(DayOfWeek dow) { + return new WeekDayDuration(dow, TimeUtil.BEGIN_DAY_TIME, TimeUtil.END_DAY_TIME); + } + + public boolean isZero() { + return this.duration.isZero(); + } + + public int getStartValue() { + return TimedDuration.getDayTimeValue(getStartDay(), getStartTime()); + } + + public int getEndValue() { + return TimedDuration.getDayTimeValue(getEndDay(), getEndTime()); + } + + /** + * get Week of the date, in DateTimeDuration + * + * @param sameWeek date + * @return DateTimeDuration + */ + public DateTimeDuration toDateTimeDuration(LocalDate sameWeek) { + LocalDateTime startDate = LocalDateTime.of(TimeUtil.dateOfSameWeek(getStartDay(), sameWeek), getStartTime()); + LocalDateTime endDate = LocalDateTime.of(TimeUtil.dateOfSameWeek(getEndDay(), sameWeek), getEndTime()); + if (getStartValue() > getEndValue()) { + endDate = endDate.plusDays(7); + } + return new DateTimeDuration(startDate, endDate); + } + + + public DayOfWeek getStartDay() { + return this.start.getDayOfWeek(); + } + + + public LocalTime getStartTime() { + return this.start.toLocalTime(); + } + + + public DayOfWeek getEndDay() { + return this.end.getDayOfWeek(); + } + + + public LocalTime getEndTime() { + return this.end.toLocalTime(); + } + + + public Duration getDuration() { + return this.duration; + } + + private DateTimeDuration getDtd() { + return new DateTimeDuration(this.start, this.end); + } + + @Override + public boolean isConnected(TimedDuration another) { + if (another instanceof DateTimeDuration) { + return another.isConnected(toDateTimeDuration(((DateTimeDuration) another).getStartDate())); + } else { + return getDtd().isConnected(((WeekDayDuration) another).getDtd()); + } + } + + @Override + public boolean isOverlapping(TimedDuration another) { + if (another instanceof DateTimeDuration) { + return another.isOverlapping(toDateTimeDuration(((DateTimeDuration) another).getStartDate())); + } else { + return getDtd().isOverlapping(((WeekDayDuration) another).getDtd()); + } + } + + @Override + public boolean isSubsetOf(TimedDuration another) { + if (another instanceof DateTimeDuration) { + return toDateTimeDuration(((DateTimeDuration) another).getStartDate()).isSubsetOf(another); + } else { + return getDtd().isSubsetOf(((WeekDayDuration) another).getDtd()); + } + } + + @Override + public List relativeComplementOf(TimedDuration another) { + if (another instanceof WeekDayDuration) { + return getDtd().relativeComplementOf(((WeekDayDuration) another).getDtd()) + .parallelStream() + .map(x -> ((DateTimeDuration) x).toWeekDayDuration()) + .collect(Collectors.toList()); + } else { + return getDtd().relativeComplementOf(another) + .parallelStream() + .map(x -> ((DateTimeDuration) x).toWeekDayDuration()) + .collect(Collectors.toList()); + } + } + + @Override + public TimedDuration intersectWith(TimedDuration another) { + if (another instanceof DateTimeDuration) { + return ( + (DateTimeDuration) another + .intersectWith(toDateTimeDuration(((DateTimeDuration) another) + .getStartDate()))).toWeekDayDuration(); + } else { + return ((DateTimeDuration) getDtd() + .intersectWith(((WeekDayDuration) another) + .getDtd())).toWeekDayDuration(); + } + } + + + public int compareTo(TimedDuration another) { + return getDuration().compareTo(another.getDuration()); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof WeekDayDuration) { + return getStartValue() == (((WeekDayDuration) obj).getStartValue()) + && getEndValue() == (((WeekDayDuration) obj).getEndValue()); + } else { + return super.equals(obj); + } + } + + @Override + public String toString() { + return getStartDay().toString() + + "T" + getStartTime() + "\n" + getEndDay().toString() + "T" + getEndTime() + "\n"; + } + + + public WeekDayDuration getCopy() { + return new WeekDayDuration(this.start, this.end, this.duration); + } +} diff --git a/src/main/java/seedu/nova/model/util/time/slotlist/DateTimeSlotList.java b/src/main/java/seedu/nova/model/util/time/slotlist/DateTimeSlotList.java new file mode 100644 index 00000000000..94fadb00a77 --- /dev/null +++ b/src/main/java/seedu/nova/model/util/time/slotlist/DateTimeSlotList.java @@ -0,0 +1,198 @@ +package seedu.nova.model.util.time.slotlist; + +import java.time.DayOfWeek; +import java.time.Duration; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.stream.Collectors; + +import seedu.nova.model.util.Copyable; +import seedu.nova.model.util.time.duration.DateTimeDuration; +import seedu.nova.model.util.time.duration.TimedDuration; +import seedu.nova.model.util.time.duration.WeekDayDuration; + +/** + * Container for DateTimeDuration + */ +public class DateTimeSlotList implements SlotList, Copyable { + private TreeSet freeSlotSet; + private TreeMap freeSlotMap; + + public DateTimeSlotList(DateTimeDuration init) { + this(new TreeSet<>(), new TreeMap<>()); + addDuration(init); + } + + public DateTimeSlotList(List init) { + this(new TreeSet<>(), new TreeMap<>()); + init.forEach(this::addDuration); + } + + private DateTimeSlotList(TreeSet freeSlotSet, + TreeMap freeSlotMap) { + this.freeSlotSet = freeSlotSet; + this.freeSlotMap = freeSlotMap; + } + + public static DateTimeSlotList ofDay(LocalDate date) { + return new DateTimeSlotList(DateTimeDuration.parseDayFromDate(date)); + } + + public static DateTimeSlotList ofWeek(LocalDate sameWeekAs) { + return new DateTimeSlotList(DateTimeDuration.parseWeekFromDate(sameWeekAs)); + } + + @Override + public List getSlotList() { + return new ArrayList<>(this.freeSlotSet); + } + + @Override + public List getSlotList(Duration greaterThan) { + DateTimeDuration d = DateTimeDuration.parseDuration(greaterThan); + return new ArrayList<>(this.freeSlotSet.tailSet(d)); + } + + public List getSlotContaining(TimedDuration d) { + return this.freeSlotSet.stream().parallel().filter(d::isSubsetOf).collect(Collectors.toList()); + } + + public List getSlotAfter(LocalDateTime dateTime) { + return new ArrayList<>(this.freeSlotMap.tailMap(dateTime).values()); + } + + public boolean isSupersetOf(TimedDuration d) { + return this.freeSlotSet.stream().parallel().anyMatch(d::isSubsetOf); + } + + public List getSlotOn(DayOfWeek day) { + return this.freeSlotSet.stream().parallel().filter(x -> x.getStartDate().getDayOfWeek().equals(day)).collect( + Collectors.toList()); + } + + /** + * if td is WeekDayDuration, cast it into DateTimeDuration by setting the startDate to the same as the forst + * entry of freeSlotMap + * + * @param td = timed duration + * @return DateTimeDuration + */ + private DateTimeDuration cast(TimedDuration td) { + if (td instanceof WeekDayDuration) { + return ((WeekDayDuration) td).toDateTimeDuration(this.freeSlotMap.firstEntry().getValue().getStartDate()); + } else { + return (DateTimeDuration) td; + } + } + + /** + * delete the duration ed from the list of durations. + * + * @param td timed duration to delete + */ + @Override + public void excludeDuration(TimedDuration td) { + DateTimeDuration ed = cast(td); + Map.Entry lastFreeSlot = this.freeSlotMap.floorEntry(ed.getStartDateTime()); + SortedMap nextFreeSlotMap = this.freeSlotMap.tailMap(ed.getStartDateTime()); + nextFreeSlotMap = nextFreeSlotMap.headMap(ed.getEndDateTime()); + + if (lastFreeSlot != null && ed.isOverlapping(lastFreeSlot.getValue())) { + deleteDuration(lastFreeSlot.getValue()); + List comp = lastFreeSlot.getValue().relativeComplementOf(ed); + comp.forEach(this::addDuration); + } + for (Map.Entry e : nextFreeSlotMap.entrySet()) { + deleteDuration(e.getValue()); + List comp = e.getValue().relativeComplementOf(ed); + comp.forEach(this::addDuration); + } + } + + /** + * exclude all the durations in another + * + * @param another date time slot list + * @return compliment of another + */ + public DateTimeSlotList relativeComplimentOf(DateTimeSlotList another) { + DateTimeSlotList ans = getCopy(); + another.freeSlotSet.forEach(ans::excludeDuration); + return ans; + } + + /** + * intersection with another timed duration + * + * @param lst another timed duration + * @return list of intersection duration + */ + public List intersectWith(TimedDuration lst) { + return this.freeSlotSet.stream() + .parallel() + .map(x -> (DateTimeDuration) x.intersectWith(lst)) + .filter(x -> !x.isZero()).collect(Collectors.toList()); + } + + /** + * add ed back to list + * + * @param td Timedduration + */ + @Override + public void includeDuration(TimedDuration td) { + DateTimeDuration ed = cast(td); + Map.Entry lastFreeSlot = this.freeSlotMap.floorEntry(ed.getStartDateTime()); + SortedMap nextFreeSlotMap = this.freeSlotMap.tailMap(ed.getStartDateTime()); + nextFreeSlotMap = nextFreeSlotMap.headMap(ed.getEndDateTime()); + + if (lastFreeSlot != null && ed.isConnected(lastFreeSlot.getValue())) { + deleteDuration(lastFreeSlot.getValue()); + ed = new DateTimeDuration(lastFreeSlot.getValue().getStartDateTime(), ed.getEndDateTime()); + } + for (Map.Entry e : nextFreeSlotMap.entrySet()) { + deleteDuration(e.getValue()); + ed = new DateTimeDuration(ed.getStartDateTime(), e.getValue().getEndDateTime()); + } + addDuration(ed); + } + + private void addDuration(TimedDuration dd) { + DateTimeDuration d = (DateTimeDuration) dd; + this.freeSlotMap.put(d.getStartDateTime(), d); + this.freeSlotSet.add(d); + } + + private void deleteDuration(TimedDuration dd) { + DateTimeDuration d = (DateTimeDuration) dd; + this.freeSlotSet.remove(d); + } + + public int size() { + return this.freeSlotSet.size(); + } + + public boolean contains(DateTimeDuration d) { + return this.freeSlotSet.contains(d); + } + + @Override + public DateTimeSlotList getCopy() { + return new DateTimeSlotList(new TreeSet<>(this.freeSlotSet), new TreeMap<>(this.freeSlotMap)); + } + + @Override + public String toString() { + return freeSlotSet.stream() + .parallel() + .map(DateTimeDuration::toString) + .map(x -> String.format("%s\n", x)) + .reduce("", (x, y) -> x + y); + } +} diff --git a/src/main/java/seedu/nova/model/util/time/slotlist/SlotList.java b/src/main/java/seedu/nova/model/util/time/slotlist/SlotList.java new file mode 100644 index 00000000000..fa11ffc26a6 --- /dev/null +++ b/src/main/java/seedu/nova/model/util/time/slotlist/SlotList.java @@ -0,0 +1,29 @@ +package seedu.nova.model.util.time.slotlist; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.List; + +import seedu.nova.model.util.time.duration.TimedDuration; + +/** + * Container for timed duration + * @param timed duration + */ +public interface SlotList { + List getSlotList(); + + List getSlotList(Duration greaterThan); + + List getSlotContaining(TimedDuration d); + + List getSlotAfter(LocalDateTime ldt); + + List intersectWith(TimedDuration td); + + boolean isSupersetOf(TimedDuration td); + + void includeDuration(TimedDuration ed); + + void excludeDuration(TimedDuration ed); +} diff --git a/src/main/java/seedu/nova/model/util/time/slotlist/WeekDaySlotList.java b/src/main/java/seedu/nova/model/util/time/slotlist/WeekDaySlotList.java new file mode 100644 index 00000000000..3609b1f8822 --- /dev/null +++ b/src/main/java/seedu/nova/model/util/time/slotlist/WeekDaySlotList.java @@ -0,0 +1,143 @@ +package seedu.nova.model.util.time.slotlist; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.stream.Collectors; + +import seedu.nova.model.util.Copyable; +import seedu.nova.model.util.time.duration.DateTimeDuration; +import seedu.nova.model.util.time.duration.TimedDuration; +import seedu.nova.model.util.time.duration.WeekDayDuration; + +/** + * container for WeekDayDuration + */ +public class WeekDaySlotList implements SlotList, Copyable { + private TreeSet freeSlotSet; + private TreeMap freeSlotMap; + + public WeekDaySlotList() { + this(new TreeSet<>(), new TreeMap<>()); + addDuration(new WeekDayDuration()); + } + + private WeekDaySlotList(TreeSet freeSlotSet, TreeMap freeSlotMap) { + this.freeSlotSet = freeSlotSet; + this.freeSlotMap = freeSlotMap; + } + + @Override + public List getSlotList() { + return new ArrayList<>(this.freeSlotSet); + } + + @Override + public List getSlotList(Duration greaterThan) { + WeekDayDuration d = WeekDayDuration.parseDuration(greaterThan); + return new ArrayList<>(this.freeSlotSet.tailSet(d)); + } + + @Override + public List getSlotContaining(TimedDuration d) { + return this.freeSlotSet.stream().parallel().filter(x -> x.isSubsetOf(d)).collect(Collectors.toList()); + } + + @Override + public List getSlotAfter(LocalDateTime ldt) { + return null; + } + + /** + * cast into week day duration + * + * @param td + * @return + */ + private WeekDayDuration cast(TimedDuration td) { + if (td instanceof DateTimeDuration) { + return ((DateTimeDuration) td).toWeekDayDuration(); + } else { + return (WeekDayDuration) td; + } + } + + @Override + public void includeDuration(TimedDuration td) { + WeekDayDuration ed = cast(td); + WeekDayDuration lastSlot = this.freeSlotMap.floorEntry(ed.getEndValue()).getValue(); + SortedMap tailMap = this.freeSlotMap.tailMap(ed.getStartValue()); + tailMap = tailMap.headMap(ed.getEndValue()); + + if (ed.isConnected(lastSlot)) { + deleteDuration(lastSlot); + ed = new WeekDayDuration(lastSlot.getStartDay(), lastSlot.getStartTime(), ed.getEndDay(), + ed.getEndTime()); + } + for (Map.Entry e : tailMap.entrySet()) { + deleteDuration(e.getValue()); + ed = new WeekDayDuration(ed.getStartDay(), ed.getStartTime(), e.getValue().getEndDay(), + e.getValue().getEndTime()); + } + addDuration(ed); + } + + @Override + public void excludeDuration(TimedDuration td) { + WeekDayDuration ed = cast(td); + WeekDayDuration lastSlot = this.freeSlotMap.floorEntry(ed.getEndValue()).getValue(); + SortedMap tailMap = this.freeSlotMap.tailMap(ed.getStartValue()); + tailMap = tailMap.headMap(ed.getEndValue()); + + if (ed.isOverlapping(lastSlot)) { + deleteDuration(lastSlot); + List comp = lastSlot.relativeComplementOf(ed); + comp.forEach(this::addDuration); + } + for (Map.Entry e : tailMap.entrySet()) { + deleteDuration(lastSlot); + List comp = lastSlot.relativeComplementOf(ed); + comp.forEach(this::addDuration); + } + } + + @Override + public boolean isSupersetOf(TimedDuration td) { + return this.freeSlotSet.stream().parallel().anyMatch(x -> x.isSubsetOf(td)); + } + + /** + * Intersection with + * + * @param lst timed duration + * @return list of intersection durations + */ + public List intersectWith(TimedDuration lst) { + return this.freeSlotSet.stream() + .parallel() + .map(x -> (WeekDayDuration) x.intersectWith(lst)) + .filter(x -> !x.isZero()).collect(Collectors.toList()); + } + + private void addDuration(TimedDuration dd) { + WeekDayDuration d = (WeekDayDuration) dd; + this.freeSlotMap.put(d.getStartValue(), d); + this.freeSlotSet.add(d); + } + + private void deleteDuration(TimedDuration dd) { + WeekDayDuration d = (WeekDayDuration) dd; + this.freeSlotMap.remove(d.getStartValue()); + this.freeSlotSet.remove(d); + } + + @Override + public WeekDaySlotList getCopy() { + return new WeekDaySlotList(new TreeSet<>(this.freeSlotSet), new TreeMap<>(this.freeSlotMap)); + } +} diff --git a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java b/src/main/java/seedu/nova/storage/JsonAdaptedPerson.java similarity index 71% rename from src/main/java/seedu/address/storage/JsonAdaptedPerson.java rename to src/main/java/seedu/nova/storage/JsonAdaptedPerson.java index a6321cec2ea..d3f57460b72 100644 --- a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java +++ b/src/main/java/seedu/nova/storage/JsonAdaptedPerson.java @@ -1,4 +1,4 @@ -package seedu.address.storage; +package seedu.nova.storage; import java.util.ArrayList; import java.util.HashSet; @@ -9,13 +9,13 @@ 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; +import seedu.nova.commons.exceptions.IllegalValueException; +import seedu.nova.model.category.Category; +import seedu.nova.model.person.Email; +import seedu.nova.model.person.Name; +import seedu.nova.model.person.Person; +import seedu.nova.model.person.Phone; +import seedu.nova.model.person.Remark; /** * Jackson-friendly version of {@link Person}. @@ -27,23 +27,24 @@ class JsonAdaptedPerson { private final String name; private final String phone; private final String email; - private final String address; private final List tagged = new ArrayList<>(); + private final String remark; /** * 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) { + @JsonProperty("email") String email, + @JsonProperty("tagged") List tagged, + @JsonProperty("remark") String remark) { this.name = name; this.phone = phone; this.email = email; - this.address = address; if (tagged != null) { this.tagged.addAll(tagged); } + this.remark = remark; } /** @@ -53,10 +54,10 @@ 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() + tagged.addAll(source.getCategory().stream() .map(JsonAdaptedTag::new) .collect(Collectors.toList())); + remark = source.getRemark().value; } /** @@ -65,9 +66,9 @@ public JsonAdaptedPerson(Person source) { * @throws IllegalValueException if there were any data constraints violated in the adapted person. */ public Person toModelType() throws IllegalValueException { - final List personTags = new ArrayList<>(); + final List personCategories = new ArrayList<>(); for (JsonAdaptedTag tag : tagged) { - personTags.add(tag.toModelType()); + personCategories.add(tag.toModelType()); } if (name == null) { @@ -94,16 +95,14 @@ public Person toModelType() throws IllegalValueException { } final Email modelEmail = new Email(email); - if (address == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName())); - } - if (!Address.isValidAddress(address)) { - throw new IllegalValueException(Address.MESSAGE_CONSTRAINTS); + final Set modelCategories = new HashSet<>(personCategories); + + if (remark == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Remark.class.getSimpleName())); } - final Address modelAddress = new Address(address); + final Remark modelRemark = new Remark(remark); - final Set modelTags = new HashSet<>(personTags); - return new Person(modelName, modelPhone, modelEmail, modelAddress, modelTags); + return new Person(modelName, modelPhone, modelEmail, modelCategories, modelRemark); } } diff --git a/src/main/java/seedu/nova/storage/JsonAdaptedPlannerTask.java b/src/main/java/seedu/nova/storage/JsonAdaptedPlannerTask.java new file mode 100644 index 00000000000..8553d5ec103 --- /dev/null +++ b/src/main/java/seedu/nova/storage/JsonAdaptedPlannerTask.java @@ -0,0 +1,70 @@ +package seedu.nova.storage; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Base64; +import java.util.HashMap; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import seedu.nova.commons.exceptions.IllegalValueException; +import seedu.nova.model.plan.Task; +import seedu.nova.model.plan.TaskDetails; + +/** + * Writable task + */ +public class JsonAdaptedPlannerTask { + private static HashMap deserializedTask = new HashMap<>(); + private final String serial; + + @JsonCreator + public JsonAdaptedPlannerTask(@JsonProperty("serial") String serial) { + this.serial = serial; + } + + public JsonAdaptedPlannerTask(Task task) { + String ser = null; + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + ObjectOutputStream out = new ObjectOutputStream(baos); + out.writeObject(task); + out.close(); + + ser = Base64.getEncoder().encodeToString(baos.toByteArray()); + baos.close(); + } catch (IOException ex) { + // do nothing + } + this.serial = ser; + } + + /** + * Converts serial back to task + * + * @return task + */ + public Task toTask() throws IllegalValueException { + Task task; + try { + byte[] originSer = Base64.getDecoder().decode(serial); + InputStream inStream = new ByteArrayInputStream(originSer); + ObjectInputStream ois = new ObjectInputStream(inStream); + task = (Task) ois.readObject(); + } catch (Exception e) { + throw new IllegalValueException("Wrong serial"); + } + if (deserializedTask.containsKey(task.getDetails())) { + return deserializedTask.get(task.getDetails()); + } else { + deserializedTask.put(task.getDetails(), task); + return task; + } + } +} diff --git a/src/main/java/seedu/nova/storage/JsonAdaptedPtTask.java b/src/main/java/seedu/nova/storage/JsonAdaptedPtTask.java new file mode 100644 index 00000000000..ddbf41da030 --- /dev/null +++ b/src/main/java/seedu/nova/storage/JsonAdaptedPtTask.java @@ -0,0 +1,110 @@ +package seedu.nova.storage; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import seedu.nova.commons.exceptions.IllegalValueException; +import seedu.nova.model.progresstracker.Ip; +import seedu.nova.model.progresstracker.Project; +import seedu.nova.model.progresstracker.PtNote; +import seedu.nova.model.progresstracker.PtTask; +import seedu.nova.model.progresstracker.PtWeek; +import seedu.nova.model.progresstracker.TaskDesc; +import seedu.nova.model.progresstracker.Tp; + +/** + * Jackson-friendly version of {@link PtTask}. + */ +public class JsonAdaptedPtTask { + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Task's %s field is missing!"; + + private String project; + private String taskDesc; + private String week; + private String note; + private String isDone; + + /** + * Constructs a {@code JsonAdaptedPerson} with the given person details. + */ + @JsonCreator + public JsonAdaptedPtTask(@JsonProperty("project") String project, @JsonProperty("taskDesc") String taskDesc, + @JsonProperty("week") String week, @JsonProperty("note") String note, + @JsonProperty("isDone") String isDone) { + this.project = project; + this.taskDesc = taskDesc; + this.week = week; + this.note = note; + this.isDone = isDone; + } + + /** + * Converts a given {@code Person} into this class for Jackson use. + */ + public JsonAdaptedPtTask(PtTask source) { + project = source.getProjectName(); + taskDesc = source.getTaskDescString(); + week = String.valueOf(source.getPtWeek()); + note = source.getNoteString(); + isDone = source.isDoneString(); + } + + /** + * Converts this Jackson-friendly adapted person object into the model's {@code PtTask} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted person. + */ + public PtTask toModelType() throws IllegalValueException { + final Project modelProject; + final boolean booleanIsDone; + + if (project == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Project.class.getSimpleName())); + } + if (!(project.toUpperCase().equals("IP") || project.toUpperCase().equals("TP"))) { + throw new IllegalValueException(Project.MESSAGE_CONSTRAINTS); + } + if (project.toUpperCase().equals("IP")) { + modelProject = new Ip(); + } else { + modelProject = new Tp(); + } + + if (taskDesc == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + TaskDesc.class.getSimpleName())); + } + if (!TaskDesc.isValidTaskDesc(taskDesc)) { + throw new IllegalValueException(TaskDesc.MESSAGE_CONSTRAINTS); + } + final TaskDesc modelTaskDesc = new TaskDesc(taskDesc); + + if (week == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, PtWeek.class.getSimpleName())); + } + if (!PtWeek.isValidWeek(week)) { + throw new IllegalValueException(PtWeek.MESSAGE_CONSTRAINTS); + } + final PtWeek modelPtWeek = new PtWeek(Integer.parseInt(week)); + + if (note == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, PtNote.class.getSimpleName())); + } + + final PtNote modelPtNote = new PtNote(note); + + if (isDone == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, "isDone")); + } + if (!(isDone.toLowerCase().equals("false") || isDone.toLowerCase().equals("true"))) { + throw new IllegalValueException("Not a valid isDone field"); + } + if (isDone.toLowerCase().equals("true")) { + booleanIsDone = true; + } else { + booleanIsDone = false; + } + + return new PtTask(modelTaskDesc, modelProject, modelPtNote, modelPtWeek.getWeekNum(), booleanIsDone); + } +} diff --git a/src/main/java/seedu/address/storage/JsonAdaptedTag.java b/src/main/java/seedu/nova/storage/JsonAdaptedTag.java similarity index 52% rename from src/main/java/seedu/address/storage/JsonAdaptedTag.java rename to src/main/java/seedu/nova/storage/JsonAdaptedTag.java index 0df22bdb754..bc805cc606a 100644 --- a/src/main/java/seedu/address/storage/JsonAdaptedTag.java +++ b/src/main/java/seedu/nova/storage/JsonAdaptedTag.java @@ -1,13 +1,13 @@ -package seedu.address.storage; +package seedu.nova.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; +import seedu.nova.commons.exceptions.IllegalValueException; +import seedu.nova.model.category.Category; /** - * Jackson-friendly version of {@link Tag}. + * Jackson-friendly version of {@link Category}. */ class JsonAdaptedTag { @@ -22,10 +22,10 @@ public JsonAdaptedTag(String tagName) { } /** - * Converts a given {@code Tag} into this class for Jackson use. + * Converts a given {@code Category} into this class for Jackson use. */ - public JsonAdaptedTag(Tag source) { - tagName = source.tagName; + public JsonAdaptedTag(Category source) { + tagName = source.categoryName; } @JsonValue @@ -34,15 +34,15 @@ public String getTagName() { } /** - * Converts this Jackson-friendly adapted tag object into the model's {@code Tag} object. + * Converts this Jackson-friendly adapted tag object into the model's {@code Category} 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); + public Category toModelType() throws IllegalValueException { + if (!Category.isValidTagName(tagName)) { + throw new IllegalValueException(Category.MESSAGE_CONSTRAINTS); } - return new Tag(tagName); + return new Category(tagName); } } diff --git a/src/main/java/seedu/nova/storage/JsonNovaStorage.java b/src/main/java/seedu/nova/storage/JsonNovaStorage.java new file mode 100644 index 00000000000..a8f40b388ef --- /dev/null +++ b/src/main/java/seedu/nova/storage/JsonNovaStorage.java @@ -0,0 +1,80 @@ +package seedu.nova.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.nova.commons.core.LogsCenter; +import seedu.nova.commons.exceptions.DataConversionException; +import seedu.nova.commons.exceptions.IllegalValueException; +import seedu.nova.commons.util.FileUtil; +import seedu.nova.commons.util.JsonUtil; +import seedu.nova.model.Nova; + +/** + * A class to access Nova data stored as a json file on the hard disk. + */ +public class JsonNovaStorage implements NovaStorage { + + private static final Logger logger = LogsCenter.getLogger(JsonNovaStorage.class); + + private Path filePath; + + public JsonNovaStorage(Path filePath) { + this.filePath = filePath; + } + + public Path getNovaFilePath() { + return filePath; + } + + @Override + public Optional readNova() throws DataConversionException { + return readNova(filePath); + } + + /** + * Similar to {@link #readNova()}. + * + * @param filePath location of the data. Cannot be null. + * @throws DataConversionException if the file is not in the correct format. + */ + public Optional readNova(Path filePath) throws DataConversionException { + requireNonNull(filePath); + + Optional< JsonSerializableNova> jsonNova = JsonUtil.readJsonFile( + filePath, JsonSerializableNova.class); + if (!jsonNova.isPresent()) { + return Optional.empty(); + } + + try { + return Optional.of(jsonNova.get().toModelType()); + } catch (IllegalValueException ive) { + logger.info("Illegal values found in " + filePath + ": " + ive.getMessage()); + throw new DataConversionException(ive); + } + } + + @Override + public void saveNova(Nova nova) throws IOException { + saveNova(nova, filePath); + } + + /** + * Similar to {@link #saveNova(Nova)}. + * + * @param filePath location of the data. Cannot be null. + */ + public void saveNova(Nova nova, Path filePath) throws IOException { + requireNonNull(nova); + requireNonNull(filePath); + + FileUtil.createIfMissing(filePath); + JsonUtil.saveJsonFile(new JsonSerializableNova(nova), filePath); + } + +} diff --git a/src/main/java/seedu/nova/storage/JsonSerializableNova.java b/src/main/java/seedu/nova/storage/JsonSerializableNova.java new file mode 100644 index 00000000000..543f7680a95 --- /dev/null +++ b/src/main/java/seedu/nova/storage/JsonSerializableNova.java @@ -0,0 +1,227 @@ +package seedu.nova.storage; + +import java.time.LocalDate; +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.nova.commons.exceptions.IllegalValueException; +import seedu.nova.model.AddressBook; +import seedu.nova.model.Nova; +import seedu.nova.model.ReadOnlyAddressBook; +import seedu.nova.model.Schedule; +import seedu.nova.model.VersionedAddressBook; +import seedu.nova.model.person.Person; +import seedu.nova.model.plan.Plan; +import seedu.nova.model.plan.StudyPlan; +import seedu.nova.model.plan.Task; +import seedu.nova.model.progresstracker.Ip; +import seedu.nova.model.progresstracker.ProgressTracker; +import seedu.nova.model.progresstracker.Project; +import seedu.nova.model.progresstracker.PtTask; +import seedu.nova.model.progresstracker.PtTaskList; +import seedu.nova.model.progresstracker.PtWeek; +import seedu.nova.model.progresstracker.PtWeekList; +import seedu.nova.model.progresstracker.Tp; +import seedu.nova.model.schedule.event.Event; +import seedu.nova.storage.event.JsonAdaptedEvent; + +/** + * An Immutable AddressBook that is serializable to JSON format. + */ +@JsonRootName(value = "nova") +class JsonSerializableNova { + + public static final String MESSAGE_DUPLICATE_PERSON = "Persons list contains duplicate person(s)."; + + private final List persons = new ArrayList<>(); + private final List ptTasks = new ArrayList<>(); + private final List events = new ArrayList<>(); + private final List tasks = new ArrayList<>(); + + //add your list of adapted class objects here + + /** + * Constructs a {@code JsonSerializableNova} with the given persons. + */ + @JsonCreator + public JsonSerializableNova(@JsonProperty("persons") List persons, + @JsonProperty("ptTasks") List ptTasks, + @JsonProperty("events") List events, + @JsonProperty("tasks") List tasks) { + this.ptTasks.addAll(ptTasks); + + if (persons != null) { + this.persons.addAll(persons); + } + if (events != null) { + this.events.addAll(events); + } + if (tasks != null) { + this.tasks.addAll(tasks); + } + } + + /** + * Converts a given {@code Nova} into this class for Jackson use. + * + * @param source future changes to this will not affect the created {@code JsonSerializableNova}. + */ + public JsonSerializableNova(Nova source) { + persons.addAll(source.getAddressBookNova().getPersonList().stream() + .map(JsonAdaptedPerson::new).collect(Collectors.toList())); + + ptTasks.addAll(getPtTasks(source)); + + events.addAll(source.getScheduleNova().getEventList().stream() + .map(JsonAdaptedEvent::new).collect(Collectors.toList())); + + tasks.addAll(source.getStudyPlan().getTaskList().stream() + .map(JsonAdaptedPlannerTask::new).collect(Collectors.toList())); + } + + /** + * Converts a given {@code Nova} into a list of adapted objects + * + * @param source uture changes to this will not affect the created {@code JsonSerializableNova}. + * @return a list of JsonAdaptedPtTask + */ + public List getPtTasks(Nova source) { + List list = new ArrayList<>(); + + //Add JsonAdaptedPtTasks into ptTasks array + ProgressTracker pt = source.getProgressTracker(); + Ip ip = pt.getIp(); + Tp tp = pt.getTp(); + PtWeekList ipList = ip.getWeekList(); + PtWeekList tpList = tp.getWeekList(); + + for (int i = 1; i < 14; i++) { + ArrayList currentIpTasks = ipList.getWeek(i).getTaskList().getList(); + ArrayList currentTpTasks = tpList.getWeek(i).getTaskList().getList(); + + for (PtTask ptTask : currentIpTasks) { + JsonAdaptedPtTask task = new JsonAdaptedPtTask(ptTask); + list.add(task); + } + + for (PtTask ptTask : currentTpTasks) { + JsonAdaptedPtTask task = new JsonAdaptedPtTask(ptTask); + list.add(task); + } + } + return list; + } + + /** + * Converts Nova into the model's {@code Nova} object. + * + * @return the converted Nova object + * @throws IllegalValueException + */ + public Nova toModelType() throws IllegalValueException { + Nova nova = new Nova(); + VersionedAddressBook ab = toModelTypeAb(); + ProgressTracker pt = toModelTypePt(); + + nova.setAddressBookNova(ab); + nova.setProgressTrackerNova(pt); + Schedule sc = toModelTypeSchedule(); + + nova.setAddressBookNova(ab); + nova.setScheduleNova(sc); + + Plan plan = toModelTypePlan(); + nova.setStudyPlan(plan); + + return nova; + } + + /** + * Converts this plan into the model's {@code Plan} object. + * + * @throws IllegalValueException if there were any data constraints violated. + */ + private Plan toModelTypePlan() throws IllegalValueException { + List taskLst = new ArrayList<>(); + for (JsonAdaptedPlannerTask t : tasks) { + taskLst.add(t.toTask()); + } + return new StudyPlan(taskLst); + } + + /** + * Converts this address book into the model's {@code AddressBook} object. + * + * @throws IllegalValueException if there were any data constraints violated. + */ + public VersionedAddressBook toModelTypeAb() throws IllegalValueException { + ReadOnlyAddressBook initialState; + AddressBook ab = new AddressBook(); + + for (JsonAdaptedPerson jsonAdaptedPerson : persons) { + Person person = jsonAdaptedPerson.toModelType(); + if (ab.hasPerson(person)) { + throw new IllegalValueException(MESSAGE_DUPLICATE_PERSON); + } + ab.addPerson(person); + } + initialState = ab; + VersionedAddressBook addressBook = new VersionedAddressBook(initialState); + + return addressBook; + } + + /** + * Converts this progress tracker into the model's {@code ProgressTracker} object. + * + * @throws IllegalValueException if there were any data constraints violated. + */ + public ProgressTracker toModelTypePt() throws IllegalValueException { + ProgressTracker pt = new ProgressTracker(); + Ip ip = pt.getIp(); + Tp tp = pt.getTp(); + + for (JsonAdaptedPtTask adaptedPtTask : ptTasks) { + PtTaskList taskList; + PtTask modelTask = adaptedPtTask.toModelType(); + Project project = modelTask.getProject(); + int week = modelTask.getPtWeek(); + + if (project instanceof Ip) { + PtWeekList weekList = ip.getWeekList(); + PtWeek ptWeek = weekList.getWeek(week); + taskList = ptWeek.getTaskList(); + } else { + PtWeekList weekList = tp.getWeekList(); + PtWeek ptWeek = weekList.getWeek(week); + taskList = ptWeek.getTaskList(); + } + + //add to correct project and week + taskList.addTask(modelTask); + } + return pt; + } + + /** + * Converts this schedule into the model's {@code Schedule} object. + */ + public Schedule toModelTypeSchedule() throws IllegalValueException { + + Schedule sc = new Schedule(LocalDate.of(2020, 1, 13), + LocalDate.of(2020, 5, 3)); + + for (JsonAdaptedEvent jsonAdaptedEvent : events) { + Event event = jsonAdaptedEvent.toModelType(); + sc.addEvent(event); + } + + return sc; + } + +} diff --git a/src/main/java/seedu/address/storage/JsonUserPrefsStorage.java b/src/main/java/seedu/nova/storage/JsonUserPrefsStorage.java similarity index 83% rename from src/main/java/seedu/address/storage/JsonUserPrefsStorage.java rename to src/main/java/seedu/nova/storage/JsonUserPrefsStorage.java index bc2bbad84aa..d1b10e81594 100644 --- a/src/main/java/seedu/address/storage/JsonUserPrefsStorage.java +++ b/src/main/java/seedu/nova/storage/JsonUserPrefsStorage.java @@ -1,13 +1,13 @@ -package seedu.address.storage; +package seedu.nova.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.nova.commons.exceptions.DataConversionException; +import seedu.nova.commons.util.JsonUtil; +import seedu.nova.model.ReadOnlyUserPrefs; +import seedu.nova.model.UserPrefs; /** * A class to access UserPrefs stored in the hard disk as a json file diff --git a/src/main/java/seedu/nova/storage/NovaStorage.java b/src/main/java/seedu/nova/storage/NovaStorage.java new file mode 100644 index 00000000000..5c7a3945d8a --- /dev/null +++ b/src/main/java/seedu/nova/storage/NovaStorage.java @@ -0,0 +1,48 @@ +package seedu.nova.storage; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; + +import seedu.nova.commons.exceptions.DataConversionException; +import seedu.nova.model.AddressBook; +import seedu.nova.model.Nova; +import seedu.nova.model.ReadOnlyAddressBook; + + +/** + * Represents a storage for {@link AddressBook}. + */ +public interface NovaStorage { + + /** + * Returns the file path of the data file. + */ + Path getNovaFilePath(); + + /** + * 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 readNova() throws DataConversionException, IOException; + + /** + * @see #getNovaFilePath() + */ + Optional readNova(Path filePath) throws DataConversionException, IOException; + + /** + * Saves the given {@link ReadOnlyAddressBook} to the storage. + * @param nova cannot be null. + * @throws IOException if there was any problem writing to the file. + */ + void saveNova(Nova nova) throws IOException; + + /** + * @see #saveNova(Nova) + */ + void saveNova(Nova nova, Path filePath) throws IOException; + +} diff --git a/src/main/java/seedu/nova/storage/Storage.java b/src/main/java/seedu/nova/storage/Storage.java new file mode 100644 index 00000000000..f38bd2494f9 --- /dev/null +++ b/src/main/java/seedu/nova/storage/Storage.java @@ -0,0 +1,32 @@ +package seedu.nova.storage; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; + +import seedu.nova.commons.exceptions.DataConversionException; +import seedu.nova.model.Nova; +import seedu.nova.model.ReadOnlyUserPrefs; +import seedu.nova.model.UserPrefs; + +/** + * API of the Storage component + */ +public interface Storage extends NovaStorage, UserPrefsStorage { + + @Override + Optional readUserPrefs() throws DataConversionException, IOException; + + @Override + void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException; + + @Override + Path getNovaFilePath(); + + @Override + Optional readNova() throws DataConversionException, IOException; + + @Override + void saveNova(Nova nova) throws IOException; + +} diff --git a/src/main/java/seedu/nova/storage/StorageManager.java b/src/main/java/seedu/nova/storage/StorageManager.java new file mode 100644 index 00000000000..cbf82242643 --- /dev/null +++ b/src/main/java/seedu/nova/storage/StorageManager.java @@ -0,0 +1,77 @@ +package seedu.nova.storage; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; +import java.util.logging.Logger; + +import seedu.nova.commons.core.LogsCenter; +import seedu.nova.commons.exceptions.DataConversionException; +import seedu.nova.model.Nova; +import seedu.nova.model.ReadOnlyUserPrefs; +import seedu.nova.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 NovaStorage novaStorage; + private UserPrefsStorage userPrefsStorage; + + + public StorageManager(NovaStorage novaStorage, UserPrefsStorage userPrefsStorage) { + super(); + this.novaStorage = novaStorage; + 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); + } + + + // ================ Nova methods ============================== + + @Override + public Path getNovaFilePath() { + return novaStorage.getNovaFilePath(); + } + + @Override + public Optional readNova() throws DataConversionException, IOException { + return readNova(novaStorage.getNovaFilePath()); + } + + @Override + public Optional readNova(Path filePath) throws DataConversionException, IOException { + logger.fine("Attempting to read data from file: " + filePath); + return novaStorage.readNova(filePath); + } + + @Override + public void saveNova(Nova nova) throws IOException { + saveNova(nova, novaStorage.getNovaFilePath()); + } + + @Override + public void saveNova(Nova nova, Path filePath) throws IOException { + logger.fine("Attempting to write to data file: " + filePath); + novaStorage.saveNova(nova, filePath); + } + +} diff --git a/src/main/java/seedu/address/storage/UserPrefsStorage.java b/src/main/java/seedu/nova/storage/UserPrefsStorage.java similarity index 71% rename from src/main/java/seedu/address/storage/UserPrefsStorage.java rename to src/main/java/seedu/nova/storage/UserPrefsStorage.java index 29eef178dbc..9bc028a978f 100644 --- a/src/main/java/seedu/address/storage/UserPrefsStorage.java +++ b/src/main/java/seedu/nova/storage/UserPrefsStorage.java @@ -1,15 +1,15 @@ -package seedu.address.storage; +package seedu.nova.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.nova.commons.exceptions.DataConversionException; +import seedu.nova.model.ReadOnlyUserPrefs; +import seedu.nova.model.UserPrefs; /** - * Represents a storage for {@link seedu.address.model.UserPrefs}. + * Represents a storage for {@link 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 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/nova/storage/event/JsonAdaptedEvent.java b/src/main/java/seedu/nova/storage/event/JsonAdaptedEvent.java new file mode 100644 index 00000000000..b71185242bf --- /dev/null +++ b/src/main/java/seedu/nova/storage/event/JsonAdaptedEvent.java @@ -0,0 +1,230 @@ +package seedu.nova.storage.event; + +import java.time.LocalDate; +import java.time.LocalTime; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import seedu.nova.commons.exceptions.IllegalValueException; +import seedu.nova.logic.parser.ParserUtil; +import seedu.nova.model.plan.Task; +import seedu.nova.model.schedule.event.Consultation; +import seedu.nova.model.schedule.event.Event; +import seedu.nova.model.schedule.event.Lesson; +import seedu.nova.model.schedule.event.Meeting; +import seedu.nova.model.schedule.event.StudySession; +import seedu.nova.model.schedule.event.WeakEvent; +import seedu.nova.model.util.time.duration.DateTimeDuration; +import seedu.nova.storage.JsonAdaptedPlannerTask; + +/** + * Jackson-friendly version of {@link Event}. + */ +public class JsonAdaptedEvent { + + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Event's %s field is missing!"; + + private final String eventType; + private final String desc; + private final String venue; + private final String date; + private final String startTime; + private final String endTime; + private final String note; + private final JsonAdaptedPlannerTask task; + + + /** + * Constructs a {@code JsonAdaptedPerson} with the given person details. + */ + @JsonCreator + public JsonAdaptedEvent(@JsonProperty("eventType") String eventType, + @JsonProperty("desc") String desc, + @JsonProperty("venue") String venue, + @JsonProperty("date") String date, + @JsonProperty("startTime") String startTime, + @JsonProperty("endTime") String endTime, + @JsonProperty("note") String note, + @JsonProperty("task") JsonAdaptedPlannerTask task) { + this.eventType = eventType; + this.desc = desc; + this.venue = venue; + this.date = date; + this.startTime = startTime; + this.endTime = endTime; + this.note = note; + this.task = task; + } + + /** + * Converts a given {@code Event} into this class for Jackson use. + */ + public JsonAdaptedEvent(Event source) { + eventType = source.getEventType(); + desc = source.getDesc(); + venue = source.getVenue(); + date = source.getDate().toString(); + startTime = source.getStartTime().toString(); + endTime = source.getEndTime().toString(); + note = source.getNote(); + if (source instanceof WeakEvent) { + task = new JsonAdaptedPlannerTask(((WeakEvent) source).getOriginTask()); + } else { + task = null; + } + } + + /** + * Converts this Jackson-friendly adapted event object into the model's {@code Event} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted event. + */ + public Event toModelType() throws IllegalValueException { + switch (eventType) { + case "meeting": + return toMeeting(); + + case "consultation": + return toConsultation(); + + case "study": + return toStudy(); + + case "lesson": + return toLesson(); + + case "weak": + return toWeak(); + + default: + throw new IllegalValueException("Unrecognised event type."); + } + + } + + /** + * Converts this Jackson-friendly adapted event object into the model's {@code Meeting} object. + * + * @return an Event + * @throws IllegalValueException if any of the fields are missing + */ + public Event toMeeting() throws IllegalValueException { + checkFields(); + + LocalDate localDate = ParserUtil.parseDate(date); + LocalTime start = ParserUtil.parseTime(startTime); + LocalTime end = ParserUtil.parseTime(endTime); + + Event meeting = new Meeting(desc, venue, start, end, localDate, note); + + return meeting; + } + + /** + * Converts this Jackson-friendly adapted event object into the model's {@code Consultation} object. + * + * @return an Event + * @throws IllegalValueException if any of the fields are missing + */ + public Event toConsultation() throws IllegalValueException { + checkFields(); + + LocalDate localDate = ParserUtil.parseDate(date); + LocalTime start = ParserUtil.parseTime(startTime); + LocalTime end = ParserUtil.parseTime(endTime); + + Event consultation = new Consultation(desc, venue, start, end, localDate, note); + + return consultation; + } + + /** + * Converts this Jackson-friendly adapted event object into the model's {@code StudySession} object. + * + * @return an Event + * @throws IllegalValueException if any of the fields are missing + */ + public Event toStudy() throws IllegalValueException { + checkFields(); + + LocalDate localDate = ParserUtil.parseDate(date); + LocalTime start = ParserUtil.parseTime(startTime); + LocalTime end = ParserUtil.parseTime(endTime); + + Event study = new StudySession(desc, venue, start, end, localDate, note); + + return study; + } + + /** + * Converts this Jackson-friendly adapted event object into the model's {@code Lesson} object. + * + * @return an Event + * @throws IllegalValueException if any of the fields are missing + */ + public Event toLesson() throws IllegalValueException { + checkFields(); + + LocalDate localDate = ParserUtil.parseDate(date); + + LocalTime start = ParserUtil.parseTime(startTime); + LocalTime end = ParserUtil.parseTime(endTime); + + Event lesson = new Lesson(desc, venue, start, end, localDate.getDayOfWeek(), localDate, note); + + return lesson; + + } + + /** + * Converts this Jackson-friendly adapted event object into the model's {@code WeakEvent} object. + * + * @return an Event + * @throws IllegalValueException if any of the fields are missing + */ + public Event toWeak() throws IllegalValueException { + checkFields(); + + LocalDate localDate = ParserUtil.parseDate(date); + + LocalTime start = ParserUtil.parseTime(startTime); + LocalTime end = ParserUtil.parseTime(endTime); + + Task t = task.toTask(); + + return new WeakEvent(desc, new DateTimeDuration(localDate, start, end), t); + } + + /** + * Checks for any missing fields. + * + * @throws IllegalValueException if any of the fields are missing + */ + public void checkFields() throws IllegalValueException { + if (desc == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, "description")); + } + + if (venue == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, "venue")); + } + + if (startTime == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, "start time")); + } + + if (endTime == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, "end time")); + } + + if (date == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, "date")); + } + + if (note == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, "note")); + } + } + +} diff --git a/src/main/java/seedu/address/ui/CommandBox.java b/src/main/java/seedu/nova/ui/CommandBox.java similarity index 88% rename from src/main/java/seedu/address/ui/CommandBox.java rename to src/main/java/seedu/nova/ui/CommandBox.java index 7d76e691f52..a45098f6e15 100644 --- a/src/main/java/seedu/address/ui/CommandBox.java +++ b/src/main/java/seedu/nova/ui/CommandBox.java @@ -1,12 +1,12 @@ -package seedu.address.ui; +package seedu.nova.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.nova.logic.commands.CommandResult; +import seedu.nova.logic.commands.exceptions.CommandException; +import seedu.nova.logic.parser.exceptions.ParseException; /** * The UI component that is responsible for receiving user command inputs. @@ -68,8 +68,6 @@ private void setStyleToIndicateCommandFailure() { public interface CommandExecutor { /** * Executes the command and returns the result. - * - * @see seedu.address.logic.Logic#execute(String) */ CommandResult execute(String commandText) throws CommandException, ParseException; } diff --git a/src/main/java/seedu/nova/ui/HelpBox.java b/src/main/java/seedu/nova/ui/HelpBox.java new file mode 100644 index 00000000000..1411750f001 --- /dev/null +++ b/src/main/java/seedu/nova/ui/HelpBox.java @@ -0,0 +1,26 @@ +package seedu.nova.ui; + +import static java.util.Objects.requireNonNull; + +import javafx.scene.control.TextArea; +import javafx.scene.layout.Region; + +/** + * Class for help ui component + */ +public class HelpBox extends UiPart { + private static final String FXML = "HelpDisplay.fxml"; + + @javafx.fxml.FXML + private TextArea helpDisplay; + + public HelpBox() { + super(FXML); + } + + public void setHelp(String help) { + requireNonNull(help); + helpDisplay.setText(help); + } + +} diff --git a/src/main/java/seedu/address/ui/HelpWindow.java b/src/main/java/seedu/nova/ui/HelpWindow.java similarity index 97% rename from src/main/java/seedu/address/ui/HelpWindow.java rename to src/main/java/seedu/nova/ui/HelpWindow.java index 9a665915949..fbc7b02e172 100644 --- a/src/main/java/seedu/address/ui/HelpWindow.java +++ b/src/main/java/seedu/nova/ui/HelpWindow.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.nova.ui; import java.util.logging.Logger; @@ -8,7 +8,7 @@ import javafx.scene.input.Clipboard; import javafx.scene.input.ClipboardContent; import javafx.stage.Stage; -import seedu.address.commons.core.LogsCenter; +import seedu.nova.commons.core.LogsCenter; /** * Controller for a help page diff --git a/src/main/java/seedu/nova/ui/MainWindow.java b/src/main/java/seedu/nova/ui/MainWindow.java new file mode 100644 index 00000000000..f0b640afad9 --- /dev/null +++ b/src/main/java/seedu/nova/ui/MainWindow.java @@ -0,0 +1,188 @@ +package seedu.nova.ui; + +import static seedu.nova.commons.core.HelpMessages.HELP_ADDRESS_BOOK; +import static seedu.nova.commons.core.HelpMessages.HELP_HOME; +import static seedu.nova.commons.core.HelpMessages.HELP_PROGRESS_TRACKER; +import static seedu.nova.commons.core.HelpMessages.HELP_SCHEDULE; + +import java.time.LocalDate; +import java.util.logging.Logger; + +import javafx.fxml.FXML; +import javafx.scene.control.ScrollPane; +import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; +import javafx.stage.Stage; +import seedu.nova.commons.core.GuiSettings; +import seedu.nova.commons.core.LogsCenter; +import seedu.nova.logic.Logic; +import seedu.nova.logic.commands.CommandResult; +import seedu.nova.logic.commands.exceptions.CommandException; +import seedu.nova.logic.parser.ModeEnum; +import seedu.nova.logic.parser.exceptions.ParseException; +import seedu.nova.model.Mode; + +/** + * The Main Window. Provides the basic application layout containing + * a menu bar and space where other JavaFX elements can be placed. + */ +public class MainWindow extends UiPart { + + private static final String FXML = "MainWindow.fxml"; + + private final Logger logger = LogsCenter.getLogger(getClass()); + + private Stage primaryStage; + private Logic logic; + + // Independent Ui parts residing in this Ui container + private ResultDisplay resultDisplay; + private HelpBox helpBox; + + //private HelpWindow helpWindow; + @FXML + private ScrollPane scrollPane; + + @FXML + private StackPane commandBoxPlaceholder; + + @FXML + private VBox resultDisplayPlaceholder; + + @FXML + private VBox helpHolder; + + + public MainWindow(Stage primaryStage, Logic logic) { + super(FXML, primaryStage); + + // Set dependencies + this.primaryStage = primaryStage; + this.logic = logic; + this.helpBox = new HelpBox(); + + // Configure the UI + setWindowDefaultSize(logic.getGuiSettings()); + + // Autoscroll to bottom + resultDisplayPlaceholder.heightProperty().addListener(( + observable, oldValue, newValue) -> scrollPane.setVvalue(scrollPane.getVmax())); + } + + public Stage getPrimaryStage() { + return primaryStage; + } + + + /** + * Fills up all the placeholders of this window. + */ + void fillInnerParts() { + CommandBox commandBox = new CommandBox(this::executeCommand); + commandBoxPlaceholder.getChildren().add(commandBox.getRoot()); + + helpHolder.getChildren().add(helpBox.getRoot()); + + helpBox.setHelp(HELP_HOME); + + //add schedule for the day in homepage + try { + //get localdate of today + String today = LocalDate.now().toString(); + + //set mode to schedule first + Mode mode = logic.getMode(); + logic.setMode(mode, ModeEnum.SCHEDULE); + + executeCommand("view t\\" + today); + + //set mode back to home + logic.setMode(mode, ModeEnum.HOME); + } catch (CommandException | ParseException e) { + //do noting + } + } + + /** + * Sets the default size based on {@code guiSettings}. + */ + private void setWindowDefaultSize(GuiSettings guiSettings) { + primaryStage.setHeight(guiSettings.getWindowHeight()); + primaryStage.setWidth(guiSettings.getWindowWidth()); + if (guiSettings.getWindowCoordinates() != null) { + primaryStage.setX(guiSettings.getWindowCoordinates().getX()); + primaryStage.setY(guiSettings.getWindowCoordinates().getY()); + } + } + + + void show() { + primaryStage.show(); + } + + /** + * Closes the application. + */ + @FXML + private void handleExit() { + GuiSettings guiSettings = new GuiSettings(primaryStage.getWidth(), primaryStage.getHeight(), + (int) primaryStage.getX(), (int) primaryStage.getY()); + logic.setGuiSettings(guiSettings); + primaryStage.hide(); + } + + /** + * Executes the command and returns the result. + */ + private CommandResult executeCommand(String commandText) throws CommandException, ParseException { + try { + CommandResult commandResult = logic.execute(commandText); + logger.info("Result: " + commandResult.getFeedbackToUser()); + + displayToUser(commandResult.getFeedbackToUser()); + + if (commandResult.isExit()) { + handleExit(); + } + + if (commandResult.isChangeMode()) { + Mode mode = logic.getMode(); + ModeEnum modeEnum = logic.getModeEnum(mode); + + switch (modeEnum) { + case HOME: + helpBox.setHelp(HELP_HOME); + break; + case ADDRESSBOOK: + helpBox.setHelp(HELP_ADDRESS_BOOK); + break; + case SCHEDULE: + helpBox.setHelp(HELP_SCHEDULE); + break; + case PROGRESSTRACKER: + helpBox.setHelp(HELP_PROGRESS_TRACKER); + break; + default: + logger.info("Invalid mode: " + logic.getModeName(modeEnum)); + } + + } + + return commandResult; + } catch (CommandException | ParseException e) { + logger.info("Invalid command: " + commandText); + displayToUser(e.getMessage()); + throw e; + } + } + + /** + * Create a box in the scrollpane to display the result + * @param s the result string + */ + private void displayToUser(String s) { + ResultDisplay r = new ResultDisplay(scrollPane.widthProperty()); + r.setFeedbackToUser(s); + resultDisplayPlaceholder.getChildren().add(r.getRoot()); + } +} diff --git a/src/main/java/seedu/address/ui/PersonCard.java b/src/main/java/seedu/nova/ui/PersonCard.java similarity index 77% rename from src/main/java/seedu/address/ui/PersonCard.java rename to src/main/java/seedu/nova/ui/PersonCard.java index 0684b088868..0e0dec4f21d 100644 --- a/src/main/java/seedu/address/ui/PersonCard.java +++ b/src/main/java/seedu/nova/ui/PersonCard.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.nova.ui; import java.util.Comparator; @@ -7,7 +7,7 @@ import javafx.scene.layout.FlowPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Region; -import seedu.address.model.person.Person; +import seedu.nova.model.person.Person; /** * An UI component that displays information of a {@code Person}. @@ -35,11 +35,13 @@ public class PersonCard extends UiPart { @FXML private Label phone; @FXML - private Label address; - @FXML private Label email; + + @FXML + private Label remark; + @FXML - private FlowPane tags; + private FlowPane categories; public PersonCard(Person person, int displayedIndex) { super(FXML); @@ -47,11 +49,13 @@ public PersonCard(Person person, int displayedIndex) { 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))); + //To retain the old UI's label for category (aka tag), + //DarkTheme.css's tags into categories. + person.getCategory().stream() + .sorted(Comparator.comparing(category -> category.categoryName)) + .forEach(category -> categories.getChildren().add(new Label(category.categoryName))); + remark.setText(person.getRemark().value); } @Override diff --git a/src/main/java/seedu/address/ui/PersonListPanel.java b/src/main/java/seedu/nova/ui/PersonListPanel.java similarity index 91% rename from src/main/java/seedu/address/ui/PersonListPanel.java rename to src/main/java/seedu/nova/ui/PersonListPanel.java index 1328917096e..620b1c4da5d 100644 --- a/src/main/java/seedu/address/ui/PersonListPanel.java +++ b/src/main/java/seedu/nova/ui/PersonListPanel.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.nova.ui; import java.util.logging.Logger; @@ -7,8 +7,8 @@ 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; +import seedu.nova.commons.core.LogsCenter; +import seedu.nova.model.person.Person; /** * Panel containing the list of persons. diff --git a/src/main/java/seedu/nova/ui/ResultDisplay.java b/src/main/java/seedu/nova/ui/ResultDisplay.java new file mode 100644 index 00000000000..086f6c30d2d --- /dev/null +++ b/src/main/java/seedu/nova/ui/ResultDisplay.java @@ -0,0 +1,42 @@ +package seedu.nova.ui; + +import static java.util.Objects.requireNonNull; + +import javafx.beans.property.ReadOnlyDoubleProperty; +import javafx.fxml.FXML; +import javafx.scene.layout.Priority; +import javafx.scene.layout.Region; +import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; +import javafx.scene.text.Text; + +/** + * A ui for the status bar that is displayed at the header of the application. + */ +public class ResultDisplay extends UiPart { + + private static final String FXML = "ResultDisplay.fxml"; + private ReadOnlyDoubleProperty width; + @FXML + private StackPane placeHolder; + + public ResultDisplay(ReadOnlyDoubleProperty width) { + super(FXML); + this.width = width; + VBox.setVgrow(placeHolder, Priority.NEVER); + + } + + public void setFeedbackToUser(String feedbackToUser) { + requireNonNull(feedbackToUser); + Text txt = new Text(); + txt.setText(feedbackToUser); + txt.setStyle("-fx-font: 18 arial;"); + width.addListener(( + observable, oldValue, newValue) -> + txt.setWrappingWidth(newValue.doubleValue() - 30.0)); + placeHolder.getChildren().add(txt); + VBox.setVgrow(txt, Priority.NEVER); + } + +} diff --git a/src/main/java/seedu/address/ui/StatusBarFooter.java b/src/main/java/seedu/nova/ui/StatusBarFooter.java similarity index 95% rename from src/main/java/seedu/address/ui/StatusBarFooter.java rename to src/main/java/seedu/nova/ui/StatusBarFooter.java index 7e17911323f..b40c025addc 100644 --- a/src/main/java/seedu/address/ui/StatusBarFooter.java +++ b/src/main/java/seedu/nova/ui/StatusBarFooter.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.nova.ui; import java.nio.file.Path; import java.nio.file.Paths; diff --git a/src/main/java/seedu/address/ui/Ui.java b/src/main/java/seedu/nova/ui/Ui.java similarity index 86% rename from src/main/java/seedu/address/ui/Ui.java rename to src/main/java/seedu/nova/ui/Ui.java index 17aa0b494fe..58bad0a3857 100644 --- a/src/main/java/seedu/address/ui/Ui.java +++ b/src/main/java/seedu/nova/ui/Ui.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.nova.ui; import javafx.stage.Stage; diff --git a/src/main/java/seedu/address/ui/UiManager.java b/src/main/java/seedu/nova/ui/UiManager.java similarity index 93% rename from src/main/java/seedu/address/ui/UiManager.java rename to src/main/java/seedu/nova/ui/UiManager.java index 876621d79b9..6ee446e76b8 100644 --- a/src/main/java/seedu/address/ui/UiManager.java +++ b/src/main/java/seedu/nova/ui/UiManager.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.nova.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.nova.MainApp; +import seedu.nova.commons.core.LogsCenter; +import seedu.nova.commons.util.StringUtil; +import seedu.nova.logic.Logic; /** * The manager of the UI component. diff --git a/src/main/java/seedu/address/ui/UiPart.java b/src/main/java/seedu/nova/ui/UiPart.java similarity index 94% rename from src/main/java/seedu/address/ui/UiPart.java rename to src/main/java/seedu/nova/ui/UiPart.java index fc820e01a9c..16ae1855c1e 100644 --- a/src/main/java/seedu/address/ui/UiPart.java +++ b/src/main/java/seedu/nova/ui/UiPart.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.nova.ui; import static java.util.Objects.requireNonNull; @@ -6,10 +6,10 @@ import java.net.URL; import javafx.fxml.FXMLLoader; -import seedu.address.MainApp; +import seedu.nova.MainApp; /** - * Represents a distinct part of the UI. e.g. Windows, dialogs, panels, status bars, etc. + * Represents distinct part of the UI. e.g. Windows, dialogs, panels, status bars, etc. * It contains a scene graph with a root node of type {@code T}. */ public abstract class UiPart { diff --git a/src/main/main.iml b/src/main/main.iml new file mode 100644 index 00000000000..b89a3f5b0a6 --- /dev/null +++ b/src/main/main.iml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/main/resources/view/DarkTheme.css b/src/main/resources/view/DarkTheme.css index 36e6b001cd8..be1dcd8b481 100644 --- a/src/main/resources/view/DarkTheme.css +++ b/src/main/resources/view/DarkTheme.css @@ -1,6 +1,6 @@ .background { - -fx-background-color: derive(#1d1d1d, 20%); - background-color: #383838; /* Used in the default.html file */ + -fx-background-color: black; + background-color: black; /* Used in the default.html file */ } .label { @@ -133,12 +133,12 @@ } .stack-pane { - -fx-background-color: derive(#1d1d1d, 20%); + -fx-background-color: white } .pane-with-border { - -fx-background-color: derive(#1d1d1d, 20%); - -fx-border-color: derive(#1d1d1d, 10%); + -fx-background-color: white; + -fx-border-color: white; -fx-border-top-width: 1px; } @@ -150,7 +150,7 @@ -fx-background-color: transparent; -fx-font-family: "Segoe UI Light"; -fx-font-size: 13pt; - -fx-text-fill: white; + -fx-text-fill: black; } .result-display .label { @@ -325,7 +325,7 @@ -fx-border-width: 1; -fx-font-family: "Segoe UI Light"; -fx-font-size: 13pt; - -fx-text-fill: white; + -fx-text-fill: black; } #filterField, #personListPanel, #personWebpage { @@ -333,7 +333,7 @@ } #resultDisplay .content { - -fx-background-color: transparent, #383838, transparent, #383838; + -fx-background-color: transparent, #FFB6C1, transparent, #FFB6C1; -fx-background-radius: 0; } diff --git a/src/main/resources/view/HelpDisplay.fxml b/src/main/resources/view/HelpDisplay.fxml new file mode 100644 index 00000000000..76110b7a9d2 --- /dev/null +++ b/src/main/resources/view/HelpDisplay.fxml @@ -0,0 +1,9 @@ + + + + + + +