diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml
index 81267b247eb..458bb3bff75 100644
--- a/.github/workflows/gradle.yml
+++ b/.github/workflows/gradle.yml
@@ -44,3 +44,8 @@ jobs:
directory: ${{ github.workspace }}/build/reports/jacoco/coverage
files: coverage.xml
fail_ci_if_error: false
+
+ - name: Upload coverage reports to Codecov
+ uses: codecov/codecov-action@v3
+ with:
+ token: ${{ secrets.CODECOV_TOKEN }}
diff --git a/.gitignore b/.gitignore
index 71c9194e8bd..ebb70b53e84 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,3 +20,10 @@ src/test/data/sandbox/
# MacOS custom attributes files created by Finder
.DS_Store
docs/_site/
+
+# Bin files
+bin/
+
+# Local Gemfile
+docs/Gemfile
+docs/Gemfile.lock
diff --git a/Alice Pauline's Progress Report.pdf b/Alice Pauline's Progress Report.pdf
new file mode 100644
index 00000000000..7109611b016
Binary files /dev/null and b/Alice Pauline's Progress Report.pdf differ
diff --git a/George Best's Progress Report.pdf b/George Best's Progress Report.pdf
new file mode 100644
index 00000000000..5da4941730b
Binary files /dev/null and b/George Best's Progress Report.pdf differ
diff --git a/README.md b/README.md
index 13f5c77403f..0a3ea9f4681 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,15 @@
-[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions)
+[![CI Status](https://github.com/AY2223S2-CS2103-W17-1/tp/workflows/Java%20CI/badge.svg)](https://github.com/AY2223S2-CS2103-W17-1/tp/actions)
+[![codecov](https://codecov.io/gh/AY2223S2-CS2103-W17-1/tp/branch/master/graph/badge.svg?token=XXJ15FVPPS)](https://codecov.io/gh/AY2223S2-CS2103-W17-1/tp)
![Ui](docs/images/Ui.png)
-* This is **a sample project for Software Engineering (SE) students**.
+* This is **a product for private Math tuition teachers**.
Example usages:
- * as a starting point of a course project (as opposed to writing everything from scratch)
- * as a case study
-* The project simulates an ongoing software project for a desktop application (called _AddressBook_) used for managing contact details.
- * It is **written in OOP fashion**. It provides a **reasonably well-written** code base **bigger** (around 6 KLoC) than what students usually write in beginner-level SE modules, without being overwhelmingly big.
+ * as an application to manage their student contact details and performance.
+* The project simulates an ongoing software project for a desktop application (called _MATHUTORING_) used for managing student contact details and performance.
+ * It is **written in OOP fashion**. It provides a **reasonably well-written** code base based on the original project **AB3**.
* It comes with a **reasonable level of user and developer documentation**.
-* It is named `AddressBook Level 3` (`AB3` for short) because it was initially created as a part of a series of `AddressBook` projects (`Level 1`, `Level 2`, `Level 3` ...).
-* For the detailed documentation of this project, see the **[Address Book Product Website](https://se-education.org/addressbook-level3)**.
-* This project is a **part of the se-education.org** initiative. If you would like to contribute code to this project, see [se-education.org](https://se-education.org#https://se-education.org/#contributing) for more info.
+* It is named `MATHUTORING v1.4` because it was initially created as a part of a series of `MATHUTORING` projects (`v1.1`, `v1.2`, `v1.3` ...).
+* For the detailed documentation of this project, see the **[MATHUTORING Product Website](https://github.com/AY2223S2-CS2103-W17-1/tp)**.
+* This project is based on the AddressBook-Level3 project created by the [SE-EDU initiative](https://se-education.org).
+
diff --git a/build.gradle b/build.gradle
index 108397716bd..934bb046b3a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -40,6 +40,10 @@ task coverage(type: JacocoReport) {
}
}
+run {
+ enableAssertions = true
+}
+
dependencies {
String jUnitVersion = '5.4.0'
String javaFxVersion = '11'
@@ -60,13 +64,21 @@ dependencies {
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.7.0'
implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.7.4'
+ implementation 'org.apache.pdfbox:pdfbox:2.0.27'
+
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: jUnitVersion
+ testImplementation "org.mockito:mockito-core:3.+"
+ testImplementation "org.mockito:mockito-inline:4.2.0"
testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: jUnitVersion
}
shadowJar {
- archiveFileName = 'addressbook.jar'
+ archiveFileName = 'mathutoring.jar'
+}
+
+run {
+ enableAssertions = true
}
defaultTasks 'clean', 'test'
diff --git a/data.json b/data.json
new file mode 100644
index 00000000000..716dd8c6408
--- /dev/null
+++ b/data.json
@@ -0,0 +1,29 @@
+{
+ "persons" : [ {
+ "name" : "Bernice Yu",
+ "phone" : "99272758",
+ "email" : "berniceyu@example.com",
+ "address" : "Blk 30 Lorong 3 Serangoon Gardens, #07-18",
+ "parentPhone" : "94597742",
+ "tagged" : [ "colleagues", "friends" ],
+ "taskList" : [ ],
+ "scores" : [ {
+ "label" : "midterm",
+ "value" : "99.8",
+ "date" : "2019-09-09"
+ }, {
+ "label" : "midterm",
+ "value" : "85.5",
+ "date" : "2020-02-01"
+ } ]
+ }, {
+ "name" : "Irfan Ibrahim",
+ "phone" : "92492021",
+ "email" : "irfan@example.com",
+ "address" : "Blk 47 Tampines Street 20, #17-35",
+ "parentPhone" : "88814569",
+ "tagged" : [ "classmates" ],
+ "taskList" : [ ],
+ "scores" : [ ]
+ } ]
+}
diff --git a/docs/AboutUs.md b/docs/AboutUs.md
index 1c9514e966a..13fe2c58bb1 100644
--- a/docs/AboutUs.md
+++ b/docs/AboutUs.md
@@ -9,51 +9,52 @@ You can reach us at the email `seer[at]comp.nus.edu.sg`
## Project team
-### John Doe
+### Toh Xin Yi
-
+
-[[homepage](http://www.comp.nus.edu.sg/~damithch)]
-[[github](https://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
+[[github](https://github.com/toh-xinyi)]
+[[portfolio](team/toh-xinyi.md)]
-* Role: Project Advisor
+* Role: Developer
+* Responsibilities: In charge of Storage
-### Jane Doe
+### Gwyneth Guo Bai Ling
-
+
-[[github](http://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
+[[github](http://github.com/gwynethguo)]
+[[portfolio](team/gwynethguo.md)]
-* Role: Team Lead
-* Responsibilities: UI
+* Role: Developer
+* Responsibilities: In charge of Model
-### Johnny Doe
+### Li Yuxuan
-
+
-[[github](http://github.com/johndoe)] [[portfolio](team/johndoe.md)]
+[[github](http://github.com/carrieli1015)]
+[[portfolio](team/carrieli1015.md)]
* Role: Developer
-* Responsibilities: Data
+* Responsibilities: In charge of UI
-### Jean Doe
+### Qiu Qianhui
-
+
-[[github](http://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
+[[github](http://github.com/qqh0828)]
+[[portfolio](team/qqh0828.md)]
* Role: Developer
-* Responsibilities: Dev Ops + Threading
+* Responsibilities: In charge of Logic
-### James Doe
+### Quek Jing Ling, Brian
-
+
-[[github](http://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
+[[github](http://github.com/astraxq)]
+[[portfolio](team/astraxq.md)]
* Role: Developer
-* Responsibilities: UI
+* Responsibilities: In charge of UI
diff --git a/docs/DevOps.md b/docs/DevOps.md
index d2fd91a6001..b652b90b7fe 100644
--- a/docs/DevOps.md
+++ b/docs/DevOps.md
@@ -3,8 +3,12 @@ layout: page
title: DevOps guide
---
-* Table of Contents
-{:toc}
+## Table of Contents
+* **[Build automation](#build-automation)**
+* **[Continuous integration (CI)](#continuous-integration-ci)**
+* **[Code coverage](#code-coverage)**
+* **[Repository-wide checks](#repository-wide-checks)**
+* **[Making a release](#making-a-release)**
--------------------------------------------------------------------------------------------------------------------
diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md
index 46eae8ee565..533768af4a2 100644
--- a/docs/DeveloperGuide.md
+++ b/docs/DeveloperGuide.md
@@ -2,14 +2,34 @@
layout: page
title: Developer Guide
---
-* Table of Contents
-{:toc}
+## Table of Contents
+* **[Acknowledgements](#acknowledgements)**
+* **[Setting up, getting started](#setting-up-getting-started)**
+* **[Design](#design)**
+ * **[Architecture](#architecture)**
+ * **[UI component](#ui-component)**
+ * **[Logic component](#logic-component)**
+ * **[Model component](#model-component)**
+ * [Task Model](#task-model)
+ * [UniqueScoreList Model](#uniquescorelist-model)
+ * **[Storage component](#storage-component)**
+ * **[Common classes](#common-classes)**
+* **[Appendix: Requirements](#appendix-requirements)**
+ * **[Product scope](#product-scope)**
+ * **[User stories](#user-stories)**
+ * **[User cases](#use-cases)**
+ * **[Non-Functional Requirements](#non-functional-requirements)**
+ * **[Glossary](#glossary)**
+* **[Appendix: Instructions for manual testing](#appendix-instructions-for-manual-testing)**
+* **[Appendix: Effort](#appendix-effort)**
+* **[Appendix: Planned Enhancements](#appendix-planned-enhancements)**
--------------------------------------------------------------------------------------------------------------------
## **Acknowledgements**
-* {list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well}
+* This project is based on the AddressBook-Level3 project created by the [SE-EDU initiative](https://se-education.org).
+* Libraries used: [JavaFX](https://openjfx.io/), [Jackson](https://github.com/FasterXML/jackson), [JUnit5](https://github.com/junit-team/junit5), [Mockito](https://site.mockito.org/), [Apache PDFBox](https://pdfbox.apache.org/)
--------------------------------------------------------------------------------------------------------------------
@@ -21,22 +41,16 @@ Refer to the guide [_Setting up and getting started_](SettingUp.md).
## **Design**
-
-
-:bulb: **Tip:** The `.puml` files used to create diagrams in this document can be found in the [diagrams](https://github.com/se-edu/addressbook-level3/tree/master/docs/diagrams/) folder. Refer to the [_PlantUML Tutorial_ at se-edu/guides](https://se-education.org/guides/tutorials/plantUml.html) to learn how to create and edit diagrams.
-
-
### Architecture
+
-
-
-The ***Architecture Diagram*** given above explains the high-level design of the App.
+The ***Architecture Diagram*** given above explains the high-level design of MATHUTORING.
Given below is a quick overview of main components and how they interact with each other.
**Main components of the architecture**
-**`Main`** has two classes called [`Main`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/Main.java) and [`MainApp`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/MainApp.java). It is responsible for,
+**`Main`** has two classes called [`Main`](https://github.com/AY2223S2-CS2103-W17-1/tp/blob/master/src/main/java/seedu/address/Main.java) and [`MainApp`](https://github.com/AY2223S2-CS2103-W17-1/tp/blob/master/src/main/java/seedu/address/MainApp.java). It is responsible for,
* At app launch: Initializes the components in the correct sequence, and connects them up with each other.
* At shut down: Shuts down the components and invokes cleanup methods where necessary.
@@ -54,274 +68,633 @@ The rest of the App consists of four components.
The *Sequence Diagram* below shows how the components interact with each other for the scenario where the user issues the command `delete 1`.
-
+
Each of the four main components (also shown in the diagram above),
* defines its *API* in an `interface` with the same name as the Component.
-* implements its functionality using a concrete `{Component Name}Manager` class (which follows the corresponding API `interface` mentioned in the previous point.
+* implements its functionality using a concrete `{Component Name} Manager` class (which follows the corresponding API `interface` mentioned in the previous point.
For example, the `Logic` component defines its API in the `Logic.java` interface and implements its functionality using the `LogicManager.java` class which follows the `Logic` interface. Other components interact with a given component through its interface rather than the concrete class (reason: to prevent outside component's being coupled to the implementation of a component), as illustrated in the (partial) class diagram below.
-
+
The sections below give more details of each component.
### UI component
-The **API** of this component is specified in [`Ui.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/Ui.java)
+**API** : [`Ui.java`](https://github.com/AY2223S2-CS2103-W17-1/tp/blob/master/src/main/java/seedu/address/ui/Ui.java)
+
+
-![Structure of the UI Component](images/UiClassDiagram.png)
+More detailed Class diagram for Ui component:
-The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `PersonListPanel`, `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class which captures the commonalities between classes that represent parts of the visible GUI.
+
-The `UI` component uses the JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files that are in the `src/main/resources/view` folder. For example, the layout of the [`MainWindow`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/MainWindow.java) is specified in [`MainWindow.fxml`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/resources/view/MainWindow.fxml)
+The UI consists of a MainWindow that is made up of different parts.
+For instance, `CommandBox`, `ResultDisplay`, `StudentListPanel`, `ScoreListPanel`,
+`TaskListPanel`, `StatusBarFooter`, `HelpWindow`, `ImportWindow`, `ExportWindow` and `ExportProgressWindow` etc. All theses, including the MainWindow,
+inherit from the abstract UiPart class which captures the commonalities between
+classes that represent parts of visible GUI.
-The `UI` component,
+The UI component uses the JavaFx UI framework. The layout of these UI
+parts are defined in matching `.fxml` files that are in the `src/main/resources/view`
+folder. For example, the layout of the `MainWindow` is specified in `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.
-* keeps a reference to the `Logic` component, because the `UI` relies on the `Logic` to execute commands.
-* depends on some classes in the `Model` component, as it displays `Person` object residing in the `Model`.
+* listens for changes to `Model` data so that the UI can be
+updated with the modified data.
+* keeps a reference to the `Logic` component, because the UI relies on the `Logic`
+to execute the commands.
+* depends on some classes in the `Model` component, as it displays `Student` object
+residing in the `Model`.
### Logic component
-**API** : [`Logic.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/logic/Logic.java)
+**API** : [`Logic.java`](https://github.com/AY2223S2-CS2103-W17-1/tp/blob/master/src/main/java/seedu/address/logic/Logic.java)
Here's a (partial) class diagram of the `Logic` component:
-
+
How the `Logic` component works:
-1. When `Logic` is called upon to execute a command, it uses the `AddressBookParser` class to parse the user command.
-1. This results in a `Command` object (more precisely, an object of one of its subclasses e.g., `AddCommand`) which is executed by the `LogicManager`.
-1. The command can communicate with the `Model` when it is executed (e.g. to add a person).
-1. The result of the command execution is encapsulated as a `CommandResult` object which is returned back from `Logic`.
+1. When `Logic` is called upon to execute a command, it uses the `MathutoringParser` class to parse the user command.
+2. This results in a `Command` object (more precisely, an object of one of its subclasses e.g., `AddCommand`) which is executed by the `LogicManager`.
+3. The command can communicate with the `Model` when it is executed (e.g. to add a student).
+4. The result of the command execution is encapsulated as a `CommandResult` object which is returned back from `Logic`.
The Sequence Diagram below illustrates the interactions within the `Logic` component for the `execute("delete 1")` API call.
-![Interactions Inside the Logic Component for the `delete 1` Command](images/DeleteSequenceDiagram.png)
+![Interactions Inside the Logic Component for the `delete 1` Command](images/DG-images/DeleteSequenceDiagram.png)
-
:information_source: **Note:** The lifeline for `DeleteCommandParser` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
+
+
+**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.
Here are the other classes in `Logic` (omitted from the class diagram above) that are used for parsing a user command:
-
+
How the parsing works:
-* When called upon to parse a user command, the `AddressBookParser` class creates an `XYZCommandParser` (`XYZ` is a placeholder for the specific command name e.g., `AddCommandParser`) which uses the other classes shown above to parse the user command and create a `XYZCommand` object (e.g., `AddCommand`) which the `AddressBookParser` returns back as a `Command` object.
+* When called upon to parse a user command, the `MathutoringParser` class creates an `XYZCommandParser` (`XYZ` is a placeholder for the specific command name e.g., `AddCommandParser`) which uses the other classes shown above to parse the user command and create a `XYZCommand` object (e.g., `AddCommand`) which the `MathutoringParser` returns back as a `Command` object.
* All `XYZCommandParser` classes (e.g., `AddCommandParser`, `DeleteCommandParser`, ...) inherit from the `Parser` interface so that they can be treated similarly where possible e.g, during testing.
### Model component
-**API** : [`Model.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/model/Model.java)
-
+**API** : [`Model.java`](https://github.com/AY2223S2-CS2103-W17-1/tp/blob/master/src/main/java/seedu/address/model/Model.java)
+
+
The `Model` component,
-* stores the address book data i.e., all `Person` objects (which are contained in a `UniquePersonList` object).
-* stores the currently 'selected' `Person` objects (e.g., results of a search query) as a separate _filtered_ list which is exposed to outsiders as an unmodifiable `ObservableList` that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change.
+* stores the mathutoring data i.e., all `Student` objects (which are contained in a `UniqueStudentList` object).
+* stores the currently 'selected' `Student` objects (e.g., results of a search query) as a separate _filtered_ list which is exposed to outsiders as an unmodifiable `ObservableList` that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change.
* stores a `UserPref` object that represents the user’s preferences. This is exposed to the outside as a `ReadOnlyUserPref` objects.
* does not depend on any of the other three components (as the `Model` represents data entities of the domain, they should make sense on their own without depending on other components)
-
:information_source: **Note:** An alternative (arguably, a more OOP) model is given below. It has a `Tag` list in the `AddressBook`, which `Person` references. This allows `AddressBook` to only require one `Tag` object per unique tag, instead of each `Person` needing their own `Tag` objects.
+
-
+**Note:** An alternative (arguably, a more OOP) model is given below. It has a `Tag` list in the `Mathutoring`, which `Student` references. This allows `Mathutoring` to only require one `Tag` object per unique tag, instead of each `Student` needing their own `Tag` objects.
+
+
+#### Task model
+**API** : [`Task.java`](https://github.com/AY2223S2-CS2103-W17-1/tp/blob/master/src/main/java/seedu/address/model/task/Task.java)
+
+
+
+* A `Student` has a `UniqueTaskList` object which holds all their `Task` objects.
+* Each `Task` object has a `TaskStatus` assigned to it and can be any of `INPROGRESS`, `LATE` or `COMPLETE`.
+* The `creationDate` will be hidden from the user and only be used for sorting the `UniqueTaskList`.
+* The `UniqueTaskList` is sorted according to `TaskStatus`, and if two tasks have the same `TaskStatus`, they will be compared using their `creationDate`.
+
+#### UniqueScoreList model
+**API** : [`UniqueScoreList.java`](https://github.com/AY2223S2-CS2103-W17-1/tp/blob/master/src/main/java/seedu/address/model/score/UniqueScoreList.java)
+
+
+
+* A `Student` has only one `UniqueScoreList` object which holds all their `Score` objects.
+* `UniqueScoreList` is a separate filtered list with recent score at front which is exposed to outsiders as an unmodifiable ObservableList that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change.
+* There is an inner class `ScoreSummary` inside `UniqueScoreList` class which holds the summary of recent five scores.
### Storage component
-**API** : [`Storage.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/storage/Storage.java)
+**API** : [`Storage.java`](https://github.com/AY2223S2-CS2103-W17-1/tp/blob/master/src/main/java/seedu/address/storage/Storage.java)
-
+
The `Storage` component,
-* can save both address book data and user preference data in json format, and read them back into corresponding objects.
-* inherits from both `AddressBookStorage` and `UserPrefStorage`, which means it can be treated as either one (if only the functionality of only one is needed).
+* can save both mathutoring data and user preference data in json format, and read them back into corresponding objects.
+* inherits from both `MathutoringStorage` and `UserPrefStorage`, which means it can be treated as either one (if only the functionality of only one is needed).
* depends on some classes in the `Model` component (because the `Storage` component's job is to save/retrieve objects that belong to the `Model`)
### Common classes
-Classes used by multiple components are in the `seedu.addressbook.commons` package.
+Classes used by multiple components are in the `seedu.address.commons` package.
--------------------------------------------------------------------------------------------------------------------
-## **Implementation**
+## **Documentation, logging, testing, configuration, dev-ops**
+
+* [Documentation guide](Documentation.md)
+* [Testing guide](Testing.md)
+* [Logging guide](Logging.md)
+* [Configuration guide](Configuration.md)
+* [DevOps guide](DevOps.md)
-This section describes some noteworthy details on how certain features are implemented.
+--------------------------------------------------------------------------------------------------------------------
-### \[Proposed\] Undo/redo feature
+## **Appendix: Requirements**
-#### Proposed Implementation
+### Product scope
-The proposed undo/redo mechanism is facilitated by `VersionedAddressBook`. It extends `AddressBook` with an undo/redo history, stored internally as an `addressBookStateList` and `currentStatePointer`. Additionally, it implements the following operations:
+**Target user profile**:
-* `VersionedAddressBook#commit()` — Saves the current address book state in its history.
-* `VersionedAddressBook#undo()` — Restores the previous address book state from its history.
-* `VersionedAddressBook#redo()` — Restores a previously undone address book state from its history.
+* Private Math tuition teachers
+* Has a need to manage a number of students' contacts and performance
+* Prefer desktop apps over other types
+* Prefers typing to mouse interactions
+* Is reasonably comfortable using CLI apps
-These operations are exposed in the `Model` interface as `Model#commitAddressBook()`, `Model#undoAddressBook()` and `Model#redoAddressBook()` respectively.
+**Value proposition**:
-Given below is an example usage scenario and how the undo/redo mechanism behaves at each step.
+Tutors tend to use multiple applications to keep track of their student's contact details and performance. MATHUTORING comes into a centralised desktop application with a contact management system to track students' contact details and performance by keeping student contact details, tasks, and scores which subsequently allows the tutors to plan their lesson plan for future lessons and overall view of their schedule for ease of planning.
-Step 1. The user launches the application for the first time. The `VersionedAddressBook` will be initialized with the initial address book state, and the `currentStatePointer` pointing to that single address book state.
-![UndoRedoState0](images/UndoRedoState0.png)
+### User stories
-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.
+Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unlikely to have) - `*`
-![UndoRedoState1](images/UndoRedoState1.png)
+| Priority | As a … | I want to … | So that I can… |
+|----------|---------|-------------------------------------------------------------|------------------------------------------------------------------------------------------|
+| `* * *` | user | see a list of my students | know who my students are and how many students I have |
+| `* * *` | user | purge all current data | get rid of sample/experimental data I used for exploring the app |
+| `* * *` | user | create my student contacts | add new students into my contact list |
+| `* * *` | user | edit my student contacts | my contact list is more extensive/flexible |
+| `* * *` | user | delete my student contacts | remove contacts of students that I don't teach anymore |
+| `* * *` | user | use the help section | learn the available commands in the application |
+| `* * *` | user | import my data | backup data and open in another device |
+| `* * *` | user | export my data | load data into a new device |
+| `* * *` | user | add / delete / mark student’s tasking(s) | keep track of the task(s) that assign to a student and identify what taskings are done/in progress/overdue |
+| `* * *` | user | add / delete student’s score(s) | record down the student's score(s) |
+| `* * *` | user | check the student’s taskings | understand how good the student is doing |
+| `* * *` | user | check the student’s score(s) | understand the student's performance in school |
+| `* * ` | user | filter my student contacts | easily find a group of students based on the tag given |
+| `* * ` | user | generate a progress chart | keep track of the student's progress |
+| `* * ` | user | extract students' progress report | show the parents their kids' performance |
-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`.
+### Use cases
-![UndoRedoState2](images/UndoRedoState2.png)
+(For all use cases below, the **System** is the `MATHUTORING` and the **Actor** is the `Tutor`, unless specified otherwise)
-
:information_source: **Note:** If a command fails its execution, it will not call `Model#commitAddressBook()`, so the address book state will not be saved into the `addressBookStateList`.
+**1. Use case: Add a task**
-
+**MSS**
-Step 4. The user now decides that adding the person was a mistake, and decides to undo that action by executing the `undo` command. The `undo` command will call `Model#undoAddressBook()`, which will shift the `currentStatePointer` once to the left, pointing it to the previous address book state, and restores the address book to that state.
+1. Tutor requests to list students.
+2. MATHUTORING shows a list of students.
+3. Tutor requests to add a task with a task name to a specific student in the list.
+4. MATHUTORING creates the task with given task name, task status and creation date.
-![UndoRedoState3](images/UndoRedoState3.png)
+ Use case ends.
-
:information_source: **Note:** If the `currentStatePointer` is at index 0, pointing to the initial AddressBook state, then there are no previous AddressBook states to restore. The `undo` command uses `Model#canUndoAddressBook()` to check if this is the case. If so, it will return an error to the user rather
-than attempting to perform the undo.
+**Extensions**
-
+* 2a. The list is empty.
-The following sequence diagram shows how the undo operation works:
+ Use case ends.
-![UndoSequenceDiagram](images/UndoSequenceDiagram.png)
+* 3a. The given student index or task name is missing.
-
:information_source: **Note:** The lifeline for `UndoCommand` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
+ * 3a1. MATHUTORING shows an error message.
-
+ Use case resumes at step 2.
-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.
+* 3b. The given student index is invalid.
-
:information_source: **Note:** If the `currentStatePointer` is at index `addressBookStateList.size() - 1`, pointing to the latest address book state, then there are no undone AddressBook states to restore. The `redo` command uses `Model#canRedoAddressBook()` to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo.
+ * 3b1. MATHUTORING shows an error message.
-
+ Use case ends.
-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.
+* 3c. The given command argument is invalid.
-![UndoRedoState4](images/UndoRedoState4.png)
+ * 3c1. MATHUTORING shows an error message.
-Step 6. The user executes `clear`, which calls `Model#commitAddressBook()`. Since the `currentStatePointer` is not pointing at the end of the `addressBookStateList`, all address book states after the `currentStatePointer` will be purged. Reason: It no longer makes sense to redo the `add n/David …` command. This is the behavior that most modern desktop applications follow.
+ Use case ends.
-![UndoRedoState5](images/UndoRedoState5.png)
+* 3d. The given task is a duplicate of a task already in the task list of the specific student.
-The following activity diagram summarizes what happens when a user executes a new command:
+ * 3d1. MATHUTORING shows an error message.
-
+ Use case ends.
-#### Design considerations:
+**2. Use case: Delete a task**
-**Aspect: How undo & redo executes:**
+**MSS**
-* **Alternative 1 (current choice):** Saves the entire address book.
- * Pros: Easy to implement.
- * Cons: May have performance issues in terms of memory usage.
+1. Tutor requests to list students.
+2. MATHUTORING shows a list of students.
+3. Tutor requests to delete a specific task from a specific student in the list.
+4. MATHUTORING deletes the task from the task list of the specific student.
-* **Alternative 2:** Individual command knows how to undo/redo by
- itself.
- * Pros: Will use less memory (e.g. for `delete`, just save the person being deleted).
- * Cons: We must ensure that the implementation of each individual command are correct.
+Use case ends.
-_{more aspects and alternatives to be added}_
+**Extensions**
-### \[Proposed\] Data archiving
+* 2a. The list is empty.
-_{Explain here how the data archiving feature will be implemented}_
+ Use case ends.
+* 3a. The given student index or task index is missing.
---------------------------------------------------------------------------------------------------------------------
+ * 3a1. MATHUTORING shows an error message.
-## **Documentation, logging, testing, configuration, dev-ops**
+ Use case resumes at step 2.
-* [Documentation guide](Documentation.md)
-* [Testing guide](Testing.md)
-* [Logging guide](Logging.md)
-* [Configuration guide](Configuration.md)
-* [DevOps guide](DevOps.md)
+* 3b. The given student index is invalid.
---------------------------------------------------------------------------------------------------------------------
+ * 3b1. MATHUTORING shows an error message.
-## **Appendix: Requirements**
+ Use case ends.
-### Product scope
+* 3c. The given task index is invalid.
-**Target user profile**:
+ * 3c1. MATHUTORING shows an error message.
-* has a need to manage a significant number of contacts
-* prefer desktop apps over other types
-* can type fast
-* prefers typing to mouse interactions
-* is reasonably comfortable using CLI apps
+ Use case ends.
-**Value proposition**: manage contacts faster than a typical mouse/GUI driven app
+**3. Use case: Mark a task as late**
+**MSS**
-### User stories
+1. Tutor requests to list students.
+2. MATHUTORING shows a list of students.
+3. Tutor requests to mark a specific task from a specific student in the list as late.
+4. MATHUTORING marks the task from the task list of the specific student as late.
-Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unlikely to have) - `*`
+Use case ends.
-| Priority | As a … | I want to … | So that I can… |
-| -------- | ------------------------------------------ | ------------------------------ | ---------------------------------------------------------------------- |
-| `* * *` | new user | see usage instructions | refer to instructions when I forget how to use the App |
-| `* * *` | user | add a new person | |
-| `* * *` | user | delete a person | remove entries that I no longer need |
-| `* * *` | user | find a person by name | locate details of persons without having to go through the entire list |
-| `* *` | user | hide private contact details | minimize chance of someone else seeing them by accident |
-| `*` | user with many persons in the address book | sort persons by name | locate a person easily |
+**Extensions**
-*{More to be added}*
+* 2a. The list is empty.
-### Use cases
+ Use case ends.
+
+* 3a. The given student index or task index is missing.
+
+ * 3a1. MATHUTORING shows an error message.
+
+ Use case resumes at step 2.
-(For all use cases below, the **System** is the `AddressBook` and the **Actor** is the `user`, unless specified otherwise)
+* 3b. The given student index is invalid.
-**Use case: Delete a person**
+ * 3b1. MATHUTORING shows an error message.
+
+ Use case ends.
+
+* 3c. The given task index is invalid.
+
+ * 3c1. MATHUTORING shows an error message.
+
+ Use case ends.
+
+**4. Use case: Add a score**
**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. Tutor requests to add a score.
+2. MATHUTORING creates the score with score label, score value and score date.
+3. MATHUTORING stores the score to the score list storage.
Use case ends.
**Extensions**
+* 1a. MATHUTORING detects that the score label, score value or score date is missing.
+
+ * 1a1. MATHUTORING informs the tutor that there is missing element.
+
+ Use case resumes at step 2.
+
+* 1b. MATHUTORING detects that score label, score value or score date has an invalid format.
+
+ * 1b1. MATHUTORING informs the tutor that the form of new score is invalid.
+
+ Use case ends.
+
+* 1c. MATHUTORING detects that the score has already exited.
+
+ * 1c1. MATHUTORING informs the tutor that the score already exists.
+
+ Use case ends.
+
+**5. Use case: Delete a score**
+
+**MSS**
+
+1. Tutor requests to list students.
+2. MATHUTORING shows a list of students.
+3. Tutor requests to check specific student.
+4. MATHUTORING shows a list of scores for that student.
+5. Tutor requests to delete a specific score of a specific student.
+6. MATHUTORING deletes the score.
+
+ Use case ends.
+
+**Extensions**
+
* 2a. The list is empty.
Use case ends.
-* 3a. The given index is invalid.
+* 3a. The given student's index is invalid.
- * 3a1. AddressBook shows an error message.
+ * 3a1. MATHUTORING informs the tutor that the index is invalid.
Use case resumes at step 2.
-*{More to be added}*
+* 4a. The score list is empty.
+
+ Use case ends.
+
+* 5a. The given student's index is invalid.
+
+ * 5a1. MATHUTORING informs the tutor that the index is invalid.
+
+ Use case resumes at step 1.
+
+* 5b. The given score's index is invalid.
+
+ * 5b1. MATHUTORING informs the tutor that the index is invalid.
+
+ Use case resumes at step 3.
+
+**6. Use case: Export a student's progress**
+
+**MSS**
+
+1. Tutor requests to export a student's progress.
+2. MATHUTORING shows an export progress window.
+3. Tutor requests to save the student's progress.
+4. MATHUTORING shows a browse files window.
+5. Tutor requests the directory and file name of the student's progress file.
+6. MATHUTORING saves the file.
+
+ Use case ends.
+
+**Extensions**
+
+* 5a. The given file name is invalid.
+
+ * 5a1. File manager informs the tutor that the file name is invalid.
+
+ Use case resumes at step 5.
+
+* 5b. A file with the exact same name and type exists and is currently being opened.
+
+ * 5b1. MATHUTORING informs the tutor that the file cannot be saved because the file with the same name exists and is
+ currently being opened.
+
+ Use case resumes at step 5.
+
+**7. Use case: Import application data via CLI**
+
+**MSS**
+
+1. Tutor requests to import application data.
+2. MATHUTORING loads the data into the application.
+
+ Use case ends.
+
+**Extensions**
+* 1a. MATHUTORING detects a command format error.
+
+ Use case resumes at step 1.
+
+* 1b. MATHUTORING detects the file does not follow the parsing format.
+
+ Use case resumes at step 1.
+
+**8. Use case: Import application data via GUI**
+
+**MSS**
+
+1. Tutor requests to import application data.
+2. MATHUTORING opens Import GUI window.
+3. Tutor request to upload file.
+4. MATHUTORING opens the OS file explorer.
+5. Tutor selects a directory to upload the data.
+6. MATHUTORING saves the file.
+7. MATHUTORING loads the data into the application.
+
+ Use case ends.
+
+**Extensions**
+* 6a. MATHUTORING detects the file does not follow the parsing format.
+
+ Use case resumes at step 3.
+
+**9. Use case: Export application data via CLI**
+
+**MSS**
+
+1. Tutor requests to export application data.
+2. MATHUTORING saves the file.
+
+ Use case ends.
+
+**Extensions**
+* 1a. MATHUTORING detects a command format error.
+
+ Use case resumes at step 1.
+
+**10. Use case: Export application data via GUI**
+
+**MSS**
+
+1. Tutor requests to export application data.
+2. MATHUTORING opens the OS file explorer.
+3. Tutor selects a directory to save the data.
+4. MATHUTORING saves the file.
+
+ Use case ends.
+
+**Extensions**
+* 2a. File explorer closed by Tutor by mistake.
+
+ Use case resumes at step 1.
+
+* 2b. File explorer closed by Tutor.
+
+ Use case ends.
+
+**11. Use case: Export student's progress via CLI**
+
+**MSS**
+
+1. Tutor requests to export student's progress.
+2. MATHUTORING saves the file.
+
+ Use case ends.
+
+**Extensions**
+* 1a. MATHUTORING detects a command format error.
+
+ Use case resumes at step 1.
+
+* 1b. MATHUTORING detects that a file with the exact same name and type exists in the selected directory and is currently being opened.
+
+ Use case ends.
+
+**12. Use case: Export student's progress via GUI**
+
+**MSS**
+
+1. Tutor requests to export student's progress.
+2. MATHUTORING shows an export progress window.
+3. MATHUTORING opens the OS file explorer.
+4. Tutor selects a directory and specifies the file name to save the PDF file.
+5. MATHUTORING saves the file.
+
+ Use case ends.
+
+**Extensions**
+* 2a. Export progress window closed by Tutor by mistake.
+
+ Use case ends.
+
+* 2b. Export progress window closed by Tutor by mistake.
+
+ Use case ends.
+
+* 3a. File explorer closed by Tutor by mistake.
+
+ Use case resumes at step 2.
+
+* 3b. File explorer closed by Tutor.
+
+ Use case resumes at step 2.
+
+* 4a. File name specified is invalid.
+
+ * 4a1. File explorer informs the tutor that the file name is invalid.
+
+ Use case resumes at step 2.
+* 4b. A file with the exact same name and type exists in the selected directory and is currently being opened.
+
+ * 4b1. MATHUTORING informs the tutor that the file cannot be saved due to a file with the same name and type in the same directory is being opened.
+
+ Use case ends.
+
+**13. Use case: Switch panel via CLI**
+
+**MSS**
+
+1. Tutor requests to switch panel.
+2. MATHUTORING switch the panel.
+
+ Use case ends.
+
+**Extensions**
+* 1a. The given command is invalid.
+
+ * 1a1. MATHUTORING shows an error message.
+
+ Use case resumes at step 1.
+
+**14. Use case: Switch panel via GUI**
+
+**MSS**
+
+1. Tutor requests to switch panel.
+
+ Use case ends.
+
+**Extensions**
+* 1a. GUI not able to render.
+
+ Use case ends.
+
+**15. Use case: Filter student list by student tag/s**
+
+**MSS**
+
+1. Tutor requests to filter the student/s by student tag/s.
+2. MATHUTORING shows the filtered result.
+
+ Use case ends.
+
+**Extensions**
+
+* 1a. The given command is invalid.
+
+ * 1a1. MATHUTORING shows an error message.
+
+ Use case resumes at step 1.
+
+* 1b. The given command argument(s) are invalid.
+
+ * 1b1. MATHUTORING shows an error message.
+
+ Tutor case resumes at step 1.
+
+**16. Use case: Check a student**
+
+**MSS**
+
+1. Tutor requests to list students.
+
+2. MATHUTORING shows a list of students.
+
+3. Tutor requests to check a student.
+
+2. MATHUTORING shows the repsective student's task list and score list.
+
+ Use case ends.
+
+**Extensions**
+* 2a. The student list is empty.
+
+ Use case ends.
+
+* 3a. The given command is invalid.
+
+ * 3a1. MATHUTORING shows an error message.
+
+ Use case resumes at step 2.
+
+* 3b. The given index is invalid.
+
+ * 3b1. MATHUTORING shows an error message.
+
+ Use case resumes at step 2.
+
### Non-Functional Requirements
1. Should work on any _mainstream OS_ as long as it has Java `11` or above installed.
-2. Should be able to hold up to 1000 persons without a noticeable sluggishness in performance for typical usage.
+2. Should be able to hold up to 1000 students' information without a noticeable sluggishness in performance for typical usage.
3. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse.
-
-*{More to be added}*
+4. The application should work on both 32-bit and 64-bit environments.
+5. The application should respond within 3 seconds.
+6. The user interface should be intuitive enough for users who are not IT-savvy.
+7. The product is free of charge.
### Glossary
* **Mainstream OS**: Windows, Linux, Unix, OS-X
-* **Private contact detail**: A contact detail that is not meant to be shared with others
+* **Tutors**: Private math tuition teachers that will be using the application
+* **Students' progress**: For our current version, the progress of a student is tracked through
+the number of tasks the student has completed
+* **Students' performance**: For our current version, the performance of a student is tracked through a view
+of the student's scores as well as a progress chart of the latest 5 scores.
+* **Index**: The number of the item shown beside its name, on the list being referred to (e.g. "index of student" refers to the index number of the student (shown beside the student's name)
+ in the student list shown in the application)
--------------------------------------------------------------------------------------------------------------------
@@ -329,7 +702,7 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli
Given below are instructions to test the app manually.
-
:information_source: **Note:** These instructions only provide a starting point for testers to work on;
+
:information_source: Note: These instructions only provide a starting point for testers to work on;
testers are expected to do more *exploratory* testing.
@@ -339,39 +712,383 @@ testers are expected to do more *exploratory* testing.
1. Initial launch
1. Download the jar file and copy into an empty folder
+ 2. Double-click the jar file
+ Expected: Shows the GUI with a set of sample students. The window size may not be optimum.
- 1. Double-click the jar file Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum.
-
-1. Saving window preferences
+2. Saving window preferences
1. Resize the window to an optimum size. Move the window to a different location. Close the window.
+ 2. Re-launch the app by double-clicking the jar file.
+ Expected: The most recent window size and location is retained.
+
+3. Typing `exit` into the command box
+ 1. Type `exit` in the command box.
+ Expected: The application window closes
+
+### Adding a task for a student
+
+1. Adding a task for a student in the student list when the student is being checked
+
+ 1. Prerequisites: Check a student using `check` command.
+
+ 2. Test case: `addtask x t/Complete Math Exercise` (where x is the index of the student being checked)
+ Expected: The task is added to the task list of the student. Student's name and details of the added task shown in the status message.
+ The displayed task list of the checked student is updated with the added task.
+
+2. Adding a task for a student in the student list when the student is not being checked
+
+ 1. Prerequisites: Student with index x is not being checked.
+
+ 2. Test case: `addtask x t/Complete Math Exercise` (where x is the index of student not being checked)
+ Expected: The task in added to the task list of the student. Student's name and details of the added task shown in the status message.
+ The displayed task list is not updated since the student is not being checked.
+
+### Deleting a task for a student
- 1. Re-launch the app by double-clicking the jar file.
- Expected: The most recent window size and location is retained.
+1. Deleting a task for a student in the student list when the student is being checked
-1. _{ more test cases … }_
+ 1. Prerequisites: Check a student with index x using `check` command. Checked student have one or more tasks.
+
+ 2. Test case: `deletetask x 1`
+ Expected: The first task is deleted from the task list of the checked student. Student's name and details of the deleted task shown in the status message.
+ The deleted task is removed from the displayed task list of the checked student.
+
+ 3. Test case: `deletetask x 0`
+ Expected: No task is deleted from the task list of the checked student. Error details shown in the status message.
+
+ 4. Test case: Other incorrect delete commands to try:`deletetask`, `deletetask x`, `deletetask x y`, `...` (where y is larger than the checked student's task list size)
+ Expected: Similar to previous.
-### Deleting a person
+### Marking a task of a student
-1. Deleting a person while all persons are being shown
+1. Marking a task of a student as late, in progress or complete
- 1. Prerequisites: List all persons using the `list` command. Multiple persons in the list.
+ 1. Prerequisites: Check a student with index x using `check` command. Checked student have one or more tasks.
+
+ 2. Test case: `markinprogress x 1`
+ Expected: The first task in the task list of the checked student is marked as in progress. Student's name and details of the marked task shown in the status message.
+ The symbol representing the status of the task selected to be marked is yellow.
- 1. Test case: `delete 1`
- Expected: First contact is deleted from the list. Details of the deleted contact shown in the status message. Timestamp in the status bar is updated.
+ 3. Test case: `marklate x 1`
+ Expected: The first task in the task list of the checked student is marked as late. Student's name and details of the marked task shown in the status message.
+ The symbol representing the status of the task selected to be marked is red.
- 1. Test case: `delete 0`
- Expected: No person is deleted. Error details shown in the status message. Status bar remains the same.
+ 4. Test case: `markcomplete x 1`
+ Expected: The first task in the task list of the checked student is marked as complete. Student's name and details of the marked task shown in the status message.
+ The symbol representing the status of the task selected to be marked is green.
- 1. Other incorrect delete commands to try: `delete`, `delete x`, `...` (where x is larger than the list size)
+ 5. Test case: `markinprogress x 0`, `marklate x 0` or `markcomplete x 0`
+ Expected: No task in the task list of the checked student is marked. Error details shown in the status message.
+
+ 6. Test case: Other incorrect delete commands to try:`markinprogress`, `marklate x`, `markcomplete x y`, `...` (where y is larger than the checked student's task list size)
+ Expected: Similar to previous.
+
+### Adding a score for a student
+
+1. Adding a score which is one of the recent five scores for a student in the student list when the student is being checked
+
+ 1. Prerequisites: Check a student using `check` command.
+
+ 2. Test case: `addscore x l/Math Miderm Paper v/96 d/2022-12-07` (where x is the index of the student being checked)
+ Expected: The score is added to the score list of the student and is being placed by date.
+ If it is a recent score, it will be shown at the front, otherwise, in the middle based on the date.
+ Score chart and score table statistic summary will be updated accordingly.
+ Student's name and details of the added score shown in the status message.
+ The displayed score list of the checked student is updated with the added score.
+
+2. Adding a score which is not one of the recent five scores for a student in the student list when the student is being checked
+
+ 1. Prerequisites: Check a student using `check` command.
+
+ 2. Test case: `addscore x l/Math Miderm Paper v/96 d/2022-12-07` (where x is the index of the student being checked)
+ Expected: The score is added to the score list of the student and is being placed by date.
+ Since it is not a recent five scores, it will be shown in the middle or at back based on the date.
+ Score chart and score table statistic summary will remain the same.
+ Student's name and details of the added score shown in the status message.
+ The displayed score list of the checked student is updated with the added score.
+
+3. Adding a score for a student in the student list when the student is not being checked.
+
+ 1. Prerequisites: Student with index x is not being checked.
+
+ 2. Test case: `addscore x l/Math Miderm Paper v/96 d/2022-12-07` (where x is the index of student not being checked)
+ Expected: The score in added to the score list of the student and is being placed by date.
+ Student's name and details of the added score shown in the status message.
+ The displayed score list is not updated since the student is not being checked.
+
+### Deleting a score for a student
+
+1. Deleting a score which is one of the recent five scores for a student in the student list when the student is being checked
+
+ 1. Prerequisites: Check a student with index x using `check` command. Checked student have one or more scores.
+
+ 2. Test case: `deletescore x 1` (where x is the index of the student being checked)
+ Expected: The first score is deleted from the score list of the checked student.
+ Since it is one of the five recent scores, score chart and score table statistic summary will be updated accordingly.
+ Student's name and details of the deleted score shown in the status message.
+ The deleted score is removed from the displayed score list of the checked student.
+
+ 3. Test case: `deletescore x 0`
+ Expected: No score is deleted from the score list of the checked student. Error details shown in the status message.
+
+ 4. Test case: Other incorrect delete commands to try: `deletescore`, `deletescore x`, `deletescore x y`, `...` (where y is larger than the checked student's score list size)
Expected: Similar to previous.
-1. _{ more test cases … }_
+2. Deleting a score which is not one of the recent five scores for a student in the student list when the student is being checked
+
+ 1. Prerequisites: Check a student with index x using `check` command. Checked student have one or more scores.
+
+ 2. Test case: `deletescore x 6` (where x is the index of the student being checked)
+ Expected: The sixth score is deleted from the score list of the checked student.
+ Since it is not one of the five recent scores, score chart and score table statistic summary will remain the same.
+ Student's name and details of the deleted score shown in the status message.
+ The deleted score is removed from the displayed score list of the checked student.
+
+### Checking a student
+
+1. Checking a student for his/her task list and score list
+
+ 1. Test case: `check x 1`
+ Expected: The first student in the student list will be checked. Student's task list and score list will then be displayed on the right side of the windows.
+
+ 2. Test case: `check x 0`
+ Expected: No student will be checked, the task list and score list will not be updated. Error details shown in the status message.
+
+ 3. Test case: `check x -10`
+ Expected: No student will be checked, the task list and score list will not be updated. Error details shown in the status message.
+
+ 4. Test case: Other incorrect check commands to try: `check`, `check x`, `check x y`, `...` (where y is larger than the checked student's task list size)
+ Expected: Similar to previous.
+
+2. Check a student for his/her task list and score list after filter
+
+ 1. Prerequisites: Filter the student list with one or more keywords k using `filter` command.
+ Expected: Similar to checking a student for his/her task list and score list, just that the check command is now working on the filtered student list produced by the filter command.
+
+3. Check a student for his/her task list and score list after find
+
+ 1. Prerequisites: Check a student with index x using `check` command. Checked student have one or more tasks.
+ Expected: Similar to checking a student for his/her task list and score list, just that the check command is now working on the filtered student list prodcued by the find command.
+
+### Filtering student list
+
+1. Filtering out a group of students based on the keyword/s given
+
+ 1. Test case: `filter x primary`
+ Expected: All students that has `primary` tag will be filtered out.
+
+ 2. Test case: `filter x primary secondary`
+ Expected: All students that has `primary` or `secondary` or both tag will be filtered out.
+
+ 3. Test case: Other incorrect check commands to try: `filter`, `filter x`, `...`
+ Expected: The student list will not be filtered. Error details shown in the status message.
+
+### Switching between score text panel and score chart panel
+
+1. Switching between score text panel and score chart panel using CLI
+
+ 1. Test case: `switch`
+ Expected: The panel will be switched.
+
+2. Switching between score text panel and score chart panel through mouse click
+
+ 1. Test case: Click on either the "text" or "chart" tab under score list
+ Expected: The panel will be switched.
+
+### Exporting out student(s) data
+
+1. Exporting student data using CLI
+ 1. Test case (Windows): `export p/data`
+ Expected: `data` directory stored in the home folder containing the `data.json` file.
+
+ 2. Test case (Mac): `export p/data`
+ Expected: `data` directory stored in the home folder containing the `data.json` file.
+
+2. Exporting student data using GUI
+
+ 1. Test case: Click on the `File` Menu on the top left, followed by `Export`. Subsequently select a folder directory to export on then after click the export button.
+ Expected: `data.json` should appear in the folder you have selected.
+
+### Importing in student(s) data
+
+1. Importing student data using CLI
+ 1. Test case (Windows): `import p/data\data.json`
+ Expected: New dataset should be reflected in the application under Student List. Tasklist and Scorelist panels should be resetted as well.
+
+ 2. Test case (Mac): `import p/data/data.json`
+ Expected: New dataset should be reflected in the application under Student List. Tasklist and Scorelist panels should be resetted as well.
+
+2. Importing student data using GUI
+
+ 1. Test case: Click on the `File` Menu on the top left, followed by `Import`. Subsequently select the `data.json` from the file explorer **OR** drag and drop the file used to import in then after click the import button.
+ Expected: New dataset should be reflected in the application under Student List. Tasklist and Scorelist panels should be resetted as well.
+
+### Exporting a student's progress
+
+1. Exporting a student's progress using CLI
+
+ 1. Test case: `exportp x` (where x is the index of the student being checked)
+ Expected: The student's progress will be exported into a PDF file in the default directory:
+ `/data`.
+
+ 2. Test case: `exportp x p/` (where x is the index of the student being checked)
+ Expected: The student's progress will be exported into a PDF file in the ``.
+
+ 3. Test case: Other incorrect check commands to try: `exportp`, `...`
+ Expected: The student's progress will not be exported. Error details shown in the status message of export progress window.
+
+2. Exporting a student's progress through mouse click
+
+ 1. Test case: Click on the "Export Student's Progress" button in the student card. An export progress window will pop up. Click the "Click to select folder" button to select a folder to export the PDF file. Click the "Save" button to save the file.
+ Expected: The student's progress will be exported in the selected folder using the default file name `'s Progress Report.pdf`.
+
+ 2. Test case: Click on the "Export Student's Progress" button in the student card. An export progress window will pop up. Click the "Click to select folder" button to select a folder to export the PDF file. Change the file name. Click the "Save" button to save the file.
+ Expected: The student's progress will be exported in the selected folder using the specified file name.
+
+ 3. Test case: Click on the "Export Student's Progress" button in the student card. An export progress window will pop up. Click the "Click to select folder" button to select a folder to export the PDF file. Change to an invalid file name. Click the "Save" button to save the file.
+ Expected: The student's progress will not be exported. Error details shown in the status message of export progress window.
### Saving data
+The data will be automatically saved by MATHUTORING.
+
+--------------------------------------------------------------------------------------------------------------------
+
+## **Appendix: Effort**
+
+### Difficulty level
+
+This is the first time our team has worked on a Brownfield project where the AB3 codebase was the largest we have encountered in our careers. At the beginning, everyone was overwhelmed by the sudden increase in the number of classes compared to our previous projects, including our most recent project (referred to as "IP"). It took us quite a while to slowly digest the purpose of each class. Additionally, for most of us, this is one of the first projects where we have had to collaborate with team members using version control tools such as Github or SourceTree.
+
+### Challenges faced
+
+We faced several challenges when we were working on this application, the following are some examples and not limited to:
+
+* As we lack experience in working with the pre-existing codebase, and AB3 is a relatively large project that we did not encounter before,
+we had to take some time to learn from how AB3 works, such as how the user input is handled by AB3.
+* Since we are relatively new to JavaFx, we also had to spend quite some time looking at the JavaFx library and learning how to use some
+specific structures when designing the GUI.
+* We also had to spend time searching CSS format in order to tweak the GUI to meet our requirements.
+* Thinking how to design the structure of our application based on existing AB3 structure to make it follow Object-oriented programming.
+* Making use of a new library to export PDF files.
+
+### Effort required
+
+As a result, the development of this application required a significant amount of effort due to the challenges mentioned above where we have to:
+
+* Debugging and looking up on both Java and JavaFX documentations.
+* Researching on the usage of the new library (PDFBox).
+* Reading on guides such as tweaking the GUI to fulfil our requirements and an overall better user experience (Drag-and-Drop/CSS).
+
+### Achievements
+
+We were able to produce a complete application that fulfills our user requirements as well as overcame the challenges we have faced. With MATHUTORING, private Math tuition teachers can:
+
+* Manage student contact details and track their performance with task and score records.
+* View score charts and statistics to easily evaluate student progress.
+* Generate PDF reports containing a student's tasks and scores for easy sharing.
+* Export and import data to easily transfer to a new device.
+
+--------------------------------------------------------------------------------------------------------------------
+
+## **Appendix: Planned Enhancements**
+
+### Solutions proposed for known feature flaws
+
+Listed are some feature flaws that were found in the current implementation of MATHUTORING. To improve the features of
+future implementations, some solutions have been provided.
+
+#### 1. Students with the same name are not allowed
+
+The reason why we do not allow any student to have the same name is to avoid potential confusion. Although the user will
+still be able to distinguish them by checking their personal information (e.g. their email, their address etc.), it would
+be more straight forward to see a difference in name, i.e. Jason 1 and Jason 2.
+Nevertheless, we should consider to allow student to have the same name.
+
+**Solution proposed:**
+
+* Instead of not allowing students to have the same name, we do not allow any students to have both the same name and the
+ same email.
+
+
+#### 2. Numeric student names are allowed
+
+Currently, student names can be numeric. i.e. student name can be "1". After some research we found out that most countries in the world do not allow for purely numeric name.
+
+**Solution proposed:**
+
+* Alter the behavior to only accept alphanumeric or alphabetic names
+* Change the name regex to `[\\p{Alnum}]*[a-zA-Z][\\p{Alnum} ]*`
+
+
+#### 3. Window does not reopen after it is minimized
+
+After help/export/import window is minimized, clicking help/export/import on the top menu will not automatically show
+the window again.
+
+**Solution proposed:**
+
+* Similarly to how ```getRoot().isShowing()``` is used to check if the window is shown, there is also a function called
+```getRoot().isIconified()``` to check whether the window is minimised (returns ```true``` if minimised).
+We can then return ```getRoot().setIconified(false)``` to restore the window to its previous state.
+
+
+#### 4. Exams are not allowed to be on the same date
+
+The aim of the score list is to keep track of a student's recent performance trend (especially the chart).
+If the majority of the exams are conducted on the same date, it defeats our purpose of having the score records.
+However, it is a reasonable assumption that more than one exam is conducted on the same date.
+
+**Solution proposed:**
+
+* Allow at most five exams on the same date.
+
+
+#### 5. Score table being cut after resizing the application window
+
+This problem might happen after user resizes the application window, which can result in the horizontal scroll bar covering
+part of the displayed score table.
+**Suggestion to be able to view the entire table:** Try to enlarge the window size, or click on the table and scroll up and down.
+
+**Solution proposed:**
+
+* Although the entire table should be able to be seen by the user after following the above suggestion, it may still hinder the user.
+* The main reason the table is cut is due to the usage of JavaFX TableView. By default, it comes with a scrollbar and is relatively hard to customize.
+* To resolve the feature flaw, JavaFX label will be used to construct the table with CSS instead of using the JavaFX TableView.
+
+
+#### 6. Edit the tags will overwrite the whole list of tags
+
+* Currently, if the user wants to add tags to students in the list, they can only use `edit` command and will need to input all existing tags along with the new tag.
+* Also, if the user wants to edit a specific tag, they will also be required to input all existing tags along with the one they want to edit.
+* There are currently no delete operations for tags.
+* Without these operations, it is very hard to manage tags since the only way to do it is by using the `edit` command which is deteriorates the user experience.
+
+**Solution proposed:**
+
+* To solve this issue, at least two more commands can be created which are `addtag` and `deletetag`.
+* With these new commands, the user will no longer be required to input the existing tags when adding a new tag.
+
+#### 7. Inconsistency in score color in Score Chart and PDF file
+
+There is an inconsistency for score color between score chart and PDF:
+* Score chart:
+ * Green color is used to indicate score values that are greater than or equal to 80.
+ * Yellow color is used to indicate score values that are greater than or equal to 50.
+ * Red color is used to indicate score values that are smaller than 50.
+* PDF:
+ * Green color is used to indicate score values of 100.
+ * Yellow color is used to indicate score values that are smaller than 80.
+ * Red color is used to indicate score values that are smaller than 50.
+
+**Solution proposed:**
+
+* To solve this issue, the criteria for the score color in the PDF file can be changed to follow the criteria of score color in the score chart.
+
+
+#### 8. Change the tags of the sample data
-1. Dealing with missing/corrupted data files
+Currently, the sample data contains tags, like `colleagues` and `friends`, that do not really fit the purpose of our application.
- 1. _{explain how to simulate a missing/corrupted file, and the expected behavior}_
+**Solution proposed:**
-1. _{ more test cases … }_
+* To solve this issue, the tag can be changed to grade (primary, secondary, etc.) and gender.
diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock
index 72767f00fd9..6c05accd779 100644
--- a/docs/Gemfile.lock
+++ b/docs/Gemfile.lock
@@ -242,6 +242,7 @@ GEM
tzinfo (1.2.10)
thread_safe (~> 0.1)
unicode-display_width (1.8.0)
+ wdm (0.1.1)
zeitwerk (2.6.1)
PLATFORMS
@@ -251,6 +252,7 @@ PLATFORMS
DEPENDENCIES
github-pages
jekyll
+ wdm (~> 0.1.0)
BUNDLED WITH
2.1.4
diff --git a/docs/SettingUp.md b/docs/SettingUp.md
index 275445bd551..840e705af15 100644
--- a/docs/SettingUp.md
+++ b/docs/SettingUp.md
@@ -3,15 +3,15 @@ layout: page
title: Setting up and getting started
---
-* Table of Contents
-{:toc}
-
+## Table of Contents
+* **[Setting up the project in your computer](#setting-up-the-project-in-your-computer)**
+* **[Before writing code](#before-writing-code)**
--------------------------------------------------------------------------------------------------------------------
## Setting up the project in your computer
-
:exclamation: **Caution:**
+**:exclamation: Caution:**
Follow the steps in the following guide precisely. Things will not work out if you deviate in some steps.
@@ -34,22 +34,15 @@ If you plan to use Intellij IDEA (highly recommended):
If using IDEA, follow the guide [_[se-edu/guides] IDEA: Configuring the code style_](https://se-education.org/guides/tutorials/intellijCodeStyle.html) to set up IDEA's coding style to match ours.
-
:bulb: **Tip:**
+ **:bulb: Tips:**
Optionally, you can follow the guide [_[se-edu/guides] Using Checkstyle_](https://se-education.org/guides/tutorials/checkstyle.html) to find how to use the CheckStyle within IDEA e.g., to report problems _as_ you write code.
-1. **Set up CI**
+2. **Set up CI**
This project comes with a GitHub Actions config files (in `.github/workflows` folder). When GitHub detects those files, it will run the CI for your project automatically at each push to the `master` branch or to any PR. No set up required.
-1. **Learn the design**
-
- When you are ready to start coding, we recommend that you get some sense of the overall design by reading about [AddressBook’s architecture](DeveloperGuide.md#architecture).
-
-1. **Do the tutorials**
- These tutorials will help you get acquainted with the codebase.
+3. **Learn the design**
- * [Tracing code](tutorials/TracingCode.md)
- * [Adding a new command](tutorials/AddRemark.md)
- * [Removing fields](tutorials/RemovingFields.md)
+ When you are ready to start coding, we recommend that you get some sense of the overall design by reading about [MATHUTORING’s architecture](DeveloperGuide.md#architecture).
diff --git a/docs/Testing.md b/docs/Testing.md
index 8a99e82438a..f907ea0c444 100644
--- a/docs/Testing.md
+++ b/docs/Testing.md
@@ -3,8 +3,9 @@ layout: page
title: Testing guide
---
-* Table of Contents
-{:toc}
+## Table of Contents
+* **[Running tests](#running-tests)**
+* **[Types of tests](#types-of-tests)**
--------------------------------------------------------------------------------------------------------------------
@@ -19,7 +20,7 @@ There are two ways to run tests.
* **Method 2: Using Gradle**
* Open a console and run the command `gradlew clean test` (Mac/Linux: `./gradlew clean test`)
-
:link: **Link**: Read [this Gradle Tutorial from the se-edu/guides](https://se-education.org/guides/tutorials/gradle.html) to learn more about using Gradle.
+**:link: Link:** Read [_[se-edu/guides] Gradle Tutorial_](https://se-education.org/guides/tutorials/gradle.html) to learn more about using Gradle.
--------------------------------------------------------------------------------------------------------------------
@@ -32,5 +33,5 @@ This project has three types of tests:
e.g. `seedu.address.commons.StringUtilTest`
1. *Integration tests* that are checking the integration of multiple code units (those code units are assumed to be working).
e.g. `seedu.address.storage.StorageManagerTest`
-1. Hybrids of unit and integration tests. These test are checking multiple code units as well as how the are connected together.
+1. Hybrids of unit and integration tests. These test are checking multiple code units as well as how they are connected together.
e.g. `seedu.address.logic.LogicManagerTest`
diff --git a/docs/UserGuide.md b/docs/UserGuide.md
index e7df68b01ea..9b70893a8e5 100644
--- a/docs/UserGuide.md
+++ b/docs/UserGuide.md
@@ -3,42 +3,242 @@ layout: page
title: User Guide
---
-AddressBook Level 3 (AB3) is a **desktop app for managing contacts, optimized for use via a Command Line Interface** (CLI) while still having the benefits of a Graphical User Interface (GUI). If you can type fast, AB3 can get your contact management tasks done faster than traditional GUI apps.
+## Table of Contents
+* **[Overview](#overview)**
+* **[Quick start](#quick-start)**
+* **[User interface layout](#user-interface-layout)**
+* **[User input restrictions](#user-input-restrictions)**
+ * **[Student contact](#student-contact)**
+ * [Name](#name)
+ * [Phone/Contact Number](#phonecontact-number)
+ * [Email](#email)
+ * [Address](#address)
+ * [Tags](#tags)
+ * **[Task](#task)**
+ * [Name/Title](#nametitle)
+ * **[Score](#score)**
+ * [Label](#label)
+ * [Value](#value)
+ * [Date](#date)
+ * **[General fields](#general-fields)**
+ * [Index](#index)
+ * [File path for export](#file-path-for-export)
+ * [File path for import](#file-path-for-import)
+* **[Features](#features)**
+ * **[Managing student contact](#managing-student-contact)**
+ * [Adding a student: `add`](#1-adding-a-student-add)
+ * [Deleting a student: `delete`](#2-deleting-a-student-delete)
+ * [Editing a student: `edit`](#3-editing-a-student-edit)
+ * [Checking a student: `check`](#4-checking-a-student-check)
+ * [Finding a student: `find`](#5-finding-a-student-find)
+ * [Filtering students: `filter`](#6-filtering-students-filter)
+ * [Listing all students: `list`](#7-listing-all-students-list)
+ * **[Managing task list of a student](#managing-task-list-of-a-student)**
+ * [Adding a task for a student: `addtask`](#1-adding-a-task-for-a-student-addtask)
+ * [Deleting a task of a student: `deletetask`](#2-deleting-a-task-of-a-student-deletetask)
+ * [Marking a task of a student: `markcomplete`, `markinprogress`, `marklate`](#3-marking-a-task-of-a-student-markcomplete-markinprogress-marklate)
+ * **[Managing score list of a student](#managing-score-list-of-a-student)**
+ * [Adding a score for a student: `addscore`](#1-adding-a-score-for-a-student-addscore)
+ * [Deleting a score of a student: `deletescore`](#2-deleting-a-score-of-a-student-deletescore)
+ * [Switching between score tabs: `switch`](#3-switching-between-score-tabs-switch)
+ * **[Managing the data of the students](#managing-the-data-of-the-students)**
+ * [Exporting the data of the students: `export`](#1-exporting-the-data-of-the-students-export)
+ * [Importing the data of the students: `import`](#2-importing-the-data-of-the-students-import)
+ * [Exporting the progress of a student: `exportp`](#3-exporting-the-progress-of-a-student-exportp)
+ * **[General commands](#general-commands)**
+ * [Viewing help: `help`](#1-viewing-help-help)
+ * [Clearing all entries: `clear`](#2-clearing-all-entries-clear)
+ * [Exiting the program: `exit`](#3-exiting-the-program-exit)
+ * **[Saving the data](#saving-the-data)**
+ * **[Editing the data file](#editing-the-data-file)**
+* **[GUI display restrictions](#gui-display-restrictions)**
+* **[FAQ](#faq)**
+* **[Command summary](#command-summary)**
-* Table of Contents
-{:toc}
+--------------------------------------------------------------------------------------------------------------------
+
+
+
+## Overview
+
+Finding it hard to manage your student contact details and keep track of their progress?
+
+Finding it a hassle to keep a list of student tasks and scores?
+
+**MATHUTORING** helps you solve these problems!
+
+**MATHUTORING**, a centralised desktop application targeted to **private Math tuition teachers**, helps you manage student contact details and performance by keeping student contact details, tasks, and scores.
+You can also see score charts and statistics!
+
+What's more, you can generate a PDF report of your student containing the student's tasks and scores.
+If you need to switch to a new device, you can also export and import your previous data into the application!
+
+**MATHUTORING** is optimised for use via a Command Line Interface (CLI) while still having the benefits of a Graphical User Interface (GUI). If you can type fast, **MATHUTORING** can get your contact management tasks done faster than traditional GUI apps.
+
+[Back to top](#table-of-contents)
--------------------------------------------------------------------------------------------------------------------
+
+
## Quick start
-1. Ensure you have Java `11` or above installed in your Computer.
+1. Ensure you have `Java 11` or above installed in your Computer.
-1. Download the latest `addressbook.jar` from [here](https://github.com/se-edu/addressbook-level3/releases).
+2. Download the latest `mathutoring.jar`.
-1. Copy the file to the folder you want to use as the _home folder_ for your AddressBook.
+3. Copy the file to the folder you want to use as the _home folder_ for your MATHUTORING app.
-1. Open a command terminal, `cd` into the folder you put the jar file in, and use the `java -jar addressbook.jar` command to run the application.
- A GUI similar to the below should appear in a few seconds. Note how the app contains some sample data.
- ![Ui](images/Ui.png)
+4. Open a command terminal, `cd` into the folder you put the jar file in, and use the `java -jar mathutoring.jar` command to run the application.
+
+ The initial GUI below should appear in a few seconds. Note how the app contains some sample data for your reference, you are free to delete the sample data after you get familiar with the MATHUTORING.
-1. Type the command in the command box and press Enter to execute it. e.g. typing **`help`** and pressing Enter will open the help window.
+ ![InitialUi](images/UG-images/InitialUi.png)
+
+5. Type the command in the command box and press `Enter` to execute it. e.g. typing **`help`** and pressing `Enter` will open the help window.
Some example commands you can try:
- * `list` : Lists all contacts.
+ * `list` : Lists all students in the student list.
- * `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.
+ * `add n/John Doe p/98765432 e/johnd@example.com a/311, Clementi Ave 2, #02-25 c/87849999 t/female t/primary` : Adds a student named `John Doe` to the student list in MATHUTORING with two tags attached.
- * `delete 3` : Deletes the 3rd contact shown in the current list.
+ * `delete 3` : Deletes the 3rd student (if the student exists) shown in the current list.
- * `clear` : Deletes all contacts.
+ * `clear` : Deletes all students.
* `exit` : Exits the app.
-1. Refer to the [Features](#features) below for details of each command.
+6. Recommended minimum screen size: 740x700.
+
+7. Refer to the [Features](#features) below for details of each command.
+
+[Back to top](#table-of-contents)
+
+--------------------------------------------------------------------------------------------------------------------
+
+
+
+## User interface layout
+
+There are two options to display a student's scores (__score list__ and __score chart__) by clicking the *Text* and *Chart* buttons (referred as Switch Display Button below) respectively.
+
+[Back to top](#table-of-contents)
+
+### Layout with score list
+
+![Layout with Score List](images/UG-images/Layout.png)
+
+[Back to top](#table-of-contents)
+
+
+
+### Layout with score chart
+
+![Layout with Score Chart](images/UG-images/Layout2.png)
+
+[Back to top](#table-of-contents)
+
+--------------------------------------------------------------------------------------------------------------------
+
+
+
+## User input restrictions
+To achieve the best performance, we have set **restrictions** for the user command input.
+Here is the **summary** for input restrictions. Users are advised to skim through all the restrictions to avoid command input mistakes.
+
+[Back to top](#table-of-contents)
+
+### Student contact
+
+#### Name
+A **name** can only contain **alphanumeric** characters and **spaces** (Blanks are not allowed).
+
+[Back to top](#table-of-contents)
+
+#### Phone/Contact number
+* A **phone/contact number** must have at least **3 digits** and no more than **15 digits**.
+
+[Back to top](#table-of-contents)
+
+#### Email
+An **email** should be of the format `local-part@domain` and adheres to the following constraints:
+1. The **local-part** should only contain **alphanumeric characters** and **these special characters**, excluding the parentheses, (+_.-).
+ The **local-part** may not start or end with any special characters.
+2. This is followed by a **'@'** and then a domain name. The domain name is made up of domain labels separated by periods.
+ The **domain name** must:
+ - end with a domain label at least **2 characters** long
+ - have each domain label start and end with **alphanumeric** characters
+ - have each domain label consist of alphanumeric characters, separated only by **hyphens**, if any.
+
+[Back to top](#table-of-contents)
+
+#### Address
+No restrictions.
+
+[Back to top](#table-of-contents)
+
+#### Tags
+A **tag** can only contain **alphanumeric** characters and should not contain more than **20 characters**.
+
+[Back to top](#table-of-contents)
+
+--------------------------------------------------------------------------------------------------------------------
+
+### Task
+
+#### Name/Title
+A **name** can only contain **alphanumeric** characters and **spaces** (Blanks are not allowed).
+
+[Back to top](#table-of-contents)
+
+--------------------------------------------------------------------------------------------------------------------
+
+### Score
+
+#### Label
+A **label** can only contain **alphanumeric** characters and **spaces** (Blanks are not allowed).
+
+[Back to top](#table-of-contents)
+
+#### Value
+A **value** can be any integer or a number with one decimal place from **0 to 100 (inclusive)**.
+
+[Back to top](#table-of-contents)
+
+#### Date
+The format of a **date** must be in **`yyyy-MM-DD`**, and must not be in the future.
+
+[Back to top](#table-of-contents)
+
+--------------------------------------------------------------------------------------------------------------------
+
+### General fields
+
+#### Index
+An index must be a **positive integer** 1, 2, 3, ... (one-based index).
+Note if the index given is too large and exceed the integer limit, the command will be deemed as invalid.
+
+[Back to top](#table-of-contents)
+
+#### File path for export
+* File path separator (e.g. backward/forward slash) should **follow user's operating system**.
+ * e.g. Windows uses backward slash and Mac uses forward slash
+* Export path should be a **directory**, not a file!
+* The directory cannot be a write-protected folder!
+
+[Back to top](#table-of-contents)
+
+#### File path for import
+* File path separator (e.g. backward/forward slash) should **follow user's operating system**.
+ * e.g. Windows uses backward slash and Mac uses forward slash
+* Import path should be a **file**, not a directory!
+
+[Back to top](#table-of-contents)
--------------------------------------------------------------------------------------------------------------------
+
+
## Features
@@ -49,145 +249,630 @@ AddressBook Level 3 (AB3) is a **desktop app for managing contacts, optimized fo
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`.
+ e.g. `n/NAME [t/TAG]` can be used as `n/John Doe t/primary` 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 ` ` (i.e. 0 times), `t/friend`, `t/friend t/family` etc.
+ e.g. `[t/TAG]…` can be used as ` ` (i.e. 0 times), `t/secondary`, `t/secondary4 t/secondary` 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.
-* If a parameter is expected only once in the command but you specified it multiple times, only the last occurrence of the parameter will be taken.
- e.g. if you specify `p/12341234 p/56785678`, only `p/56785678` will be taken.
+* If a parameter is expected only once in the command, but the user specified it multiple times, only the last occurrence of the parameter will be taken.
+ e.g. if the user specify `p/12341234 p/56785678`, only `p/56785678` will be taken.
-* Extraneous parameters for commands that do not take in parameters (such as `help`, `list`, `exit` and `clear`) will be ignored.
+* __Extraneous parameters for commands that do not take in parameters (including `help`, `list`, `exit`, `clear`, and `switch`) will be ignored. __
e.g. if the command specifies `help 123`, it will be interpreted as `help`.
+* Command words are case-insensitive.
+ e.g. if the command specifies such as `help`, `HELP`, `Help`..., it will be interpreted as `help`.
+
-### Viewing help : `help`
+[Back to top](#table-of-contents)
-Shows a message explaning how to access the help page.
+
-![help message](images/helpMessage.png)
+### Managing student contact
-Format: `help`
+### 1. Adding a student: `add`
+Adds a student to the student list.
-### Adding a person: `add`
+Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS c/CONTACT_PARENT [t/TAG]…`
-Adds a person to the address book.
+
-Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…`
+**:bulb: Tips:**
+* A student is allowed to have alphanumeric or numeric names, however, special characters (e.g. `à`, `_`) are not allowed.
+* If a student does not have a phone number, their parent's contact number can be used as the student's phone number.
+* A student can have any number of tags (including 0)
+
-
:bulb: **Tip:**
-A person can have any number of tags (including 0)
+
+
+**:exclamation: Caution:**
+* In order to avoid potential confusion, we do not allow any student to have the same name regardless of the letter cases (e.g. Harry, HARRY, harry are deemed as same name).
+* If the user wishes to add in a student who has the same name as an existing student in the student list, consider adding a number after the student's name to distinguish them. e.g. Emily and Emily 2 are acceptable names.
+
+
+**:information_source: Note about the avatars (profile pictures):**
+* The avatar will change if the student has a "gender" tag.
+* The "gender" tag include male and female (case-insensitive). If a student does not have any gender tag or have both gender tags (male and female), the avatar will stay as default.
+
+
+
+
Examples:
-* `add n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01`
-* `add n/Betsy Crowe t/friend e/betsycrowe@example.com a/Newgate Prison p/1234567 t/criminal`
+* `add n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01 c/948372948`
+* `add n/Betsy Crowe t/primary c/83927482 e/betsycrowe@example.com a/Downtown p/1234567 t/primary3`
+ ![Add a student](images/UG-images/Add%20a%20student.png)
-### Listing all persons : `list`
+[Back to top](#table-of-contents)
-Shows a list of all persons in the address book.
+
-Format: `list`
+### 2. Deleting a student: `delete`
+
+Deletes the specified student from the student list.
+
+Format: `delete INDEX`
+
+* Deletes the student at the specified `INDEX`.
+* The index refers to the index number shown in the displayed student list.
+* The index **must be a positive integer** 1, 2, 3, …
+
+Examples:
+* `list` followed by `delete 2` deletes the 2nd student (if the student exists) in the student list.
+ Before `delete 2` is executed, the student `Bernice Yu` is shown in the Student List.
+ ![Delete a student (before)](images/UG-images/Delete%20a%20student%20(before).png)
+ After `delete 2` is executed, the student `Bernice Yu` is deleted from the Student List.
+ ![Delete a student (after)](images/UG-images/Delete%20a%20student%20(after).png)
+* `find Betsy` followed by `delete 1` deletes the 1st student (if the student exists) in the results of the `find` command.
+* `filter female` followed by `delete 3` deletes the 3rd student (if the student exists) in the results of the `filter` command.
-### Editing a person : `edit`
+[Back to top](#table-of-contents)
-Edits an existing person in the address book.
+
-Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…`
+### 3. Editing a student: `edit`
-* 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, …
+Edits an existing student in the student list.
+
+Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [c/CONTACT_PARENT] [t/TAG]…`
+
+* Edits the student at the specified `INDEX`. The index refers to the index number shown in the displayed student 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.
+* When editing tags, all the existing tags of the student will be removed. i.e. adding of tags is not cumulative.
+* The user can remove all the student’s tags by typing `t/` without specifying any tags after it.
+* We allow the user to edit the `PHONE_NUMBER` and `CONTACT_PARENT` to be the same.
+* The user is not allowed to change the name of a student to be the same as any existing student in the list.
+
+Examples:
+* `edit 1 p/91234567 e/johndoe@example.com` Edits the phone number and email address of the 1st student to be `91234567` and `johndoe@example.com` respectively.
+* `edit 2 n/Betsy Crower t/` Edits the name of the 2nd student to be `Betsy Crower` and clears all existing tags.
+ Before command execution:
+ ![Edit a student (before)](images/UG-images/Edit%20a%20student%20(before).png)
+ After command execution:
+ ![Edit a student (after)](images/UG-images/Edit%20a%20student%20(after).png)
+
+[Back to top](#table-of-contents)
+
+
+
+### 4. Checking a student: `check`
+
+Displays the information of the student being checked, including their task list and score list.
+
+Format: `check INDEX`
+
+* Checks the student at the specified `INDEX` and displays the task list and score list of the student.
+* The index refers to the index number shown in the displayed student list.
+* The index **must be a positive integer** 1, 2, 3, …
+
+
+
+**:information_source: Note:**
+After the user checked a student, the respective student's task list and score list will continue to be displayed, unless:
+ * The respective student (the latest checked student) is being deleted.
+ * The respective student (the latest checked student) is being edited.
+ * The student list being cleared.
+ * New student data (.json file) is being imported.
+
+**Any of the above actions will alter the status to no student being checked now.**
+
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.
+* `check 2` checks the 2nd student in the student list, the respective task list and score list will show on the right side of the application window.
-### Locating persons by name: `find`
+
+
+ Before command execution:
+ ![Check a student (before)](images/UG-images/Check%20a%20student%20(before).png)
+ After command execution:
+ * Student has no tasks and scores:
+ ![Check a student with no tasks and scores](images/UG-images/Check%20a%20student%20Empty%20Lists.png)
+ * Student has tasks and scores:
+ ![Check a student with tasks and scores](images/UG-images/Check%20a%20student.png)
-Finds persons whose names contain any of the given keywords.
+[Back to top](#table-of-contents)
+
+
+
+### 5. Finding a student: `find`
+
+Finds students whose names contain any of the given keywords.
Format: `find KEYWORD [MORE_KEYWORDS]`
-* The search is case-insensitive. e.g `hans` will match `Hans`
+* 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).
+* Students matching at least one keyword will be returned (i.e. `OR` search).
e.g. `Hans Bo` will return `Hans Gruber`, `Bo Yang`
Examples:
* `find John` returns `john` and `John Doe`
* `find alex david` returns `Alex Yeoh`, `David Li`
- ![result for 'find alex david'](images/findAlexDavidResult.png)
+ ![Find a student](images/UG-images/Find%20a%20student.png)
-### Deleting a person : `delete`
+[Back to top](#table-of-contents)
-Deletes the specified person from the address book.
+
-Format: `delete INDEX`
+### 6. Filtering students: `filter`
+
+Filters students whose tags match with any of the given keywords.
+
+Format: `filter KEYWORD [MORE_KEYWORDS]`
+
+* The filter is case-insensitive. e.g. `primary` will match `Primary`
+* The order of the keywords does not matter. e.g. `primary primary4` and `primary4 primary` have the same filter results.
+* Only the tag is searched.
+* Only full words will be matched e.g. `primary` will not match `primary4`
+* Students whose tag matches at least one keyword will be returned (i.e. `OR` search).
+ e.g. `primary primary4` will return students with tags of either `primary`, `primary4`, or both.
+
+Examples:
+* `filter primary` returns tag `primary`, `Primary`.
+* `filter secondary secondary2` returns students with tags of either `secondary`, `secondary2`, or both.
+ ![Filter students](images/UG-images/Filter%20students.png)
+
+[Back to top](#table-of-contents)
+
+
+
+### 7. Listing all students: `list`
+
+Lists all the students in the student list.
+
+Format: `list`
+
+![List all students](images/UG-images/List%20all%20students.png)
+
+[Back to top](#table-of-contents)
+
+--------------------------------------------------------------------------------------------------------------------
+
+
+
+### Managing task list of a student
+
+### 1. Adding a task for a student: `addtask`
+
+Adds a task to a specific student.
+
+Format: `addtask INDEX t/TASK_TITLE`
+
+* Adds the given task to the student at the specified `INDEX`.
+* The index refers to the index number shown in the displayed student list.
+* The index **must be a positive integer** 1, 2, 3, …
+* A task can have alphanumeric or numeric names, however, special characters (e.g. `à`, `_`) are not allowed.
+* A task with the same name as a task already in the task list cannot be added to the task list.
+* The name of a task is case-insensitive. e.g. `do homework` is the same as `Do Homework`
+
+Examples:
+
+* `list` followed by `addtask 2 t/finish Math Paper 1` adds the task `finish Math Paper 1` to the 2nd student of the
+ student list.
+ The following result assumes that the user has [checked](#4-checking-a-student-check) the student before.
+ ![Add a task](images/UG-images/Add%20a%20task.png)
+* `check 2` followed by `addtask 1 t/Complete A Math Exercise` adds the task `Complete A Math Exercise` to the student
+ being checked.
+
+[Back to top](#table-of-contents)
+
+
+
+### 2. Deleting a task of a student: `deletetask`
-* Deletes the person at the specified `INDEX`.
-* The index refers to the index number shown in the displayed person list.
+Deletes the specified task from the task list of a student.
+
+Format: `deletetask INDEX_OF_STUDENT INDEX_OF_TASK`
+
+* Deletes the task at the specified `INDEX_OF_TASK` of a specified student (`INDEX_OF_STUDENT`).
+* The first index refers to the index number shown in the displayed student list and
+ the second index refers to the index of the task in the displayed task list of the student.
+* Both indexes **must be positive integers** 1, 2, 3, …
+
+Examples:
+
+* `list` followed by `deletetask 2 3` deletes the third task of the 2nd student in the student list.
+ The following result assumes that the user has [checked](#4-checking-a-student-check) the student before.
+ Before command execution:
+ ![Delete a task (before)](images/UG-images/Delete%20a%20task%20(before).png)
+ After command execution:
+ ![Delete a task (after)](images/UG-images/Delete%20a%20task%20(after).png)
+* `find Betsy` followed by `deletetask 1 6` deletes the sixth task of the 1st student in the results of the `find` command.
+
+[Back to top](#table-of-contents)
+
+
+
+### 3. Marking a task of a student: `markcomplete`, `markinprogress`, `marklate`
+
+Marks the specified task from the task list of a student as complete, in progress, or late.
+
+Format:
+ Mark task as complete: `markcomplete INDEX_OF_STUDENT INDEX_OF_TASK`
+ Mark task as in progress: `markinprogress INDEX_OF_STUDENT INDEX_OF_TASK`
+ Mark task as late: `marklate INDEX_OF_STUDENT INDEX_OF_TASK`
+
+* Marks the task at the specified `INDEX_OF_TASK` of a specified student (`INDEX_OF_STUDENT`) as complete, in progress or late, depending on the command called.
+* The first index refers to the index number shown in the displayed student list and
+ the second index refers to the index of the task in the displayed task list of the student.
+* Both indexes **must be positive integers** 1, 2, 3, …
+* The default status for a newly added task is in progress.
+
+Examples:
+
+* `list` followed by `markcomplete 2 3` marks the third task of the 2nd student in the student list as complete.
+
+
+
+ The following result assumes that the user has [checked](#4-checking-a-student-check) the student before.
+ Before command execution:
+ ![Mark a task (before)](images/UG-images/Mark%20a%20task%20(before).png)
+ After command execution:
+ ![Mark a task complete (after)](images/UG-images/Mark%20a%20task%20complete%20(after)png)
+* `list` followed by `marklate 2 3` marks the third task of the 2nd student in the student list as late.
+ The following result assumes that the user has [checked](#4-checking-a-student-check) the student before.
+ Before command execution:
+ ![Mark a task late (before)](images/UG-images/Mark%20a%20task%20late%20(before).png)
+ After command execution:
+ ![Mark a task late (after)](images/UG-images/Mark%20a%20task%20late%20(after).png)
+* `find Betsy` followed by `marklate 1 6` marks the sixth task of the 1st student in the results of the
+ `find` command as late.
+
+[Back to top](#table-of-contents)
+
+--------------------------------------------------------------------------------------------------------------------
+
+
+
+### Managing score list of a student
+
+### 1. Adding a score for a student: `addscore`
+
+Adds a score to a specific student.
+
+Format: `addscore INDEX l/LABEL v/VALUE_OF_SCORE d/DATE`
+
+* Adds the given score to the student at the specified `INDEX`.
+* The index refers to the index number shown in the displayed student list.
* The index **must be a positive integer** 1, 2, 3, …
+* A score can have alphanumeric or numeric labels, however, special characters (e.g. `à`, `_`) are not allowed.
+* The given `VALUE_OF_SCORE` can be **any number from 0 to 100**.
+* The given `VALUE_OF_SCORE` can either be **an integer or a number with one decimal place**
+* Exams on different dates can have the same `Label`.
+* No two exams can have the same `Date`.
+
+
+
+**:information_source: Note about format of `DATE`:**
+* The format of `DATE` must be `yyyy-MM-DD`. For example, `2022-02-20` represents 20 February 2022.
+* If the `DATE` entered by the user is in the future, will not be allowed.
+
+
+
+
+
+**:exclamation: Caution:**
+* In order to avoid potential confusion, we only allow one score per day and the full marks of each score is 100.
+* If there are two exams in one day, e.g. `Midterm Math Paper 1` and `Midterm Math Paper 2`, the user can either combine these two exams to one score with label `Midterm Math` or add them in separate dates.
+* If the full score of a test is not 100, please convert the score value to a percentage value before entering.
+
Examples:
-* `list` followed by `delete 2` deletes the 2nd person in the address book.
-* `find Betsy` followed by `delete 1` deletes the 1st person in the results of the `find` command.
-### Clearing all entries : `clear`
+* `list` followed by `addscore 2 l/Midterm Math Paper v/99.5 d/2023-03-02` adds a `Midterm Math Paper` score with a
+ value of `99.5` and dated `2022-03-02` to the 2nd student in the student list.
+ The following result assumes that the user has [checked](#4-checking-a-student-check) the student before.
+ * Current display for score tab is using score list.
+ ![Add a score Score List](images/UG-images/Add%20a%20score%20Score%20List.png)
+ * Current display for score tab is using score chart.
+ ![Add a score Score Chart](images/UG-images/Add%20a%20score%20Score%20Chart.png)
+* `check 5` followed by `addscore 5 l/CA2 A Math v/50 d/2021-09-09` adds a `CA2 A Math` score with a
+ value of `50` and dated `2021-09-09` to the student being checked.
-Clears all entries from the address book.
+[Back to top](#table-of-contents)
+
+
+
+### 2. Deleting a score of a student: `deletescore`
+
+Deletes the specified score from the score list of a student.
+
+Format: `deletescore INDEX_OF_STUDENT INDEX_OF_SCORE `
+
+* Deletes the score at the specified `INDEX_OF_SCORE` of a specified student (`INDEX_OF_STUDENT`).
+* The first index refers to the index number shown in the displayed student list and
+ the second index refers to the index of the score in the displayed score list of the student.
+* Both indexes **must be positive integers** 1, 2, 3, …
+
+Examples:
+
+* `list` followed by `deletescore 2 1` deletes first score of the 2nd student in the student list.
+ The following result assumes that the user has [checked](#4-checking-a-student-check) the student before.
+ * Current display for score tab is using score list.
+ Before command execution:
+ ![Delete a score Score List (before)](images/UG-images/Delete%20a%20score%20Score%20List%20(before).png)
+ After command execution:
+ ![Delete a score Score List (after)](images/UG-images/Delete%20a%20score%20Score%20List%20(after).png)
+* `find Betsy` followed by `deletescore 1 2` deletes second score of the 1st student in the results of the `find` command.
+
+[Back to top](#table-of-contents)
+
+
+
+### 3. Switching between score tabs: `switch`
+
+Switches between the score list and score chart tabs.
+
+Format: CLI and GUI supported.
+
+**CLI** `switch`
+
+**GUI** Click the **Text** or **Chart** button to switch between score list and score chart tabs respectively.
+![Switch between Score Tabs](images/UG-images/Switch%20between%20Score%20Tabs.png)
+
+[Back to top](#table-of-contents)
+
+--------------------------------------------------------------------------------------------------------------------
+
+
+
+### Managing the data of the students
+
+### 1. Exporting the data of the students: `export`
+
+Exports all the student's data out. Users can export with or without specifying the path. `export p/` is also allowed.
+The default exported position for CLI will be in the _data folder_ under the _home folder_ (the folder that contains the "mathutoring.jar" file).
+The exported file name is `data.json`.
+
+Format: CLI and GUI supported.
+
+**CLI** `export [p/FILE_PATH]`
+
+**GUI**
+Click the "File" on the top menu, then choose "Export" under the drop-down list.
+
+An export window will pop up, the user is required to specify which folder to store the exported file.
+
+![Export the data of the students.png](images/UG-images/Export%20the%20data%20of%20the%20students.png)
+
+Examples:
+* `export` will export the file as a JSON file `[JAR file location]/data/data.json`
+* For Windows users
+ * `export` p/C:\bin export `data.json` to bin folder under C disk.
+* For Mac users
+ * `export` p//Users/username/Desktop export `data.json` to desktop.
+
+
+
+**:information_source: Note about the format of `FILE_PATH`:**
+* For Windows users, the format of `FILE_PATH` must be `p/C:\Users\username\Downloads`.
+* For Mac users, the format of `FILE_PATH` must be `p//Users/username/Desktop`.
+
+
+
+[Back to top](#table-of-contents)
+
+
+
+### 2. Importing the data of the students: `import`
+
+Imports student data into the application. Users can import the file by dragging the file in or choosing the file path.
+The imported file must be in `.json` format.
+
+Format: CLI and GUI supported.
+
+**CLI** `import p/FILE_PATH`
+
+**GUI** Click the "File" on the top menu, then choose "Import" under the drop-down list. An import window will pop up, the user can choose to either drag the file in or choose a specific file path.
+
+![Import the data of the students.png](images/UG-images/Import%20the%20data%20of%20the%20students.png)
+
+Examples:
+* For Windows users
+ * `import` p/C:\bin\data.json.
+* For Mac users
+ * `import` p//Users/username/Desktop/data.json.
+
+
+
+**:information_source: Note about the format of `FILE_PATH`:**
+* For Windows users, the format of `FILE_PATH` must be `p/C:\bin\FILE_NAME`.
+* For Mac users, the format of `FILE_PATH` must be `p//Users/username/Desktop/FILE_NAME`.
+
+
+
+[Back to top](#table-of-contents)
+
+
+
+### 3. Exporting the progress of a student: `exportp`
+
+Exports the specified student progress into a PDF file. Users can export without specifying the path. `exportp p/` is not allowed. The default
+exported position for CLI will be under the _home folder_.
+
+Format: CLI and GUI supported.
+
+**CLI** `exportp INDEX [p/FILE_PATH]`
+
+**GUI** Click the "Export Progress Report" button of a student in the student list. An export progress window will pop
+up, the user is required to specify which folder to store the exported file.
+
+* Exports the score list and task list of the specified student `INDEX` in the form of a PDF file.
+* The `INDEX` **must be a positive integer** 1, 2, 3, ...
+* If `FILE_PATH` is not specified, file will be exported as a JSON file in `/data`
+* `FILE_PATH` should be a valid path to a **directory**.
+* The exported file name is `'s Progress Report.pdf`, e.g. `Alex Yeoh's Progress Report.pdf`
+* Sample PDF output file: [Alex Yeoh's Progress Report.pdf](pdfs/Alex Yeoh's Progress Report.pdf)
+
+![Exporting the progress of a student](images/UG-images/Exporting%20the%20progress%20of%20a%20student.png)
+
+Examples:
+
+* `exportp 1` will export the file as a PDF file `/data/'s Progress Report.pdf`
+* For Windows users
+ * `exportp 1 p/C:\Users\John Doe\Downloads`
+* For Mac users
+ * `exportp 1 p//Users/johndoe/Documents`
+
+
+
+**:information_source: Note about the format of `FILE_PATH`:**
+* For Windows users, the format of `FILE_PATH` must be `p/C:\Users\username\Downloads`.
+* For Mac users, the format of `FILE_PATH` must be `p//Users/username/Desktop`.
+
+
+
+[Back to top](#table-of-contents)
+
+--------------------------------------------------------------------------------------------------------------------
+
+
+
+### General commands
+
+### 1. Viewing help: `help`
+
+Prompts the help page link together with a brief user guide that explain what commands are provided in MATHUTORING.
+For more detailed information such as how to use the syntax, please refer to the help page.
+
+Format: CLI and GUI supported.
+
+**CLI** `help`
+
+**GUI**
+Click the "Help" on the top menu, then choose "Help" again under the drop-down list.
+
+![Viewing help](images/UG-images/Viewing%20help.png)
+
+[Back to top](#table-of-contents)
+
+
+
+### 2. Clearing all entries: `clear`
+
+Clears all entries from the MATHUTORING.
Format: `clear`
-### Exiting the program : `exit`
+![Clear all entries](images/UG-images/Clear%20all%20entries.png)
+
+[Back to top](#table-of-contents)
-Exits the program.
+
+
+### 3. Exiting the program: `exit`
+
+Exits from MATHUTORING.
Format: `exit`
+[Back to top](#table-of-contents)
+
+--------------------------------------------------------------------------------------------------------------------
+
+
+
### Saving the data
-AddressBook data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually.
+* MATHUTORING data is saved in the hard disk automatically after any command. There is no need to save manually.
+* If the user would like to save the MATHUTORING data in a different location, they can use the [`export`](#1-exporting-the-data-of-the-students-export)
+command.
+
+[Back to top](#table-of-contents)
### Editing the data file
-AddressBook data are saved as a JSON file `[JAR file location]/data/addressbook.json`. Advanced users are welcome to update data directly by editing that data file.
+MATHUTORING data are saved as a JSON file `/data/mathutoring.json`. Advanced users are welcome to update data directly by editing that data file.
-
:exclamation: **Caution:**
-If your changes to the data file makes its format invalid, AddressBook will discard all data and start with an empty data file at the next run.
+
:exclamation: Caution:
+If the changes to the data file make its format invalid, MATHUTORING will discard all data and start with an empty data file at the next run.
-### Archiving data files `[coming in v2.0]`
+[Back to top](#table-of-contents)
+
+--------------------------------------------------------------------------------------------------------------------
+
+
+
+## GUI display restrictions
+We have set up a few GUI display restrictions in order to avoid the GUI being affect by extreme inputs (e.g. super long name) or small screen size, and thus hinder the user experience.
+* If the student name is more than 29 letters, the full student name will be displayed in the student list. However, the
+task list will only display the first 29 letters, with the remaining letters shown as ellipsis.
+
+* If the score label is more than 11 letters, the full score label will be displayed in the score list, text panel.
+However, the chart tooltip will only display the first 11 letters, with the remaining letters shown as ellipsis.
-_Details coming soon ..._
+* When you try to open external windows (Import, Export, Export Progress, and Help Windows), you might notice that it doesn't open if it is minimized. The proposed solution is in the developer guide.
--------------------------------------------------------------------------------------------------------------------
+
+
## 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 AddressBook home folder.
+**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 MATHUTORING home folder.
+
+**Q**: I can't find the `mathutoring.json` file and the files exported for `export` and `exportp`. What should I do?
+**A**: Open the application using `java -jar mathutoring.jar` instead of opening the application by clicking the JAR file. More details can be found in the [Quick start](#quick-start) section.
+
+[Back to top](#table-of-contents)
--------------------------------------------------------------------------------------------------------------------
+
+
## Command summary
-Action | Format, Examples
---------|------------------
-**Add** | `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…` e.g., `add n/James Ho p/22224444 e/jamesho@example.com a/123, Clementi Rd, 1234665 t/friend t/colleague`
-**Clear** | `clear`
-**Delete** | `delete INDEX` e.g., `delete 3`
-**Edit** | `edit INDEX [n/NAME] [p/PHONE_NUMBER] [e/EMAIL] [a/ADDRESS] [t/TAG]…` e.g.,`edit 2 n/James Lee e/jameslee@example.com`
-**Find** | `find KEYWORD [MORE_KEYWORDS]` e.g., `find James Jake`
-**List** | `list`
-**Help** | `help`
+| Action | Format, Examples |
+|----------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| [**Add a Student**](#1-adding-a-student-add) | `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS c/PARENT_PHONE [t/TAG]…` e.g., `add n/James Ho p/22224444 e/jamesho@example.com a/123, Clementi Rd, 1234665 c/11112222 t/friend t/primary` |
+| [**Delete a Student**](#2-deleting-a-student-delete) | `delete INDEX` e.g., `delete 1` |
+| [**Edit a Student**](#3-editing-a-student-edit) | `edit INDEX [n/NAME] [p/PHONE_NUMBER] [e/EMAIL] [a/ADDRESS] [c/PARENT_PHONE] [t/TAG]…` e.g.,`edit 2 n/James Lee e/jameslee@example.com c/12348888` |
+| [**Check a Student**](#4-checking-a-student-check) | `check INDEX` e.g. `check 1` |
+| [**Find a Student**](#5-finding-a-student-find) | `find KEYWORD [MORE_KEYWORDS]` e.g., `find James Jake` |
+| [**Filter Students**](#6-filtering-students-filter) | `filter KEYWORD [MORE_KEYWORDS]` e.g., `filter primary secondary` |
+| [**List Students**](#7-listing-all-students-list) | `list` |
+| [**Add a Task**](#1-adding-a-task-for-a-student-addtask) | `addtask INDEX t/TITLE` e.g `addtask 2 t/Homework Assignment 1` |
+| [**Delete a Task**](#2-deleting-a-task-of-a-student-deletetask) | `deletetask INDEX_OF_STUDENT INDEX_OF_TASK` e.g `deletetask 2 1` |
+| [**Mark a Task**](#3-marking-a-task-of-a-student-markcomplete-markinprogress-marklate) | `mark(STATUS) INDEX_OF_STUDENT INDEX_OF_TASK` (`mark(STATUS)` can be either `markcomplete`, `marklate` or `markinprogress`) e.g. `markComplete 1 2` |
+| [**Add a Score**](#1-adding-a-score-for-a-student-addscore) | `addscore INDEX_OF_STUDENT l/LABEL v/VALUE_OF_SCORE d/DATE` e.g `addscore 1 l/CA2 A Math v/70 d/2022-03-03` |
+| [**Delete a Score**](#2-deleting-a-score-of-a-student-deletescore) | `deletescore INDEX_OF_STUDENT INDEX_OF_SCORE` e.g. `deletescore 3 4` |
+| [**Switch Score Tab**](#3-switching-between-score-tabs-switch) | `switch` |
+| [**Export Data**](#1-exporting-the-data-of-the-students-export) | `export [p/FILE_PATH]` e.g. `export p//Users/John` |
+| [**Import Data**](#2-importing-the-data-of-the-students-import) | `import p/FILE_PATH` e.g. `import p//Users/John/data.json` |
+| [**Export a Student's Progress**](#3-exporting-the-progress-of-a-student-exportp) | `exportp INDEX [p/FILE_PATH]` e.g. `export 1 p//Users/John` |
+| [**Help**](#1-viewing-help-help) | `help` |
+| [**Clear**](#2-clearing-all-entries-clear) | `clear` |
+| [**Exit**](#3-exiting-the-program-exit) | `exit` |
+
+[Back to top](#table-of-contents)
diff --git a/docs/_config.yml b/docs/_config.yml
index 6bd245d8f4e..c7791d0deac 100644
--- a/docs/_config.yml
+++ b/docs/_config.yml
@@ -1,4 +1,4 @@
-title: "AB-3"
+title: "MATHUTORING"
theme: minima
header_pages:
@@ -8,7 +8,7 @@ header_pages:
markdown: kramdown
-repository: "se-edu/addressbook-level3"
+repository: "AY2223S2-CS2103-W17-1/tp"
github_icon: "images/github-icon.png"
plugins:
diff --git a/docs/_sass/minima/_base.scss b/docs/_sass/minima/_base.scss
index 0d3f6e80ced..82bec051a48 100644
--- a/docs/_sass/minima/_base.scss
+++ b/docs/_sass/minima/_base.scss
@@ -103,7 +103,7 @@ li {
* Headings
*/
h1, h2, h3, h4, h5, h6 {
- font-weight: $base-font-weight;
+ font-weight: $bold-font-weight;
}
@@ -253,7 +253,7 @@ table {
}
}
th, td {
- padding: ($spacing-unit / 3) ($spacing-unit / 2);
+ padding: ($spacing-unit / 4) ($spacing-unit / 3);
}
th {
background-color: $table-header-bg-color;
@@ -288,7 +288,7 @@ table {
text-align: center;
}
.site-header:before {
- content: "AB-3";
+ content: "MATHUTORING";
font-size: 32px;
}
}
diff --git a/docs/_sass/minima/custom-styles.scss b/docs/_sass/minima/custom-styles.scss
index 56b5d56b430..c280c819ee9 100644
--- a/docs/_sass/minima/custom-styles.scss
+++ b/docs/_sass/minima/custom-styles.scss
@@ -1,7 +1,7 @@
// Placeholder to allow defining custom styles that override everything else.
// (Use `_sass/minima/custom-variables.scss` to override variable defaults)
h2, h3, h4, h5, h6 {
- color: #e46c0a;
+ color: #605BF1;
}
// Bootstrap style alerts
diff --git a/docs/_sass/minima/initialize.scss b/docs/_sass/minima/initialize.scss
index 30288811151..778a53d1c51 100644
--- a/docs/_sass/minima/initialize.scss
+++ b/docs/_sass/minima/initialize.scss
@@ -6,6 +6,7 @@ $base-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Segoe UI Symb
$code-font-family: "Menlo", "Inconsolata", "Consolas", "Roboto Mono", "Ubuntu Mono", "Liberation Mono", "Courier New", monospace;
$base-font-size: 16px !default;
$base-font-weight: 400 !default;
+$bold-font-weight: $base-font-weight * 1.75 !default;
$small-font-size: $base-font-size * 0.875 !default;
$base-line-height: 1.5 !default;
@@ -14,7 +15,7 @@ $spacing-unit: 30px !default;
$table-text-align: left !default;
// Width of the content area
-$content-width: 800px !default;
+$content-width: 2000px !default;
$on-palm: 600px !default;
$on-laptop: 800px !default;
diff --git a/docs/diagrams/ArchitectureSequenceDiagram.puml b/docs/diagrams/ArchitectureSequenceDiagram.puml
index ef81d18c337..d2bcb2e8458 100644
--- a/docs/diagrams/ArchitectureSequenceDiagram.puml
+++ b/docs/diagrams/ArchitectureSequenceDiagram.puml
@@ -13,13 +13,13 @@ activate ui UI_COLOR
ui -[UI_COLOR]> logic : execute("delete 1")
activate logic LOGIC_COLOR
-logic -[LOGIC_COLOR]> model : deletePerson(p)
+logic -[LOGIC_COLOR]> model : deleteStudent(s)
activate model MODEL_COLOR
model -[MODEL_COLOR]-> logic
deactivate model
-logic -[LOGIC_COLOR]> storage : saveAddressBook(addressBook)
+logic -[LOGIC_COLOR]> storage : saveMathutoring(mathutoring)
activate storage STORAGE_COLOR
storage -[STORAGE_COLOR]> storage : Save to file
diff --git a/docs/diagrams/BetterModelClassDiagram.puml b/docs/diagrams/BetterModelClassDiagram.puml
index 598474a5c82..06c46a94d85 100644
--- a/docs/diagrams/BetterModelClassDiagram.puml
+++ b/docs/diagrams/BetterModelClassDiagram.puml
@@ -4,18 +4,21 @@ skinparam arrowThickness 1.1
skinparam arrowColor MODEL_COLOR
skinparam classBackgroundColor MODEL_COLOR
-AddressBook *-right-> "1" UniquePersonList
-AddressBook *-right-> "1" UniqueTagList
-UniqueTagList -[hidden]down- UniquePersonList
-UniqueTagList -[hidden]down- UniquePersonList
+Mathutoring *-right-> "1" UniqueStudentList
+Mathutoring *-right-> "1" UniqueTagList
+UniqueTagList -[hidden]down- UniqueStudentList
+UniqueTagList -[hidden]down- UniqueStudentList
UniqueTagList -right-> "*" Tag
-UniquePersonList -right-> Person
+UniqueStudentList -right-> Student
-Person -up-> "*" Tag
+Student -up-> "*" Tag
+
+Student *--> Name
+Student *--> Phone
+Student *--> Email
+Student *--> Address
+Student *--> UniqueScoreList
+Student *--> UniqueTaskList
-Person *--> Name
-Person *--> Phone
-Person *--> Email
-Person *--> Address
@enduml
diff --git a/docs/diagrams/BetterUiClassDiagram.puml b/docs/diagrams/BetterUiClassDiagram.puml
new file mode 100644
index 00000000000..3bf7ec5c1f7
--- /dev/null
+++ b/docs/diagrams/BetterUiClassDiagram.puml
@@ -0,0 +1,47 @@
+@startuml
+!include style.puml
+skinparam arrowThickness 1.1
+skinparam arrowColor UI_COLOR_T4
+skinparam classBackgroundColor UI_COLOR
+
+package UI <> {
+Class MainWindow
+Class HelpWindow
+Class ExportProgressWindow
+Class ExportWindow
+Class ImportWindow
+Class ResultDisplay
+Class StudentListPanel
+Class StudentCard
+Class TaskListPanel
+Class TaskCard
+Class ScoreListPanel
+Class ScoreCard
+Class StatusBarFooter
+Class CommandBox
+}
+
+package Model <> {
+Class HiddenModel #FFFFFF
+}
+
+MainWindow *-up-> "1" CommandBox
+MainWindow *-up-> "1" ResultDisplay
+MainWindow *-down-> "1" StudentListPanel
+MainWindow *-down-> "1" TaskListPanel
+MainWindow *-down-> "1" ScoreListPanel
+MainWindow *-up-> "1" StatusBarFooter
+MainWindow --> "0..1" HelpWindow
+MainWindow -up-> "0..1" ExportProgressWindow
+MainWindow -up-> "0..1" ExportWindow
+MainWindow --> "0..1" ImportWindow
+
+StudentListPanel -down-> "*" StudentCard
+TaskListPanel -down-> "*" TaskCard
+ScoreListPanel -down-> "*" ScoreCard
+
+StudentCard ..> Model
+TaskCard ..> Model
+ScoreCard ..> Model
+
+@enduml
diff --git a/docs/diagrams/DeleteSequenceDiagram.puml b/docs/diagrams/DeleteSequenceDiagram.puml
index 1dc2311b245..153ee123675 100644
--- a/docs/diagrams/DeleteSequenceDiagram.puml
+++ b/docs/diagrams/DeleteSequenceDiagram.puml
@@ -3,7 +3,7 @@
box Logic LOGIC_COLOR_T1
participant ":LogicManager" as LogicManager LOGIC_COLOR
-participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR
+participant ":MathutoringParser" as MathutoringParser LOGIC_COLOR
participant ":DeleteCommandParser" as DeleteCommandParser LOGIC_COLOR
participant "d:DeleteCommand" as DeleteCommand LOGIC_COLOR
participant ":CommandResult" as CommandResult LOGIC_COLOR
@@ -16,17 +16,17 @@ end box
[-> LogicManager : execute("delete 1")
activate LogicManager
-LogicManager -> AddressBookParser : parseCommand("delete 1")
-activate AddressBookParser
+LogicManager -> MathutoringParser : parseCommand("delete 1")
+activate MathutoringParser
create DeleteCommandParser
-AddressBookParser -> DeleteCommandParser
+MathutoringParser -> DeleteCommandParser
activate DeleteCommandParser
-DeleteCommandParser --> AddressBookParser
+DeleteCommandParser --> MathutoringParser
deactivate DeleteCommandParser
-AddressBookParser -> DeleteCommandParser : parse("1")
+MathutoringParser -> DeleteCommandParser : parse("1")
activate DeleteCommandParser
create DeleteCommand
@@ -36,19 +36,19 @@ activate DeleteCommand
DeleteCommand --> DeleteCommandParser : d
deactivate DeleteCommand
-DeleteCommandParser --> AddressBookParser : d
+DeleteCommandParser --> MathutoringParser : d
deactivate DeleteCommandParser
'Hidden arrow to position the destroy marker below the end of the activation bar.
-DeleteCommandParser -[hidden]-> AddressBookParser
+DeleteCommandParser -[hidden]-> MathutoringParser
destroy DeleteCommandParser
-AddressBookParser --> LogicManager : d
-deactivate AddressBookParser
+MathutoringParser --> LogicManager : d
+deactivate MathutoringParser
LogicManager -> DeleteCommand : execute()
activate DeleteCommand
-DeleteCommand -> Model : deletePerson(1)
+DeleteCommand -> Model : deleteStudent(1)
activate Model
Model --> DeleteCommand
diff --git a/docs/diagrams/DeleteTaskSequenceDiagram.puml b/docs/diagrams/DeleteTaskSequenceDiagram.puml
new file mode 100644
index 00000000000..3c80e833dac
--- /dev/null
+++ b/docs/diagrams/DeleteTaskSequenceDiagram.puml
@@ -0,0 +1,56 @@
+@startuml
+!include style.puml
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":MathutoringParser" as AddressBookParser LOGIC_COLOR
+participant ":DeleteTaskCommandParser" as DeleteTaskCommandParser LOGIC_COLOR
+participant "d:DeleteTaskCommand" as DeleteTaskCommand LOGIC_COLOR
+end box
+
+[-> LogicManager : execute("deleteTask 1 2")
+activate LogicManager
+
+LogicManager -> AddressBookParser : parseCommand("deleteTask 1 2")
+activate AddressBookParser
+
+create DeleteTaskCommandParser
+AddressBookParser -> DeleteTaskCommandParser
+activate DeleteTaskCommandParser
+
+DeleteTaskCommandParser --> AddressBookParser
+deactivate DeleteTaskCommandParser
+
+AddressBookParser -> DeleteTaskCommandParser : parse("1 2")
+activate DeleteTaskCommandParser
+
+create DeleteTaskCommand
+DeleteTaskCommandParser -> DeleteTaskCommand
+activate DeleteTaskCommand
+
+DeleteTaskCommand --> DeleteTaskCommandParser : d
+deactivate DeleteTaskCommand
+
+DeleteTaskCommandParser --> AddressBookParser : d
+deactivate DeleteTaskCommandParser
+'Hidden arrow to position the destroy marker below the end of the activation bar.
+DeleteTaskCommandParser -[hidden]-> AddressBookParser
+destroy DeleteTaskCommandParser
+
+AddressBookParser --> LogicManager : d
+deactivate AddressBookParser
+
+LogicManager -> DeleteTaskCommand : execute()
+activate DeleteTaskCommand
+
+LogicManager -[hidden]-> DeleteTaskCommand : execute()
+DeleteTaskCommand -[hidden]-> LogicManager : result
+LogicManager -[hidden]-> DeleteTaskCommand : execute()
+DeleteTaskCommand -[hidden]-> LogicManager : result
+
+DeleteTaskCommand --> LogicManager : result
+deactivate DeleteTaskCommand
+
+[<--LogicManager
+deactivate LogicManager
+@enduml
diff --git a/docs/diagrams/LogicClassDiagram.puml b/docs/diagrams/LogicClassDiagram.puml
index d4193173e18..2341d51d3f8 100644
--- a/docs/diagrams/LogicClassDiagram.puml
+++ b/docs/diagrams/LogicClassDiagram.puml
@@ -6,7 +6,7 @@ skinparam classBackgroundColor LOGIC_COLOR
package Logic {
-Class AddressBookParser
+Class MathutoringParser
Class XYZCommand
Class CommandResult
Class "{abstract}\nCommand" as Command
@@ -27,8 +27,8 @@ Class HiddenOutside #FFFFFF
HiddenOutside ..> Logic
LogicManager .right.|> Logic
-LogicManager -right->"1" AddressBookParser
-AddressBookParser ..> XYZCommand : creates >
+LogicManager -right->"1" MathutoringParser
+MathutoringParser ..> XYZCommand : creates >
XYZCommand -up-|> Command
LogicManager .left.> Command : executes >
diff --git a/docs/diagrams/ModelClassDiagram.puml b/docs/diagrams/ModelClassDiagram.puml
index 4439108973a..e88609dcf80 100644
--- a/docs/diagrams/ModelClassDiagram.puml
+++ b/docs/diagrams/ModelClassDiagram.puml
@@ -5,46 +5,52 @@ skinparam arrowColor MODEL_COLOR
skinparam classBackgroundColor MODEL_COLOR
Package Model <>{
-Class "<>\nReadOnlyAddressBook" as ReadOnlyAddressBook
+Class "<>\nReadOnlyMathutoring" as ReadOnlyMathutoring
Class "<>\nReadOnlyUserPrefs" as ReadOnlyUserPrefs
Class "<>\nModel" as Model
-Class AddressBook
+Class Mathutoring
Class ModelManager
Class UserPrefs
-Class UniquePersonList
-Class Person
+Class UniqueStudentList
+Class Student
Class Address
Class Email
Class Name
Class Phone
Class Tag
+Class UniqueScoreList
+Class UniqueTaskList
}
Class HiddenOutside #FFFFFF
HiddenOutside ..> Model
-AddressBook .up.|> ReadOnlyAddressBook
+Mathutoring .up.|> ReadOnlyMathutoring
ModelManager .up.|> Model
Model .right.> ReadOnlyUserPrefs
-Model .left.> ReadOnlyAddressBook
-ModelManager -left-> "1" AddressBook
+Model .left.> ReadOnlyMathutoring
+ModelManager -left-> "1" Mathutoring
ModelManager -right-> "1" UserPrefs
UserPrefs .up.|> ReadOnlyUserPrefs
-AddressBook *--> "1" UniquePersonList
-UniquePersonList --> "~* all" Person
-Person *--> Name
-Person *--> Phone
-Person *--> Email
-Person *--> Address
-Person *--> "*" Tag
+Mathutoring *---> "1" UniqueStudentList
+UniqueStudentList ---> "~* all" Student
-Name -[hidden]right-> Phone
+Student *--> Name
+Student *--> "2" Phone
+Student *--> Email
+Student *--> Address
+Student *--> "*" Tag
+Student *--> "1" UniqueTaskList
+Student *--> "1" UniqueScoreList
+
+Name -[hidden]right-> "2" Phone
Phone -[hidden]right-> Address
Address -[hidden]right-> Email
+Name -[hidden]up-> UniqueStudentList
-ModelManager -->"~* filtered" Person
+ModelManager --> "~* filtered" Student
@enduml
diff --git a/docs/diagrams/ParserClasses.puml b/docs/diagrams/ParserClasses.puml
index 0c7424de6e0..6697cfe3900 100644
--- a/docs/diagrams/ParserClasses.puml
+++ b/docs/diagrams/ParserClasses.puml
@@ -9,7 +9,7 @@ Class XYZCommand
package "Parser classes"{
Class "<>\nParser" as Parser
-Class AddressBookParser
+Class MathutoringParser
Class XYZCommandParser
Class CliSyntax
Class ParserUtil
@@ -19,12 +19,12 @@ Class Prefix
}
Class HiddenOutside #FFFFFF
-HiddenOutside ..> AddressBookParser
+HiddenOutside ..> MathutoringParser
-AddressBookParser .down.> XYZCommandParser: creates >
+MathutoringParser .down.> XYZCommandParser: creates >
XYZCommandParser ..> XYZCommand : creates >
-AddressBookParser ..> Command : returns >
+MathutoringParser ..> Command : returns >
XYZCommandParser .up.|> Parser
XYZCommandParser ..> ArgumentMultimap
XYZCommandParser ..> ArgumentTokenizer
diff --git a/docs/diagrams/ScoreClassDiagram.puml b/docs/diagrams/ScoreClassDiagram.puml
new file mode 100644
index 00000000000..e8787f5128b
--- /dev/null
+++ b/docs/diagrams/ScoreClassDiagram.puml
@@ -0,0 +1,31 @@
+@startuml
+!include style.puml
+skinparam arrowThickness 1.1
+skinparam arrowColor SCORE_MODEL_COLOR
+skinparam classBackgroundColor SCORE_MODEL_COLOR
+
+Class Date
+Class ScoreValue
+Class Label
+Class Student
+Class UniqueScoreList
+Class Score
+Class ScoreSummary
+
+class ScoreSummary {
+ - maxScore: Double
+ - minScore: Double
+ - average: Double
+ - percentage: Double
+}
+
+Student *--> UniqueScoreList
+
+UniqueScoreList --> "~* " Score
+UniqueScoreList +-- "1" ScoreSummary
+
+Score *--> "1" Date
+Score *--> "1" Label
+Score *--> "1" ScoreValue
+
+@enduml
diff --git a/docs/diagrams/StorageClassDiagram.puml b/docs/diagrams/StorageClassDiagram.puml
index 760305e0e58..c971bd9059d 100644
--- a/docs/diagrams/StorageClassDiagram.puml
+++ b/docs/diagrams/StorageClassDiagram.puml
@@ -14,11 +14,11 @@ Class JsonUserPrefsStorage
Class "<>\nStorage" as Storage
Class StorageManager
-package "AddressBook Storage" #F4F6F6{
-Class "<>\nAddressBookStorage" as AddressBookStorage
-Class JsonAddressBookStorage
-Class JsonSerializableAddressBook
-Class JsonAdaptedPerson
+package "Mathutoring Storage" #F4F6F6{
+Class "<>\nMathutoringStorage" as MathutoringStorage
+Class JsonMathutoringStorage
+Class JsonSerializableMathutoring
+Class JsonAdaptedStudent
Class JsonAdaptedTag
}
@@ -29,15 +29,15 @@ HiddenOutside ..> Storage
StorageManager .up.|> Storage
StorageManager -up-> "1" UserPrefsStorage
-StorageManager -up-> "1" AddressBookStorage
+StorageManager -up-> "1" MathutoringStorage
Storage -left-|> UserPrefsStorage
-Storage -right-|> AddressBookStorage
+Storage -right-|> MathutoringStorage
JsonUserPrefsStorage .up.|> UserPrefsStorage
-JsonAddressBookStorage .up.|> AddressBookStorage
-JsonAddressBookStorage ..> JsonSerializableAddressBook
-JsonSerializableAddressBook --> "*" JsonAdaptedPerson
-JsonAdaptedPerson --> "*" JsonAdaptedTag
+JsonMathutoringStorage .up.|> MathutoringStorage
+JsonMathutoringStorage ..> JsonSerializableMathutoring
+JsonSerializableMathutoring --> "*" JsonAdaptedStudent
+JsonAdaptedStudent --> "*" JsonAdaptedTag
@enduml
diff --git a/docs/diagrams/TaskClassDiagram.puml b/docs/diagrams/TaskClassDiagram.puml
new file mode 100644
index 00000000000..5e61c1cc906
--- /dev/null
+++ b/docs/diagrams/TaskClassDiagram.puml
@@ -0,0 +1,27 @@
+@startuml
+!include style.puml
+skinparam arrowThickness 1.1
+skinparam arrowColor TASK_MODEL_COLOR
+skinparam classBackgroundColor TASK_MODEL_COLOR
+
+Class LocalDateTime
+Class Name
+Class Student
+Class UniqueTaskList
+Class Task
+
+Enum "<>\n TaskStatus" as TaskStatus {
+INPROGRESS
+LATE
+COMPLETE
+}
+
+Student *--> UniqueTaskList
+
+UniqueTaskList --> "~* " Task
+
+Task *--> "creationDate" LocalDateTime
+Task *--> Name
+Task *--> "1" TaskStatus
+
+@enduml
diff --git a/docs/diagrams/UiClassDiagram.puml b/docs/diagrams/UiClassDiagram.puml
index 95473d5aa19..4d150d0589e 100644
--- a/docs/diagrams/UiClassDiagram.puml
+++ b/docs/diagrams/UiClassDiagram.puml
@@ -9,16 +9,6 @@ Class "<>\nUi" as Ui
Class "{abstract}\nUiPart" as UiPart
Class UiManager
Class MainWindow
-Class HelpWindow
-Class ResultDisplay
-Class PersonListPanel
-Class PersonCard
-Class StatusBarFooter
-Class CommandBox
-}
-
-package Model <> {
-Class HiddenModel #FFFFFF
}
package Logic <> {
@@ -30,31 +20,10 @@ HiddenOutside ..> Ui
UiManager .left.|> Ui
UiManager -down-> "1" MainWindow
-MainWindow *-down-> "1" CommandBox
-MainWindow *-down-> "1" ResultDisplay
-MainWindow *-down-> "1" PersonListPanel
-MainWindow *-down-> "1" StatusBarFooter
-MainWindow --> "0..1" HelpWindow
-
-PersonListPanel -down-> "*" PersonCard
-MainWindow -left-|> UiPart
+MainWindow -down-|> UiPart
-ResultDisplay --|> UiPart
-CommandBox --|> UiPart
-PersonListPanel --|> UiPart
-PersonCard --|> UiPart
-StatusBarFooter --|> UiPart
-HelpWindow --|> UiPart
-
-PersonCard ..> Model
UiManager -right-> Logic
-MainWindow -left-> Logic
-
-PersonListPanel -[hidden]left- HelpWindow
-HelpWindow -[hidden]left- CommandBox
-CommandBox -[hidden]left- ResultDisplay
-ResultDisplay -[hidden]left- StatusBarFooter
+MainWindow -right-> Logic
-MainWindow -[hidden]-|> UiPart
@enduml
diff --git a/docs/diagrams/style.puml b/docs/diagrams/style.puml
index fad8b0adeaa..a1ff4f05edd 100644
--- a/docs/diagrams/style.puml
+++ b/docs/diagrams/style.puml
@@ -25,6 +25,18 @@
!define MODEL_COLOR_T3 #7B000E
!define MODEL_COLOR_T4 #51000A
+!define TASK_MODEL_COLOR #D60055
+!define TASK_MODEL_COLOR_T1 #FF0568
+!define TASK_MODEL_COLOR_T2 #FF247B
+!define TASK_MODEL_COLOR_T3 #FF75AC
+!define TASK_MODEL_COLOR_T4 #FFBDD7
+
+!define SCORE_MODEL_COLOR #69A92C
+!define SCORE_MODEL_COLOR_T1 #69AB2A
+!define SCORE_MODEL_COLOR_T2 #599C19
+!define SCORE_MODEL_COLOR_T3 #FF75AC
+!define SCORE_MODEL_COLOR_T4 #FFBDD7
+
!define STORAGE_COLOR #A38300
!define STORAGE_COLOR_T1 #FFE374
!define STORAGE_COLOR_T2 #EDC520
@@ -45,6 +57,14 @@ skinparam Class {
FontName Arial
}
+skinparam Enum {
+ FontColor #FFFFFF
+ BorderThickness 1
+ BorderColor #FFFFFF
+ StereotypeFontColor #FFFFFF
+ FontName Arial
+}
+
skinparam Actor {
BorderColor USER_COLOR
Color USER_COLOR
@@ -71,5 +91,5 @@ skinparam DefaultTextAlignment center
skinparam packageStyle Rectangle
hide footbox
-hide members
+hide empty members
hide circle
diff --git a/docs/diagrams/tracing/LogicSequenceDiagram.puml b/docs/diagrams/tracing/LogicSequenceDiagram.puml
index fdcbe1c0ccc..ed19a8dc32d 100644
--- a/docs/diagrams/tracing/LogicSequenceDiagram.puml
+++ b/docs/diagrams/tracing/LogicSequenceDiagram.puml
@@ -2,7 +2,7 @@
!include ../style.puml
Participant ":LogicManager" as logic LOGIC_COLOR
-Participant ":AddressBookParser" as abp LOGIC_COLOR
+Participant ":MathutoringParser" as abp LOGIC_COLOR
Participant ":EditCommandParser" as ecp LOGIC_COLOR
Participant "command:EditCommand" as ec LOGIC_COLOR
@@ -13,7 +13,7 @@ create ecp
abp -> ecp
abp -> ecp ++: parse(arguments)
create ec
-ecp -> ec ++: index, editPersonDescriptor
+ecp -> ec ++: index, editStudentDescriptor
ec --> ecp --
ecp --> abp --: command
abp --> logic --: command
diff --git a/docs/images/ArchitectureSequenceDiagram.png b/docs/images/ArchitectureSequenceDiagram.png
deleted file mode 100644
index 2f1346869d0..00000000000
Binary files a/docs/images/ArchitectureSequenceDiagram.png and /dev/null differ
diff --git a/docs/images/BetterModelClassDiagram.png b/docs/images/BetterModelClassDiagram.png
deleted file mode 100644
index 94440f0ac4a..00000000000
Binary files a/docs/images/BetterModelClassDiagram.png and /dev/null differ
diff --git a/docs/images/ArchitectureDiagram.png b/docs/images/DG-images/ArchitectureDiagram.png
similarity index 100%
rename from docs/images/ArchitectureDiagram.png
rename to docs/images/DG-images/ArchitectureDiagram.png
diff --git a/docs/images/DG-images/ArchitectureSequenceDiagram.png b/docs/images/DG-images/ArchitectureSequenceDiagram.png
new file mode 100644
index 00000000000..ce77be93b6d
Binary files /dev/null and b/docs/images/DG-images/ArchitectureSequenceDiagram.png differ
diff --git a/docs/images/DG-images/BetterModelClassDiagram.png b/docs/images/DG-images/BetterModelClassDiagram.png
new file mode 100644
index 00000000000..9657235bd21
Binary files /dev/null and b/docs/images/DG-images/BetterModelClassDiagram.png differ
diff --git a/docs/images/DG-images/BetterUiClassDiagram.png b/docs/images/DG-images/BetterUiClassDiagram.png
new file mode 100644
index 00000000000..d11c61dd4d0
Binary files /dev/null and b/docs/images/DG-images/BetterUiClassDiagram.png differ
diff --git a/docs/images/CommitActivityDiagram.png b/docs/images/DG-images/CommitActivityDiagram.png
similarity index 100%
rename from docs/images/CommitActivityDiagram.png
rename to docs/images/DG-images/CommitActivityDiagram.png
diff --git a/docs/images/ComponentManagers.png b/docs/images/DG-images/ComponentManagers.png
similarity index 100%
rename from docs/images/ComponentManagers.png
rename to docs/images/DG-images/ComponentManagers.png
diff --git a/docs/images/DG-images/DeleteSequenceDiagram.png b/docs/images/DG-images/DeleteSequenceDiagram.png
new file mode 100644
index 00000000000..0bb497b8e36
Binary files /dev/null and b/docs/images/DG-images/DeleteSequenceDiagram.png differ
diff --git a/docs/images/DG-images/LogicClassDiagram.png b/docs/images/DG-images/LogicClassDiagram.png
new file mode 100644
index 00000000000..8a39f1aaa99
Binary files /dev/null and b/docs/images/DG-images/LogicClassDiagram.png differ
diff --git a/docs/images/LogicStorageDIP.png b/docs/images/DG-images/LogicStorageDIP.png
similarity index 100%
rename from docs/images/LogicStorageDIP.png
rename to docs/images/DG-images/LogicStorageDIP.png
diff --git a/docs/images/DG-images/ModelClassDiagram.png b/docs/images/DG-images/ModelClassDiagram.png
new file mode 100644
index 00000000000..73b581d2920
Binary files /dev/null and b/docs/images/DG-images/ModelClassDiagram.png differ
diff --git a/docs/images/DG-images/ParserClasses.png b/docs/images/DG-images/ParserClasses.png
new file mode 100644
index 00000000000..2f583c3f0c1
Binary files /dev/null and b/docs/images/DG-images/ParserClasses.png differ
diff --git a/docs/images/DG-images/ScoreClassDiagram.png b/docs/images/DG-images/ScoreClassDiagram.png
new file mode 100644
index 00000000000..9a95f640530
Binary files /dev/null and b/docs/images/DG-images/ScoreClassDiagram.png differ
diff --git a/docs/images/DG-images/StorageClassDiagram.png b/docs/images/DG-images/StorageClassDiagram.png
new file mode 100644
index 00000000000..8bb59f18f9e
Binary files /dev/null and b/docs/images/DG-images/StorageClassDiagram.png differ
diff --git a/docs/images/DG-images/TaskClassDiagram.png b/docs/images/DG-images/TaskClassDiagram.png
new file mode 100644
index 00000000000..8296d578ccd
Binary files /dev/null and b/docs/images/DG-images/TaskClassDiagram.png differ
diff --git a/docs/images/DG-images/UiClassDiagram.png b/docs/images/DG-images/UiClassDiagram.png
new file mode 100644
index 00000000000..3126b75ec67
Binary files /dev/null and b/docs/images/DG-images/UiClassDiagram.png differ
diff --git a/docs/images/UndoRedoState0.png b/docs/images/DG-images/UndoRedoState0.png
similarity index 100%
rename from docs/images/UndoRedoState0.png
rename to docs/images/DG-images/UndoRedoState0.png
diff --git a/docs/images/UndoRedoState1.png b/docs/images/DG-images/UndoRedoState1.png
similarity index 100%
rename from docs/images/UndoRedoState1.png
rename to docs/images/DG-images/UndoRedoState1.png
diff --git a/docs/images/UndoRedoState2.png b/docs/images/DG-images/UndoRedoState2.png
similarity index 100%
rename from docs/images/UndoRedoState2.png
rename to docs/images/DG-images/UndoRedoState2.png
diff --git a/docs/images/UndoRedoState3.png b/docs/images/DG-images/UndoRedoState3.png
similarity index 100%
rename from docs/images/UndoRedoState3.png
rename to docs/images/DG-images/UndoRedoState3.png
diff --git a/docs/images/UndoRedoState4.png b/docs/images/DG-images/UndoRedoState4.png
similarity index 100%
rename from docs/images/UndoRedoState4.png
rename to docs/images/DG-images/UndoRedoState4.png
diff --git a/docs/images/UndoRedoState5.png b/docs/images/DG-images/UndoRedoState5.png
similarity index 100%
rename from docs/images/UndoRedoState5.png
rename to docs/images/DG-images/UndoRedoState5.png
diff --git a/docs/images/UndoSequenceDiagram.png b/docs/images/DG-images/UndoSequenceDiagram.png
similarity index 100%
rename from docs/images/UndoSequenceDiagram.png
rename to docs/images/DG-images/UndoSequenceDiagram.png
diff --git a/docs/images/DeleteSequenceDiagram.png b/docs/images/DeleteSequenceDiagram.png
deleted file mode 100644
index fa327b39618..00000000000
Binary files a/docs/images/DeleteSequenceDiagram.png and /dev/null differ
diff --git a/docs/images/LogicClassDiagram.png b/docs/images/LogicClassDiagram.png
deleted file mode 100644
index 9e9ba9f79e5..00000000000
Binary files a/docs/images/LogicClassDiagram.png and /dev/null differ
diff --git a/docs/images/ModelClassDiagram.png b/docs/images/ModelClassDiagram.png
deleted file mode 100644
index 04070af60d8..00000000000
Binary files a/docs/images/ModelClassDiagram.png and /dev/null differ
diff --git a/docs/images/ParserClasses.png b/docs/images/ParserClasses.png
deleted file mode 100644
index e7b4c8880cd..00000000000
Binary files a/docs/images/ParserClasses.png and /dev/null differ
diff --git a/docs/images/StorageClassDiagram.png b/docs/images/StorageClassDiagram.png
deleted file mode 100644
index 2533a5c1af0..00000000000
Binary files a/docs/images/StorageClassDiagram.png and /dev/null differ
diff --git a/docs/images/UG-images/Add a score Score Chart.png b/docs/images/UG-images/Add a score Score Chart.png
new file mode 100644
index 00000000000..c2bd435e9e7
Binary files /dev/null and b/docs/images/UG-images/Add a score Score Chart.png differ
diff --git a/docs/images/UG-images/Add a score Score List.png b/docs/images/UG-images/Add a score Score List.png
new file mode 100644
index 00000000000..1992923b8fe
Binary files /dev/null and b/docs/images/UG-images/Add a score Score List.png differ
diff --git a/docs/images/UG-images/Add a student.png b/docs/images/UG-images/Add a student.png
new file mode 100644
index 00000000000..d8db6a32425
Binary files /dev/null and b/docs/images/UG-images/Add a student.png differ
diff --git a/docs/images/UG-images/Add a task.png b/docs/images/UG-images/Add a task.png
new file mode 100644
index 00000000000..7c482bcddb2
Binary files /dev/null and b/docs/images/UG-images/Add a task.png differ
diff --git a/docs/images/UG-images/Check a student (before).png b/docs/images/UG-images/Check a student (before).png
new file mode 100644
index 00000000000..8f280b18a95
Binary files /dev/null and b/docs/images/UG-images/Check a student (before).png differ
diff --git a/docs/images/UG-images/Check a student Empty Lists.png b/docs/images/UG-images/Check a student Empty Lists.png
new file mode 100644
index 00000000000..16b1f06ce79
Binary files /dev/null and b/docs/images/UG-images/Check a student Empty Lists.png differ
diff --git a/docs/images/UG-images/Check a student.png b/docs/images/UG-images/Check a student.png
new file mode 100644
index 00000000000..c086043e372
Binary files /dev/null and b/docs/images/UG-images/Check a student.png differ
diff --git a/docs/images/UG-images/Clear all entries.png b/docs/images/UG-images/Clear all entries.png
new file mode 100644
index 00000000000..2e0e954022b
Binary files /dev/null and b/docs/images/UG-images/Clear all entries.png differ
diff --git a/docs/images/UG-images/Delete a score Score Chart (after).png b/docs/images/UG-images/Delete a score Score Chart (after).png
new file mode 100644
index 00000000000..e9083d16ff5
Binary files /dev/null and b/docs/images/UG-images/Delete a score Score Chart (after).png differ
diff --git a/docs/images/UG-images/Delete a score Score Chart (before).png b/docs/images/UG-images/Delete a score Score Chart (before).png
new file mode 100644
index 00000000000..531b7543355
Binary files /dev/null and b/docs/images/UG-images/Delete a score Score Chart (before).png differ
diff --git a/docs/images/UG-images/Delete a score Score List (after).png b/docs/images/UG-images/Delete a score Score List (after).png
new file mode 100644
index 00000000000..c0abdef10d9
Binary files /dev/null and b/docs/images/UG-images/Delete a score Score List (after).png differ
diff --git a/docs/images/UG-images/Delete a score Score List (before).png b/docs/images/UG-images/Delete a score Score List (before).png
new file mode 100644
index 00000000000..41e08c61bf6
Binary files /dev/null and b/docs/images/UG-images/Delete a score Score List (before).png differ
diff --git a/docs/images/UG-images/Delete a student (after).png b/docs/images/UG-images/Delete a student (after).png
new file mode 100644
index 00000000000..cd926baa7df
Binary files /dev/null and b/docs/images/UG-images/Delete a student (after).png differ
diff --git a/docs/images/UG-images/Delete a student (before).png b/docs/images/UG-images/Delete a student (before).png
new file mode 100644
index 00000000000..3412fcc63d3
Binary files /dev/null and b/docs/images/UG-images/Delete a student (before).png differ
diff --git a/docs/images/UG-images/Delete a task (after).png b/docs/images/UG-images/Delete a task (after).png
new file mode 100644
index 00000000000..37dd89101e8
Binary files /dev/null and b/docs/images/UG-images/Delete a task (after).png differ
diff --git a/docs/images/UG-images/Delete a task (before).png b/docs/images/UG-images/Delete a task (before).png
new file mode 100644
index 00000000000..2f250d41054
Binary files /dev/null and b/docs/images/UG-images/Delete a task (before).png differ
diff --git a/docs/images/UG-images/Edit a student (after).png b/docs/images/UG-images/Edit a student (after).png
new file mode 100644
index 00000000000..6b9d266e971
Binary files /dev/null and b/docs/images/UG-images/Edit a student (after).png differ
diff --git a/docs/images/UG-images/Edit a student (before).png b/docs/images/UG-images/Edit a student (before).png
new file mode 100644
index 00000000000..97f79500cec
Binary files /dev/null and b/docs/images/UG-images/Edit a student (before).png differ
diff --git a/docs/images/UG-images/Export the data of the students.png b/docs/images/UG-images/Export the data of the students.png
new file mode 100644
index 00000000000..f398c4cc801
Binary files /dev/null and b/docs/images/UG-images/Export the data of the students.png differ
diff --git a/docs/images/UG-images/Exporting the progress of a student.png b/docs/images/UG-images/Exporting the progress of a student.png
new file mode 100644
index 00000000000..7818b217cf7
Binary files /dev/null and b/docs/images/UG-images/Exporting the progress of a student.png differ
diff --git a/docs/images/UG-images/Filter students.png b/docs/images/UG-images/Filter students.png
new file mode 100644
index 00000000000..5e2c81ae9b5
Binary files /dev/null and b/docs/images/UG-images/Filter students.png differ
diff --git a/docs/images/UG-images/Find a student.png b/docs/images/UG-images/Find a student.png
new file mode 100644
index 00000000000..e8686d0cde4
Binary files /dev/null and b/docs/images/UG-images/Find a student.png differ
diff --git a/docs/images/UG-images/Import the data of the students.png b/docs/images/UG-images/Import the data of the students.png
new file mode 100644
index 00000000000..387fdc76933
Binary files /dev/null and b/docs/images/UG-images/Import the data of the students.png differ
diff --git a/docs/images/UG-images/InitialUi.png b/docs/images/UG-images/InitialUi.png
new file mode 100644
index 00000000000..fa11179d255
Binary files /dev/null and b/docs/images/UG-images/InitialUi.png differ
diff --git a/docs/images/UG-images/Layout.png b/docs/images/UG-images/Layout.png
new file mode 100644
index 00000000000..1f3e4e5a70c
Binary files /dev/null and b/docs/images/UG-images/Layout.png differ
diff --git a/docs/images/UG-images/Layout2.png b/docs/images/UG-images/Layout2.png
new file mode 100644
index 00000000000..1f8c58e5472
Binary files /dev/null and b/docs/images/UG-images/Layout2.png differ
diff --git a/docs/images/UG-images/List all students.png b/docs/images/UG-images/List all students.png
new file mode 100644
index 00000000000..e46450a04c6
Binary files /dev/null and b/docs/images/UG-images/List all students.png differ
diff --git a/docs/images/UG-images/Mark a task (before).png b/docs/images/UG-images/Mark a task (before).png
new file mode 100644
index 00000000000..8cf841ed3c6
Binary files /dev/null and b/docs/images/UG-images/Mark a task (before).png differ
diff --git a/docs/images/UG-images/Mark a task complete (after)png b/docs/images/UG-images/Mark a task complete (after)png
new file mode 100644
index 00000000000..516c35d7b09
Binary files /dev/null and b/docs/images/UG-images/Mark a task complete (after)png differ
diff --git a/docs/images/UG-images/Mark a task late (after).png b/docs/images/UG-images/Mark a task late (after).png
new file mode 100644
index 00000000000..5e8fe939734
Binary files /dev/null and b/docs/images/UG-images/Mark a task late (after).png differ
diff --git a/docs/images/UG-images/Mark a task late (before).png b/docs/images/UG-images/Mark a task late (before).png
new file mode 100644
index 00000000000..ad7ab5acfe6
Binary files /dev/null and b/docs/images/UG-images/Mark a task late (before).png differ
diff --git a/docs/images/UG-images/Switch between Score Tabs.png b/docs/images/UG-images/Switch between Score Tabs.png
new file mode 100644
index 00000000000..71e174a2e4e
Binary files /dev/null and b/docs/images/UG-images/Switch between Score Tabs.png differ
diff --git a/docs/images/UG-images/Ui.png b/docs/images/UG-images/Ui.png
new file mode 100644
index 00000000000..3f997a50182
Binary files /dev/null and b/docs/images/UG-images/Ui.png differ
diff --git a/docs/images/UG-images/Viewing help.png b/docs/images/UG-images/Viewing help.png
new file mode 100644
index 00000000000..d8df341e3fb
Binary files /dev/null and b/docs/images/UG-images/Viewing help.png differ
diff --git a/docs/images/UG-images/findAlexDavidResult.png b/docs/images/UG-images/findAlexDavidResult.png
new file mode 100644
index 00000000000..b804a2d4eab
Binary files /dev/null and b/docs/images/UG-images/findAlexDavidResult.png differ
diff --git a/docs/images/UG-images/helpMessage.png b/docs/images/UG-images/helpMessage.png
new file mode 100644
index 00000000000..f76385d5473
Binary files /dev/null and b/docs/images/UG-images/helpMessage.png differ
diff --git a/docs/images/request_access.png b/docs/images/UG-images/request_access.png
similarity index 100%
rename from docs/images/request_access.png
rename to docs/images/UG-images/request_access.png
diff --git a/docs/images/Ui.png b/docs/images/Ui.png
deleted file mode 100644
index 5bd77847aa2..00000000000
Binary files a/docs/images/Ui.png and /dev/null differ
diff --git a/docs/images/UiClassDiagram.png b/docs/images/UiClassDiagram.png
deleted file mode 100644
index 785e04dbab4..00000000000
Binary files a/docs/images/UiClassDiagram.png and /dev/null differ
diff --git a/docs/images/astraxq.png b/docs/images/astraxq.png
new file mode 100644
index 00000000000..a459f896576
Binary files /dev/null and b/docs/images/astraxq.png differ
diff --git a/docs/images/carrieli1015.png b/docs/images/carrieli1015.png
new file mode 100644
index 00000000000..fc6dfc61105
Binary files /dev/null and b/docs/images/carrieli1015.png differ
diff --git a/docs/images/findAlexDavidResult.png b/docs/images/findAlexDavidResult.png
deleted file mode 100644
index 235da1c273e..00000000000
Binary files a/docs/images/findAlexDavidResult.png and /dev/null differ
diff --git a/docs/images/gwynethguo.png b/docs/images/gwynethguo.png
new file mode 100644
index 00000000000..da693890d07
Binary files /dev/null and b/docs/images/gwynethguo.png differ
diff --git a/docs/images/helpMessage.png b/docs/images/helpMessage.png
deleted file mode 100644
index b1f70470137..00000000000
Binary files a/docs/images/helpMessage.png and /dev/null differ
diff --git a/docs/images/qqh0828.png b/docs/images/qqh0828.png
new file mode 100644
index 00000000000..4b2d296062c
Binary files /dev/null and b/docs/images/qqh0828.png differ
diff --git a/docs/images/toh-xinyi.png b/docs/images/toh-xinyi.png
new file mode 100644
index 00000000000..c3a13f766de
Binary files /dev/null and b/docs/images/toh-xinyi.png differ
diff --git a/docs/index.md b/docs/index.md
index 7601dbaad0d..ad3dff14add 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,19 +1,20 @@
---
layout: page
-title: AddressBook Level-3
+title: MATHUTORING
---
-[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions)
-[![codecov](https://codecov.io/gh/se-edu/addressbook-level3/branch/master/graph/badge.svg)](https://codecov.io/gh/se-edu/addressbook-level3)
+[![CI Status](https://github.com/AY2223S2-CS2103-W17-1/tp/workflows/Java%20CI/badge.svg)](https://github.com/AY2223S2-CS2103-W17-1/tp/actions)
+[![codecov](https://codecov.io/gh/AY2223S2-CS2103-W17-1/tp/branch/master/graph/badge.svg?token=XXJ15FVPPS)](https://codecov.io/gh/AY2223S2-CS2103-W17-1/tp)
-![Ui](images/Ui.png)
+![Ui](images/UG-images/Ui.png)
-**AddressBook is a desktop application for managing your contact details.** While it has a GUI, most of the user interactions happen using a CLI (Command Line Interface).
+**MATHUTORING is a desktop application for private Math tuition teachers to manage their student contact details and performance.** While it has a GUI, most of the user interactions happen using a CLI (Command Line Interface).
-* If you are interested in using AddressBook, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start).
-* If you are interested about developing AddressBook, the [**Developer Guide**](DeveloperGuide.html) is a good place to start.
+* If you are interested in using MATHUTORING, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start).
+* If you are interested about developing MATHUTORING, the [**Developer Guide**](DeveloperGuide.html) is a good place to start.
**Acknowledgements**
-* Libraries used: [JavaFX](https://openjfx.io/), [Jackson](https://github.com/FasterXML/jackson), [JUnit5](https://github.com/junit-team/junit5)
+* This project is based on the AddressBook-Level3 project created by the [SE-EDU initiative](https://se-education.org).
+* Libraries used: [JavaFX](https://openjfx.io/), [Jackson](https://github.com/FasterXML/jackson), [JUnit5](https://github.com/junit-team/junit5), [Mockito](https://site.mockito.org/), [Apache PDFBox](https://pdfbox.apache.org/)
diff --git a/docs/pdfs/Alex Yeoh's Progress Report.pdf b/docs/pdfs/Alex Yeoh's Progress Report.pdf
new file mode 100644
index 00000000000..d421bd2cc96
Binary files /dev/null and b/docs/pdfs/Alex Yeoh's Progress Report.pdf differ
diff --git a/docs/team/astraxq.md b/docs/team/astraxq.md
new file mode 100644
index 00000000000..ecbcf41e6ed
--- /dev/null
+++ b/docs/team/astraxq.md
@@ -0,0 +1,40 @@
+---
+layout: page
+title: Quek Jing Ling, Brian's Project Portfolio Page
+---
+
+### Project: MATHUTORING
+
+MATHUTORING is a desktop application used for private Math tuition teachers to manage their students' contacts and performances. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC.
+
+Given below are my contributions to the project.
+
+* **New Feature**:
+ * Implemented `export` and `import` functionality.
+ * Created the GUI functionality for both import and export.
+
+* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=astraxq&breakdown=true&sort=groupTitle&sortWithin=title&since=2023-02-17&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other)
+
+* **Project management**:
+ * Handled and finalised `v1.3` release.
+
+* **Enhancements to existing features**:
+ * Added Person.java to take in an additional attribute `parentPhone`.
+ * Created the `Task`, `TaskList`, `Score`, `ScoreList` Model.
+ * Added JUnit Testing for new features.
+
+* **Documentation**:
+ * Updated AboutUs page and README page.
+ * Updated UG for Export and Import.
+ * Contributed into Use Cases, Manual Testing and Effort sections of the DG.
+
+* **Contributions to team-based tasks**:
+ * Conceptualized the ideas and came up with v1.1 UI design.
+ * Added several GitHub issues and helped manage the issues through adding labels and milestones.
+
+* **Contributions beyond the project team**:
+ * Handled a multi responsibility PR by breaking it to smaller PRs (cherry picking) to resolve conflicts in `v1.3`.
+
+* **Community**:
+ * Contributed to forum discussions (examples: [1](https://github.com/nus-cs2103-AY2223S2/forum/issues/171))
+
diff --git a/docs/team/carrieli1015.md b/docs/team/carrieli1015.md
new file mode 100644
index 00000000000..2276703c328
--- /dev/null
+++ b/docs/team/carrieli1015.md
@@ -0,0 +1,69 @@
+---
+layout: page
+title: Li Yuxuan's Project Portfolio Page
+---
+
+### Project: MATHUTORING
+
+MATHUTORING is a desktop application used for private Math tuition teachers to manage their students' contacts and performances. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC.
+
+Given below are my contributions to the project.
+
+* **New Features**:
+ 1. Switch command
+ * What it does: Help the user to easily switch between score text panel and score chart panel. Support both CLI and mouse click.
+ * Justification: This feature is convenient for the user to switch between score text panel and score chart panel.
+ * Highlights: Since the switch support both CLI and mouse click. It is important to make the behaviour consistence, so the application need to remember the panel status and switch based on it.
+ 2. Check command
+ * What it does: For the user to check a specific student's task/s and score/s information.
+ * Justification: This feature is important as it indicates which student the user want to check and display the selected student's information.
+ * Highlights: The check result is normally continue to display. The panel will reset to no student being checked when the currently checked student is being deleted/edited, or the entire student list is cleared, or a new student list is being imported in.
+ 3. Filter command
+ * What it does: To filter out student/s from the existing list. Filter key is the tag/s.
+ * Justification: This feature help the user to quickly filter out a group of students based on their given tag/s.
+ * Highlights: To allow more flexibility, when filter by tag, it is fully case-insensitive.
+ 4. Score list chart
+ * What it does: Created the score list chart to track the student's performance.
+ * Justification: The chart can clearly show the trend of a student's recent performance, whether his/her grade is improving or decreasing.
+ * Highlights: The user can hover over the chart node to see tooltip that contains score label and score value.
+
+* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=carrieli1015&breakdown=true&sort=groupTitle%20dsc&sortWithin=title&since=2023-02-17&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other)
+
+* **Project management**:
+ * Individual
+ * Completed weekly requirements on time.
+ * Created several GitHub branches for different increments.
+ * Group
+ * Met the milestone deadlines.
+ * Used GitHub for progress tracking.
+
+* **Enhancements to existing features**:
+ * Revamped the existing student list UI. (Pull requests [#71](https://github.com/AY2223S2-CS2103-W17-1/tp/pull/71) [#72](https://github.com/AY2223S2-CS2103-W17-1/tp/pull/72))
+ * UI integration. (Pull requests [#81](https://github.com/AY2223S2-CS2103-W17-1/tp/pull/81))
+ * Revamped the overall UI layout and make it more responsive (Pull requests [#117](https://github.com/AY2223S2-CS2103-W17-1/tp/pull/117))
+ * Wrote additional tests for existing features and improve the Codecov. (Pull requests [#170](https://github.com/AY2223S2-CS2103-W17-1/tp/pull/170))
+
+* **Documentation**:
+ * Updated AboutUs page and README page.
+ * Added a quick user guide inside the help window.
+ * User Guide (Pull requests [#116](https://github.com/AY2223S2-CS2103-W17-1/tp/pull/166) [#176](https://github.com/AY2223S2-CS2103-W17-1/tp/pull/176))
+ * Added in missing items.
+ * Updated UG with more details.
+ * Developer Guide (Pull requests [#176](https://github.com/AY2223S2-CS2103-W17-1/tp/pull/176))
+ * Helped with generate the UML diagram.
+ * Tweaked relevant guides, add in several User case, manual testing, Non-Functional Requirement, and planned enhancements.
+
+* **Contributions to team-based tasks**:
+ * Conceptualized the ideas and came up with the overall UI design.
+ * Added several GitHub issues and helped manage the issues through adding labels and milestones.
+ * Organized target user stories.
+ * Created illustrations. Including the avatars, student list icons, task list icons, and import/export window icons.
+
+* **Review/mentoring contributions**:
+ * Reviewed other teammates' pull requests. (Pull requests [#119](https://github.com/AY2223S2-CS2103-W17-1/tp/pull/119) [#76](https://github.com/AY2223S2-CS2103-W17-1/tp/pull/76))
+
+* **Community**:
+ * Shared idea and gave suggestions to another group.
+ * Contributed to forum discussions
+ * [#206](https://github.com/nus-cs2103-AY2223S2/forum/issues/206)
+ * [#290](https://github.com/nus-cs2103-AY2223S2/forum/issues/290)
diff --git a/docs/team/gwynethguo.md b/docs/team/gwynethguo.md
new file mode 100644
index 00000000000..f2856700624
--- /dev/null
+++ b/docs/team/gwynethguo.md
@@ -0,0 +1,66 @@
+---
+layout: page
+title: Gwyneth Guo Bai Ling's Project Portfolio Page
+---
+
+### Project: MATHUTORING
+
+MATHUTORING is a desktop application used for private Math tuition teachers to manage their students contact details and performance. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC.
+
+Given below are my contributions to the project.
+
+* **New Feature**: Added the ability to export a student's progress using Command Line Interface (CLI) `exportp` and Graphical User Interface (GUI).
+ * What it does: allows the user to export a student's progress into a PDF file using CLI or GUI. The PDF file consists of the student's name, date created, score list, and task list.
+ * Justification:
+ * This feature improves the product significantly because a user can directly export a student's progress to send it to the student or parent.
+ * This feature can also solve the problem where labels are not fully displayed in the application's User Interface.
+ * Highlights:
+ * The implementation was too challenging as it required writing to PDF files using coordinates.
+ * Manual wrapping must also be implemented to ensure texts do not overflow in the PDF file.
+ * Algorithms are needed to create lines and texts for task list and score list.
+ * A new command is also needed for CLI, involving user directory paths, which creates a lot of possible bugs for user with different OSes.
+ * Credits: Used a third-party library [Apache PDFBox](https://pdfbox.apache.org/)
+
+* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=gwynethguo&breakdown=true&sort=groupTitle%20dsc&sortWithin=title&since=2023-02-17&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other&tabOpen=true&tabType=authorship&tabAuthor=gwynethguo&tabRepo=AY2223S2-CS2103-W17-1%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code~other&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false)
+
+* **Project management**:
+ * Managed a release `v1.3.1` on GitHub
+
+* **Enhancements to existing features**:
+ * Added GUI for Task List and Score List panels ([#78](https://github.com/AY2223S2-CS2103-W17-1/tp/pull/78))
+ * Added unit and integration tests for `exportp` command involving Stubs and Mocks ([#103](https://github.com/AY2223S2-CS2103-W17-1/tp/pull/103), [#179](https://github.com/AY2223S2-CS2103-W17-1/tp/pull/179))
+ * Enhanced code quality for `PdfConverter.java` ([#171](https://github.com/AY2223S2-CS2103-W17-1/tp/pull/171))
+
+* **Documentation**:
+ * User Guide: ([#132](https://github.com/AY2223S2-CS2103-W17-1/tp/pull/132), [#133](https://github.com/AY2223S2-CS2103-W17-1/tp/pull/133), [#176](https://github.com/AY2223S2-CS2103-W17-1/tp/pull/176))
+ * Added screenshots to User Guide
+ * Rearranged User Guide to ensure good flow
+ * Added table of contents, user interface layout, user input restrictions, added links to sections.
+ * Developer Guide: ([#176](https://github.com/AY2223S2-CS2103-W17-1/tp/pull/176))
+ * Added manual testing for exporting a student's progress feature
+ * Arranged some parts of the Developer Guide
+
+* **Contributions to team-based tasks**:
+ * Setup tP Team Organization
+ * Created a [GitHub Organization](https://github.com/AY2223S2-CS2103-W17-1) for the team.
+ * Add members to the organization and created a `developers` team.
+ * Setup tP Team Repository
+ * Forked the [repo](https://github.com/nus-cs2103-AY2223S2/tp) to the team organization.
+ * Enabled the issue tracker and GitHub Actions.
+ * Setup CodeCov.
+ * Added members to the repository and managed access of the team members.
+ * Added GitHub issues and helped manage the issues by adding labels and milestones.
+ * Updated site-wide settings ([#26](https://github.com/AY2223S2-CS2103-W17-1/tp/pull/26), [#28](https://github.com/AY2223S2-CS2103-W17-1/tp/pull/28), [#176](https://github.com/AY2223S2-CS2103-W17-1/tp/pull/176))
+ * Changed project name, title, and badges.
+ * Changed site header to ensure that the details are correct when converting the documentation to the `.pdf` format.
+ * Changed site settings to change fonts and colors of the sites.
+
+* **Community**:
+ * PRs reviewed (with non-trivial review comments): ([#30](https://github.com/AY2223S2-CS2103-W17-1/tp/pull/30), [#119](https://github.com/AY2223S2-CS2103-W17-1/tp/pull/119), [#70](https://github.com/AY2223S2-CS2103-W17-1/tp/pull/70))
+ * Reported bugs and suggestions for other teams in the class ([#16](https://github.com/gwynethguo/ped/issues/16), [#3](https://github.com/gwynethguo/ped/issues/3), [#6](https://github.com/gwynethguo/ped/issues/6))
+ * Assist a group in git branching and fixing JUnit tests.
+
+* **Tools**:
+ * Integrated Code Coverage ([CodeCov](https://app.codecov.io/gh/AY2223S2-CS2103-W17-1/tp)) to the project.
+ * Integrated a third party library ([Apache PDFBox](https://pdfbox.apache.org/))
+ * Integrated a mocking framework ([Mockito](https://site.mockito.org/))
diff --git a/docs/team/qqh0828.md b/docs/team/qqh0828.md
new file mode 100644
index 00000000000..b0af6d5c0e9
--- /dev/null
+++ b/docs/team/qqh0828.md
@@ -0,0 +1,72 @@
+---
+layout: page
+title: Qiu Qianhui's Project Portfolio Page
+---
+
+### Project: MATHUTORING
+
+MATHUTORING is a centralised desktop application targeted to private Math tuition teachers, helps them manage student contact details and performance by keeping student contact details, tasks, and scores.
+
+Given below are my contributions to the project.
+
+* **New Feature**:
+ 1. Add a score to a student [#75](https://github.com/AY2223S2-CS2103-W17-1/tp/pull/75)
+ * What it does: With this new feature, user can add a score to a student.
+ * Justification: This feature is important as users can keep track of the student's performance by adding their exam scores.
+ * Highlights: User can add new scores at any time. After adding a new score, it will be automatically saved in the student's unique score list.
+ When scores are checked by 'check' command, they will be automatically displayed with sorted order that nearest score shown at front.
+
+ 2. Delete a score to a student [#75](https://github.com/AY2223S2-CS2103-W17-1/tp/pull/75)
+ * What it does: With this new feature, user can delete a score from a student.
+ * Justification: This feature is important as a use may add a wrong score and want to delete or edit it.
+ * Highlights: User can delete an existing scores at any time. After deleting an existing score, it will be automatically updated in the student's unique score list.
+
+ 3. Score list chart and table statistic summary of recent five scores [#101](https://github.com/AY2223S2-CS2103-W17-1/tp/pull/101) [#106](https://github.com/AY2223S2-CS2103-W17-1/tp/pull/106)
+ * What it does: With ths new feature, user can view student's performance easily.
+ * Justification: The chart and the table statistic summary can easily show the student's recent performance, hence the user can plan their future sessions easily to suit the student's needs.
+ * Highlights: There are three colours to represent the different level of exam scores, e.g. red colour in percentage table column represents the decreasing grades.
+
+* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=qqh&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2023-02-17)
+
+* **Project management**:
+* Individual
+ * Completed weekly requirements on time
+ * Created several GitHub branches for different increments
+* Group
+ * Met the milestone deadlines
+ * Used GitHub for progress tracking
+
+* **Enhancements to existing features**:
+ * Refactor the whole AB3 to suit our project [#119](https://github.com/AY2223S2-CS2103-W17-1/tp/pull/119)
+ * Fixed some bugs on existing AB3 codes [#119](https://github.com/AY2223S2-CS2103-W17-1/tp/pull/119)
+ * Wrote some tests to increase the coverage [#186](https://github.com/AY2223S2-CS2103-W17-1/tp/pull/186)
+ * Bug fixing received from PE-D
+
+* **Documentation**:
+ * User Guide:
+ * Updated user guide with 'Managing score list of student' part
+ * Updated 'Command summary' part
+ * Developer Guide:
+ * Updated the target user profile, value proposition and user stories
+ * Updated the table of contents
+ * Updated most UML diagrams with puml files except task model diagram
+ * Added UniqueScoreList model in the Design Model section
+ * Added use cases that are related to score such as 'Add a score' and 'Delete a score'
+ * Added 'Instructions for manual testing' that are related to score such as 'Adding a score for a student' and 'Deleting a score for a student'
+ * Added 'Challenges faced' inside 'Effort' section
+ * Contributed in Planned Enhancements, such as 'Edit the tags will overwrite the whole list of tags'
+ * Update Other Documentations: AboutUs.md, README.md and documentations are inside tutorial folder
+
+* **Contributions to team-based tasks**:
+ * Added several GitHub issues and helped manage the issues through adding labels and milestones
+ * Contributed to conceptualize the Score List Chart panel design
+
+* **Community**:
+ * Reviewed PRs of peers (with non-trivial review comments) and helped to merge to the master
+ * Contributed to forum discussions (examples: [#320](https://github.com/nus-cs2103-AY2223S2/forum/issues/320)
+ [#296](https://github.com/nus-cs2103-AY2223S2/forum/issues/296) [#291](https://github.com/nus-cs2103-AY2223S2/forum/issues/291))
+ * Contributed to help with smoke testing (examples:[#236](https://github.com/nus-cs2103-AY2223S2/forum/issues/236#issuecomment-1453378705))
+ * Reported bugs and suggestions for other teams in the class (examples: [PE-D](https://github.com/QQH0828/ped/issues))
+
+* **Tools**:
+ * Java 11, JavaFX, CSS
diff --git a/docs/team/toh-xinyi.md b/docs/team/toh-xinyi.md
new file mode 100644
index 00000000000..95e9cbb41d0
--- /dev/null
+++ b/docs/team/toh-xinyi.md
@@ -0,0 +1,42 @@
+---
+layout: page
+title: Toh Xin Yi's Project Portfolio Page
+---
+
+MATHUTORING is a desktop application for private math tuition teachers to manage their students' contact as well as
+their progress. It allows these tutors to track their students’ progress report which subsequently allows them to plan
+future lessons and have an overall view of their schedule for ease of planning.
+
+Given below are my contributions to the project.
+
+* **Enhancements added**:
+ * Added task model and related commands to the original model
+ * [`addtask`](https://ay2223s2-cs2103-w17-1.github.io/tp/UserGuide.html#1-adding-a-task-for-a-student-addtask)
+ * [`deletetask`](https://ay2223s2-cs2103-w17-1.github.io/tp/UserGuide.html#2-deleting-a-task-of-a-student-deletetask)
+ * [`markcomplete`, `marklate`, `markinprogress`](https://ay2223s2-cs2103-w17-1.github.io/tp/UserGuide.html#3-marking-a-task-of-a-student-markcomplete-markinprogress-marklate)
+
+* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2023-02-17)
+
+* **Contributions to team-based tasks**:
+ * Created issues during weekly team meeting to keep track of what needs to be done in detail each week and by who
+ * Upload screenshots of product demo during multiple milestones
+
+* **Project management**:
+ * Managed releases `v1.1` - `current release` on GitHub
+
+* **Contributions to documentation**:
+ * User Guide:
+ * Added documentation for the features `delete`, `add` and `listStudents` [\#48](https://github.com/AY2223S2-CS2103-W17-1/tp/pull/48)
+ * Did tweaks to existing documentation of command format notes: [\#47](https://github.com/AY2223S2-CS2103-W17-1/tp/pull/47)
+ * Developer Guide:
+ * Tweaked and added terminologies to glossary
+ * Added UML diagram for Task model
+ * Added instructions for manual testing of adding students, deleting students, adding tasks, deleting tasks and marking tasks
+
+* **Community**:
+ * PRs reviewed (with non-trivial review comments): [\#52](https://github.com/AY2223S2-CS2103-W17-1/tp/pull/52),
+[\#49](https://github.com/AY2223S2-CS2103-W17-1/tp/pull/49), [\#19](https://github.com/AY2223S2-CS2103-W17-1/tp/pull/19),
+[\#20](https://github.com/AY2223S2-CS2103-W17-1/tp/pull/20)
+ * PRs reviewed (with thorough comments and suggestions): [\#119](https://github.com/AY2223S2-CS2103-W17-1/tp/pull/119), [\#166](https://github.com/AY2223S2-CS2103-W17-1/tp/pull/166)
+ * Contributed to forum discussions (examples: [Issue regarding tweaking GUI for better user experience](https://github.com/nus-cs2103-AY2223S2/forum/issues/161#issuecomment-1427028125))
+ * Reported bugs and suggestions for other teams in the class (examples: [PE-D](https://github.com/toh-xinyi/ped/issues))
diff --git a/docs/tutorials/AddRemark.md b/docs/tutorials/AddRemark.md
index 880c701042f..962780ca198 100644
--- a/docs/tutorials/AddRemark.md
+++ b/docs/tutorials/AddRemark.md
@@ -5,7 +5,16 @@ title: "Tutorial: Adding a command"
Let's walk you through the implementation of a new command — `remark`.
-This command allows users of the AddressBook application to add optional remarks to people in their address book and edit it if required. The command should have the following format:
+
+
+**:exclamation: Caution:**
+* In order to avoid potential confusion, you many find some classes' name are different from the commit link provided.
+* This is because you will be doing this tutorial based on `Mathutoring` application which is based on pre-existing codebase `AB3`.
+* Some examples are and are not limited to: `Person` to `Student`, `AddressBook` to `Mathutoring`.
+* Please change your codes accordingly when you are going through this tutorial.
+
+
+This command allows users of the Mathutoring application to add optional remarks to people in their mathutoring and edit it if required. The command should have the following format:
`remark INDEX r/REMARK` (e.g., `remark 2 r/Likes baseball`)
@@ -28,7 +37,7 @@ package seedu.address.logic.commands;
import seedu.address.model.Model;
/**
- * Changes the remark of an existing person in the address book.
+ * Changes the remark of an existing student in the address book.
*/
public class RemarkCommand extends Command {
@@ -43,7 +52,7 @@ public class RemarkCommand extends Command {
### Hook `RemarkCommand` into the application
-Now that we have our `RemarkCommand` ready to be executed, we need to update `AddressBookParser#parseCommand()` to recognize the `remark` keyword. Add the new command to the `switch` block by creating a new `case` that returns a new instance of `RemarkCommand`.
+Now that we have our `RemarkCommand` ready to be executed, we need to update `MathutoringParser#parseCommand()` to recognize the `remark` keyword. Add the new command to the `switch` block by creating a new `case` that returns a new instance of `RemarkCommand`.
You can refer to the changes in this [diff](https://github.com/se-edu/addressbook-level3/commit/35eb7286f18a029d39cb7a29df8f172a001e4fd8#diff-399c284cb892c20b7c04a69116fcff6ccc0666c5230a1db8e4a9145def8fa4ee).
@@ -65,8 +74,8 @@ Following the convention in other commands, we add relevant messages as constant
``` java
public static final String MESSAGE_USAGE = COMMAND_WORD
- + ": Edits the remark of the person identified "
- + "by the index number used in the last person listing. "
+ + ": Edits the remark of the student identified "
+ + "by the index number used in the last student listing. "
+ "Existing remark will be overwritten by the input.\n"
+ "Parameters: INDEX (must be a positive integer) "
+ "r/ [REMARK]\n"
@@ -101,8 +110,8 @@ public class RemarkCommand extends Command {
private final String remark;
/**
- * @param index of the person in the filtered person list to edit the remark
- * @param remark of the person to be updated to
+ * @param index of the student in the filtered student list to edit the remark
+ * @param remark of the student to be updated to
*/
public RemarkCommand(Index index, String remark) {
requireAllNonNull(index, remark);
@@ -216,7 +225,7 @@ public RemarkCommand parse(String args) throws ParseException {
-:information_source: Don’t forget to update `AddressBookParser` to use our new `RemarkCommandParser`!
+:information_source: Don’t forget to update `MathutoringParser` to use our new `RemarkCommandParser`!
@@ -225,11 +234,11 @@ If you are stuck, check out the sample
## Add `Remark` to the model
-Now that we have all the information that we need, let’s lay the groundwork for propagating the remarks added into the in-memory storage of person data. We achieve that by working with the `Person` model. Each field in a Person is implemented as a separate class (e.g. a `Name` object represents the person’s name). That means we should add a `Remark` class so that we can use a `Remark` object to represent a remark given to a person.
+Now that we have all the information that we need, let’s lay the groundwork for propagating the remarks added into the in-memory storage of student data. We achieve that by working with the `Student` model. Each field in a Student is implemented as a separate class (e.g. a `Name` object represents the student’s name). That means we should add a `Remark` class so that we can use a `Remark` object to represent a remark given to a student.
### Add a new `Remark` class
-Create a new `Remark` in `seedu.address.model.person`. Since a `Remark` is a field that is similar to `Address`, we can reuse a significant bit of code.
+Create a new `Remark` in `seedu.address.model.student`. Since a `Remark` is a field that is similar to `Address`, we can reuse a significant bit of code.
A copy-paste and search-replace later, you should have something like [this](https://github.com/se-edu/addressbook-level3/commit/4516e099699baa9e2d51801bd26f016d812dedcc#diff-41bb13c581e280c686198251ad6cc337cd5e27032772f06ed9bf7f1440995ece). Note how `Remark` has no constrains and thus does not require input
validation.
@@ -240,9 +249,9 @@ Let’s change `RemarkCommand` and `RemarkCommandParser` to use the new `Remark`
## Add a placeholder element for remark to the UI
-Without getting too deep into `fxml`, let’s go on a 5 minute adventure to get some placeholder text to show up for each person.
+Without getting too deep into `fxml`, let’s go on a 5 minute adventure to get some placeholder text to show up for each student.
-Simply add the following to [`seedu.address.ui.PersonCard`](https://github.com/se-edu/addressbook-level3/commit/850b78879582f38accb05dd20c245963c65ea599#diff-639834f1e05afe2276a86372adf0fe5f69314642c2d93cfa543d614ce5a76688).
+Simply add the following to [`seedu.address.ui.StudentCard`](https://github.com/se-edu/addressbook-level3/commit/850b78879582f38accb05dd20c245963c65ea599#diff-639834f1e05afe2276a86372adf0fe5f69314642c2d93cfa543d614ce5a76688).
**`PersonCard.java`:**
@@ -254,9 +263,9 @@ private Label remark;
`@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.
-Then insert the following into [`main/resources/view/PersonListCard.fxml`](https://github.com/se-edu/addressbook-level3/commit/850b78879582f38accb05dd20c245963c65ea599#diff-d44c4f51c24f6253c277a2bb9bc440b8064d9c15ad7cb7ceda280bca032efce9).
+Then insert the following into [`main/resources/view/StudentListCard.fxml`](https://github.com/se-edu/addressbook-level3/commit/850b78879582f38accb05dd20c245963c65ea599#diff-d44c4f51c24f6253c277a2bb9bc440b8064d9c15ad7cb7ceda280bca032efce9).
-**`PersonListCard.fxml`:**
+**`StudentListCard.fxml`:**
``` xml
@@ -266,21 +275,21 @@ That’s it! Fire up the application again and you should see something like thi
![$remark shows up in each entry](../images/add-remark/$Remark.png)
-## Modify `Person` to support a `Remark` field
+## Modify `Student` to support a `Remark` field
-Since `PersonCard` displays data from a `Person`, we need to update `Person` to get our `Remark` displayed!
+Since `StudentCard` displays data from a `Student`, we need to update `Student` to get our `Remark` displayed!
-### Modify `Person`
+### Modify `Student`
-We change the constructor of `Person` to take a `Remark`. We will also need to define new fields and accessors accordingly to store our new addition.
+We change the constructor of `Student` to take a `Remark`. We will also need to define new fields and accessors accordingly to store our new addition.
-### Update other usages of `Person`
+### Update other usages of `Student`
-Unfortunately, a change to `Person` will cause other commands to break, you will have to modify these commands to use the updated `Person`!
+Unfortunately, a change to `Student` will cause other commands to break, you will have to modify these commands to use the updated `Student`!
-:bulb: Use the `Find Usages` feature in IntelliJ IDEA on the `Person` class to find these commands.
+:bulb: Use the `Find Usages` feature in IntelliJ IDEA on the `Student` class to find these commands.
@@ -289,13 +298,13 @@ Refer to [this commit](https://github.com/se-edu/addressbook-level3/commit/ce998
## Updating Storage
-AddressBook stores data by serializing `JsonAdaptedPerson` into `json` with the help of an external library — Jackson. Let’s update `JsonAdaptedPerson` to work with our new `Person`!
+Mathutoring stores data by serializing `JsonAdaptedStudent` into `json` with the help of an external library — Jackson. Let’s update `JsonAdaptedStudent` to work with our new `Student`!
While the changes to code may be minimal, the test data will have to be updated as well.
-:exclamation: You must delete AddressBook’s storage file located at `/data/addressbook.json` before running it! Not doing so will cause AddressBook to default to an empty address book!
+:exclamation: You must delete Mathutoring’s storage file located at `/data/mathutoring.json` before running it! Not doing so will cause Mathutoring to default to an empty mathutoring!
@@ -304,16 +313,16 @@ to see what the changes entail.
## Finalizing the UI
-Now that we have finalized the `Person` class and its dependencies, we can now bind the `Remark` field to the UI.
+Now that we have finalized the `Student` class and its dependencies, we can now bind the `Remark` field to the UI.
Just add [this one line of code!](https://github.com/se-edu/addressbook-level3/commit/5b98fee11b6b3f5749b6b943c4f3bd3aa049b692)
**`PersonCard.java`:**
``` java
-public PersonCard(Person person, int displayedIndex) {
+public PersonCard(Student student, int displayedIndex) {
//...
- remark.setText(person.getRemark().value);
+ remark.setText(student.getRemark().value);
}
```
@@ -325,43 +334,43 @@ After the previous step, we notice a peculiar regression — we went from di
### Update `RemarkCommand` and `RemarkCommandParser`
-In this last step, we modify `RemarkCommand#execute()` to change the `Remark` of a `Person`. Since all fields in a `Person` are immutable, we create a new instance of a `Person` with the values that we want and
-save it with `Model#setPerson()`.
+In this last step, we modify `RemarkCommand#execute()` to change the `Remark` of a `Student`. Since all fields in a `Student` are immutable, we create a new instance of a `Student` with the values that we want and
+save it with `Model#setStudent()`.
**`RemarkCommand.java`:**
``` java
//...
- public static final String MESSAGE_ADD_REMARK_SUCCESS = "Added remark to Person: %1$s";
- public static final String MESSAGE_DELETE_REMARK_SUCCESS = "Removed remark from Person: %1$s";
+ public static final String MESSAGE_ADD_REMARK_SUCCESS = "Added remark to Student: %1$s";
+ public static final String MESSAGE_DELETE_REMARK_SUCCESS = "Removed remark from Student: %1$s";
//...
@Override
public CommandResult execute(Model model) throws CommandException {
- List lastShownList = model.getFilteredPersonList();
+ List lastShownList = model.getFilteredStudentList();
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.getAddress(), remark, personToEdit.getTags());
+ Person studentToEdit = lastShownList.get(index.getZeroBased());
+ Person editedStudent = new Student(
+ studentToEdit.getName(), studentToEdit.getPhone(), studentToEdit.getEmail(),
+ studentToEdit.getAddress(), remark, studentToEdit.getTags());
- model.setPerson(personToEdit, editedPerson);
- model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+ model.setStudent(studentToEdit, editedStudent);
+ model.updateFilteredStudentList(PREDICATE_SHOW_ALL_PERSONS);
- return new CommandResult(generateSuccessMessage(editedPerson));
+ return new CommandResult(generateSuccessMessage(editedStudent));
}
/**
* Generates a command execution success message based on whether
* the remark is added to or removed from
- * {@code personToEdit}.
+ * {@code studentToEdit}.
*/
- private String generateSuccessMessage(Person personToEdit) {
+ private String generateSuccessMessage(Student studentToEdit) {
String message = !remark.value.isEmpty() ? MESSAGE_ADD_REMARK_SUCCESS : MESSAGE_DELETE_REMARK_SUCCESS;
- return String.format(message, personToEdit);
+ return String.format(message, studentToEdit);
}
```
diff --git a/docs/tutorials/RemovingFields.md b/docs/tutorials/RemovingFields.md
index f29169bc924..a275f818130 100644
--- a/docs/tutorials/RemovingFields.md
+++ b/docs/tutorials/RemovingFields.md
@@ -7,8 +7,17 @@ title: "Tutorial: Removing Fields"
>
> — Antoine de Saint-Exupery
+
+
+**:exclamation: Caution:**
+* In order to avoid potential confusion, you many find some classes' name are different from the commit link provided.
+* This is because you will be doing this tutorial based on `Mathutoring` application which is based on pre-existing codebase `AB3`.
+* Some examples are and are not limited to: `Person` to `Student`, `AddressBook` to `Mathutoring`.
+* Please change your codes accordingly when you are going through this tutorial.
+
+
When working on an existing code base, you will most likely find that some features that are no longer necessary.
-This tutorial aims to give you some practice on such a code 'removal' activity by removing the `address` field from `Person` class.
+This tutorial aims to give you some practice on such a code 'removal' activity by removing the `address` field from `Student` class.
@@ -28,7 +37,7 @@ IntelliJ IDEA provides a refactoring tool that can identify *most* parts of a re
### Assisted refactoring
-The `address` field in `Person` is actually an instance of the `seedu.address.model.person.Address` class. Since removing the `Address` class will break the application, we start by identifying `Address`'s usages. This allows us to see code that depends on `Address` to function properly and edit them on a case-by-case basis. Right-click the `Address` class and select `Refactor` \> `Safe Delete` through the menu.
+The `address` field in `Student` is actually an instance of the `seedu.address.model.student.Address` class. Since removing the `Address` class will break the application, we start by identifying `Address`'s usages. This allows us to see code that depends on `Address` to function properly and edit them on a case-by-case basis. Right-click the `Address` class and select `Refactor` \> `Safe Delete` through the menu.
* :bulb: To make things simpler, you can unselect the options `Search in comments and strings` and `Search for text occurrences`
![Usages detected](../images/remove/UnsafeDelete.png)
@@ -37,11 +46,11 @@ Choose to `View Usages` and you should be presented with a list of `Safe Delete
![List of conflicts](../images/remove/SafeDeleteConflicts.png)
-Remove usages of `Address` by performing `Safe Delete`s on each entry i.e., double-click on the entry (which takes you to the code in concern, right-click on that entity, and choose `Refactor` -> `Safe delete` as before). You will need to exercise discretion when removing usages of `Address`. Functions like `ParserUtil#parseAddress()` can be safely removed but its usages must be removed as well. Other usages like in `EditPersonDescriptor` may require more careful inspection.
+Remove usages of `Address` by performing `Safe Delete`s on each entry i.e., double-click on the entry (which takes you to the code in concern, right-click on that entity, and choose `Refactor` -> `Safe delete` as before). You will need to exercise discretion when removing usages of `Address`. Functions like `ParserUtil#parseAddress()` can be safely removed but its usages must be removed as well. Other usages like in `EditStudentDescriptor` may require more careful inspection.
-Let’s try removing references to `Address` in `EditPersonDescriptor`.
+Let’s try removing references to `Address` in `EditStudentDescriptor`.
-1. Safe delete the field `address` in `EditPersonDescriptor`.
+1. Safe delete the field `address` in `EditStudentDescriptor`.
1. Select `Yes` when prompted to remove getters and setters.
@@ -52,7 +61,7 @@ Let’s try removing references to `Address` in `EditPersonDescriptor`.
- :bulb: **Tip:** Removing usages may result in errors. Exercise discretion and fix them. For example, removing the `address` field from the `Person` class will require you to modify its constructor.
+ :bulb: **Tip:** Removing usages may result in errors. Exercise discretion and fix them. For example, removing the `address` field from the `Student` class will require you to modify its constructor.
1. Repeat the steps for the remaining usages of `Address`
@@ -63,13 +72,13 @@ After you are done, verify that the application still works by compiling and run
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`).
-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. For example, there is a `$address` in each `PersonCard` that has not been removed nor identified.
+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. For example, there is a `$address` in each `StudentCard` that has not been removed nor identified.
![$address](../images/remove/$address.png)
-A quick look at the `PersonCard` class and its `fxml` file quickly reveals why it slipped past the automated refactoring.
+A quick look at the `StudentCard` class and its `fxml` file quickly reveals why it slipped past the automated refactoring.
-**`PersonCard.java`**
+**`StudentCard.java`**
``` java
...
@@ -78,7 +87,7 @@ private Label address;
...
```
-**`PersonCard.fxml`**
+**`StudentCard.fxml`**
``` xml
...
@@ -96,12 +105,12 @@ At this point, your application is working as intended and all your tests are pa
In `src/test/data/`, data meant for testing purposes are stored. While keeping the `address` field in the json files does not cause the tests to fail, it is not good practice to let cruft from old features accumulate.
-**`invalidPersonAddressBook.json`:**
+**`invalidStudentMathutoring.json`:**
```json
{
- "persons": [ {
- "name": "Person with invalid name field: Ha!ns Mu@ster",
+ "students": [ {
+ "name": "Student with invalid name field: Ha!ns Mu@ster",
"phone": "9482424",
"email": "hans@example.com",
"address": "4th street"
diff --git a/docs/tutorials/TracingCode.md b/docs/tutorials/TracingCode.md
index 4fb62a83ef6..e58eb397bde 100644
--- a/docs/tutorials/TracingCode.md
+++ b/docs/tutorials/TracingCode.md
@@ -7,6 +7,15 @@ title: "Tutorial: Tracing code"
>
> — Robert C. Martin Clean Code: A Handbook of Agile Software Craftsmanship
+
+
+**:exclamation: Caution:**
+* In order to avoid potential confusion, you many find some classes' name are different from the commit link provided.
+* This is because you will be doing this tutorial based on `Mathutoring` application which is based on pre-existing codebase `AB3`.
+* Some examples are and are not limited to: `Person` to `Student`, `AddressBook` to `Mathutoring`.
+* Please change your codes accordingly when you are going through this tutorial.
+
+
When trying to understand an unfamiliar code base, one common strategy used is to trace some representative execution path through the code base. One easy way to trace an execution path is to use a debugger to step through the code. In this tutorial, you will be using the IntelliJ IDEA’s debugger to trace the execution path of a specific user command.
* Table of Contents
@@ -171,7 +180,7 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [
1. Stepping through the method shows that it calls `ArgumentTokenizer#tokenize()` and `ParserUtil#parseIndex()` to obtain the arguments and index required.
-1. The rest of the method seems to exhaustively check for the existence of each possible parameter of the `edit` command and store any possible changes in an `EditPersonDescriptor`. Recall that we can verify the contents of `editPersonDesciptor` through the 'Variables' window.
+1. The rest of the method seems to exhaustively check for the existence of each possible parameter of the `edit` command and store any possible changes in an `EditStudentDescriptor`. Recall that we can verify the contents of `editStudentDesciptor` through the 'Variables' window.
![EditCommand](../images/tracing/EditCommand.png)
1. As you just traced through some code involved in parsing a command, you can take a look at this class diagram to see where the various parsing-related classes you encountered fit into the design of the `Logic` component.
@@ -189,22 +198,22 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [
@Override
public CommandResult execute(Model model) throws CommandException {
...
- Person personToEdit = lastShownList.get(index.getZeroBased());
- Person editedPerson = createEditedPerson(personToEdit, editPersonDescriptor);
- if (!personToEdit.isSamePerson(editedPerson) && model.hasPerson(editedPerson)) {
+ Person studentToEdit = lastShownList.get(index.getZeroBased());
+ Person editedStudent = createEditedPerson(studentToEdit, editPersonDescriptor);
+ if (!studentToEdit.isSamePerson(editedStudent) && model.hasPerson(editedStudent)) {
throw new CommandException(MESSAGE_DUPLICATE_PERSON);
}
- model.setPerson(personToEdit, editedPerson);
+ model.setPerson(studentToEdit, editedStudent);
model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
- return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, editedPerson));
+ return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, editedStudent));
}
```
1. As suspected, `command#execute()` does indeed make changes to the `model` object. Specifically,
- * it uses the `setPerson()` method (defined in the interface `Model` and implemented in `ModelManager` as per the usual pattern) to update the person data.
- * it uses the `updateFilteredPersonList` method to ask the `Model` to populate the 'filtered list' with _all_ persons.
- FYI, The 'filtered list' is the list of persons resulting from the most recent operation that will be shown to the user immediately after. For the `edit` command, we populate it with all the persons so that the user can see the edited person along with all other persons. If this was a `find` command, we would be setting that list to contain the search results instead.
- To provide some context, given below is the class diagram of the `Model` component. See if you can figure out where the 'filtered list' of persons is being tracked.
+ * it uses the `setStudent()` method (defined in the interface `Model` and implemented in `ModelManager` as per the usual pattern) to update the student data.
+ * it uses the `updateFilteredStudentList` method to ask the `Model` to populate the 'filtered list' with _all_ students.
+ FYI, The 'filtered list' is the list of students resulting from the most recent operation that will be shown to the user immediately after. For the `edit` command, we populate it with all the students so that the user can see the edited student along with all other students. If this was a `find` command, we would be setting that list to contain the search results instead.
+ To provide some context, given below is the class diagram of the `Model` component. See if you can figure out where the 'filtered list' of students is being tracked.
* :bulb: This may be a good time to read through the [`Model` component section of the DG](../DeveloperGuide.html#model-component)
@@ -220,9 +229,9 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [
:bulb: **Intellij Tip:** When trying to step into a statement such as `storage.saveAddressBook(model.getAddressBook())` which contains multiple method calls, Intellij will let you choose (by clicking) which one you want to step into.
-1. As you step through the code inside the `Storage` component, you will eventually arrive at the `JsonAddressBook#saveAddressBook()` method which calls the `JsonSerializableAddressBook` constructor, to create an object that can be _serialized_ (i.e., stored in storage medium) in JSON format. That constructor is given below (with added line breaks for easier readability):
+1. As you step through the code inside the `Storage` component, you will eventually arrive at the `JsonMathutoring#saveMathutoring()` method which calls the `JsonSerializableMathutoring` constructor, to create an object that can be _serialized_ (i.e., stored in storage medium) in JSON format. That constructor is given below (with added line breaks for easier readability):
- **`JsonSerializableAddressBook` constructor:**
+ **`JsonSerializableMathutoring` constructor:**
``` java
/**
* Converts a given {@code ReadOnlyAddressBook} into this class for Jackson use.
@@ -231,7 +240,7 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [
* {@code JsonSerializableAddressBook}.
*/
public JsonSerializableAddressBook(ReadOnlyAddressBook source) {
- persons.addAll(
+ students.addAll(
source.getPersonList()
.stream()
.map(JsonAdaptedPerson::new)
@@ -239,7 +248,7 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [
}
```
-1. It appears that a `JsonAdaptedPerson` is created for each `Person` and then added to the `JsonSerializableAddressBook`.
+1. It appears that a `JsonAdaptedStudent` is created for each `Student` and then added to the `JsonSerializableMathutoring`.
This is because regular Java objects need to go through an _adaptation_ for them to be suitable to be saved in JSON format.
1. While you are stepping through the classes in the `Storage` component, here is the component's class diagram to help you understand how those classes fit into the structure of the component.
@@ -296,6 +305,6 @@ Here are some quick questions you can try to answer based on your execution path
4. Add a new command
- 5. Add a new field to `Person`
+ 5. Add a new field to `Student`
6. Add a new entity to the address book
diff --git a/src/main/java/seedu/address/Main.java b/src/main/java/seedu/address/Main.java
index 052a5068631..66a9a77a1f4 100644
--- a/src/main/java/seedu/address/Main.java
+++ b/src/main/java/seedu/address/Main.java
@@ -5,18 +5,17 @@
/**
* The main entry point to the application.
*
- * This is a workaround for the following error when MainApp is made the
- * entry point of the application:
+ * This is a workaround for the following error when MainApp is made the entry point of the
+ * application:
*
- * Error: JavaFX runtime components are missing, and are required to run this application
+ * Error: JavaFX runtime components are missing, and are required to run this application
*
- * The reason is that MainApp extends Application. In that case, the
- * LauncherHelper will check for the javafx.graphics module to be present
- * as a named module. We don't use JavaFX via the module system so it can't
- * find the javafx.graphics module, and so the launch is aborted.
+ * The reason is that MainApp extends Application. In that case, the LauncherHelper will check for
+ * the javafx.graphics module to be present as a named module. We don't use JavaFX via the module
+ * system so it can't find the javafx.graphics module, and so the launch is aborted.
*
- * By having a separate main class (Main) that doesn't extend Application
- * to be the entry point of the application, we avoid this issue.
+ * By having a separate main class (Main) that doesn't extend Application to be the entry point of
+ * the application, we avoid this issue.
*/
public class Main {
public static void main(String[] args) {
diff --git a/src/main/java/seedu/address/MainApp.java b/src/main/java/seedu/address/MainApp.java
index 4133aaa0151..5b0a3733ff5 100644
--- a/src/main/java/seedu/address/MainApp.java
+++ b/src/main/java/seedu/address/MainApp.java
@@ -15,22 +15,24 @@
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.Mathutoring;
import seedu.address.model.Model;
import seedu.address.model.ModelManager;
-import seedu.address.model.ReadOnlyAddressBook;
+import seedu.address.model.ReadOnlyMathutoring;
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.JsonMathutoringStorage;
import seedu.address.storage.JsonUserPrefsStorage;
+import seedu.address.storage.MathutoringStorage;
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;
+
+
/**
* Runs the application.
*/
@@ -48,7 +50,7 @@ public class MainApp extends Application {
@Override
public void init() throws Exception {
- logger.info("=============================[ Initializing AddressBook ]===========================");
+ logger.info("=============================[ Initializing MATHTUTORING ]===========================");
super.init();
AppParameters appParameters = AppParameters.parse(getParameters());
@@ -56,8 +58,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);
+ MathutoringStorage mathutoringStorage = new JsonMathutoringStorage(userPrefs.getMathutoringFilePath());
+ storage = new StorageManager(mathutoringStorage, userPrefsStorage);
initLogging(config);
@@ -69,25 +71,25 @@ 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 mathutoring and {@code userPrefs}.
+ * The data from the sample mathutoring will be used instead if {@code storage}'s mathutoring is not found,
+ * or an empty mathutoring will be used instead if errors occur when reading {@code storage}'s mathutoring.
*/
private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) {
- Optional addressBookOptional;
- ReadOnlyAddressBook initialData;
+ Optional mathutoringOptional;
+ ReadOnlyMathutoring initialData;
try {
- addressBookOptional = storage.readAddressBook();
- if (!addressBookOptional.isPresent()) {
- logger.info("Data file not found. Will be starting with a sample AddressBook");
+ mathutoringOptional = storage.readMathutoring();
+ if (!mathutoringOptional.isPresent()) {
+ logger.info("Data file not found. Will be starting with a sample MATHTUTORING");
}
- initialData = addressBookOptional.orElseGet(SampleDataUtil::getSampleAddressBook);
+ initialData = mathutoringOptional.orElseGet(SampleDataUtil::getSampleMathutoring);
} 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 MATHTUTORING");
+ initialData = new Mathutoring();
} 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 MATHTUTORING");
+ initialData = new Mathutoring();
}
return new ModelManager(initialData, userPrefs);
@@ -151,7 +153,7 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) {
+ "Using default user prefs");
initializedPrefs = new UserPrefs();
} catch (IOException e) {
- logger.warning("Problem while reading from the file. Will be starting with an empty AddressBook");
+ logger.warning("Problem while reading from the file. Will be starting with an empty MATHTUTORING");
initializedPrefs = new UserPrefs();
}
@@ -167,13 +169,13 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) {
@Override
public void start(Stage primaryStage) {
- logger.info("Starting AddressBook " + MainApp.VERSION);
+ logger.info("Starting MATHTUTORING " + MainApp.VERSION);
ui.start(primaryStage);
}
@Override
public void stop() {
- logger.info("============================ [ Stopping Address Book ] =============================");
+ logger.info("============================ [ Stopping MATHTUTORING ] =============================");
try {
storage.saveUserPrefs(model.getUserPrefs());
} catch (IOException e) {
diff --git a/src/main/java/seedu/address/commons/core/GuiSettings.java b/src/main/java/seedu/address/commons/core/GuiSettings.java
index ba33653be67..da2f2d64e0e 100644
--- a/src/main/java/seedu/address/commons/core/GuiSettings.java
+++ b/src/main/java/seedu/address/commons/core/GuiSettings.java
@@ -1,6 +1,7 @@
package seedu.address.commons.core;
import java.awt.Point;
+import java.awt.Toolkit;
import java.io.Serializable;
import java.util.Objects;
@@ -21,8 +22,15 @@ public class GuiSettings implements Serializable {
* Constructs a {@code GuiSettings} with the default height, width and position.
*/
public GuiSettings() {
- windowWidth = DEFAULT_WIDTH;
- windowHeight = DEFAULT_HEIGHT;
+ String osName = System.getProperty("os.name").toLowerCase();
+
+ if (osName.indexOf("win") >= 0 || osName.indexOf("mac") >= 0) {
+ windowWidth = Toolkit.getDefaultToolkit().getScreenSize().getWidth() - 100;
+ windowHeight = Toolkit.getDefaultToolkit().getScreenSize().getHeight() - 100;
+ } else {
+ windowWidth = DEFAULT_WIDTH;
+ windowHeight = DEFAULT_HEIGHT;
+ }
windowCoordinates = null; // null represent no coordinates
}
diff --git a/src/main/java/seedu/address/commons/core/LogsCenter.java b/src/main/java/seedu/address/commons/core/LogsCenter.java
index 431e7185e76..4c5839a52f5 100644
--- a/src/main/java/seedu/address/commons/core/LogsCenter.java
+++ b/src/main/java/seedu/address/commons/core/LogsCenter.java
@@ -18,7 +18,7 @@
public class LogsCenter {
private static final int MAX_FILE_COUNT = 5;
private static final int MAX_FILE_SIZE_IN_BYTES = (int) (Math.pow(2, 20) * 5); // 5MB
- private static final String LOG_FILE = "addressbook.log";
+ private static final String LOG_FILE = "MATHUTORING.log";
private static Level currentLogLevel = Level.INFO;
private static final Logger logger = LogsCenter.getLogger(LogsCenter.class);
private static FileHandler fileHandler;
diff --git a/src/main/java/seedu/address/commons/core/Messages.java b/src/main/java/seedu/address/commons/core/Messages.java
index 1deb3a1e469..2e19af0cea0 100644
--- a/src/main/java/seedu/address/commons/core/Messages.java
+++ b/src/main/java/seedu/address/commons/core/Messages.java
@@ -7,7 +7,12 @@ 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!";
-
+ public static final String MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX = "The student index provided is invalid";
+ public static final String MESSAGE_STUDENTS_LISTED_OVERVIEW = "%1$d students listed!";
+ public static final String MESSAGE_INVALID_TASK_DISPLAYED_INDEX = "This task index provided is invalid";
+ public static final String MESSAGE_INVALID_SCORE_DISPLAYED_INDEX = "This score index provided is invalid";
+ public static final String MESSAGE_INVALID_DIRECTORY = "Path to directory is invalid!";
+ public static final String MESSAGE_BAD_FILE_INPUT = "Bad file input!";
+ public static final String MESSAGE_MISSING_FILE = "File does not exist in the path!";
+ public static final String MESSAGE_INVALID_FILE = "Path to file is invalid!";
}
diff --git a/src/main/java/seedu/address/commons/util/StringUtil.java b/src/main/java/seedu/address/commons/util/StringUtil.java
index 61cc8c9a1cb..7a4c774e7bf 100644
--- a/src/main/java/seedu/address/commons/util/StringUtil.java
+++ b/src/main/java/seedu/address/commons/util/StringUtil.java
@@ -65,4 +65,13 @@ public static boolean isNonZeroUnsignedInteger(String s) {
return false;
}
}
+
+ /**
+ * Returns true if {@code s} represents two non-blank strings separated by a white space
+ */
+ public static boolean isTwoIndexString(String s) {
+ requireNonNull(s);
+
+ return s.split("\\s+").length == 2;
+ }
}
diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/address/logic/Logic.java
index 92cd8fa605a..a93845821ec 100644
--- a/src/main/java/seedu/address/logic/Logic.java
+++ b/src/main/java/seedu/address/logic/Logic.java
@@ -1,5 +1,6 @@
package seedu.address.logic;
+import java.io.IOException;
import java.nio.file.Path;
import javafx.collections.ObservableList;
@@ -7,8 +8,9 @@
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;
+import seedu.address.model.Model;
+import seedu.address.model.ReadOnlyMathutoring;
+import seedu.address.model.student.Student;
/**
* API of the Logic component
@@ -24,19 +26,30 @@ public interface Logic {
CommandResult execute(String commandText) throws CommandException, ParseException;
/**
- * Returns the AddressBook.
+ * Returns the Mathutoring.
*
- * @see seedu.address.model.Model#getAddressBook()
+ * @see Model#getMathutoring()
*/
- ReadOnlyAddressBook getAddressBook();
+ ReadOnlyMathutoring getMathutoring();
- /** Returns an unmodifiable view of the filtered list of persons */
- ObservableList getFilteredPersonList();
+ /**
+ * Sets the mathutoring to the given mathutoring.
+ */
+ void setMathutoring(ReadOnlyMathutoring mathutoring) throws CommandException;
+
+ /**
+ * Stores the mathutoring data to the given file path.
+ */
+ void storeMathutoring(Path filePath) throws IOException;
+
+
+ /** Returns an unmodifiable view of the filtered list of students */
+ ObservableList getFilteredStudentList();
/**
- * Returns the user prefs' address book file path.
+ * Returns the user prefs' math tutoring file path.
*/
- Path getAddressBookFilePath();
+ Path getMathutoringFilePath();
/**
* Returns the user prefs' GUI settings.
@@ -47,4 +60,15 @@ public interface Logic {
* Set the user prefs' GUI settings.
*/
void setGuiSettings(GuiSettings guiSettings);
+
+ /**
+ * Returns the student that user want to check.
+ *
+ * @return the student being selected by the user.
+ */
+
+ Student findCheckedStudent();
+
+ void exportProgress(Student target, String completePath) throws IOException;
+
}
diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/address/logic/LogicManager.java
index 9d9c6d15bdc..eb46dad42e0 100644
--- a/src/main/java/seedu/address/logic/LogicManager.java
+++ b/src/main/java/seedu/address/logic/LogicManager.java
@@ -10,13 +10,15 @@
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.MathutoringParser;
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.model.ReadOnlyMathutoring;
+import seedu.address.model.student.Student;
import seedu.address.storage.Storage;
+
+
/**
* The main LogicManager of the app.
*/
@@ -26,7 +28,7 @@ public class LogicManager implements Logic {
private final Model model;
private final Storage storage;
- private final AddressBookParser addressBookParser;
+ private final MathutoringParser mathutoringParser;
/**
* Constructs a {@code LogicManager} with the given {@code Model} and {@code Storage}.
@@ -34,7 +36,7 @@ public class LogicManager implements Logic {
public LogicManager(Model model, Storage storage) {
this.model = model;
this.storage = storage;
- addressBookParser = new AddressBookParser();
+ mathutoringParser = new MathutoringParser();
}
@Override
@@ -42,11 +44,11 @@ public CommandResult execute(String commandText) throws CommandException, ParseE
logger.info("----------------[USER COMMAND][" + commandText + "]");
CommandResult commandResult;
- Command command = addressBookParser.parseCommand(commandText);
+ Command command = mathutoringParser.parseCommand(commandText);
commandResult = command.execute(model);
try {
- storage.saveAddressBook(model.getAddressBook());
+ storage.saveMathutoring(model.getMathutoring());
} catch (IOException ioe) {
throw new CommandException(FILE_OPS_ERROR_MESSAGE + ioe, ioe);
}
@@ -55,18 +57,33 @@ public CommandResult execute(String commandText) throws CommandException, ParseE
}
@Override
- public ReadOnlyAddressBook getAddressBook() {
- return model.getAddressBook();
+ public ReadOnlyMathutoring getMathutoring() {
+ return model.getMathutoring();
+ }
+
+ @Override
+ public void setMathutoring(ReadOnlyMathutoring mathutoring) throws CommandException {
+ model.setMathutoring(mathutoring);
+
+ try {
+ storage.saveMathutoring(model.getMathutoring());
+ } catch (IOException ioe) {
+ throw new CommandException(FILE_OPS_ERROR_MESSAGE + ioe, ioe);
+ }
}
@Override
- public ObservableList getFilteredPersonList() {
- return model.getFilteredPersonList();
+ public void storeMathutoring(Path filePath) throws IOException {
+ storage.saveMathutoring(model.getMathutoring(), filePath);
+ }
+
+ public ObservableList getFilteredStudentList() {
+ return model.getFilteredStudentList();
}
@Override
- public Path getAddressBookFilePath() {
- return model.getAddressBookFilePath();
+ public Path getMathutoringFilePath() {
+ return model.getMathutoringFilePath();
}
@Override
@@ -78,4 +95,14 @@ public GuiSettings getGuiSettings() {
public void setGuiSettings(GuiSettings guiSettings) {
model.setGuiSettings(guiSettings);
}
+
+ @Override
+ public Student findCheckedStudent() {
+ return model.findSelectedStudent();
+ }
+
+ @Override
+ public void exportProgress(Student target, String completePath) throws IOException {
+ model.exportProgress(target, completePath);
+ }
}
diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/address/logic/commands/AddCommand.java
index 71656d7c5c8..0e2c17272d2 100644
--- a/src/main/java/seedu/address/logic/commands/AddCommand.java
+++ b/src/main/java/seedu/address/logic/commands/AddCommand.java
@@ -4,57 +4,60 @@
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_PARENT_PHONE;
import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.model.Model;
-import seedu.address.model.person.Person;
+import seedu.address.model.student.Student;
/**
- * Adds a person to the address book.
+ * Adds a student to the student list.
*/
public class AddCommand extends Command {
public static final String COMMAND_WORD = "add";
- public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a person to the address book. "
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a student to the student list. "
+ "Parameters: "
+ PREFIX_NAME + "NAME "
+ PREFIX_PHONE + "PHONE "
+ PREFIX_EMAIL + "EMAIL "
+ PREFIX_ADDRESS + "ADDRESS "
+ + PREFIX_PARENT_PHONE + "PARENT PHONE "
+ "[" + PREFIX_TAG + "TAG]...\n"
+ "Example: " + COMMAND_WORD + " "
+ PREFIX_NAME + "John Doe "
+ PREFIX_PHONE + "98765432 "
+ PREFIX_EMAIL + "johnd@example.com "
+ PREFIX_ADDRESS + "311, Clementi Ave 2, #02-25 "
- + PREFIX_TAG + "friends "
- + PREFIX_TAG + "owesMoney";
+ + PREFIX_PARENT_PHONE + "87849999 "
+ + PREFIX_TAG + "primary "
+ + PREFIX_TAG + "female";
- public static final String MESSAGE_SUCCESS = "New person added: %1$s";
- public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book";
+ public static final String MESSAGE_SUCCESS = "New student added: %1$s";
+ public static final String MESSAGE_DUPLICATE_STUDENT = "This student already exists in the student list";
- private final Person toAdd;
+ private final Student toAdd;
/**
- * Creates an AddCommand to add the specified {@code Person}
+ * Creates an AddCommand to add the specified {@code Student}
*/
- public AddCommand(Person person) {
- requireNonNull(person);
- toAdd = person;
+ public AddCommand(Student student) {
+ requireNonNull(student);
+ toAdd = student;
}
@Override
public CommandResult execute(Model model) throws CommandException {
requireNonNull(model);
- if (model.hasPerson(toAdd)) {
- throw new CommandException(MESSAGE_DUPLICATE_PERSON);
+ if (model.hasStudent(toAdd)) {
+ throw new CommandException(MESSAGE_DUPLICATE_STUDENT);
}
- model.addPerson(toAdd);
+ model.addStudent(toAdd);
return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd));
}
diff --git a/src/main/java/seedu/address/logic/commands/AddScoreCommand.java b/src/main/java/seedu/address/logic/commands/AddScoreCommand.java
new file mode 100644
index 00000000000..68e45377371
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/AddScoreCommand.java
@@ -0,0 +1,85 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SCORE_DATE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SCORE_LABEL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SCORE_VALUE;
+
+import java.util.List;
+import java.util.logging.Logger;
+
+import seedu.address.AppParameters;
+import seedu.address.commons.core.LogsCenter;
+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.score.Score;
+import seedu.address.model.student.Student;
+
+/**
+ * Adds a score to a student identified using it's displayed index in the student list.
+ */
+public class AddScoreCommand extends Command {
+
+ public static final String COMMAND_WORD = "addscore";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a score detail to the specified student. "
+ + "Every score must have different date.\n"
+ + "Parameters: INDEX (must be a positive number) "
+ + PREFIX_SCORE_LABEL + "LABEL "
+ + PREFIX_SCORE_VALUE + "VALUE "
+ + PREFIX_SCORE_DATE + "DATE \n"
+ + "Example: " + COMMAND_WORD + " 1 "
+ + PREFIX_SCORE_LABEL + "Midterm "
+ + PREFIX_SCORE_VALUE + "99.8 "
+ + PREFIX_SCORE_DATE + "2012-08-09 ";
+
+ public static final String MESSAGE_SUCCESS = "Added score to Student %1$s: %2$s";
+ public static final String MESSAGE_DUPLICATE_SCORE = "This score already exists in this student's score list. "
+ + "Please recheck the score date entered.";
+
+ private static final Logger logger = LogsCenter.getLogger(AppParameters.class);
+ private final Index index;
+ private final Score toAdd;
+
+ /**
+ * Creates an AddScoreCommand to add the specified {@code Score} to a student
+ */
+ public AddScoreCommand(Index index, Score score) {
+ requireNonNull(index);
+ requireNonNull(score);
+ this.index = index;
+ this.toAdd = score;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+
+ List lastShownList = model.getFilteredStudentList();
+ if (index.getZeroBased() >= lastShownList.size()) {
+ logger.info(Messages.MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX);
+ throw new CommandException(Messages.MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX);
+ }
+
+ Student studentToEdit = lastShownList.get(index.getZeroBased());
+
+ if (studentToEdit.hasScore(toAdd)) {
+ logger.info(MESSAGE_DUPLICATE_SCORE);
+ throw new CommandException(MESSAGE_DUPLICATE_SCORE);
+ }
+
+ studentToEdit.addScore(toAdd);
+ return new CommandResult(String.format(MESSAGE_SUCCESS,
+ studentToEdit.getName(), toAdd));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof AddScoreCommand // instanceof handles nulls
+ && toAdd.equals(((AddScoreCommand) other).toAdd))
+ && index.equals(((AddScoreCommand) other).index); // state check;
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/AddTaskCommand.java b/src/main/java/seedu/address/logic/commands/AddTaskCommand.java
new file mode 100644
index 00000000000..3b28cb22e46
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/AddTaskCommand.java
@@ -0,0 +1,75 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_TASK_TITLE;
+
+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.student.Student;
+import seedu.address.model.task.Task;
+
+
+/**
+ * Adds a task to a student identified using it's displayed index in the student list.
+ */
+public class AddTaskCommand extends Command {
+
+ public static final String COMMAND_WORD = "addtask";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Adds a task to the student identified by the index number in the displayed student list.\n"
+ + "Parameters: INDEX_OF_STUDENT (must be a positive integer) "
+ + PREFIX_TASK_TITLE + "TASK_TITLE\n"
+ + "Example: " + COMMAND_WORD + " 1 "
+ + PREFIX_TASK_TITLE + "Complete E Math Paper 1";
+
+ public static final String MESSAGE_ADD_TASK_SUCCESS = "Added task for %1$s: %2$s";
+
+ public static final String MESSAGE_DUPLICATE_TASK = "This task already exists in this student's task list";
+
+ private final Index targetIndex;
+ private final Task taskToAdd;
+
+ /**
+ * Creates an AddTaskCommand to add the specified {@code Task} to
+ * a student
+ */
+ public AddTaskCommand(Index targetIndex, Task taskToAdd) {
+ requireAllNonNull(targetIndex, taskToAdd);
+ this.targetIndex = targetIndex;
+ this.taskToAdd = taskToAdd;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ List lastShownList = model.getFilteredStudentList();
+
+ if (targetIndex.getZeroBased() >= lastShownList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX);
+ }
+
+ Student studentToAddTaskTo = lastShownList.get(targetIndex.getZeroBased());
+
+ if (studentToAddTaskTo.hasTask(taskToAdd)) {
+ throw new CommandException(MESSAGE_DUPLICATE_TASK);
+ }
+
+ studentToAddTaskTo.addTask(taskToAdd);
+ return new CommandResult(String.format(MESSAGE_ADD_TASK_SUCCESS,
+ studentToAddTaskTo.getName(), taskToAdd.getName()));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof AddTaskCommand // instanceof handles nulls
+ && taskToAdd.equals(((AddTaskCommand) other).taskToAdd)
+ && targetIndex.equals(((AddTaskCommand) other).targetIndex)); // state check;
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/CheckCommand.java b/src/main/java/seedu/address/logic/commands/CheckCommand.java
new file mode 100644
index 00000000000..2d958d013b2
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/CheckCommand.java
@@ -0,0 +1,78 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+import java.util.logging.Logger;
+
+import seedu.address.AppParameters;
+import seedu.address.commons.core.LogsCenter;
+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.student.Student;
+
+/**
+ * Creates check command for user to find the respective student's information they want to check.
+ */
+public class CheckCommand extends Command {
+ public static final String COMMAND_WORD = "check";
+ public static final String MESSAGE_CHECK_STUDENT_SUCCESS = "Check Student: %1$s";
+ private static final Logger logger = LogsCenter.getLogger(AppParameters.class);
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Check the student identified by the index number used in the displayed student list.\n"
+ + "Parameters: INDEX (must be a positive integer)\n"
+ + "Example: " + COMMAND_WORD + " 1";
+
+ private final Index targetIndex;
+
+ /**
+ * Constructs a {@code CheckCommand} with the specified {@code Index}.
+ *
+ * @param targetIndex The index of the targeted student that going to be checked.
+ */
+ public CheckCommand(Index targetIndex) {
+ this.targetIndex = targetIndex;
+ }
+
+ /**
+ * Executes the command and returns the result message.
+ *
+ * @param model {@code Model} which the command should operate on.
+ * @return Feedback message of the check command result for display.
+ * @throws CommandException If an error occurs during command execution.
+ */
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+
+ List studentList = model.getFilteredStudentList();
+
+ if (targetIndex.getZeroBased() >= studentList.size()) {
+ logger.info("The student index is exceeding the total number of students. Index is invalid.");
+ throw new CommandException(Messages.MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX);
+ }
+
+ Student studentToCheck = studentList.get(targetIndex.getZeroBased());
+ model.checkStudent(studentToCheck);
+
+ logger.info("Check student success.");
+
+ return new CommandResult(String.format(MESSAGE_CHECK_STUDENT_SUCCESS, studentToCheck));
+ }
+
+ /**
+ * Checks if an object is equal to the check command.
+ *
+ * @param other The object that need to compare with.
+ * @return A boolean value if the object and the check command are the same.
+ */
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof CheckCommand // instanceof handles nulls
+ && targetIndex.equals(((CheckCommand) other).targetIndex)); // state check
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/ClearCommand.java b/src/main/java/seedu/address/logic/commands/ClearCommand.java
index 9c86b1fa6e4..79e324eb5bb 100644
--- a/src/main/java/seedu/address/logic/commands/ClearCommand.java
+++ b/src/main/java/seedu/address/logic/commands/ClearCommand.java
@@ -2,22 +2,24 @@
import static java.util.Objects.requireNonNull;
-import seedu.address.model.AddressBook;
+import seedu.address.model.Mathutoring;
import seedu.address.model.Model;
+
/**
- * Clears the address book.
+ * Clears the mathutoring.
*/
public class ClearCommand 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_SUCCESS = "Student list has been cleared!";
@Override
public CommandResult execute(Model model) {
requireNonNull(model);
- model.setAddressBook(new AddressBook());
+ model.checkStudent(null);
+ model.setMathutoring(new Mathutoring());
return new CommandResult(MESSAGE_SUCCESS);
}
}
diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/seedu/address/logic/commands/CommandResult.java
index 92f900b7916..c85a24293e9 100644
--- a/src/main/java/seedu/address/logic/commands/CommandResult.java
+++ b/src/main/java/seedu/address/logic/commands/CommandResult.java
@@ -17,6 +17,9 @@ public class CommandResult {
/** The application should exit. */
private final boolean exit;
+ /** Score list tab should switch. */
+ private final boolean tabSwitch;
+
/**
* Constructs a {@code CommandResult} with the specified fields.
*/
@@ -24,6 +27,20 @@ public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) {
this.feedbackToUser = requireNonNull(feedbackToUser);
this.showHelp = showHelp;
this.exit = exit;
+ this.tabSwitch = false;
+ }
+
+ /**
+ * Constructs a {@code CommandResult} with switch field.
+ *
+ * @param feedbackToUser The feedback to user.
+ * @param tabSwitch If the tab should be switched.
+ */
+ public CommandResult(String feedbackToUser, boolean tabSwitch) {
+ this.feedbackToUser = requireNonNull(feedbackToUser);
+ this.showHelp = false;
+ this.exit = false;
+ this.tabSwitch = tabSwitch;
}
/**
@@ -46,6 +63,15 @@ public boolean isExit() {
return exit;
}
+ /**
+ * Returns tab should be switched.
+ *
+ * @return Boolean value to indicate the tab should be switched.
+ */
+ public boolean isTabSwitch() {
+ return tabSwitch;
+ }
+
@Override
public boolean equals(Object other) {
if (other == this) {
diff --git a/src/main/java/seedu/address/logic/commands/DeleteCommand.java b/src/main/java/seedu/address/logic/commands/DeleteCommand.java
index 02fd256acba..943fc875160 100644
--- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java
+++ b/src/main/java/seedu/address/logic/commands/DeleteCommand.java
@@ -8,21 +8,21 @@
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;
+import seedu.address.model.student.Student;
/**
- * Deletes a person identified using it's displayed index from the address book.
+ * Deletes a student identified using it's displayed index from the mathutoring.
*/
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"
+ + ": Deletes the student identified by the index number used in the displayed student 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";
+ public static final String MESSAGE_DELETE_STUDENT_SUCCESS = "Deleted Student: %1$s";
private final Index targetIndex;
@@ -33,15 +33,20 @@ public DeleteCommand(Index targetIndex) {
@Override
public CommandResult execute(Model model) throws CommandException {
requireNonNull(model);
- List lastShownList = model.getFilteredPersonList();
+ List lastShownList = model.getFilteredStudentList();
if (targetIndex.getZeroBased() >= lastShownList.size()) {
- throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ throw new CommandException(Messages.MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX);
}
- Person personToDelete = lastShownList.get(targetIndex.getZeroBased());
- model.deletePerson(personToDelete);
- return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, personToDelete));
+ Student studentToDelete = lastShownList.get(targetIndex.getZeroBased());
+ model.deleteStudent(studentToDelete);
+
+ if (model.findSelectedStudent() != null && model.findSelectedStudent().equals(studentToDelete)) {
+ model.checkStudent(null);
+ }
+
+ return new CommandResult(String.format(MESSAGE_DELETE_STUDENT_SUCCESS, studentToDelete));
}
@Override
diff --git a/src/main/java/seedu/address/logic/commands/DeleteScoreCommand.java b/src/main/java/seedu/address/logic/commands/DeleteScoreCommand.java
new file mode 100644
index 00000000000..469c511e8eb
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/DeleteScoreCommand.java
@@ -0,0 +1,77 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+
+import java.util.List;
+import java.util.logging.Logger;
+
+import seedu.address.AppParameters;
+import seedu.address.commons.core.LogsCenter;
+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.score.Score;
+import seedu.address.model.student.Student;
+
+/**
+ * Deletes a score identified using it's displayed index a specified student's score list.
+ */
+public class DeleteScoreCommand extends Command {
+
+ public static final String COMMAND_WORD = "deletescore";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Deletes the score identified by the index number used in the score list of "
+ + "the student specified.\n"
+ + "Parameters: INDEX_OF_STUDENT INDEX_OF_SCORE (must be a positive integer)\n"
+ + "Example: " + COMMAND_WORD + " 1 2";
+
+ public static final String MESSAGE_DELETE_SCORE_SUCCESS = "Deleted score for %1$s: %2$s";
+ private static final Logger logger = LogsCenter.getLogger(AppParameters.class);
+ private final Index studentIndex;
+ private final Index scoreIndex;
+
+ /**
+ * Creates a DeleteScoreCommand to delete the specified score from a specified student
+ */
+ public DeleteScoreCommand(Index studentIndex, Index scoreIndex) {
+ requireAllNonNull(studentIndex, scoreIndex);
+ this.studentIndex = studentIndex;
+ this.scoreIndex = scoreIndex;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ List lastShownList = model.getFilteredStudentList();
+
+ if (studentIndex.getZeroBased() >= lastShownList.size()) {
+ logger.info(Messages.MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX);
+ throw new CommandException(Messages.MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX);
+ }
+
+ Student studentToDeleteScore = lastShownList.get(studentIndex.getZeroBased());
+
+ List lastShownScoreList = studentToDeleteScore.getFilteredScoreList();
+ if (scoreIndex.getZeroBased() >= lastShownScoreList.size()) {
+ logger.info(Messages.MESSAGE_INVALID_SCORE_DISPLAYED_INDEX);
+ throw new CommandException(Messages.MESSAGE_INVALID_SCORE_DISPLAYED_INDEX);
+ }
+
+ Score scoreToDelete = lastShownScoreList.get(scoreIndex.getZeroBased());
+ studentToDeleteScore.removeScore(scoreToDelete);
+
+ return new CommandResult(String.format(MESSAGE_DELETE_SCORE_SUCCESS,
+ studentToDeleteScore.getName(), scoreToDelete));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof DeleteScoreCommand // instanceof handles nulls
+ && studentIndex.equals(((DeleteScoreCommand) other).studentIndex)
+ && scoreIndex.equals(((DeleteScoreCommand) other).scoreIndex)); // state check
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/DeleteTaskCommand.java b/src/main/java/seedu/address/logic/commands/DeleteTaskCommand.java
new file mode 100644
index 00000000000..4083e3c4b51
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/DeleteTaskCommand.java
@@ -0,0 +1,74 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+
+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.student.Student;
+import seedu.address.model.task.Task;
+
+
+/**
+ * Deletes a task identified by its displayed index from a specified student's task list.
+ */
+public class DeleteTaskCommand extends Command {
+
+ public static final String COMMAND_WORD = "deletetask";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Deletes the task identified by the index number used in the task list of the "
+ + "student specified.\n"
+ + "Parameters: INDEX_OF_STUDENT INDEX_OF_TASK (must be a positive integer)\n"
+ + "Example: " + COMMAND_WORD + " 2 3";
+
+ public static final String MESSAGE_DELETE_TASK_SUCCESS = "Deleted task for %1$s: %2$s";
+
+ private final Index studentIndex;
+ private final Index taskIndex;
+
+ /**
+ * Creates an DeleteTaskCommand to delete the specified task from
+ * a specified student
+ */
+ public DeleteTaskCommand(Index studentIndex, Index taskIndex) {
+ requireAllNonNull(studentIndex, taskIndex);
+ this.taskIndex = taskIndex;
+ this.studentIndex = studentIndex;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ List lastShownList = model.getFilteredStudentList();
+
+ if (studentIndex.getZeroBased() >= lastShownList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX);
+ }
+
+ Student studentToDeleteTask = lastShownList.get(studentIndex.getZeroBased());
+ List studentTaskList = studentToDeleteTask.getFilteredTaskList();
+
+ if (taskIndex.getZeroBased() >= studentTaskList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+ }
+
+ Task taskToDelete = studentTaskList.get(taskIndex.getZeroBased());
+ studentToDeleteTask.removeTask(taskToDelete);
+
+ return new CommandResult(String.format(MESSAGE_DELETE_TASK_SUCCESS,
+ studentToDeleteTask.getName(), taskToDelete.getName()));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof DeleteTaskCommand // instanceof handles nulls
+ && studentIndex.equals(((DeleteTaskCommand) other).studentIndex)
+ && taskIndex.equals(((DeleteTaskCommand) other).taskIndex)); // state check;
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/EditCommand.java b/src/main/java/seedu/address/logic/commands/EditCommand.java
index 7e36114902f..0ee5db80a7f 100644
--- a/src/main/java/seedu/address/logic/commands/EditCommand.java
+++ b/src/main/java/seedu/address/logic/commands/EditCommand.java
@@ -4,9 +4,10 @@
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_PARENT_PHONE;
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.address.model.Model.PREDICATE_SHOW_ALL_STUDENTS;
import java.util.Collections;
import java.util.HashSet;
@@ -19,87 +20,99 @@
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.student.Address;
+import seedu.address.model.student.Email;
+import seedu.address.model.student.Name;
+import seedu.address.model.student.Phone;
+import seedu.address.model.student.Student;
import seedu.address.model.tag.Tag;
+
/**
- * Edits the details of an existing person in the address book.
+ * Edits the details of an existing student in the mathutoring.
*/
public class EditCommand extends Command {
public static final String COMMAND_WORD = "edit";
- public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the person identified "
- + "by the index number used in the displayed person list. "
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the student identified "
+ + "by the index number used in the displayed student 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_PARENT_PHONE + "PARENT PHONE] "
+ "[" + PREFIX_TAG + "TAG]...\n"
+ "Example: " + COMMAND_WORD + " 1 "
+ PREFIX_PHONE + "91234567 "
+ PREFIX_EMAIL + "johndoe@example.com";
- public static final String MESSAGE_EDIT_PERSON_SUCCESS = "Edited Person: %1$s";
+ public static final String MESSAGE_EDIT_STUDENT_SUCCESS = "Edited Student: %1$s";
public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided.";
- public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book.";
+ public static final String MESSAGE_DUPLICATE_STUDENT = "This student already exists in the mathutoring.";
private final Index index;
- private final EditPersonDescriptor editPersonDescriptor;
+ private final EditStudentDescriptor editStudentDescriptor;
/**
- * @param index of the person in the filtered person list to edit
- * @param editPersonDescriptor details to edit the person with
+ * @param index of the student in the filtered student list to edit
+ * @param editStudentDescriptor details to edit the student with
*/
- public EditCommand(Index index, EditPersonDescriptor editPersonDescriptor) {
+ public EditCommand(Index index, EditStudentDescriptor editStudentDescriptor) {
requireNonNull(index);
- requireNonNull(editPersonDescriptor);
+ requireNonNull(editStudentDescriptor);
this.index = index;
- this.editPersonDescriptor = new EditPersonDescriptor(editPersonDescriptor);
+ this.editStudentDescriptor = new EditStudentDescriptor(editStudentDescriptor);
}
@Override
public CommandResult execute(Model model) throws CommandException {
requireNonNull(model);
- List lastShownList = model.getFilteredPersonList();
+ List lastShownList = model.getFilteredStudentList();
if (index.getZeroBased() >= lastShownList.size()) {
- throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ throw new CommandException(Messages.MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX);
+ }
+
+ Student studentToEdit = lastShownList.get(index.getZeroBased());
+ Student editedStudent = createEditedStudent(studentToEdit, editStudentDescriptor);
+
+ if (!studentToEdit.isSameStudent(editedStudent) && model.hasStudent(editedStudent)) {
+ throw new CommandException(MESSAGE_DUPLICATE_STUDENT);
}
- Person personToEdit = lastShownList.get(index.getZeroBased());
- Person editedPerson = createEditedPerson(personToEdit, editPersonDescriptor);
- if (!personToEdit.isSamePerson(editedPerson) && model.hasPerson(editedPerson)) {
- throw new CommandException(MESSAGE_DUPLICATE_PERSON);
+ if (model.findSelectedStudent() != null && model.findSelectedStudent().equals(studentToEdit)) {
+ model.checkStudent(null);
}
- model.setPerson(personToEdit, editedPerson);
- model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
- return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, editedPerson));
+ model.setStudent(studentToEdit, editedStudent);
+ model.updateFilteredStudentList(PREDICATE_SHOW_ALL_STUDENTS);
+ return new CommandResult(String.format(MESSAGE_EDIT_STUDENT_SUCCESS, editedStudent));
+
}
/**
- * Creates and returns a {@code Person} with the details of {@code personToEdit}
- * edited with {@code editPersonDescriptor}.
+ * Creates and returns a {@code Student} with the details of {@code studentToEdit}
+ * edited with {@code editStudentDescriptor}.
*/
- private static Person createEditedPerson(Person personToEdit, EditPersonDescriptor editPersonDescriptor) {
- assert personToEdit != null;
-
- Name updatedName = editPersonDescriptor.getName().orElse(personToEdit.getName());
- Phone updatedPhone = editPersonDescriptor.getPhone().orElse(personToEdit.getPhone());
- Email updatedEmail = editPersonDescriptor.getEmail().orElse(personToEdit.getEmail());
- Address updatedAddress = editPersonDescriptor.getAddress().orElse(personToEdit.getAddress());
- Set updatedTags = editPersonDescriptor.getTags().orElse(personToEdit.getTags());
-
- return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedTags);
+ private static Student createEditedStudent(Student studentToEdit, EditStudentDescriptor editStudentDescriptor) {
+ assert studentToEdit != null;
+
+ Name updatedName = editStudentDescriptor.getName().orElse(studentToEdit.getName());
+ Phone updatedPhone = editStudentDescriptor.getPhone().orElse(studentToEdit.getPhone());
+ Email updatedEmail = editStudentDescriptor.getEmail().orElse(studentToEdit.getEmail());
+ Address updatedAddress = editStudentDescriptor.getAddress().orElse(studentToEdit.getAddress());
+ Phone updatedParentPhone = editStudentDescriptor.getParentPhone().orElse(studentToEdit.getParentPhone());
+ Set updatedTags = editStudentDescriptor.getTags().orElse(studentToEdit.getTags());
+ seedu.address.model.task.UniqueTaskList originalUniqueTaskList = studentToEdit.getTaskList();
+ seedu.address.model.score.UniqueScoreList originalUniqueScoreList = studentToEdit.getScoreList();
+
+ return new Student(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedParentPhone, updatedTags,
+ originalUniqueTaskList, originalUniqueScoreList);
}
@Override
@@ -117,31 +130,33 @@ public boolean equals(Object other) {
// state check
EditCommand e = (EditCommand) other;
return index.equals(e.index)
- && editPersonDescriptor.equals(e.editPersonDescriptor);
+ && editStudentDescriptor.equals(e.editStudentDescriptor);
}
/**
- * Stores the details to edit the person with. Each non-empty field value will replace the
- * corresponding field value of the person.
+ * Stores the details to edit the student with. Each non-empty field value will replace the
+ * corresponding field value of the student.
*/
- public static class EditPersonDescriptor {
+ public static class EditStudentDescriptor {
private Name name;
private Phone phone;
private Email email;
private Address address;
+ private Phone parentPhone;
private Set tags;
- public EditPersonDescriptor() {}
+ public EditStudentDescriptor() {}
/**
* Copy constructor.
* A defensive copy of {@code tags} is used internally.
*/
- public EditPersonDescriptor(EditPersonDescriptor toCopy) {
+ public EditStudentDescriptor(EditStudentDescriptor toCopy) {
setName(toCopy.name);
setPhone(toCopy.phone);
setEmail(toCopy.email);
setAddress(toCopy.address);
+ setParentPhone(toCopy.parentPhone);
setTags(toCopy.tags);
}
@@ -149,7 +164,7 @@ public EditPersonDescriptor(EditPersonDescriptor toCopy) {
* 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, address, parentPhone, tags);
}
public void setName(Name name) {
@@ -184,6 +199,14 @@ public Optional getAddress() {
return Optional.ofNullable(address);
}
+ public void setParentPhone(Phone parentPhone) {
+ this.parentPhone = parentPhone;
+ }
+
+ public Optional getParentPhone() {
+ return Optional.ofNullable(parentPhone);
+ }
+
/**
* Sets {@code tags} to this object's {@code tags}.
* A defensive copy of {@code tags} is used internally.
@@ -209,17 +232,18 @@ public boolean equals(Object other) {
}
// instanceof handles nulls
- if (!(other instanceof EditPersonDescriptor)) {
+ if (!(other instanceof EditStudentDescriptor)) {
return false;
}
// state check
- EditPersonDescriptor e = (EditPersonDescriptor) other;
+ EditStudentDescriptor e = (EditStudentDescriptor) other;
return getName().equals(e.getName())
&& getPhone().equals(e.getPhone())
&& getEmail().equals(e.getEmail())
&& getAddress().equals(e.getAddress())
+ && getParentPhone().equals(e.getParentPhone())
&& getTags().equals(e.getTags());
}
}
diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/seedu/address/logic/commands/ExitCommand.java
index 3dd85a8ba90..a1696139cac 100644
--- a/src/main/java/seedu/address/logic/commands/ExitCommand.java
+++ b/src/main/java/seedu/address/logic/commands/ExitCommand.java
@@ -9,11 +9,11 @@ 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 Mathutoring as requested ...";
@Override
public CommandResult execute(Model model) {
+ model.checkStudent(null);
return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true);
}
-
}
diff --git a/src/main/java/seedu/address/logic/commands/ExportDataCommand.java b/src/main/java/seedu/address/logic/commands/ExportDataCommand.java
new file mode 100644
index 00000000000..3ff282af3f1
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/ExportDataCommand.java
@@ -0,0 +1,62 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_FILEPATH;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import seedu.address.commons.util.FileUtil;
+import seedu.address.commons.util.JsonUtil;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.ReadOnlyMathutoring;
+import seedu.address.storage.JsonSerializableMathutoring;
+
+
+
+/**
+ * Imports data from a JSON file.
+ */
+public class ExportDataCommand extends Command {
+
+ public static final String COMMAND_WORD = "export";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Export data to a Directory. "
+ + "Parameters: "
+ + PREFIX_FILEPATH + "FILE_PATH [Optional] \n"
+ + "Example: " + COMMAND_WORD + " "
+ + "p/C:\\Users\\User\\Desktop\\";
+
+ public static final String MESSAGE_SUCCESS = "Data exported successfully.";
+
+ private final Path filePath;
+
+ /**
+ * Creates an ExportDataCommand to export the data at the specified {@code filePath}
+ */
+ public ExportDataCommand(String filePath) {
+ requireNonNull(filePath);
+ if (filePath.isEmpty()) {
+ this.filePath = Paths.get("data\\data.json");
+ } else {
+ this.filePath = Paths.get(filePath, "data.json");
+ }
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ requireNonNull(model.getMathutoring());
+
+ try {
+ ReadOnlyMathutoring data = model.getMathutoring();
+ FileUtil.createIfMissing(filePath);
+ JsonUtil.saveJsonFile(new JsonSerializableMathutoring(data), filePath);
+ } catch (IOException e) {
+ throw new CommandException("Error!\n" + e.getMessage());
+ }
+ return new CommandResult(MESSAGE_SUCCESS);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/ExportProgressCommand.java b/src/main/java/seedu/address/logic/commands/ExportProgressCommand.java
new file mode 100644
index 00000000000..4cd56d0843f
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/ExportProgressCommand.java
@@ -0,0 +1,95 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_FILEPATH;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.logging.Logger;
+
+import seedu.address.AppParameters;
+import seedu.address.commons.core.LogsCenter;
+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.student.Student;
+
+/**
+ * Exports a student's progress.
+ */
+public class ExportProgressCommand extends Command {
+ public static final String COMMAND_WORD = "exportp";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Exports a student's progress.\n"
+ + "Parameters: INDEX (must be a positive integer) "
+ + "[" + PREFIX_FILEPATH + "FILE PATH TO DIRECTORY]\n"
+ + "Example: " + COMMAND_WORD + " "
+ + "1 "
+ + PREFIX_FILEPATH + Paths.get("").toAbsolutePath();
+
+ public static final String MESSAGE_SUCCESS = "%1$s's progress report exported in %2$s with filename %3$s";
+
+ private static final Logger logger = LogsCenter.getLogger(AppParameters.class);
+ private final Index targetIndex;
+ private String filePath;
+
+ /**
+ * Creates an ExportProgressCommand to export the specified Student {@code Student}'s progress to a PDF file.
+ */
+ public ExportProgressCommand(Index targetIndex, String filePath) {
+ requireNonNull(targetIndex);
+ this.targetIndex = targetIndex;
+ this.filePath = filePath;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ List studentList = model.getFilteredStudentList();
+
+ if (targetIndex.getZeroBased() >= studentList.size()) {
+ logger.info("The student index is exceeding the total number of students. Index is invalid.");
+ throw new CommandException(Messages.MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX);
+ }
+
+ Student studentToExport = studentList.get(targetIndex.getZeroBased());
+ String studentName = studentToExport.getName().fullName;
+ String fileName = studentName + "'s Progress Report.pdf";
+
+ if (this.filePath.equals("")) {
+ this.filePath = "data";
+ }
+
+ Path parentDir = Paths.get(this.filePath).toAbsolutePath().getParent();
+
+ if (parentDir != null) {
+ try {
+ Files.createDirectories(parentDir);
+ } catch (IOException e) {
+ throw new CommandException(e.getMessage());
+ }
+ }
+
+ try {
+ model.exportProgress(studentToExport, String.valueOf(Paths.get(this.filePath, fileName).toAbsolutePath()));
+ } catch (IOException e) {
+ throw new CommandException("Error!\n" + e.getMessage());
+ }
+ if (this.filePath.equals("data")) {
+ this.filePath = Paths.get("data").toAbsolutePath().toString();
+ }
+ return new CommandResult(String.format(MESSAGE_SUCCESS, studentToExport.getName().fullName,
+ this.filePath, fileName));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof ExportProgressCommand // instanceof handles nulls
+ && targetIndex.equals(((ExportProgressCommand) other).targetIndex)); // state check
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/FilterCommand.java b/src/main/java/seedu/address/logic/commands/FilterCommand.java
new file mode 100644
index 00000000000..74baa8f90c8
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/FilterCommand.java
@@ -0,0 +1,69 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.logging.Logger;
+
+import seedu.address.AppParameters;
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.commons.core.Messages;
+import seedu.address.model.Model;
+import seedu.address.model.tag.TagContainsKeywordsPredicate;
+
+/**
+ * Filters and list all students in mathutoring that has tag/s which contains any of the
+ * filter keyword/s.
+ * Keyword matching is case-insensitive, but only full words will be matched.
+ */
+public class FilterCommand extends Command {
+ public static final String COMMAND_WORD = "filter";
+ private static final Logger logger = LogsCenter.getLogger(AppParameters.class);
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all students whose has any tag "
+ + "that matches with the filter condition (case-insensitive) and displays the "
+ + "filtered result as a list with index numbers.\n"
+ + "Parameters: KEYWORD [MORE_KEYWORDS]...\n"
+ + "Example: " + COMMAND_WORD + " primary";
+
+ private final TagContainsKeywordsPredicate predicate;
+
+ /**
+ * Constructs a {@code FilterCommand} with the specified {@code TagContainsKeywordsPredicate},
+ * filter out the student/s that fulfill the given predicate.
+ *
+ * @param predicate The user provided predicate.
+ */
+ public FilterCommand(TagContainsKeywordsPredicate predicate) {
+ this.predicate = predicate;
+ }
+
+ /**
+ * Executes the command and returns the result message.
+ *
+ * @param model {@code Model} which the command should operate on.
+ * @return Feedback message of the filter command result for display.
+ */
+ @Override
+ public CommandResult execute(Model model) {
+ requireNonNull(model);
+ model.updateFilteredStudentList(predicate);
+
+ logger.info("Filter student base on their tag success.");
+
+ return new CommandResult(
+ String.format(Messages.MESSAGE_STUDENTS_LISTED_OVERVIEW, model.getFilteredStudentList().size()));
+ }
+
+ /**
+ * Checks if an object is equal to the filter command.
+ *
+ * @param other The object that need to compare with.
+ * @return A boolean value if the object and the filter command are the same.
+ */
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof FilterCommand // instanceof handles nulls
+ && predicate.equals(((FilterCommand) other).predicate)); // state check
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/FindCommand.java b/src/main/java/seedu/address/logic/commands/FindCommand.java
index d6b19b0a0de..af8fb8f2538 100644
--- a/src/main/java/seedu/address/logic/commands/FindCommand.java
+++ b/src/main/java/seedu/address/logic/commands/FindCommand.java
@@ -4,17 +4,17 @@
import seedu.address.commons.core.Messages;
import seedu.address.model.Model;
-import seedu.address.model.person.NameContainsKeywordsPredicate;
+import seedu.address.model.student.NameContainsKeywordsPredicate;
/**
- * Finds and lists all persons in address book whose name contains any of the argument keywords.
- * Keyword matching is case insensitive.
+ * Finds and lists all students in mathutoring 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 "
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all students 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";
@@ -28,9 +28,9 @@ public FindCommand(NameContainsKeywordsPredicate predicate) {
@Override
public CommandResult execute(Model model) {
requireNonNull(model);
- model.updateFilteredPersonList(predicate);
+ model.updateFilteredStudentList(predicate);
return new CommandResult(
- String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredPersonList().size()));
+ String.format(Messages.MESSAGE_STUDENTS_LISTED_OVERVIEW, model.getFilteredStudentList().size()));
}
@Override
diff --git a/src/main/java/seedu/address/logic/commands/ImportDataCommand.java b/src/main/java/seedu/address/logic/commands/ImportDataCommand.java
new file mode 100644
index 00000000000..127f6e2554f
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/ImportDataCommand.java
@@ -0,0 +1,64 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.core.Messages.MESSAGE_BAD_FILE_INPUT;
+import static seedu.address.commons.core.Messages.MESSAGE_INVALID_FILE;
+import static seedu.address.commons.core.Messages.MESSAGE_MISSING_FILE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_FILEPATH;
+
+import java.io.IOException;
+import java.nio.file.InvalidPathException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.NoSuchElementException;
+
+import seedu.address.commons.exceptions.DataConversionException;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.ReadOnlyMathutoring;
+import seedu.address.storage.JsonMathutoringStorage;
+import seedu.address.storage.MathutoringStorage;
+
+/**
+ * Imports data from a JSON file.
+ */
+public class ImportDataCommand extends Command {
+
+ public static final String COMMAND_WORD = "import";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Imports data from a JSON file. "
+ + "Parameters: "
+ + PREFIX_FILEPATH + "FILE_PATH\n"
+ + "Example: " + COMMAND_WORD + " "
+ + "p/C:\\Users\\User\\Desktop\\data.json";
+
+ public static final String MESSAGE_SUCCESS = "Data imported successfully.";
+
+ private final String filePath;
+
+ public ImportDataCommand(String filePath) {
+ this.filePath = filePath;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+
+ try {
+ Path dataFilePath = Paths.get(filePath);
+ MathutoringStorage mathutoringStorage = new JsonMathutoringStorage(dataFilePath);
+ ReadOnlyMathutoring data = mathutoringStorage.readMathutoring().get();
+ model.setMathutoring(data);
+ } catch (DataConversionException d) {
+ throw new CommandException(MESSAGE_BAD_FILE_INPUT + "\n" + MESSAGE_USAGE);
+ } catch (IOException e) {
+ throw new CommandException("Error while importing data");
+ } catch (NoSuchElementException e) {
+ throw new CommandException(MESSAGE_MISSING_FILE);
+ } catch (InvalidPathException e) {
+ throw new CommandException(MESSAGE_INVALID_FILE);
+ }
+
+ return new CommandResult(MESSAGE_SUCCESS);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/ListCommand.java b/src/main/java/seedu/address/logic/commands/ListCommand.java
index 84be6ad2596..5785f622f8a 100644
--- a/src/main/java/seedu/address/logic/commands/ListCommand.java
+++ b/src/main/java/seedu/address/logic/commands/ListCommand.java
@@ -1,24 +1,24 @@
package seedu.address.logic.commands;
import static java.util.Objects.requireNonNull;
-import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_STUDENTS;
import seedu.address.model.Model;
/**
- * Lists all persons in the address book to the user.
+ * Lists all students in the mathutoring to the user.
*/
public class ListCommand extends Command {
public static final String COMMAND_WORD = "list";
- public static final String MESSAGE_SUCCESS = "Listed all persons";
+ public static final String MESSAGE_SUCCESS = "Listed all students";
@Override
public CommandResult execute(Model model) {
requireNonNull(model);
- model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+ model.updateFilteredStudentList(PREDICATE_SHOW_ALL_STUDENTS);
return new CommandResult(MESSAGE_SUCCESS);
}
}
diff --git a/src/main/java/seedu/address/logic/commands/MarkTaskCompleteCommand.java b/src/main/java/seedu/address/logic/commands/MarkTaskCompleteCommand.java
new file mode 100644
index 00000000000..0e99e3163bf
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/MarkTaskCompleteCommand.java
@@ -0,0 +1,74 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+
+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.student.Student;
+import seedu.address.model.task.Task;
+
+
+/**
+ * Marks a task identified by its displayed index from a specified student's task list as complete.
+ */
+public class MarkTaskCompleteCommand extends Command {
+
+ public static final String COMMAND_WORD = "markcomplete";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Marks the task identified by its index number in the task list of the "
+ + "student specified as complete.\n"
+ + "Parameters: INDEX_OF_STUDENT INDEX_OF_TASK (must be a positive integer)\n"
+ + "Example: " + COMMAND_WORD + " 1 2";
+
+ public static final String MESSAGE_MARK_COMPLETE_SUCCESS = "Marked task as complete for %1$s: %2$s";
+
+ private final Index studentIndex;
+ private final Index taskIndex;
+
+ /**
+ * Creates an MarkTaskCompleteCommand to mark the specified task of
+ * a specified student as complete
+ */
+ public MarkTaskCompleteCommand(Index studentIndex, Index taskIndex) {
+ requireAllNonNull(studentIndex, taskIndex);
+ this.taskIndex = taskIndex;
+ this.studentIndex = studentIndex;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ List lastShownList = model.getFilteredStudentList();
+
+ if (studentIndex.getZeroBased() >= lastShownList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX);
+ }
+
+ Student studentToMarkTask = lastShownList.get(studentIndex.getZeroBased());
+ List studentTaskList = studentToMarkTask.getFilteredTaskList();
+
+ if (taskIndex.getZeroBased() >= studentTaskList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+ }
+
+ Task taskToMark = studentTaskList.get(taskIndex.getZeroBased());
+ studentToMarkTask.markTaskAsComplete(taskToMark);
+
+ return new CommandResult(String.format(MESSAGE_MARK_COMPLETE_SUCCESS,
+ studentToMarkTask.getName(), taskToMark.getName()));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof MarkTaskCompleteCommand // instanceof handles nulls
+ && studentIndex.equals(((MarkTaskCompleteCommand) other).studentIndex)
+ && taskIndex.equals(((MarkTaskCompleteCommand) other).taskIndex)); // state check;
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/MarkTaskInProgressCommand.java b/src/main/java/seedu/address/logic/commands/MarkTaskInProgressCommand.java
new file mode 100644
index 00000000000..329471048c3
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/MarkTaskInProgressCommand.java
@@ -0,0 +1,74 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+
+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.student.Student;
+import seedu.address.model.task.Task;
+
+
+/**
+ * Marks a task identified by its displayed index from a specified student's task list as in progress.
+ */
+public class MarkTaskInProgressCommand extends Command {
+
+ public static final String COMMAND_WORD = "markinprogress";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Marks the task identified by its index number in the task list of the "
+ + "student specified as in progress.\n"
+ + "Parameters: INDEX_OF_STUDENT INDEX_OF_TASK (must be a positive integer)\n"
+ + "Example: " + COMMAND_WORD + " 1 2";
+
+ public static final String MESSAGE_MARK_INPROGRESS_SUCCESS = "Marked task as in progress for %1$s: %2$s";
+
+ private final Index studentIndex;
+ private final Index taskIndex;
+
+ /**
+ * Creates an MarkTaskInProgressCommand to mark the specified task of
+ * a specified student as in progress
+ */
+ public MarkTaskInProgressCommand(Index studentIndex, Index taskIndex) {
+ requireAllNonNull(studentIndex, taskIndex);
+ this.taskIndex = taskIndex;
+ this.studentIndex = studentIndex;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ List lastShownList = model.getFilteredStudentList();
+
+ if (studentIndex.getZeroBased() >= lastShownList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX);
+ }
+
+ Student studentToMarkTask = lastShownList.get(studentIndex.getZeroBased());
+ List studentTaskList = studentToMarkTask.getFilteredTaskList();
+
+ if (taskIndex.getZeroBased() >= studentTaskList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+ }
+
+ Task taskToMark = studentTaskList.get(taskIndex.getZeroBased());
+ studentToMarkTask.markTaskAsInProgress(taskToMark);
+
+ return new CommandResult(String.format(MESSAGE_MARK_INPROGRESS_SUCCESS,
+ studentToMarkTask.getName(), taskToMark.getName()));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof MarkTaskInProgressCommand // instanceof handles nulls
+ && studentIndex.equals(((MarkTaskInProgressCommand) other).studentIndex)
+ && taskIndex.equals(((MarkTaskInProgressCommand) other).taskIndex)); // state check;
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/MarkTaskLateCommand.java b/src/main/java/seedu/address/logic/commands/MarkTaskLateCommand.java
new file mode 100644
index 00000000000..bb208c4c4b6
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/MarkTaskLateCommand.java
@@ -0,0 +1,74 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+
+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.student.Student;
+import seedu.address.model.task.Task;
+
+
+/**
+ * Marks a task identified by its displayed index from a specified student's task list as late.
+ */
+public class MarkTaskLateCommand extends Command {
+
+ public static final String COMMAND_WORD = "marklate";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Marks the task identified by its index number in the task list of the "
+ + "student specified as late.\n"
+ + "Parameters: INDEX_OF_STUDENT INDEX_OF_TASK (must be a positive integer)\n"
+ + "Example: " + COMMAND_WORD + " 1 2";
+
+ public static final String MESSAGE_MARK_LATE_SUCCESS = "Marked task as late for %1$s: %2$s";
+
+ private final Index studentIndex;
+ private final Index taskIndex;
+
+ /**
+ * Creates an MarkTaskLateCommand to mark the specified task of
+ * a specified student as late
+ */
+ public MarkTaskLateCommand(Index studentIndex, Index taskIndex) {
+ requireAllNonNull(studentIndex, taskIndex);
+ this.taskIndex = taskIndex;
+ this.studentIndex = studentIndex;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ List lastShownList = model.getFilteredStudentList();
+
+ if (studentIndex.getZeroBased() >= lastShownList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX);
+ }
+
+ Student studentToMarkTask = lastShownList.get(studentIndex.getZeroBased());
+ List studentTaskList = studentToMarkTask.getFilteredTaskList();
+
+ if (taskIndex.getZeroBased() >= studentTaskList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX);
+ }
+
+ Task taskToMark = studentTaskList.get(taskIndex.getZeroBased());
+ studentToMarkTask.markTaskAsLate(taskToMark);
+
+ return new CommandResult(String.format(MESSAGE_MARK_LATE_SUCCESS,
+ studentToMarkTask.getName(), taskToMark.getName()));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof MarkTaskLateCommand // instanceof handles nulls
+ && studentIndex.equals(((MarkTaskLateCommand) other).studentIndex)
+ && taskIndex.equals(((MarkTaskLateCommand) other).taskIndex)); // state check;
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/SwitchCommand.java b/src/main/java/seedu/address/logic/commands/SwitchCommand.java
new file mode 100644
index 00000000000..236d4eece89
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/SwitchCommand.java
@@ -0,0 +1,32 @@
+package seedu.address.logic.commands;
+
+import java.util.logging.Logger;
+
+import seedu.address.AppParameters;
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.model.Model;
+
+/**
+ * Switches between different panels under student task list.
+ */
+public class SwitchCommand extends Command {
+ public static final String MESSAGE_SWITCH_SUCCESS = "Score list tab switched.";
+ public static final String COMMAND_WORD = "switch";
+ private static final Logger logger = LogsCenter.getLogger(AppParameters.class);
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Switches between score text tab and score chart tab.\n"
+ + "Example: " + COMMAND_WORD;
+
+ /**
+ * Executes the command and returns the result message.
+ *
+ * @param model {@code Model} which the command should operate on.
+ * @return Feedback message of the switch command result for display.
+ */
+ @Override
+ public CommandResult execute(Model model) {
+ logger.info("Tab switch success.");
+
+ return new CommandResult(MESSAGE_SWITCH_SUCCESS, true);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/seedu/address/logic/parser/AddCommandParser.java
index 3b8bfa035e8..922761e1857 100644
--- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/AddCommandParser.java
@@ -4,6 +4,7 @@
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_PARENT_PHONE;
import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
@@ -12,13 +13,14 @@
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.student.Address;
+import seedu.address.model.student.Email;
+import seedu.address.model.student.Name;
+import seedu.address.model.student.Phone;
+import seedu.address.model.student.Student;
import seedu.address.model.tag.Tag;
+
/**
* Parses input arguments and creates a new AddCommand object
*/
@@ -31,10 +33,11 @@ public class AddCommandParser implements Parser {
*/
public AddCommand parse(String args) throws ParseException {
ArgumentMultimap argMultimap =
- ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG);
+ ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS,
+ PREFIX_PARENT_PHONE, PREFIX_TAG);
- if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL)
- || !argMultimap.getPreamble().isEmpty()) {
+ if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE,
+ PREFIX_EMAIL, PREFIX_PARENT_PHONE) || !argMultimap.getPreamble().isEmpty()) {
throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE));
}
@@ -42,11 +45,12 @@ public AddCommand parse(String args) throws ParseException {
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());
+ Phone parentPhone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PARENT_PHONE).get());
Set tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG));
- Person person = new Person(name, phone, email, address, tagList);
+ Student student = new Student(name, phone, email, address, parentPhone, tagList);
- return new AddCommand(person);
+ return new AddCommand(student);
}
/**
diff --git a/src/main/java/seedu/address/logic/parser/AddScoreCommandParser.java b/src/main/java/seedu/address/logic/parser/AddScoreCommandParser.java
new file mode 100644
index 00000000000..9ab771f2ded
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/AddScoreCommandParser.java
@@ -0,0 +1,63 @@
+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_SCORE_DATE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SCORE_LABEL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SCORE_VALUE;
+
+import java.util.stream.Stream;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.AddScoreCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.score.Date;
+import seedu.address.model.score.Label;
+import seedu.address.model.score.Score;
+import seedu.address.model.score.ScoreValue;
+
+
+/**
+ * Parses input arguments and creates a new AddScoreCommand object
+ */
+public class AddScoreCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the AddScoreCommand
+ * and returns an AddScoreCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public AddScoreCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ ArgumentMultimap argMultimap =
+ ArgumentTokenizer.tokenize(args, PREFIX_SCORE_LABEL, PREFIX_SCORE_VALUE, PREFIX_SCORE_DATE);
+
+ Index studentIndex;
+
+ try {
+ studentIndex = ParserUtil.parseIndex(argMultimap.getPreamble());
+ } catch (ParseException pe) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddScoreCommand.MESSAGE_USAGE));
+ }
+
+ if (!arePrefixesPresent(argMultimap, PREFIX_SCORE_LABEL, PREFIX_SCORE_VALUE, PREFIX_SCORE_DATE)) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddScoreCommand.MESSAGE_USAGE));
+ }
+
+ Label label = ParserUtil.parseScoreLabel(argMultimap.getValue(PREFIX_SCORE_LABEL).get());
+ ScoreValue value = ParserUtil.parseScoreValue(argMultimap.getValue(PREFIX_SCORE_VALUE).get());
+ Date date = ParserUtil.parseScoreDate(argMultimap.getValue(PREFIX_SCORE_DATE).get());
+
+ Score score = new Score(label, value, date);
+ return new AddScoreCommand(studentIndex, score);
+ }
+
+ /**
+ * 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/AddTaskCommandParser.java b/src/main/java/seedu/address/logic/parser/AddTaskCommandParser.java
new file mode 100644
index 00000000000..53162fe5292
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/AddTaskCommandParser.java
@@ -0,0 +1,55 @@
+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_TASK_TITLE;
+
+import java.util.stream.Stream;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.AddTaskCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.student.Name;
+import seedu.address.model.task.Task;
+
+/**
+ * Parses input arguments and creates a new AddTaskCommand object
+ */
+public class AddTaskCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the AddTaskCommand
+ * and returns an AddTaskCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public AddTaskCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_TASK_TITLE);
+
+ Index studentIndex;
+
+ try {
+ studentIndex = ParserUtil.parseIndex(argMultimap.getPreamble());
+ } catch (ParseException pe) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ AddTaskCommand.MESSAGE_USAGE), pe);
+ }
+
+ if (!arePrefixesPresent(argMultimap, PREFIX_TASK_TITLE)) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddTaskCommand.MESSAGE_USAGE));
+ }
+
+ Name taskName = ParserUtil.parseTaskName(argMultimap.getValue(PREFIX_TASK_TITLE).get());
+ Task task = new Task(taskName);
+
+ return new AddTaskCommand(studentIndex, task);
+ }
+
+ /**
+ * 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/CheckCommandParser.java b/src/main/java/seedu/address/logic/parser/CheckCommandParser.java
new file mode 100644
index 00000000000..81e7c93b12d
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/CheckCommandParser.java
@@ -0,0 +1,28 @@
+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.CheckCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Creates check command parser to parse the user input.
+ */
+public class CheckCommandParser implements Parser {
+ /**
+ * Parses the given {@code String} of arguments in the context of the CheckCommand
+ * and returns a CheckCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format.
+ */
+ public CheckCommand parse(String args) throws ParseException {
+ try {
+ Index index = ParserUtil.parseIndex(args);
+ return new CheckCommand(index);
+ } catch (ParseException pe) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, CheckCommand.MESSAGE_USAGE), pe);
+ }
+ }
+}
+
diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java
index 75b1a9bf119..fc8ec39d36f 100644
--- a/src/main/java/seedu/address/logic/parser/CliSyntax.java
+++ b/src/main/java/seedu/address/logic/parser/CliSyntax.java
@@ -10,6 +10,12 @@ public class CliSyntax {
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_PARENT_PHONE = new Prefix("c/");
public static final Prefix PREFIX_TAG = new Prefix("t/");
+ public static final Prefix PREFIX_TASK_TITLE = new Prefix("t/");
+ public static final Prefix PREFIX_SCORE_LABEL = new Prefix("l/");
+ public static final Prefix PREFIX_SCORE_VALUE = new Prefix("v/");
+ public static final Prefix PREFIX_SCORE_DATE = new Prefix("d/");
+ public static final Prefix PREFIX_FILEPATH = new Prefix("p/");
}
diff --git a/src/main/java/seedu/address/logic/parser/DeleteScoreCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteScoreCommandParser.java
new file mode 100644
index 00000000000..2302c77fecd
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/DeleteScoreCommandParser.java
@@ -0,0 +1,30 @@
+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.DeleteScoreCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new DeleteScoreCommand object
+ */
+public class DeleteScoreCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the DeleteScoreCommand
+ * and returns a DeleteScoreCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public DeleteScoreCommand parse(String args) throws ParseException {
+ try {
+ Index studentIndex = ParserUtil.parseFirstIndex(args);
+ Index scoreIndex = ParserUtil.parseSecondIndex(args);
+ return new DeleteScoreCommand(studentIndex, scoreIndex);
+ } catch (ParseException pe) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteScoreCommand.MESSAGE_USAGE), pe);
+ }
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/parser/DeleteTaskCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteTaskCommandParser.java
new file mode 100644
index 00000000000..5ebd2c87788
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/DeleteTaskCommandParser.java
@@ -0,0 +1,29 @@
+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.DeleteTaskCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new DeleteTaskCommand object
+ */
+public class DeleteTaskCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the DeleteTaskCommand
+ * and returns an DeleteTaskCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public DeleteTaskCommand parse(String args) throws ParseException {
+ try {
+ Index studentIndex = ParserUtil.parseFirstIndex(args);
+ Index taskIndex = ParserUtil.parseSecondIndex(args);
+ return new DeleteTaskCommand(studentIndex, taskIndex);
+ } catch (ParseException pe) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteTaskCommand.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
index 845644b7dea..dc4ba2c3992 100644
--- a/src/main/java/seedu/address/logic/parser/EditCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/EditCommandParser.java
@@ -5,6 +5,7 @@
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_PARENT_PHONE;
import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
@@ -15,7 +16,7 @@
import seedu.address.commons.core.index.Index;
import seedu.address.logic.commands.EditCommand;
-import seedu.address.logic.commands.EditCommand.EditPersonDescriptor;
+import seedu.address.logic.commands.EditCommand.EditStudentDescriptor;
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.tag.Tag;
@@ -32,36 +33,41 @@ public class EditCommandParser implements Parser {
public EditCommand parse(String args) throws ParseException {
requireNonNull(args);
ArgumentMultimap argMultimap =
- ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG);
+ ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS,
+ PREFIX_PARENT_PHONE, PREFIX_TAG);
- Index index;
+ Index studentIndex;
try {
- index = ParserUtil.parseIndex(argMultimap.getPreamble());
+ studentIndex = ParserUtil.parseIndex(argMultimap.getPreamble());
} catch (ParseException pe) {
throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE), pe);
}
- EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor();
+ EditStudentDescriptor editStudentDescriptor = new EditStudentDescriptor();
if (argMultimap.getValue(PREFIX_NAME).isPresent()) {
- editPersonDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()));
+ editStudentDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()));
}
if (argMultimap.getValue(PREFIX_PHONE).isPresent()) {
- editPersonDescriptor.setPhone(ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get()));
+ editStudentDescriptor.setPhone(ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get()));
}
if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) {
- editPersonDescriptor.setEmail(ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get()));
+ editStudentDescriptor.setEmail(ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get()));
}
if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) {
- editPersonDescriptor.setAddress(ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get()));
+ editStudentDescriptor.setAddress(ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get()));
}
- parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editPersonDescriptor::setTags);
+ if (argMultimap.getValue(PREFIX_PARENT_PHONE).isPresent()) {
+ editStudentDescriptor.setParentPhone(ParserUtil.parsePhone(argMultimap
+ .getValue(PREFIX_PARENT_PHONE).get()));
+ }
+ parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editStudentDescriptor::setTags);
- if (!editPersonDescriptor.isAnyFieldEdited()) {
+ if (!editStudentDescriptor.isAnyFieldEdited()) {
throw new ParseException(EditCommand.MESSAGE_NOT_EDITED);
}
- return new EditCommand(index, editPersonDescriptor);
+ return new EditCommand(studentIndex, editStudentDescriptor);
}
/**
diff --git a/src/main/java/seedu/address/logic/parser/ExportDataCommandParser.java b/src/main/java/seedu/address/logic/parser/ExportDataCommandParser.java
new file mode 100644
index 00000000000..6ea7b1881b5
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/ExportDataCommandParser.java
@@ -0,0 +1,22 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.parser.CliSyntax.PREFIX_FILEPATH;
+
+import seedu.address.logic.commands.ExportDataCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new ImportDataCommand object
+ */
+public class ExportDataCommandParser implements Parser {
+
+ @Override
+ public ExportDataCommand parse(String args) throws ParseException {
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_FILEPATH);
+
+ String filePath = ParserUtil.parseFilePath(argMultimap.getValue(PREFIX_FILEPATH));
+
+ return new ExportDataCommand(filePath);
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/parser/ExportProgressCommandParser.java b/src/main/java/seedu/address/logic/parser/ExportProgressCommandParser.java
new file mode 100644
index 00000000000..1424f0a8a4f
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/ExportProgressCommandParser.java
@@ -0,0 +1,38 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_FILEPATH;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.ExportProgressCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new ExportProgressCommand object
+ */
+public class ExportProgressCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the ExportProgressCommand
+ * and returns an ExportProgressCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public ExportProgressCommand parse(String args) throws ParseException {
+ ArgumentMultimap argMultimap =
+ ArgumentTokenizer.tokenize(args, PREFIX_FILEPATH);
+
+ Index index;
+
+ try {
+ index = ParserUtil.parseIndex(argMultimap.getPreamble());
+ } catch (ParseException pe) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ ExportProgressCommand.MESSAGE_USAGE));
+ }
+
+ String filePath = ParserUtil.parseFilePath(argMultimap.getValue(PREFIX_FILEPATH));
+
+ return new ExportProgressCommand(index, filePath);
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/parser/FilterCommandParser.java b/src/main/java/seedu/address/logic/parser/FilterCommandParser.java
new file mode 100644
index 00000000000..2b94442402b
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/FilterCommandParser.java
@@ -0,0 +1,39 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import java.util.Arrays;
+import java.util.logging.Logger;
+
+import seedu.address.AppParameters;
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.logic.commands.FilterCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.tag.TagContainsKeywordsPredicate;
+
+/**
+ * Creates filter command parser to parse the user input.
+ */
+public class FilterCommandParser implements Parser {
+ private static final Logger logger = LogsCenter.getLogger(AppParameters.class);
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the FilterCommand
+ * and returns a FilterCommand object for execution.
+ *
+ * @throws ParseException if the user input does not conform the expected format.
+ */
+ public FilterCommand parse(String args) throws ParseException {
+ String trimmedArgs = args.trim();
+
+ if (trimmedArgs.isEmpty()) {
+ logger.info("There is no keyword provided for filtering.");
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, FilterCommand.MESSAGE_USAGE));
+ }
+
+ String[] tagKeywords = trimmedArgs.split("\\s+");
+
+ return new FilterCommand(new TagContainsKeywordsPredicate(Arrays.asList(tagKeywords)));
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/FindCommandParser.java b/src/main/java/seedu/address/logic/parser/FindCommandParser.java
index 4fb71f23103..9e0833aca08 100644
--- a/src/main/java/seedu/address/logic/parser/FindCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/FindCommandParser.java
@@ -6,7 +6,7 @@
import seedu.address.logic.commands.FindCommand;
import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.person.NameContainsKeywordsPredicate;
+import seedu.address.model.student.NameContainsKeywordsPredicate;
/**
* Parses input arguments and creates a new FindCommand object
diff --git a/src/main/java/seedu/address/logic/parser/ImportDataCommandParser.java b/src/main/java/seedu/address/logic/parser/ImportDataCommandParser.java
new file mode 100644
index 00000000000..46e29f1c190
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/ImportDataCommandParser.java
@@ -0,0 +1,34 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_FILEPATH;
+
+import seedu.address.logic.commands.ImportDataCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new ImportDataCommand object
+ */
+public class ImportDataCommandParser implements Parser {
+
+ @Override
+ public ImportDataCommand parse(String args) throws ParseException {
+ String trimmedArgs = args.trim();
+
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_FILEPATH);
+
+
+ if (trimmedArgs.isEmpty()) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, ImportDataCommand.MESSAGE_USAGE));
+ }
+
+ if (!argMultimap.getValue(PREFIX_FILEPATH).isPresent()) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, ImportDataCommand.MESSAGE_USAGE));
+ }
+
+ return new ImportDataCommand(argMultimap.getValue(PREFIX_FILEPATH).get());
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/parser/MarkTaskCompleteParserCommand.java b/src/main/java/seedu/address/logic/parser/MarkTaskCompleteParserCommand.java
new file mode 100644
index 00000000000..8ccb7bd5e5f
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/MarkTaskCompleteParserCommand.java
@@ -0,0 +1,29 @@
+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.MarkTaskCompleteCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new MarkTaskCompleteParserCommand object
+ */
+public class MarkTaskCompleteParserCommand implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the MarkTaskCompleteCommand
+ * and returns an MarkTaskCompleteCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public MarkTaskCompleteCommand parse(String args) throws ParseException {
+ try {
+ Index studentIndex = ParserUtil.parseFirstIndex(args);
+ Index taskIndex = ParserUtil.parseSecondIndex(args);
+ return new MarkTaskCompleteCommand(studentIndex, taskIndex);
+ } catch (ParseException pe) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, MarkTaskCompleteCommand.MESSAGE_USAGE), pe);
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/MarkTaskInProgressParserCommand.java b/src/main/java/seedu/address/logic/parser/MarkTaskInProgressParserCommand.java
new file mode 100644
index 00000000000..b8aecbde270
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/MarkTaskInProgressParserCommand.java
@@ -0,0 +1,29 @@
+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.MarkTaskInProgressCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new MarkTaskInProgressParserCommand object
+ */
+public class MarkTaskInProgressParserCommand implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the MarkTaskInProgressCommand
+ * and returns an MarkTaskInProgressCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public MarkTaskInProgressCommand parse(String args) throws ParseException {
+ try {
+ Index studentIndex = ParserUtil.parseFirstIndex(args);
+ Index taskIndex = ParserUtil.parseSecondIndex(args);
+ return new MarkTaskInProgressCommand(studentIndex, taskIndex);
+ } catch (ParseException pe) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, MarkTaskInProgressCommand.MESSAGE_USAGE), pe);
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/MarkTaskLateParserCommand.java b/src/main/java/seedu/address/logic/parser/MarkTaskLateParserCommand.java
new file mode 100644
index 00000000000..eb76237d75d
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/MarkTaskLateParserCommand.java
@@ -0,0 +1,29 @@
+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.MarkTaskLateCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new MarkTaskLateParserCommand object
+ */
+public class MarkTaskLateParserCommand implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the MarkTaskLateCommand
+ * and returns an MarkTaskLateCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public MarkTaskLateCommand parse(String args) throws ParseException {
+ try {
+ Index studentIndex = ParserUtil.parseFirstIndex(args);
+ Index taskIndex = ParserUtil.parseSecondIndex(args);
+ return new MarkTaskLateCommand(studentIndex, taskIndex);
+ } catch (ParseException pe) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, MarkTaskLateCommand.MESSAGE_USAGE), pe);
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/MathutoringParser.java
similarity index 51%
rename from src/main/java/seedu/address/logic/parser/AddressBookParser.java
rename to src/main/java/seedu/address/logic/parser/MathutoringParser.java
index 1e466792b46..49014d18d85 100644
--- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java
+++ b/src/main/java/seedu/address/logic/parser/MathutoringParser.java
@@ -7,20 +7,34 @@
import java.util.regex.Pattern;
import seedu.address.logic.commands.AddCommand;
+import seedu.address.logic.commands.AddScoreCommand;
+import seedu.address.logic.commands.AddTaskCommand;
+import seedu.address.logic.commands.CheckCommand;
import seedu.address.logic.commands.ClearCommand;
import seedu.address.logic.commands.Command;
import seedu.address.logic.commands.DeleteCommand;
+import seedu.address.logic.commands.DeleteScoreCommand;
+import seedu.address.logic.commands.DeleteTaskCommand;
import seedu.address.logic.commands.EditCommand;
import seedu.address.logic.commands.ExitCommand;
+import seedu.address.logic.commands.ExportDataCommand;
+import seedu.address.logic.commands.ExportProgressCommand;
+import seedu.address.logic.commands.FilterCommand;
import seedu.address.logic.commands.FindCommand;
import seedu.address.logic.commands.HelpCommand;
+import seedu.address.logic.commands.ImportDataCommand;
import seedu.address.logic.commands.ListCommand;
+import seedu.address.logic.commands.MarkTaskCompleteCommand;
+import seedu.address.logic.commands.MarkTaskInProgressCommand;
+import seedu.address.logic.commands.MarkTaskLateCommand;
+import seedu.address.logic.commands.SwitchCommand;
import seedu.address.logic.parser.exceptions.ParseException;
+
/**
* Parses user input.
*/
-public class AddressBookParser {
+public class MathutoringParser {
/**
* Used for initial separation of command word and args.
@@ -40,7 +54,7 @@ public Command parseCommand(String userInput) throws ParseException {
throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE));
}
- final String commandWord = matcher.group("commandWord");
+ final String commandWord = matcher.group("commandWord").toLowerCase();
final String arguments = matcher.group("arguments");
switch (commandWord) {
@@ -53,6 +67,9 @@ public Command parseCommand(String userInput) throws ParseException {
case DeleteCommand.COMMAND_WORD:
return new DeleteCommandParser().parse(arguments);
+ case CheckCommand.COMMAND_WORD:
+ return new CheckCommandParser().parse(arguments);
+
case ClearCommand.COMMAND_WORD:
return new ClearCommand();
@@ -68,9 +85,44 @@ public Command parseCommand(String userInput) throws ParseException {
case HelpCommand.COMMAND_WORD:
return new HelpCommand();
+ case SwitchCommand.COMMAND_WORD:
+ return new SwitchCommand();
+
+ case FilterCommand.COMMAND_WORD:
+ return new FilterCommandParser().parse(arguments);
+
+ case AddTaskCommand.COMMAND_WORD:
+ return new AddTaskCommandParser().parse(arguments);
+
+ case DeleteTaskCommand.COMMAND_WORD:
+ return new DeleteTaskCommandParser().parse(arguments);
+
+ case MarkTaskInProgressCommand.COMMAND_WORD:
+ return new MarkTaskInProgressParserCommand().parse(arguments);
+
+ case MarkTaskLateCommand.COMMAND_WORD:
+ return new MarkTaskLateParserCommand().parse(arguments);
+
+ case MarkTaskCompleteCommand.COMMAND_WORD:
+ return new MarkTaskCompleteParserCommand().parse(arguments);
+
+ case AddScoreCommand.COMMAND_WORD:
+ return new AddScoreCommandParser().parse(arguments);
+
+ case DeleteScoreCommand.COMMAND_WORD:
+ return new DeleteScoreCommandParser().parse(arguments);
+
+ case ImportDataCommand.COMMAND_WORD:
+ return new ImportDataCommandParser().parse(arguments);
+
+ case ExportDataCommand.COMMAND_WORD:
+ return new ExportDataCommandParser().parse(arguments);
+
+ case ExportProgressCommand.COMMAND_WORD:
+ return new ExportProgressCommandParser().parse(arguments);
+
default:
throw new ParseException(MESSAGE_UNKNOWN_COMMAND);
}
}
-
}
diff --git a/src/main/java/seedu/address/logic/parser/ParserUtil.java b/src/main/java/seedu/address/logic/parser/ParserUtil.java
index b117acb9c55..0242c51d9a8 100644
--- a/src/main/java/seedu/address/logic/parser/ParserUtil.java
+++ b/src/main/java/seedu/address/logic/parser/ParserUtil.java
@@ -2,24 +2,38 @@
import static java.util.Objects.requireNonNull;
+import java.nio.file.Files;
+import java.nio.file.InvalidPathException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.Collection;
import java.util.HashSet;
+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.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.score.Date;
+import seedu.address.model.score.Label;
+import seedu.address.model.score.ScoreValue;
+import seedu.address.model.student.Address;
+import seedu.address.model.student.Email;
+import seedu.address.model.student.Name;
+import seedu.address.model.student.Phone;
import seedu.address.model.tag.Tag;
+import seedu.address.model.task.Task;
/**
* Contains utility methods used for parsing strings in the various *Parser classes.
*/
public class ParserUtil {
+ public static final int FIRST_INDEX = 0;
+
+ public static final int SECOND_INDEX = 1;
+
public static final String MESSAGE_INVALID_INDEX = "Index is not a non-zero unsigned integer.";
/**
@@ -35,6 +49,47 @@ public static Index parseIndex(String oneBasedIndex) throws ParseException {
return Index.fromOneBased(Integer.parseInt(trimmedIndex));
}
+ /**
+ * Parses the first of {@code twoIndexes} 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 parseFirstIndex(String twoIndexes) throws ParseException {
+ String trimmedIndex = twoIndexes.trim();
+
+ if (!StringUtil.isTwoIndexString(trimmedIndex)) {
+ throw new ParseException(MESSAGE_INVALID_INDEX);
+ }
+
+ String firstIndex = trimmedIndex.split("\\s+")[FIRST_INDEX];
+
+ if (!StringUtil.isNonZeroUnsignedInteger(firstIndex)) {
+ throw new ParseException(MESSAGE_INVALID_INDEX);
+ }
+ return Index.fromOneBased(Integer.parseInt(firstIndex));
+ }
+
+ /**
+ * Parses the second of {@code twoIndexes} 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 parseSecondIndex(String twoIndexes) throws ParseException {
+ String trimmedIndex = twoIndexes.trim();
+
+ if (!StringUtil.isTwoIndexString(trimmedIndex)) {
+ throw new ParseException(MESSAGE_INVALID_INDEX);
+ }
+
+ String secondIndex = trimmedIndex.split("\\s+")[SECOND_INDEX];
+
+ if (!StringUtil.isNonZeroUnsignedInteger(secondIndex)) {
+ throw new ParseException(MESSAGE_INVALID_INDEX);
+ }
+
+ return Index.fromOneBased(Integer.parseInt(secondIndex));
+ }
+
/**
* Parses a {@code String name} into a {@code Name}.
* Leading and trailing whitespaces will be trimmed.
@@ -62,6 +117,9 @@ public static Phone parsePhone(String phone) throws ParseException {
if (!Phone.isValidPhone(trimmedPhone)) {
throw new ParseException(Phone.MESSAGE_CONSTRAINTS);
}
+ if (!Phone.isMoreThanMaxDigits(trimmedPhone)) {
+ throw new ParseException(Phone.MESSAGE_EXCEED_MAX_DIGITS);
+ }
return new Phone(trimmedPhone);
}
@@ -107,6 +165,9 @@ public static Tag parseTag(String tag) throws ParseException {
if (!Tag.isValidTagName(trimmedTag)) {
throw new ParseException(Tag.MESSAGE_CONSTRAINTS);
}
+ if (!Tag.isMoreThanMaxLetters(trimmedTag)) {
+ throw new ParseException(Tag.MESSAGE_EXCEED_MAX_LETTERS);
+ }
return new Tag(trimmedTag);
}
@@ -121,4 +182,93 @@ public static Set parseTags(Collection tags) throws ParseException
}
return tagSet;
}
+
+ /**
+ * Parses a {@code String taskName} into a {@code Name}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given {@code taskName} is invalid.
+ */
+ public static Name parseTaskName(String taskName) throws ParseException {
+ requireNonNull(taskName);
+ String trimmedName = taskName.trim();
+ if (!Task.isValidTaskName(trimmedName)) {
+ throw new ParseException(Task.MESSAGE_CONSTRAINTS);
+ }
+ return new Name(trimmedName);
+ }
+
+ //=========== Score ================================================================================
+ /**
+ * Parses a {@code String label} into an {@code Label}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given {@code Label} is invalid.
+ */
+ public static Label parseScoreLabel(String label) throws ParseException {
+ requireNonNull(label);
+ String trimmedLabel = label.trim();
+ if (!Label.isValidLabel(trimmedLabel)) {
+ throw new ParseException(Label.MESSAGE_CONSTRAINTS);
+ }
+ return new Label(trimmedLabel);
+ }
+
+ /**
+ * Parses a {@code Double value} into an {@code ScoreValue}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given {@code ScoreValue} is invalid.
+ */
+ public static ScoreValue parseScoreValue(String value) throws ParseException {
+ requireNonNull(value);
+ String trimmedValue = value.trim();
+ if (!ScoreValue.isValidScoreValue(trimmedValue)) {
+ throw new ParseException(ScoreValue.MESSAGE_CONSTRAINTS);
+ }
+ return new ScoreValue(trimmedValue);
+ }
+
+ /**
+ * Parses a {@code LocalDate date} into an {@code Date}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given {@code Date} is invalid.
+ */
+ public static Date parseScoreDate(String date) throws ParseException {
+ requireNonNull(date);
+ String trimmedDate = date.trim();
+ if (!Date.isValidDate(trimmedDate)) {
+ throw new ParseException(Date.MESSAGE_CONSTRAINTS);
+ }
+ if (!Date.isFutureDate(trimmedDate)) {
+ throw new ParseException(Date.MESSAGE_INVALID_DATE);
+ }
+ return new Date(trimmedDate);
+ }
+
+
+
+ /**
+ * Parses a file path.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given file path is invalid.
+ */
+ public static String parseFilePath(Optional filePathOpt) throws ParseException {
+ String filePath = filePathOpt.isEmpty() ? "" : filePathOpt.get();
+ filePath = filePath.trim();
+ Path path;
+ try {
+ path = Paths.get(filePath);
+ } catch (InvalidPathException e) {
+ throw new ParseException(Messages.MESSAGE_INVALID_DIRECTORY);
+ }
+ if (Files.isDirectory(path)) {
+ return path.toString();
+ } else {
+ throw new ParseException(Messages.MESSAGE_INVALID_DIRECTORY);
+ }
+ }
+
}
diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java
deleted file mode 100644
index 1a943a0781a..00000000000
--- a/src/main/java/seedu/address/model/AddressBook.java
+++ /dev/null
@@ -1,120 +0,0 @@
-package seedu.address.model;
-
-import static java.util.Objects.requireNonNull;
-
-import java.util.List;
-
-import javafx.collections.ObservableList;
-import seedu.address.model.person.Person;
-import seedu.address.model.person.UniquePersonList;
-
-/**
- * Wraps all data at the address-book level
- * Duplicates are not allowed (by .isSamePerson comparison)
- */
-public class AddressBook implements ReadOnlyAddressBook {
-
- private final UniquePersonList persons;
-
- /*
- * The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication
- * between constructors. See https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html
- *
- * Note that non-static init blocks are not recommended to use. There are other ways to avoid duplication
- * among constructors.
- */
- {
- persons = new UniquePersonList();
- }
-
- public AddressBook() {}
-
- /**
- * Creates an AddressBook using the Persons in the {@code toBeCopied}
- */
- public AddressBook(ReadOnlyAddressBook toBeCopied) {
- this();
- resetData(toBeCopied);
- }
-
- //// list overwrite operations
-
- /**
- * Replaces the contents of the person list with {@code persons}.
- * {@code persons} must not contain duplicate persons.
- */
- public void setPersons(List persons) {
- this.persons.setPersons(persons);
- }
-
- /**
- * Resets the existing data of this {@code AddressBook} with {@code newData}.
- */
- public void resetData(ReadOnlyAddressBook newData) {
- requireNonNull(newData);
-
- setPersons(newData.getPersonList());
- }
-
- //// person-level operations
-
- /**
- * Returns true if a person with the same identity as {@code person} exists in the address book.
- */
- public boolean hasPerson(Person person) {
- requireNonNull(person);
- return persons.contains(person);
- }
-
- /**
- * Adds a person to the address book.
- * The person must not already exist in the address book.
- */
- public void addPerson(Person p) {
- persons.add(p);
- }
-
- /**
- * Replaces the given person {@code target} in the list with {@code editedPerson}.
- * {@code target} must exist in the address book.
- * The person identity of {@code editedPerson} must not be the same as another existing person in the address book.
- */
- public void setPerson(Person target, Person editedPerson) {
- requireNonNull(editedPerson);
-
- persons.setPerson(target, editedPerson);
- }
-
- /**
- * Removes {@code key} from this {@code AddressBook}.
- * {@code key} must exist in the address book.
- */
- public void removePerson(Person key) {
- persons.remove(key);
- }
-
- //// util methods
-
- @Override
- public String toString() {
- return persons.asUnmodifiableObservableList().size() + " persons";
- // TODO: refine later
- }
-
- @Override
- public ObservableList getPersonList() {
- return persons.asUnmodifiableObservableList();
- }
-
- @Override
- public boolean equals(Object other) {
- return other == this // short circuit if same object
- || (other instanceof AddressBook // instanceof handles nulls
- && persons.equals(((AddressBook) other).persons));
- }
-
- @Override
- public int hashCode() {
- return persons.hashCode();
- }
-}
diff --git a/src/main/java/seedu/address/model/Mathutoring.java b/src/main/java/seedu/address/model/Mathutoring.java
new file mode 100644
index 00000000000..d8c779139b2
--- /dev/null
+++ b/src/main/java/seedu/address/model/Mathutoring.java
@@ -0,0 +1,130 @@
+package seedu.address.model;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+
+import javafx.collections.ObservableList;
+import seedu.address.model.student.Student;
+import seedu.address.model.student.UniqueStudentList;
+
+/**
+ * Wraps all data at the mathutoring level
+ * Duplicates are not allowed (by .isSameStudent comparison)
+ */
+public class Mathutoring implements ReadOnlyMathutoring {
+
+ private final UniqueStudentList students;
+
+ private Student selectedStudent;
+
+ /*
+ * The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication
+ * between constructors. See https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html
+ *
+ * Note that non-static init blocks are not recommended to use. There are other ways to avoid duplication
+ * among constructors.
+ */
+ {
+ students = new UniqueStudentList();
+ }
+
+ public Mathutoring() {}
+
+ /**
+ * Creates a Mathutoring using the Students in the {@code toBeCopied}
+ */
+ public Mathutoring(ReadOnlyMathutoring toBeCopied) {
+ this();
+ resetData(toBeCopied);
+ }
+
+ //// list overwrite operations
+
+ /**
+ * Replaces the contents of the student list with {@code students}.
+ * {@code students} must not contain duplicate students.
+ */
+ public void setStudents(List students) {
+ this.students.setStudents(students);
+ }
+
+ /**
+ * Resets the existing data of this {@code Mathutoring} with {@code newData}.
+ */
+ public void resetData(ReadOnlyMathutoring newData) {
+ requireNonNull(newData);
+ this.selectedStudent = null;
+ setStudents(newData.getStudentList());
+ }
+
+ //// student-level operations
+
+ /**
+ * Returns true if a student with the same identity as {@code student} exists in the matutoring.
+ */
+ public boolean hasStudent(Student student) {
+ requireNonNull(student);
+ return students.contains(student);
+ }
+
+ /**
+ * Adds a student to the mathutoring.
+ * The student must not already exist in the mathutoring.
+ */
+ public void addStudent(Student p) {
+ students.add(p);
+ }
+
+ /**
+ * Replaces the given student {@code target} in the list with {@code editedStudent}.
+ * {@code target} must exist in the mathutoring.
+ * The student identity of {@code editedStudent} must not be the same as
+ * another existing student in the mathutoring.
+ */
+ public void setStudent(Student target, Student editedStudent) {
+ requireNonNull(editedStudent);
+
+ students.setStudent(target, editedStudent);
+ }
+
+ /**
+ * Removes {@code key} from this {@code Mathutoring}.
+ * {@code key} must exist in the mathutoring.
+ */
+ public void removeStudent(Student key) {
+ students.remove(key);
+ }
+
+ public void checkStudent(Student key) {
+ selectedStudent = key;
+ }
+
+ public Student findCheckedStudent() {
+ return selectedStudent;
+ }
+
+ //// util methods
+
+ @Override
+ public String toString() {
+ return students.asUnmodifiableObservableList().size() + " students";
+ }
+
+ @Override
+ public ObservableList getStudentList() {
+ return students.asUnmodifiableObservableList();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof Mathutoring // instanceof handles nulls
+ && students.equals(((Mathutoring) other).students));
+ }
+
+ @Override
+ public int hashCode() {
+ return students.hashCode();
+ }
+}
diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java
index d54df471c1f..bdf13b256b8 100644
--- a/src/main/java/seedu/address/model/Model.java
+++ b/src/main/java/seedu/address/model/Model.java
@@ -1,18 +1,19 @@
package seedu.address.model;
+import java.io.IOException;
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;
+import seedu.address.model.student.Student;
/**
* The API of the Model component.
*/
public interface Model {
/** {@code Predicate} that always evaluate to true */
- Predicate PREDICATE_SHOW_ALL_PERSONS = unused -> true;
+ Predicate PREDICATE_SHOW_ALL_STUDENTS = unused -> true;
/**
* Replaces user prefs data with the data in {@code userPrefs}.
@@ -35,53 +36,72 @@ public interface Model {
void setGuiSettings(GuiSettings guiSettings);
/**
- * Returns the user prefs' address book file path.
+ * Returns the user prefs' mathutoring file path.
*/
- Path getAddressBookFilePath();
+ Path getMathutoringFilePath();
/**
- * Sets the user prefs' address book file path.
+ * Sets the user prefs' mathutoring file path.
*/
- void setAddressBookFilePath(Path addressBookFilePath);
+ void setMathutoringFilePath(Path mathutoringFilePath);
/**
- * Replaces address book data with the data in {@code addressBook}.
+ * Replaces mathutoring data with the data in {@code mathutoring}.
*/
- void setAddressBook(ReadOnlyAddressBook addressBook);
+ void setMathutoring(ReadOnlyMathutoring mathutoring);
- /** Returns the AddressBook */
- ReadOnlyAddressBook getAddressBook();
+ /** Returns the Mathutoring */
+ ReadOnlyMathutoring getMathutoring();
/**
- * Returns true if a person with the same identity as {@code person} exists in the address book.
+ * Returns true if a student with the same identity as {@code student} exists in the mathutoring.
*/
- boolean hasPerson(Person person);
+ boolean hasStudent(Student student);
/**
- * Deletes the given person.
- * The person must exist in the address book.
+ * Deletes the given student.
+ * The student must exist in the mathutoring.
*/
- void deletePerson(Person target);
+ void deleteStudent(Student target);
/**
- * Adds the given person.
- * {@code person} must not already exist in the address book.
+ * Check the given student.
+ * The student must exist in the mathutoring.
*/
- void addPerson(Person person);
+ void checkStudent(Student target);
/**
- * 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.
+ * Adds the given student.
+ * {@code student} must not already exist in the mathutoring.
*/
- void setPerson(Person target, Person editedPerson);
+ void addStudent(Student student);
+
+ /**
+ * Replaces the given student {@code target} with {@code editedStudent}.
+ * {@code target} must exist in the mathutoring.
+ * The student identity of {@code editedStudent} must not be the same as
+ * another existing student in the mathutoring.
+ */
+ void setStudent(Student target, Student editedStudent);
+
+
+ /** Returns an unmodifiable view of the filtered student list */
+ ObservableList getFilteredStudentList();
+
+ void exportProgress(Student target, String completePath) throws IOException;
- /** 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}.
+ * Updates the filter of the filtered student list to filter by the given {@code predicate}.
* @throws NullPointerException if {@code predicate} is null.
*/
- void updateFilteredPersonList(Predicate predicate);
+ void updateFilteredStudentList(Predicate predicate);
+
+ /**
+ * Returns the student who is currently being checked.
+ *
+ * @return The student that currently being checked.
+ */
+ Student findSelectedStudent();
+
}
diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java
index 86c1df298d7..87e45d6d0d9 100644
--- a/src/main/java/seedu/address/model/ModelManager.java
+++ b/src/main/java/seedu/address/model/ModelManager.java
@@ -3,41 +3,48 @@
import static java.util.Objects.requireNonNull;
import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+import java.io.IOException;
import java.nio.file.Path;
import java.util.function.Predicate;
import java.util.logging.Logger;
+import org.apache.pdfbox.pdmodel.PDDocument;
+
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;
+import seedu.address.model.student.Student;
/**
- * Represents the in-memory model of the address book data.
+ * Represents the in-memory model of the mathutoring data.
*/
public class ModelManager implements Model {
private static final Logger logger = LogsCenter.getLogger(ModelManager.class);
- private final AddressBook addressBook;
+ private final Mathutoring mathutoring;
private final UserPrefs userPrefs;
- private final FilteredList filteredPersons;
+ private final FilteredList filteredStudents;
+
+ private final PdfConverter pdfConverter;
/**
- * Initializes a ModelManager with the given addressBook and userPrefs.
+ * Initializes a ModelManager with the given mathutoring and userPrefs.
*/
- public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs) {
- requireAllNonNull(addressBook, userPrefs);
+ public ModelManager(ReadOnlyMathutoring mathutoring, ReadOnlyUserPrefs userPrefs) {
+ requireAllNonNull(mathutoring, userPrefs);
- logger.fine("Initializing with address book: " + addressBook + " and user prefs " + userPrefs);
+ logger.fine("Initializing with mathutoring: " + mathutoring + " and user prefs " + userPrefs);
- this.addressBook = new AddressBook(addressBook);
+ this.mathutoring = new Mathutoring(mathutoring);
this.userPrefs = new UserPrefs(userPrefs);
- filteredPersons = new FilteredList<>(this.addressBook.getPersonList());
+
+ filteredStudents = new FilteredList<>(this.mathutoring.getStudentList());
+ this.pdfConverter = new PdfConverter();
}
public ModelManager() {
- this(new AddressBook(), new UserPrefs());
+ this(new Mathutoring(), new UserPrefs());
}
//=========== UserPrefs ==================================================================================
@@ -65,67 +72,86 @@ public void setGuiSettings(GuiSettings guiSettings) {
}
@Override
- public Path getAddressBookFilePath() {
- return userPrefs.getAddressBookFilePath();
+ public Path getMathutoringFilePath() {
+ return userPrefs.getMathutoringFilePath();
}
@Override
- public void setAddressBookFilePath(Path addressBookFilePath) {
- requireNonNull(addressBookFilePath);
- userPrefs.setAddressBookFilePath(addressBookFilePath);
+ public void setMathutoringFilePath(Path mathutoringFilePath) {
+ requireNonNull(mathutoringFilePath);
+ userPrefs.setMathutoringFilePath(mathutoringFilePath);
}
- //=========== AddressBook ================================================================================
+ //=========== Mathutoring ================================================================================
@Override
- public void setAddressBook(ReadOnlyAddressBook addressBook) {
- this.addressBook.resetData(addressBook);
+ public void setMathutoring(ReadOnlyMathutoring mathutoring) {
+ this.mathutoring.resetData(mathutoring);
+ updateFilteredStudentList(PREDICATE_SHOW_ALL_STUDENTS);
}
@Override
- public ReadOnlyAddressBook getAddressBook() {
- return addressBook;
+ public ReadOnlyMathutoring getMathutoring() {
+ return mathutoring;
}
@Override
- public boolean hasPerson(Person person) {
- requireNonNull(person);
- return addressBook.hasPerson(person);
+ public boolean hasStudent(Student student) {
+ requireNonNull(student);
+ return mathutoring.hasStudent(student);
}
@Override
- public void deletePerson(Person target) {
- addressBook.removePerson(target);
+ public void deleteStudent(Student target) {
+ mathutoring.removeStudent(target);
}
@Override
- public void addPerson(Person person) {
- addressBook.addPerson(person);
- updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+ public void checkStudent(Student target) {
+ mathutoring.checkStudent(target);
}
@Override
- public void setPerson(Person target, Person editedPerson) {
- requireAllNonNull(target, editedPerson);
+ public void addStudent(Student student) {
+ mathutoring.addStudent(student);
+ updateFilteredStudentList(PREDICATE_SHOW_ALL_STUDENTS);
+ }
- addressBook.setPerson(target, editedPerson);
+ @Override
+ public void setStudent(Student target, Student editedStudent) {
+ requireAllNonNull(target, editedStudent);
+
+ mathutoring.setStudent(target, editedStudent);
}
- //=========== Filtered Person List Accessors =============================================================
+ //=========== Filtered Student List Accessors =============================================================
+
+ @Override
+ public void exportProgress(Student target, String completePath) throws IOException {
+ requireAllNonNull(target, completePath);
+ PDDocument document = pdfConverter.exportProgress(target);
+ document.save(completePath);
+ document.close();
+ }
/**
- * Returns an unmodifiable view of the list of {@code Person} backed by the internal list of
- * {@code versionedAddressBook}
+ * Returns an unmodifiable view of the list of {@code Student} backed by the internal list of
+ * {@code versionedMathutoring}
*/
@Override
- public ObservableList getFilteredPersonList() {
- return filteredPersons;
+ public ObservableList getFilteredStudentList() {
+ return filteredStudents;
}
@Override
- public void updateFilteredPersonList(Predicate predicate) {
+ public void updateFilteredStudentList(Predicate predicate) {
requireNonNull(predicate);
- filteredPersons.setPredicate(predicate);
+ filteredStudents.setPredicate(predicate);
+ }
+
+ @Override
+ public Student findSelectedStudent() {
+ return mathutoring.findCheckedStudent();
}
@Override
@@ -142,9 +168,8 @@ public boolean equals(Object obj) {
// state check
ModelManager other = (ModelManager) obj;
- return addressBook.equals(other.addressBook)
+ return mathutoring.equals(other.mathutoring)
&& userPrefs.equals(other.userPrefs)
- && filteredPersons.equals(other.filteredPersons);
+ && filteredStudents.equals(other.filteredStudents);
}
-
}
diff --git a/src/main/java/seedu/address/model/PdfConverter.java b/src/main/java/seedu/address/model/PdfConverter.java
new file mode 100644
index 00000000000..27a697da068
--- /dev/null
+++ b/src/main/java/seedu/address/model/PdfConverter.java
@@ -0,0 +1,489 @@
+package seedu.address.model;
+
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+
+import java.awt.Color;
+import java.io.IOException;
+import java.time.LocalDate;
+import java.util.Arrays;
+import java.util.List;
+import java.util.logging.Logger;
+
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDPageContentStream;
+import org.apache.pdfbox.pdmodel.font.PDFont;
+import org.apache.pdfbox.pdmodel.font.PDType1Font;
+
+import javafx.collections.ObservableList;
+import seedu.address.AppParameters;
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.model.score.Score;
+import seedu.address.model.score.UniqueScoreList;
+import seedu.address.model.student.Student;
+import seedu.address.model.task.Task;
+import seedu.address.model.task.UniqueTaskList;
+
+/**
+ * Converts student's progress (tasks and scores) to pdf format
+ */
+
+public class PdfConverter {
+ private static final Logger logger = LogsCenter.getLogger(AppParameters.class);
+
+ private final float horizontalWrap = 432;
+ private final float xInit = 90;
+ private final float yInit = 702;
+
+ private float x = 90;
+ private float y = 702;
+ private final float margin = 5;
+ private float yInitTable = -1;
+ private final float leftMarginPosition = 90;
+ private final float rightMarginPosition = 522;
+
+ private final PDFont fontBold = PDType1Font.HELVETICA_BOLD;
+ private final PDFont fontItalic = PDType1Font.HELVETICA_OBLIQUE;
+ private final PDFont font = PDType1Font.HELVETICA;
+ private final Color black = Color.BLACK;
+ private final Color red = new Color(194, 47, 40);
+ private final Color yellow = new Color(219, 168, 0);
+ private final Color green = new Color(0, 100, 0);
+ private Color curColor;
+
+ private final int fontTitleSize = 20;
+
+ private final int fontHeadingSize = 18;
+ private final int fontTableHeaderSize = 16;
+
+ private final int fontTableContentSize = 14;
+ private final int fontDateSize = 12;
+
+ private PDDocument document;
+
+ private PDPage page;
+
+ private PDPageContentStream contentStream;
+
+ public void setup() throws IOException {
+ this.document = new PDDocument();
+ this.page = new PDPage();
+ this.document.addPage(page);
+ this.contentStream = new PDPageContentStream(document, page);
+ this.x = this.xInit;
+ this.y = this.yInit;
+ this.curColor = black;
+ }
+
+ /**
+ * Create contents of the pdf file
+ * @param key student to be exported
+ */
+ public void createContents(Student key) throws IOException {
+ String docTitle = key.getName().fullName + "'s Progress Report";
+ String dateCreated = "Date created: " + LocalDate.now();
+ String taskList = "Task List";
+ String scoreList = "Score List";
+
+ wrapText(docTitle, this.horizontalWrap, this.fontBold, this.fontTitleSize, List.of());
+ this.x = this.xInit;
+ this.y -= textHeight(fontBold, fontTitleSize, margin / 2);
+
+ wrapText(dateCreated, this.horizontalWrap, fontItalic, this.fontDateSize, List.of());
+ this.x = this.xInit;
+ this.y -= 3 * textHeight(fontBold, fontTitleSize, 0);
+
+ wrapText(taskList, this.horizontalWrap, fontBold, this.fontHeadingSize, List.of());
+ this.x = this.xInit;
+ this.y -= 2 * textHeight(fontBold, fontHeadingSize, 0);
+
+ createTaskTable(key.getTaskList());
+ this.x = this.xInit;
+ this.y -= 3 * textHeight(fontBold, fontTitleSize, 0);
+
+ wrapText(scoreList, this.horizontalWrap, fontBold, this.fontHeadingSize, List.of());
+ this.x = this.xInit;
+ this.y -= 2 * textHeight(fontBold, 18, 0);
+
+ createScoreTable(key.getScoreList());
+ }
+
+ /**
+ * Exports {@code key}'s task and score list in the form of a PDF file from this {@code Mathutoring}.
+ */
+ public PDDocument exportProgress(Student key) throws IOException {
+ requireAllNonNull(key);
+ setup();
+ createContents(key);
+ this.contentStream.close();
+ return this.document;
+ }
+
+ /**
+ * Creates table for task list.
+ * @param tasks Task list of the student
+ */
+ public void createTaskTable(UniqueTaskList tasks) throws IOException {
+ if (tasks.size() == 0) {
+ wrapText("No task found", this.horizontalWrap, this.fontBold, this.fontTableHeaderSize, List.of());
+ return;
+ }
+ List headers = Arrays.asList("Status", "Name");
+ List maxContentWidthString = Arrays.asList("In Progress", "");
+ this.yInitTable = (this.y + textHeight(this.fontBold, this.fontTableHeaderSize, 0) / 2 + this.margin);
+ createTableRow(headers, maxContentWidthString, this.fontBold, this.fontTableHeaderSize);
+
+ this.x = this.leftMarginPosition;
+ this.y -= textHeight(this.fontBold, this.fontTableContentSize, 0) / 2 + 2 * this.margin;
+ createTableContentForTask(tasks, maxContentWidthString, this.font, this.fontTableContentSize);
+ }
+
+ /**
+ * Creates table for score list.
+ * @param scores Score list of the student
+ */
+ public void createScoreTable(UniqueScoreList scores) throws IOException {
+ if (scores.size() == 0) {
+ wrapText("No score history found", this.horizontalWrap, this.fontBold, this.fontTableHeaderSize, List.of());
+ return;
+ }
+ List headers = Arrays.asList("Date", "Test", "Score");
+ List maxContentWidthString = Arrays.asList("8888-88-88", "WWWWWWWWWWWWWWWWWW", "Score");
+ this.yInitTable = (this.y + textHeight(this.fontBold, this.fontTableHeaderSize, 0) / 2 + this.margin);
+ createTableRow(headers, maxContentWidthString, this.fontBold, this.fontTableHeaderSize);
+
+ this.x = this.leftMarginPosition;
+ this.y -= textHeight(this.fontBold, this.fontTableContentSize, 0) / 2 + 2 * this.margin;
+ createTableContentForScore(scores, maxContentWidthString, this.font, this.fontTableContentSize);
+ }
+
+ /**
+ * Creates a horizontal line for the table.
+ * @param y The y-coordinate of the horizontal line
+ * @throws IOException
+ */
+ public void createHorizontalLine(float y) throws IOException {
+ this.contentStream.drawLine(90, y, 522, y);
+ }
+
+ /**
+ * Creates a row's contents.
+ * @param headers text contents in for the row
+ * @param maxContentWidthString list of strings with the maximum length for each column
+ * @param font font of the contents
+ * @param fontSize font size of the contents
+ * @throws IOException
+ */
+ public void createTableRow(List headers, List maxContentWidthString, PDFont font, int fontSize)
+ throws IOException {
+ float yFinal = this.y;
+ boolean isDoneDrawLine = false;
+ if (this.y - this.margin > 90) {
+ isDoneDrawLine = true;
+ createHorizontalLine((this.y + textHeight(font, fontSize, 0) / 2 + this.margin));
+ }
+
+ for (int i = 0; i < headers.size(); i++) {
+ float wrapCur;
+ if (i != 0 && i != headers.size() - 1) {
+ wrapCur = this.horizontalWrap - (this.x - this.xInit) - 4 * this.margin
+ - textLength(maxContentWidthString.get(i + 1), fontBold, fontTableHeaderSize);
+ } else {
+ wrapCur = this.horizontalWrap - (this.x - this.xInit) - 2 * this.margin;
+ }
+ this.x += this.margin;
+ this.y -= this.margin;
+ wrapText(headers.get(i), wrapCur, font, fontSize, maxContentWidthString);
+
+ yFinal = this.y;
+ this.x += textLength(maxContentWidthString.get(i), this.fontBold, this.fontTableHeaderSize) + this.margin;
+ this.y += this.margin;
+
+ if (!isDoneDrawLine && i == 0) {
+ createHorizontalLine((this.y + textHeight(font, fontSize, 0) / 2 + this.margin));
+ }
+ }
+ this.y = yFinal;
+ }
+
+ /**
+ * Converts string status of a task to a more readable string for the pdf.
+ * @param input status from Task
+ * @return converted string
+ */
+ public String convertStatusString(String input) {
+ String statement = input;
+ String converted = "";
+ switch(statement) {
+ case "INPROGRESS":
+ converted = "In Progress";
+ break;
+ case "COMPLETE":
+ converted = "Complete";
+ break;
+ case "LATE":
+ converted = "Late";
+ break;
+ default:
+ converted = "";
+ break;
+ }
+ return converted;
+ }
+
+ /**
+ * Create the table contents for task list.
+ * @param tasks task list of the student
+ * @param maxContentWidthString list of strings with the maximum length for each column
+ * @param font font of the text contents
+ * @param fontSize font size of the text contents
+ */
+ public void createTableContentForTask(UniqueTaskList tasks, List maxContentWidthString, PDFont font,
+ int fontSize)
+ throws IOException {
+ float yFinal = this.y;
+ ObservableList sortedTaskList = tasks.getInternalList();
+ for (int i = 0; i < tasks.size(); i++) {
+ String status = convertStatusString(sortedTaskList.get(i).getStatus().name());
+ if (status.equals("Late")) {
+ this.curColor = this.red;
+ } else if (status.equals("Complete")) {
+ this.curColor = this.green;
+ }
+ List contents = Arrays.asList(status, sortedTaskList.get(i).getName().fullName);
+ createTableRow(contents, maxContentWidthString, font, fontSize);
+ this.curColor = this.black;
+ yFinal = this.y - margin;
+ this.x = this.xInit;
+ this.y -= textHeight(font, fontSize, 0) / 2 + this.margin * 2;
+ }
+ createVerticalLines(yFinal, maxContentWidthString);
+ createHorizontalLine(yFinal);
+ this.yInitTable = -1;
+ }
+
+ /**
+ * Creates the table contents for score list.
+ * @param scores score list of the student
+ * @param maxContentWidthString list of strings with the maximum length for each column
+ * @param font font of the text contents
+ * @param fontSize font size of the text contents
+ */
+ public void createTableContentForScore(UniqueScoreList scores, List maxContentWidthString, PDFont font,
+ int fontSize) throws IOException {
+ float yFinal = this.y;
+ ObservableList sortedScoreList = scores.getSortedScoreList();
+ for (int i = 0; i < scores.size(); i++) {
+ Score currentScore = sortedScoreList.get(i);
+ double value = currentScore.getValue().value;
+ if (value == 100) {
+ this.curColor = this.green;
+ }
+ if (value < 80) {
+ this.curColor = this.yellow;
+ }
+ if (value < 50) {
+ this.curColor = this.red;
+ }
+ List contents = Arrays.asList(currentScore.getDate().toString(), currentScore.getLabel().toString(),
+ currentScore.getValue().toString());
+ createTableRow(contents, maxContentWidthString, font, fontSize);
+ this.curColor = this.black;
+ yFinal = this.y - this.margin;
+ this.x = this.xInit;
+ this.y -= textHeight(font, fontSize, 0) / 2 + this.margin * 2;
+ }
+ createVerticalLines(yFinal, maxContentWidthString);
+ createHorizontalLine(yFinal);
+ this.yInitTable = -1;
+ }
+
+ /**
+ * Set up the content stream for writing the text contents.
+ * @param font font of the text contents
+ * @param fontSize font size of the text contents
+ * @param x initial x-coordinate of the text
+ * @param y initial y-coordinate of the text
+ * @throws IOException
+ */
+ public void setUpContentStream(String curString, PDFont font, int fontSize, float x, float y, Color color)
+ throws IOException {
+ this.contentStream.beginText();
+ this.contentStream.setFont(font, fontSize);
+ this.contentStream.moveTextPositionByAmount(x, y);
+ this.contentStream.setNonStrokingColor(color);
+ drawText(curString, font, fontSize);
+ }
+
+ /**
+ * Calculates the height of the string.
+ * @param font font of the te
+ * @param fontSize font size of the text
+ * @param margin margin of the text
+ * @return height of the text as a float
+ */
+ public float textHeight(PDFont font, int fontSize, float margin) {
+ return (font.getFontDescriptor().getCapHeight() / 1000 * fontSize) + 2 * margin;
+ }
+
+ /**
+ * Calculates the length of the text.
+ * @param curString current string of the text
+ * @param font font of the text
+ * @param fontSize font size of the text
+ * @return length of the text
+ */
+ public float textLength(String curString, PDFont font, int fontSize) throws IOException {
+ return font.getStringWidth(curString) / 1000 * fontSize;
+ }
+
+ /**
+ * Calculates the maximum number of characters possible inside the wrap values.
+ * @param curString current string of the text
+ * @param font font of the text
+ * @param fontSize font size of the text
+ * @param wrap length wrap of the text
+ * @return maximum number of characters possible for the wrap given
+ */
+ public int getNumberOfCharsPossible(String curString, PDFont font, int fontSize, float wrap) throws IOException {
+ float len = textLength("-", font, fontSize);
+ int count = 0;
+ for (int i = 0; i < curString.length(); i++) {
+ float lenCur = textLength(String.valueOf(curString.charAt(i)), font, fontSize);
+ if (len + lenCur > wrap) {
+ break;
+ }
+ len += lenCur;
+ count += 1;
+ }
+ return count;
+ }
+
+ /**
+ * Creates vertical lines for the table.
+ * @param yFinal final y-coordinate of the table
+ * @param maxContentWidthString list of strings with the maximum length for each column
+ */
+ public void createVerticalLines(float yFinal, List maxContentWidthString) throws IOException {
+ contentStream.drawLine(this.leftMarginPosition, this.yInitTable, this.leftMarginPosition, yFinal);
+ contentStream.drawLine(this.rightMarginPosition, this.yInitTable, this.rightMarginPosition, yFinal);
+ float xPos = this.leftMarginPosition;
+ for (int i = 0; i < maxContentWidthString.size() - 1; i++) {
+ float cur = textLength(maxContentWidthString.get(i), this.fontBold, this.fontTableHeaderSize) + 2
+ * this.margin;
+ xPos += cur;
+ contentStream.drawLine(xPos, yInitTable, xPos, yFinal);
+ }
+ }
+
+ /**
+ * Sets up a new page if the current page reaches the margin.
+ * @param yFinal final y-coordinate of the current page
+ * @param maxContentWidthString list of strings with the maximum length for each column
+ * @param margin margin of the text
+ * @param font font of the text
+ * @param fontSize font size of the text
+ * @return new content stream for the next page
+ */
+ public PDPageContentStream handleNextPage(float yFinal, List maxContentWidthString, float margin,
+ PDFont font, int fontSize)
+ throws IOException {
+ if (this.yInitTable != -1 && this.yInitTable - 90 >= textHeight(font, fontSize, 0) + 2 * margin) {
+ float yTemp = yFinal + textHeight(font, fontSize, this.margin / 2) + this.margin;
+ createVerticalLines(yTemp, maxContentWidthString);
+ createHorizontalLine(yTemp);
+ }
+ contentStream.close();
+ this.page = new PDPage();
+ this.document.addPage(this.page);
+ this.contentStream = new PDPageContentStream(document, page);
+ if (this.yInitTable != -1 && this.yInitTable - 90 >= textHeight(font, fontSize, 0) + 2 * margin) {
+ createHorizontalLine(this.yInit + textHeight(font, fontSize, 0) + this.margin);
+ }
+ return contentStream;
+ }
+
+ /**
+ * Write text to the pdf
+ * @param curString current string to be written
+ * @param font font of the text
+ * @param fontSize font size of the text
+ */
+ public void drawText(String curString, PDFont font, int fontSize) throws IOException {
+ this.contentStream.drawString(curString);
+ this.contentStream.endText();
+ this.x += textLength(curString, font, fontSize);
+ }
+
+ /**
+ * Change position to next line
+ * @param count determines whether the current string is the first line of the string
+ * @param font font of the text
+ * @param fontSize font size of the text
+ * @param xPosition x coordinate of the text
+ */
+ public void handleNextLine(int count, PDFont font, int fontSize, float xPosition) {
+ if (count != 0) {
+ this.y -= textHeight(font, fontSize, this.margin / 2);
+ }
+ this.x = xPosition;
+ }
+
+ /**
+ * Handle next page for currently wrapped text
+ * @param count determines whether the current string is the first line of the string
+ * @param yPrev y coordinate of the previous line
+ * @param maxContentWidthString list of strings with the maximum length for each column
+ * @param font font of the text
+ * @param fontSize font size of the text
+ * @param xPosition x coordinate of the text
+ */
+ public void handleWrapNextPage(int count, float yPrev, List maxContentWidthString, PDFont font,
+ int fontSize, float xPosition) throws IOException {
+ float yTemp = (count != 0) ? this.y - 2 * this.margin : yPrev - this.margin;
+ this.contentStream = handleNextPage(yTemp, maxContentWidthString, this.margin, font, fontSize);
+ this.x = xPosition;
+ this.y = yInit;
+ this.yInitTable = this.y + textHeight(font, fontSize, 0) / 2 + 2 * this.margin;
+ }
+
+ /**
+ * Wraps the string given to fit in the specified wrap length.
+ * @param text text string
+ * @param wrap length wrap of the text
+ * @param font font of the text
+ * @param fontSize font size of the text
+ * @param maxContentWidthString list of strings with the maximum length for each column
+ */
+ public void wrapText(String text, float wrap, PDFont font, int fontSize, List maxContentWidthString)
+ throws IOException {
+ String[] textSplit = text.split(" ");
+ float lengthUsed = 0;
+ float xPosition = this.x;
+ int count = 0;
+ for (int i = 0; i < textSplit.length; i++) {
+ String curString = textSplit[i];
+ lengthUsed += textLength(curString, font, fontSize);
+ if (textLength(curString, font, fontSize) <= wrap) {
+ curString = curString + " ";
+ lengthUsed += 1;
+ } else {
+ int limit = getNumberOfCharsPossible(curString, font, fontSize, wrap);
+ curString = curString.substring(0, limit) + "-";
+ textSplit[i] = textSplit[i].substring(limit);
+ i--;
+ }
+ float yPrev = this.y;
+ if (lengthUsed > wrap) {
+ handleNextLine(count, font, fontSize, xPosition);
+ lengthUsed = textLength(curString, font, fontSize);
+ }
+ if (this.y <= 90) {
+ handleWrapNextPage(count, yPrev, maxContentWidthString, font, fontSize, xPosition);
+ }
+ setUpContentStream(curString, font, fontSize, this.x, this.y, this.curColor);
+ count += 1;
+ }
+ this.x = xPosition;
+ }
+}
diff --git a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java
deleted file mode 100644
index 6ddc2cd9a29..00000000000
--- a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package seedu.address.model;
-
-import javafx.collections.ObservableList;
-import seedu.address.model.person.Person;
-
-/**
- * Unmodifiable view of an address book
- */
-public interface ReadOnlyAddressBook {
-
- /**
- * Returns an unmodifiable view of the persons list.
- * This list will not contain any duplicate persons.
- */
- ObservableList getPersonList();
-
-}
diff --git a/src/main/java/seedu/address/model/ReadOnlyMathutoring.java b/src/main/java/seedu/address/model/ReadOnlyMathutoring.java
new file mode 100644
index 00000000000..31bdcb15643
--- /dev/null
+++ b/src/main/java/seedu/address/model/ReadOnlyMathutoring.java
@@ -0,0 +1,17 @@
+package seedu.address.model;
+
+import javafx.collections.ObservableList;
+import seedu.address.model.student.Student;
+
+/**
+ * Unmodifiable view of a mathutoring
+ */
+public interface ReadOnlyMathutoring {
+
+ /**
+ * Returns an unmodifiable view of the students list.
+ * This list will not contain any duplicate students.
+ */
+ ObservableList getStudentList();
+
+}
diff --git a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java b/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java
index befd58a4c73..5d670a662e4 100644
--- a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java
+++ b/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java
@@ -11,6 +11,6 @@ public interface ReadOnlyUserPrefs {
GuiSettings getGuiSettings();
- Path getAddressBookFilePath();
+ Path getMathutoringFilePath();
}
diff --git a/src/main/java/seedu/address/model/UserPrefs.java b/src/main/java/seedu/address/model/UserPrefs.java
index 25a5fd6eab9..cbbab7f31c2 100644
--- a/src/main/java/seedu/address/model/UserPrefs.java
+++ b/src/main/java/seedu/address/model/UserPrefs.java
@@ -14,7 +14,7 @@
public class UserPrefs implements ReadOnlyUserPrefs {
private GuiSettings guiSettings = new GuiSettings();
- private Path addressBookFilePath = Paths.get("data" , "addressbook.json");
+ private Path mathutoringFilePath = Paths.get("data" , "mathutoring.json");
/**
* Creates a {@code UserPrefs} with default values.
@@ -35,7 +35,7 @@ public UserPrefs(ReadOnlyUserPrefs userPrefs) {
public void resetData(ReadOnlyUserPrefs newUserPrefs) {
requireNonNull(newUserPrefs);
setGuiSettings(newUserPrefs.getGuiSettings());
- setAddressBookFilePath(newUserPrefs.getAddressBookFilePath());
+ setMathutoringFilePath(newUserPrefs.getMathutoringFilePath());
}
public GuiSettings getGuiSettings() {
@@ -47,13 +47,13 @@ public void setGuiSettings(GuiSettings guiSettings) {
this.guiSettings = guiSettings;
}
- public Path getAddressBookFilePath() {
- return addressBookFilePath;
+ public Path getMathutoringFilePath() {
+ return mathutoringFilePath;
}
- public void setAddressBookFilePath(Path addressBookFilePath) {
- requireNonNull(addressBookFilePath);
- this.addressBookFilePath = addressBookFilePath;
+ public void setMathutoringFilePath(Path mathutoringFilePath) {
+ requireNonNull(mathutoringFilePath);
+ this.mathutoringFilePath = mathutoringFilePath;
}
@Override
@@ -68,19 +68,19 @@ public boolean equals(Object other) {
UserPrefs o = (UserPrefs) other;
return guiSettings.equals(o.guiSettings)
- && addressBookFilePath.equals(o.addressBookFilePath);
+ && mathutoringFilePath.equals(o.mathutoringFilePath);
}
@Override
public int hashCode() {
- return Objects.hash(guiSettings, addressBookFilePath);
+ return Objects.hash(guiSettings, mathutoringFilePath);
}
@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 : " + mathutoringFilePath);
return sb.toString();
}
diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/address/model/person/Person.java
deleted file mode 100644
index 8ff1d83fe89..00000000000
--- a/src/main/java/seedu/address/model/person/Person.java
+++ /dev/null
@@ -1,123 +0,0 @@
-package seedu.address.model.person;
-
-import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Objects;
-import java.util.Set;
-
-import seedu.address.model.tag.Tag;
-
-/**
- * Represents a Person in the address book.
- * Guarantees: details are present and not null, field values are validated, immutable.
- */
-public class Person {
-
- // Identity fields
- private final Name name;
- private final Phone phone;
- private final Email email;
-
- // Data fields
- private final Address address;
- private final Set tags = new HashSet<>();
-
- /**
- * Every field must be present and not null.
- */
- public Person(Name name, Phone phone, Email email, Address address, Set tags) {
- requireAllNonNull(name, phone, email, address, tags);
- this.name = name;
- this.phone = phone;
- this.email = email;
- this.address = address;
- this.tags.addAll(tags);
- }
-
- public Name getName() {
- return name;
- }
-
- public Phone getPhone() {
- return phone;
- }
-
- public Email getEmail() {
- return email;
- }
-
- public Address getAddress() {
- return address;
- }
-
- /**
- * Returns an immutable tag set, which throws {@code UnsupportedOperationException}
- * if modification is attempted.
- */
- public Set getTags() {
- return Collections.unmodifiableSet(tags);
- }
-
- /**
- * Returns true if both persons have the same name.
- * This defines a weaker notion of equality between two persons.
- */
- public boolean isSamePerson(Person otherPerson) {
- if (otherPerson == this) {
- return true;
- }
-
- return otherPerson != null
- && otherPerson.getName().equals(getName());
- }
-
- /**
- * Returns true if both persons have the same identity and data fields.
- * This defines a stronger notion of equality between two persons.
- */
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- if (!(other instanceof Person)) {
- return false;
- }
-
- Person otherPerson = (Person) other;
- return otherPerson.getName().equals(getName())
- && otherPerson.getPhone().equals(getPhone())
- && otherPerson.getEmail().equals(getEmail())
- && otherPerson.getAddress().equals(getAddress())
- && otherPerson.getTags().equals(getTags());
- }
-
- @Override
- public int hashCode() {
- // use this method for custom fields hashing instead of implementing your own
- return Objects.hash(name, phone, email, address, tags);
- }
-
- @Override
- public String toString() {
- final StringBuilder builder = new StringBuilder();
- builder.append(getName())
- .append("; Phone: ")
- .append(getPhone())
- .append("; Email: ")
- .append(getEmail())
- .append("; Address: ")
- .append(getAddress());
-
- Set tags = getTags();
- if (!tags.isEmpty()) {
- builder.append("; Tags: ");
- tags.forEach(builder::append);
- }
- return builder.toString();
- }
-
-}
diff --git a/src/main/java/seedu/address/model/person/UniquePersonList.java b/src/main/java/seedu/address/model/person/UniquePersonList.java
deleted file mode 100644
index 0fee4fe57e6..00000000000
--- a/src/main/java/seedu/address/model/person/UniquePersonList.java
+++ /dev/null
@@ -1,137 +0,0 @@
-package seedu.address.model.person;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
-
-import java.util.Iterator;
-import java.util.List;
-
-import javafx.collections.FXCollections;
-import javafx.collections.ObservableList;
-import seedu.address.model.person.exceptions.DuplicatePersonException;
-import seedu.address.model.person.exceptions.PersonNotFoundException;
-
-/**
- * A list of persons that enforces uniqueness between its elements and does not allow nulls.
- * A person is considered unique by comparing using {@code Person#isSamePerson(Person)}. As such, adding and updating of
- * persons uses Person#isSamePerson(Person) for equality so as to ensure that the person being added or updated is
- * unique in terms of identity in the UniquePersonList. However, the removal of a person uses Person#equals(Object) so
- * as to ensure that the person with exactly the same fields will be removed.
- *
- * Supports a minimal set of list operations.
- *
- * @see Person#isSamePerson(Person)
- */
-public class UniquePersonList implements Iterable {
-
- private final ObservableList internalList = FXCollections.observableArrayList();
- private final ObservableList internalUnmodifiableList =
- FXCollections.unmodifiableObservableList(internalList);
-
- /**
- * Returns true if the list contains an equivalent person as the given argument.
- */
- public boolean contains(Person toCheck) {
- requireNonNull(toCheck);
- return internalList.stream().anyMatch(toCheck::isSamePerson);
- }
-
- /**
- * Adds a person to the list.
- * The person must not already exist in the list.
- */
- public void add(Person toAdd) {
- requireNonNull(toAdd);
- if (contains(toAdd)) {
- throw new DuplicatePersonException();
- }
- internalList.add(toAdd);
- }
-
- /**
- * Replaces the person {@code target} in the list with {@code editedPerson}.
- * {@code target} must exist in the list.
- * The person identity of {@code editedPerson} must not be the same as another existing person in the list.
- */
- public void setPerson(Person target, Person editedPerson) {
- requireAllNonNull(target, editedPerson);
-
- int index = internalList.indexOf(target);
- if (index == -1) {
- throw new PersonNotFoundException();
- }
-
- if (!target.isSamePerson(editedPerson) && contains(editedPerson)) {
- throw new DuplicatePersonException();
- }
-
- internalList.set(index, editedPerson);
- }
-
- /**
- * Removes the equivalent person from the list.
- * The person must exist in the list.
- */
- public void remove(Person toRemove) {
- requireNonNull(toRemove);
- if (!internalList.remove(toRemove)) {
- throw new PersonNotFoundException();
- }
- }
-
- public void setPersons(UniquePersonList replacement) {
- requireNonNull(replacement);
- internalList.setAll(replacement.internalList);
- }
-
- /**
- * Replaces the contents of this list with {@code persons}.
- * {@code persons} must not contain duplicate persons.
- */
- public void setPersons(List persons) {
- requireAllNonNull(persons);
- if (!personsAreUnique(persons)) {
- throw new DuplicatePersonException();
- }
-
- internalList.setAll(persons);
- }
-
- /**
- * Returns the backing list as an unmodifiable {@code ObservableList}.
- */
- public ObservableList asUnmodifiableObservableList() {
- return internalUnmodifiableList;
- }
-
- @Override
- public Iterator iterator() {
- return internalList.iterator();
- }
-
- @Override
- public boolean equals(Object other) {
- return other == this // short circuit if same object
- || (other instanceof UniquePersonList // instanceof handles nulls
- && internalList.equals(((UniquePersonList) other).internalList));
- }
-
- @Override
- public int hashCode() {
- return internalList.hashCode();
- }
-
- /**
- * Returns true if {@code persons} contains only unique persons.
- */
- private boolean personsAreUnique(List persons) {
- for (int i = 0; i < persons.size() - 1; i++) {
- for (int j = i + 1; j < persons.size(); j++) {
- if (persons.get(i).isSamePerson(persons.get(j))) {
- return false;
- }
- }
- }
- return true;
- }
-}
diff --git a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java b/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java
deleted file mode 100644
index d7290f59442..00000000000
--- a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package seedu.address.model.person.exceptions;
-
-/**
- * Signals that the operation will result in duplicate Persons (Persons are considered duplicates if they have the same
- * identity).
- */
-public class DuplicatePersonException extends RuntimeException {
- public DuplicatePersonException() {
- super("Operation would result in duplicate persons");
- }
-}
diff --git a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java b/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java
deleted file mode 100644
index fa764426ca7..00000000000
--- a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package seedu.address.model.person.exceptions;
-
-/**
- * Signals that the operation is unable to find the specified person.
- */
-public class PersonNotFoundException extends RuntimeException {}
diff --git a/src/main/java/seedu/address/model/score/Date.java b/src/main/java/seedu/address/model/score/Date.java
new file mode 100644
index 00000000000..56929bb8459
--- /dev/null
+++ b/src/main/java/seedu/address/model/score/Date.java
@@ -0,0 +1,85 @@
+package seedu.address.model.score;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.AppUtil.checkArgument;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeParseException;
+
+//@@author astraxq
+/**
+ * Represents a Score's date in the Score object.
+ * Guarantees: immutable; is valid as declared in {@link #isValidDate(String)}
+ */
+public class Date {
+ public static final String MESSAGE_CONSTRAINTS =
+ "Dates should only be in yyyy-MM-dd format, and it should not be blank";
+
+ public static final String MESSAGE_INVALID_DATE =
+ "The date entered is in the future. Please recheck the date entered.";
+
+
+ // Identity field(s)
+ public final LocalDate date;
+
+ /**
+ * Constructs a {@code Date}.
+ *
+ * @param date A valid date.
+ */
+ public Date(String date) {
+ requireNonNull(date);
+ checkArgument(isValidDate(date), MESSAGE_CONSTRAINTS);
+ checkArgument(isFutureDate(date), MESSAGE_INVALID_DATE);
+ this.date = LocalDate.parse(date);
+ }
+
+ public LocalDate getDate() {
+ return this.date;
+ }
+
+ /**
+ * Returns true if the date is a valid date.
+ */
+ public static boolean isValidDate(String test) {
+ try {
+ LocalDate.parse(test);
+ return true;
+ } catch (DateTimeParseException e) {
+ return false;
+ }
+ }
+ //@@author astraxq
+ //@@author QQH0828
+ /**
+ * Returns true if the date is not a future date.
+ */
+ public static boolean isFutureDate(String test) {
+ LocalDate inputDate = LocalDate.parse(test);
+ LocalDate computerDate = LocalDate.now();
+ if (inputDate.isAfter(computerDate)) {
+ return false;
+ }
+ return true;
+ }
+ //@@author QQH0828
+ //@@author astraxq
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof Date // instanceof handles nulls
+ && date.equals(((Date) other).date)); // state check
+ }
+
+ @Override
+ public int hashCode() {
+ return date.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return date.toString();
+ }
+
+}
+//@@author astraxq
diff --git a/src/main/java/seedu/address/model/score/Label.java b/src/main/java/seedu/address/model/score/Label.java
new file mode 100644
index 00000000000..d233dc0d6ac
--- /dev/null
+++ b/src/main/java/seedu/address/model/score/Label.java
@@ -0,0 +1,52 @@
+package seedu.address.model.score;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.AppUtil.checkArgument;
+
+//@@author astraxq
+/**
+ * Represents the score title in a score.
+ * Guarantees: immutable; is valid as declared in {@link #isValidLabel(String)}
+ */
+public class Label {
+
+ public static final String MESSAGE_CONSTRAINTS =
+ "Label should only contain alphanumeric characters and spaces, and it should not be blank";
+
+ public static final String VALIDATION_REGEX = "[\\p{Alnum}][\\p{Alnum} ]*";
+
+ public final String label;
+
+ /**
+ * Constructs a {@code label}.
+ *
+ * @param label A valid label.
+ */
+ public Label(String label) {
+ requireNonNull(label);
+ checkArgument(isValidLabel(label), MESSAGE_CONSTRAINTS);
+ this.label = label;
+ }
+
+ public static boolean isValidLabel(String test) {
+ return test.matches(VALIDATION_REGEX);
+ }
+
+ @Override
+ public String toString() {
+ return label;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof Label // instanceof handles nulls
+ && label.equals(((Label) other).label)); // state check
+ }
+
+ @Override
+ public int hashCode() {
+ return label.hashCode();
+ }
+}
+//@@author astraxq
diff --git a/src/main/java/seedu/address/model/score/Score.java b/src/main/java/seedu/address/model/score/Score.java
new file mode 100644
index 00000000000..e419d95adf3
--- /dev/null
+++ b/src/main/java/seedu/address/model/score/Score.java
@@ -0,0 +1,108 @@
+package seedu.address.model.score;
+
+import static java.util.Objects.requireNonNull;
+
+import java.time.LocalDate;
+import java.util.Objects;
+
+//@@author astraxq
+/**
+ * Represents a Score in the mathutoring.
+ * Guarantees: immutable; fields are validated; details are present and not null;
+ */
+public class Score {
+
+ public static final String MESSAGE_CONSTRAINTS = "Score format is wrong!";
+
+ // Identity field(s)
+ public final Label scoreLabel;
+ public final ScoreValue scoreValue;
+ public final Date scoreDate;
+
+ /**
+ * Constructs a {@code Score}.
+ *
+ * @param scoreLabel A valid Score name.
+ * @param scoreValue A valid Score value.
+ * @param scoreDate A valid Score date.
+ */
+ public Score(Label scoreLabel, ScoreValue scoreValue, Date scoreDate) {
+ requireNonNull(scoreLabel);
+ requireNonNull(scoreValue);
+ requireNonNull(scoreDate);
+
+ this.scoreLabel = scoreLabel;
+ this.scoreValue = scoreValue;
+ this.scoreDate = scoreDate;
+ }
+
+ /**
+ * Returns the score name.
+ */
+ public Label getLabel() {
+ return this.scoreLabel;
+ }
+
+ /**
+ * Returns the score value.
+ */
+ public ScoreValue getValue() {
+ return this.scoreValue;
+ }
+
+ /**
+ * Returns the score date in the format of yyyy-MM-dd HH:mm:ss.
+ */
+ public Date getDate() {
+ return this.scoreDate;
+ }
+
+ public LocalDate getLocalDate() {
+ return this.scoreDate.getDate();
+ }
+ //@@author astraxq
+ //@@author QQH0828
+ /**
+ * Returns true if both scores have the same date.
+ */
+ public boolean isSameScore(Score otherScore) {
+ if (otherScore == this) {
+ return true;
+ }
+
+ return otherScore != null
+ && otherScore.getDate().equals(getDate());
+ }
+ //@@author QQH0828
+ //@@author astraxq
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof Score // instanceof handles nulls
+ && scoreLabel.equals(((Score) other).scoreLabel)
+ && scoreValue.equals(((Score) other).scoreValue)
+ && scoreDate.equals(((Score) other).scoreDate)); // state check
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(scoreLabel, scoreValue, scoreDate);
+ }
+
+ /**
+ * Format state as text for viewing.
+ */
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append("Label: ")
+ .append(getLabel())
+ .append("; Score: ")
+ .append(getValue())
+ .append("; Date: ")
+ .append(getDate());
+
+ return builder.toString();
+ }
+}
+//@@author astraxq
diff --git a/src/main/java/seedu/address/model/score/ScoreValue.java b/src/main/java/seedu/address/model/score/ScoreValue.java
new file mode 100644
index 00000000000..5743049376f
--- /dev/null
+++ b/src/main/java/seedu/address/model/score/ScoreValue.java
@@ -0,0 +1,57 @@
+package seedu.address.model.score;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.AppUtil.checkArgument;
+
+//@@author astraxq
+/**
+ * Represents a Score's value in the Score object.
+ * Guarantees: immutable; is valid as declared in {@link #isValidScoreValue(String)}
+ */
+public class ScoreValue {
+ public static final String MESSAGE_CONSTRAINTS =
+ "Score value should be a number between 0 and 100 "
+ + "(with 1 decimal place [optional]), and it should not be blank";
+
+ public static final String VALIDATION_REGEX = "^(([1-9][0-9]?|0)(\\.\\d)?|100(\\.0)?)$";
+
+ // Identity field(s)
+ public final Double value;
+
+ /**
+ * Constructs a {@code ScoreValue}.
+ *
+ * @param value A valid Score value.
+ */
+ public ScoreValue(String value) {
+ requireNonNull(value);
+ checkArgument(isValidScoreValue(value), MESSAGE_CONSTRAINTS);
+
+ this.value = Double.parseDouble(value);
+ }
+
+ /**
+ * Returns true if the value is a valid score value (0 <= s <= 100).
+ */
+ public static boolean isValidScoreValue(String test) {
+ return test.matches(VALIDATION_REGEX);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof ScoreValue // instanceof handles nulls
+ && value.equals(((ScoreValue) other).value)); // state check
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return value.toString();
+ }
+}
+//@@author astraxq
diff --git a/src/main/java/seedu/address/model/score/UniqueScoreList.java b/src/main/java/seedu/address/model/score/UniqueScoreList.java
new file mode 100644
index 00000000000..7cc75443e7b
--- /dev/null
+++ b/src/main/java/seedu/address/model/score/UniqueScoreList.java
@@ -0,0 +1,179 @@
+package seedu.address.model.score;
+
+import static java.util.Objects.requireNonNull;
+
+import java.text.DecimalFormat;
+import java.util.DoubleSummaryStatistics;
+import java.util.Iterator;
+
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import seedu.address.model.score.exceptions.DuplicateScoreException;
+import seedu.address.model.score.exceptions.ScoreNotFoundException;
+
+/**
+ * A list of scores that enforces uniqueness between its elements and does not allow nulls.
+ * A score is considered unique by comparing using {@code score#isSameScore(otherScore)}. As such, adding and updating
+ * of scores uses Score#isSameScore(otherScore) for equality so as to ensure that the score being added or updated is
+ * unique in terms of identity in the UniqueScoreList. However, the removal of a score uses score#equals(Object) so
+ * as to ensure that the score with exactly the same fields will be removed.
+ *
+ * Supports a minimal set of list operations.
+ *
+ * @see Score#isSameScore(Score)
+ */
+public class UniqueScoreList implements Iterable {
+ private final ObservableList internalList = FXCollections.observableArrayList();
+ private final ObservableList internalUnmodifiableList =
+ FXCollections.unmodifiableObservableList(internalList);
+
+ /**
+ * Returns true if the list contains an equivalent score as the given argument.
+ */
+ public boolean contains(Score toCheck) {
+ requireNonNull(toCheck);
+ return internalList.stream().anyMatch(toCheck::isSameScore);
+ }
+
+ //@@author astraxq
+ /**
+ * Adds a score to the list.
+ * The score must not already exist in the list.
+ */
+ public void add(Score toAdd) {
+ requireNonNull(toAdd);
+ if (contains(toAdd)) {
+ throw new DuplicateScoreException();
+ }
+ internalList.add(toAdd);
+ internalList.sort((s1, s2) -> s1.getLocalDate().compareTo(s2.getLocalDate()));
+ FXCollections.reverse(internalList);
+ }
+
+ /**
+ * Removes the equivalent score from the list.
+ * The score must exist in the list.
+ */
+ public void remove(Score toRemove) {
+ requireNonNull(toRemove);
+ if (!internalList.remove(toRemove)) {
+ throw new ScoreNotFoundException();
+ }
+ }
+
+ /**
+ * Returns the backing score list as an unmodifiable {@code ObservableList}.
+ */
+ public ObservableList asUnmodifiableObservableList() {
+ return internalUnmodifiableList;
+ }
+ //@@author astraxq
+
+ /**
+ * Gets the sorted score list with recent score at front.
+ * @return A view of list of sorted score.
+ */
+ public ObservableList getSortedScoreList() {
+ return internalList;
+ }
+
+ /**
+ * Gets the recent 5 scores with recent score at back.
+ * @return A view of list of recent 5 scores.
+ */
+ public ObservableList getRecentScoreList() {
+ if (internalList.size() < 5) {
+ ObservableList recentScoreList = FXCollections.observableArrayList(internalList.stream()
+ .limit(internalList.size()).collect(java.util.stream.Collectors.toList()));
+ FXCollections.reverse(recentScoreList);
+ return recentScoreList;
+ }
+ ObservableList recentScoreList = FXCollections.observableArrayList(internalList.stream()
+ .limit(5).collect(java.util.stream.Collectors.toList()));
+ FXCollections.reverse(recentScoreList);
+ return recentScoreList;
+ }
+
+ /**
+ * Gets the summary statistic of recent 5 scores.
+ * @return A view of list of recent 5 scores' summary statistic.
+ */
+ public ObservableList getScoreSummary() {
+ DecimalFormat df = new DecimalFormat("#.##");
+ ObservableList recentScoreList = getRecentScoreList();
+ DoubleSummaryStatistics scoreSummary = new DoubleSummaryStatistics();
+ for (int i = 0; i < recentScoreList.size(); i++) {
+ scoreSummary.accept(recentScoreList.get(i).getValue().value);
+ }
+ Double average = scoreSummary.getAverage();
+ Double maxValue = scoreSummary.getMax();
+ Double minValue = scoreSummary.getMin();
+ Double oldValue = recentScoreList.get(0).getValue().value;
+ Double newValue = recentScoreList.get(recentScoreList.size() - 1).getValue().value;
+ Double percentage = newValue - oldValue == 0 ? 0 : oldValue == 0 ? (newValue - oldValue) / 1
+ : (newValue - oldValue) / oldValue * 100;
+ average = Double.valueOf(df.format(average));
+ percentage = Double.valueOf(df.format(percentage));
+ ScoreSummary ss = new ScoreSummary(maxValue, minValue, average, percentage);
+ ObservableList summary = FXCollections.observableArrayList();
+ summary.add(ss);
+ return summary;
+ }
+
+ /**
+ * Stores the recent 5 scores' summary statistic details.
+ */
+ public class ScoreSummary {
+
+ private Double maxScore;
+ private Double minScore;
+ private Double average;
+ private Double percentage;
+
+ /**
+ * Class constructor
+ */
+ public ScoreSummary(Double max, Double min, Double average, Double percentage) {
+ this.maxScore = max;
+ this.minScore = min;
+ this.average = average;
+ this.percentage = percentage;
+ }
+
+ public Double getMaxScore() {
+ return this.maxScore;
+ }
+
+ public Double getMinScore() {
+ return this.minScore;
+ }
+
+ public Double getAverage() {
+ return this.average;
+ }
+
+ public Double getPercentage() {
+ return this.percentage;
+ }
+ }
+
+ @Override
+ public Iterator iterator() {
+ return internalList.iterator();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof UniqueScoreList // instanceof handles nulls
+ && internalList.equals(((UniqueScoreList) other).internalList));
+ }
+
+ public int size() {
+ return internalList.size();
+ }
+ public Score get(int index) {
+ return internalList.get(index);
+ }
+
+}
diff --git a/src/main/java/seedu/address/model/score/exceptions/DuplicateScoreException.java b/src/main/java/seedu/address/model/score/exceptions/DuplicateScoreException.java
new file mode 100644
index 00000000000..56288139dc9
--- /dev/null
+++ b/src/main/java/seedu/address/model/score/exceptions/DuplicateScoreException.java
@@ -0,0 +1,13 @@
+package seedu.address.model.score.exceptions;
+
+//@@author astraxq
+/**
+ * Signals that the operation will result in duplicate Scores (Scores are considered duplicates if they have the same
+ * identity).
+ */
+public class DuplicateScoreException extends RuntimeException {
+ public DuplicateScoreException() {
+ super("Operation would result in duplicate scores");
+ }
+}
+//@author astraxq
diff --git a/src/main/java/seedu/address/model/score/exceptions/ScoreNotFoundException.java b/src/main/java/seedu/address/model/score/exceptions/ScoreNotFoundException.java
new file mode 100644
index 00000000000..13ee650901b
--- /dev/null
+++ b/src/main/java/seedu/address/model/score/exceptions/ScoreNotFoundException.java
@@ -0,0 +1,8 @@
+package seedu.address.model.score.exceptions;
+
+//@@author astraxq
+/**
+ * Signals that the operation is unable to find the specified score.
+ */
+public class ScoreNotFoundException extends RuntimeException{}
+//@@author astraxq
diff --git a/src/main/java/seedu/address/model/person/Address.java b/src/main/java/seedu/address/model/student/Address.java
similarity index 94%
rename from src/main/java/seedu/address/model/person/Address.java
rename to src/main/java/seedu/address/model/student/Address.java
index 60472ca22a0..41627a19f81 100644
--- a/src/main/java/seedu/address/model/person/Address.java
+++ b/src/main/java/seedu/address/model/student/Address.java
@@ -1,10 +1,10 @@
-package seedu.address.model.person;
+package seedu.address.model.student;
import static java.util.Objects.requireNonNull;
import static seedu.address.commons.util.AppUtil.checkArgument;
/**
- * Represents a Person's address in the address book.
+ * Represents a Student's address in the mathutoring.
* Guarantees: immutable; is valid as declared in {@link #isValidAddress(String)}
*/
public class Address {
diff --git a/src/main/java/seedu/address/model/person/Email.java b/src/main/java/seedu/address/model/student/Email.java
similarity index 96%
rename from src/main/java/seedu/address/model/person/Email.java
rename to src/main/java/seedu/address/model/student/Email.java
index f866e7133de..a2cc0eabf60 100644
--- a/src/main/java/seedu/address/model/person/Email.java
+++ b/src/main/java/seedu/address/model/student/Email.java
@@ -1,10 +1,10 @@
-package seedu.address.model.person;
+package seedu.address.model.student;
import static java.util.Objects.requireNonNull;
import static seedu.address.commons.util.AppUtil.checkArgument;
/**
- * Represents a Person's email in the address book.
+ * Represents a Student's email in the mathutoring.
* Guarantees: immutable; is valid as declared in {@link #isValidEmail(String)}
*/
public class Email {
diff --git a/src/main/java/seedu/address/model/person/Name.java b/src/main/java/seedu/address/model/student/Name.java
similarity index 88%
rename from src/main/java/seedu/address/model/person/Name.java
rename to src/main/java/seedu/address/model/student/Name.java
index 79244d71cf7..2d4625fcba1 100644
--- a/src/main/java/seedu/address/model/person/Name.java
+++ b/src/main/java/seedu/address/model/student/Name.java
@@ -1,10 +1,10 @@
-package seedu.address.model.person;
+package seedu.address.model.student;
import static java.util.Objects.requireNonNull;
import static seedu.address.commons.util.AppUtil.checkArgument;
/**
- * Represents a Person's name in the address book.
+ * Represents a Student's name in the mathutoring.
* Guarantees: immutable; is valid as declared in {@link #isValidName(String)}
*/
public class Name {
@@ -48,7 +48,7 @@ public String toString() {
public boolean equals(Object other) {
return other == this // short circuit if same object
|| (other instanceof Name // instanceof handles nulls
- && fullName.equals(((Name) other).fullName)); // state check
+ && fullName.toLowerCase().equals(((Name) other).fullName.toLowerCase())); // state check
}
@Override
diff --git a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/student/NameContainsKeywordsPredicate.java
similarity index 77%
rename from src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java
rename to src/main/java/seedu/address/model/student/NameContainsKeywordsPredicate.java
index c9b5868427c..b036ed35273 100644
--- a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java
+++ b/src/main/java/seedu/address/model/student/NameContainsKeywordsPredicate.java
@@ -1,4 +1,4 @@
-package seedu.address.model.person;
+package seedu.address.model.student;
import java.util.List;
import java.util.function.Predicate;
@@ -6,9 +6,9 @@
import seedu.address.commons.util.StringUtil;
/**
- * Tests that a {@code Person}'s {@code Name} matches any of the keywords given.
+ * Tests that a {@code Student}'s {@code Name} matches any of the keywords given.
*/
-public class NameContainsKeywordsPredicate implements Predicate {
+public class NameContainsKeywordsPredicate implements Predicate {
private final List keywords;
public NameContainsKeywordsPredicate(List keywords) {
@@ -16,9 +16,9 @@ public NameContainsKeywordsPredicate(List keywords) {
}
@Override
- public boolean test(Person person) {
+ public boolean test(Student student) {
return keywords.stream()
- .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getName().fullName, keyword));
+ .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(student.getName().fullName, keyword));
}
@Override
diff --git a/src/main/java/seedu/address/model/person/Phone.java b/src/main/java/seedu/address/model/student/Phone.java
similarity index 69%
rename from src/main/java/seedu/address/model/person/Phone.java
rename to src/main/java/seedu/address/model/student/Phone.java
index 872c76b382f..2757e7adab3 100644
--- a/src/main/java/seedu/address/model/person/Phone.java
+++ b/src/main/java/seedu/address/model/student/Phone.java
@@ -1,10 +1,10 @@
-package seedu.address.model.person;
+package seedu.address.model.student;
import static java.util.Objects.requireNonNull;
import static seedu.address.commons.util.AppUtil.checkArgument;
/**
- * Represents a Person's phone number in the address book.
+ * Represents a Student's phone number in the mathutoring.
* Guarantees: immutable; is valid as declared in {@link #isValidPhone(String)}
*/
public class Phone {
@@ -12,6 +12,8 @@ public class Phone {
public static final String MESSAGE_CONSTRAINTS =
"Phone numbers should only contain numbers, and it should be at least 3 digits long";
+ public static final String MESSAGE_EXCEED_MAX_DIGITS =
+ "Phone numbers should not be more than 15 digits. Please recheck the phone number entered.";
public static final String VALIDATION_REGEX = "\\d{3,}";
public final String value;
@@ -23,6 +25,7 @@ public class Phone {
public Phone(String phone) {
requireNonNull(phone);
checkArgument(isValidPhone(phone), MESSAGE_CONSTRAINTS);
+ checkArgument(isMoreThanMaxDigits(phone), MESSAGE_EXCEED_MAX_DIGITS);
value = phone;
}
@@ -33,6 +36,16 @@ public static boolean isValidPhone(String test) {
return test.matches(VALIDATION_REGEX);
}
+ /**
+ * Returns true if a given phone number is not exceed 15 digits long.
+ */
+ public static boolean isMoreThanMaxDigits(String test) {
+ if (test.length() > 15) {
+ return false;
+ }
+ return true;
+ }
+
@Override
public String toString() {
return value;
diff --git a/src/main/java/seedu/address/model/student/Student.java b/src/main/java/seedu/address/model/student/Student.java
new file mode 100644
index 00000000000..52a37b1ec0f
--- /dev/null
+++ b/src/main/java/seedu/address/model/student/Student.java
@@ -0,0 +1,313 @@
+package seedu.address.model.student;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+import javafx.collections.ObservableList;
+import javafx.collections.transformation.FilteredList;
+import seedu.address.model.score.Score;
+import seedu.address.model.score.UniqueScoreList;
+import seedu.address.model.score.UniqueScoreList.ScoreSummary;
+import seedu.address.model.tag.Tag;
+import seedu.address.model.task.Task;
+import seedu.address.model.task.UniqueTaskList;
+
+
+/**
+ * Represents a Student in the mathutoring.
+ * Guarantees: details are present and not null, field values are validated, immutable.
+ */
+public class Student {
+
+ // Identity fields
+ private final Name name;
+ private final Phone phone;
+ private final Email email;
+
+ // Data fields
+ private final Address address;
+ private final Phone parentPhone;
+ private final Set tags = new HashSet<>();
+ private final UniqueTaskList uniqueTaskList;
+ private final UniqueScoreList uniqueScoreList;
+
+ /**
+ * Every field must be present and not null.
+ */
+ public Student(Name name, Phone phone, Email email, Address address, Phone parentPhone, Set tags) {
+ requireAllNonNull(name, phone, email, address, tags);
+ this.name = name;
+ this.phone = phone;
+ this.email = email;
+ this.address = address;
+ this.parentPhone = parentPhone;
+ this.tags.addAll(tags);
+ this.uniqueTaskList = new UniqueTaskList();
+ this.uniqueScoreList = new UniqueScoreList();
+ }
+
+ /**
+ * Every field must be present and not null.
+ */
+ public Student(Name name, Phone phone, Email email, Address address, Phone parentPhone, Set tags,
+ UniqueTaskList uniqueTaskList, UniqueScoreList uniqueScoreList) {
+ requireAllNonNull(name, phone, email, address, tags, uniqueTaskList, uniqueScoreList);
+ this.name = name;
+ this.phone = phone;
+ this.email = email;
+ this.address = address;
+ this.parentPhone = parentPhone;
+ this.tags.addAll(tags);
+ this.uniqueTaskList = uniqueTaskList;
+ this.uniqueScoreList = uniqueScoreList;
+ }
+
+ public Name getName() {
+ return name;
+ }
+
+ public Phone getPhone() {
+ return phone;
+ }
+
+ public Email getEmail() {
+ return email;
+ }
+
+ public Address getAddress() {
+ return address;
+ }
+
+ public Phone getParentPhone() {
+ return parentPhone;
+ }
+
+ /**
+ * Returns an immutable tag set, which throws {@code UnsupportedOperationException}
+ * if modification is attempted.
+ */
+ public Set getTags() {
+ return Collections.unmodifiableSet(tags);
+ }
+
+ public UniqueTaskList getTaskList() {
+ return uniqueTaskList;
+ }
+
+ public ObservableList getTaskListAsObservableList() {
+ return uniqueTaskList.asUnmodifiableObservableList();
+ }
+
+ /**
+ * Returns true if both students have the same name.
+ * This defines a weaker notion of equality between two students.
+ */
+ public boolean isSameStudent(Student otherStudent) {
+ if (otherStudent == this) {
+ return true;
+ }
+
+ return otherStudent != null
+ && otherStudent.getName().equals(getName());
+ }
+
+ //=========== Task ================================================================================
+
+ /**
+ * Returns true if a task with the same identity as {@code task} exists in the task list of this student.
+ */
+ public boolean hasTask(Task task) {
+ requireNonNull(task);
+ return uniqueTaskList.contains(task);
+ }
+
+ /**
+ * Adds a task to this student's task list.
+ * The task must not already exist in their task list.
+ */
+ public void addTask(Task t) {
+ requireNonNull(t);
+ uniqueTaskList.add(t);
+ }
+
+ /**
+ * Replaces the given task {@code target} in this student's task list with {@code editedTask}.
+ * {@code target} must exist in the task list.
+ * The task identity of {@code editedTask} must not be the same as another existing task in the task list.
+ */
+ public void setTask(Task target, Task editedTask) {
+ requireAllNonNull(target, editedTask);
+
+ uniqueTaskList.setTask(target, editedTask);
+ }
+
+ /**
+ * Removes {@code key} from this student's task list.
+ * {@code key} must exist in the task list.
+ */
+ public void removeTask(Task key) {
+ requireNonNull(key);
+ uniqueTaskList.remove(key);
+ }
+
+ /**
+ * Marks {@code task} in this student's task list as complete.
+ * {@code task} must exist in the task list.
+ */
+ public void markTaskAsComplete(Task task) {
+ requireNonNull(task);
+ uniqueTaskList.markTaskAsComplete(task);
+ }
+
+ /**
+ * Marks {@code task} in this student's task list as inprogress.
+ * {@code task} must exist in the task list.
+ */
+ public void markTaskAsInProgress(Task task) {
+ requireNonNull(task);
+ uniqueTaskList.markTaskAsInProgress(task);
+ }
+
+ /**
+ * Marks {@code task} in this student's task list as late.
+ * {@code task} must exist in the task list.
+ */
+ public void markTaskAsLate(Task task) {
+ requireNonNull(task);
+ uniqueTaskList.markTaskAsLate(task);
+ }
+
+ /**
+ * Returns an unmodifiable view of the list of {@code Task}
+ */
+ public ObservableList getFilteredTaskList() {
+ FilteredList filteredTasks = new FilteredList<>(this.getTaskListAsObservableList());
+ return filteredTasks;
+ }
+
+ //=========== Score ================================================================================
+
+ /**
+ * Returns true if a score with the same identity as {@code score} exists in the score list of this student.
+ */
+ public boolean hasScore(Score score) {
+ requireNonNull(score);
+ return uniqueScoreList.contains(score);
+ }
+
+ /**
+ * Adds a score to this student's score list.
+ * The score must not already exist in their score list.
+ */
+ public void addScore(Score score) {
+ uniqueScoreList.add(score);
+ }
+
+ public ObservableList getScoreListAsObservableList() {
+ return uniqueScoreList.asUnmodifiableObservableList();
+ }
+
+ public UniqueScoreList getScoreList() {
+ return uniqueScoreList;
+ }
+
+ /**
+ * Removes {@code key} from this student's score list.
+ * {@code key} must exist in the score list.
+ */
+ public void removeScore(Score key) {
+ uniqueScoreList.remove(key);
+ }
+
+ /**
+ * Returns an unmodifiable view of the list of {@code Score}
+ */
+ public ObservableList getFilteredScoreList() {
+ FilteredList filteredScores = new FilteredList<>(this.getScoreListAsObservableList());
+ return filteredScores;
+ }
+
+ /**
+ * Gets the sorted score list with recent score at front.
+ * @return A view of list of sorted scores.
+ */
+ public ObservableList getSortedScoreList() {
+ ObservableList sortedScoreList = this.uniqueScoreList.getSortedScoreList();
+ return sortedScoreList;
+ }
+
+ /**
+ * Gets the recent 5 scores with recent score at back.
+ * @return A view of list of recent 5 scores.
+ */
+ public ObservableList getRecentScoreList() {
+ ObservableList recentScoreList = this.uniqueScoreList.getRecentScoreList();
+ return recentScoreList;
+ }
+
+ /**
+ * Gets the summary statistic of recent 5 scores.
+ * @return A view of list of recent 5 scores' summary statistic.
+ */
+ public ObservableList getScoreSummary() {
+ ObservableList scoreSummary = this.uniqueScoreList.getScoreSummary();
+ return scoreSummary;
+ }
+
+ /**
+ * Returns true if both persons have the same identity and data fields.
+ * This defines a stronger notion of equality between two persons.
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof Student)) {
+ return false;
+ }
+
+ Student otherStudent = (Student) other;
+ return otherStudent.getName().equals(getName())
+ && otherStudent.getPhone().equals(getPhone())
+ && otherStudent.getParentPhone().equals(getParentPhone())
+ && otherStudent.getEmail().equals(getEmail())
+ && otherStudent.getAddress().equals(getAddress())
+ && otherStudent.getTags().equals(getTags());
+ }
+
+ @Override
+ public int hashCode() {
+ // use this method for custom fields hashing instead of implementing your own
+ return Objects.hash(name, phone, email, address, parentPhone, tags);
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append(getName())
+ .append("; Phone: ")
+ .append(getPhone())
+ .append("; Email: ")
+ .append(getEmail())
+ .append("; Address: ")
+ .append(getAddress())
+ .append("; ParentPhone: ")
+ .append(getParentPhone());
+
+ Set tags = getTags();
+ if (!tags.isEmpty()) {
+ builder.append("; Tags: ");
+ tags.forEach(builder::append);
+ }
+
+ return builder.toString();
+ }
+
+}
diff --git a/src/main/java/seedu/address/model/student/UniqueStudentList.java b/src/main/java/seedu/address/model/student/UniqueStudentList.java
new file mode 100644
index 00000000000..d30ca9617b9
--- /dev/null
+++ b/src/main/java/seedu/address/model/student/UniqueStudentList.java
@@ -0,0 +1,138 @@
+package seedu.address.model.student;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+
+import java.util.Iterator;
+import java.util.List;
+
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import seedu.address.model.student.exceptions.DuplicateStudentException;
+import seedu.address.model.student.exceptions.StudentNotFoundException;
+
+/**
+ * A list of persons that enforces uniqueness between its elements and does not allow nulls.
+ * A student is considered unique by comparing using {@code Student#isSamePerson(Student)}.
+ * As such, adding and updating of students uses Student#isSamePerson(Student) for equality
+ * so as to ensure that the student being added or updated is unique in terms of identity in the UniqueStudentList.
+ * However, the removal of a student uses Student#equals(Object) so
+ * as to ensure that the student with exactly the same fields will be removed.
+ *
+ * Supports a minimal set of list operations.
+ *
+ * @see Student#isSameStudent(Student)
+ */
+public class UniqueStudentList implements Iterable {
+
+ private final ObservableList internalList = FXCollections.observableArrayList();
+ private final ObservableList internalUnmodifiableList =
+ FXCollections.unmodifiableObservableList(internalList);
+
+ /**
+ * Returns true if the list contains an equivalent student as the given argument.
+ */
+ public boolean contains(Student toCheck) {
+ requireNonNull(toCheck);
+ return internalList.stream().anyMatch(toCheck::isSameStudent);
+ }
+
+ /**
+ * Adds a student to the list.
+ * The student must not already exist in the list.
+ */
+ public void add(Student toAdd) {
+ requireNonNull(toAdd);
+ if (contains(toAdd)) {
+ throw new DuplicateStudentException();
+ }
+ internalList.add(toAdd);
+ }
+
+ /**
+ * Replaces the student {@code target} in the list with {@code editedStudent}.
+ * {@code target} must exist in the list.
+ * The student identity of {@code editedStudent} must not be the same as another existing student in the list.
+ */
+ public void setStudent(Student target, Student editedStudent) {
+ requireAllNonNull(target, editedStudent);
+
+ int index = internalList.indexOf(target);
+ if (index == -1) {
+ throw new StudentNotFoundException();
+ }
+
+ if (!target.isSameStudent(editedStudent) && contains(editedStudent)) {
+ throw new DuplicateStudentException();
+ }
+
+ internalList.set(index, editedStudent);
+ }
+
+ /**
+ * Removes the equivalent student from the list.
+ * The student must exist in the list.
+ */
+ public void remove(Student toRemove) {
+ requireNonNull(toRemove);
+ if (!internalList.remove(toRemove)) {
+ throw new StudentNotFoundException();
+ }
+ }
+
+ public void setStudents(UniqueStudentList replacement) {
+ requireNonNull(replacement);
+ internalList.setAll(replacement.internalList);
+ }
+
+ /**
+ * Replaces the contents of this list with {@code students}.
+ * {@code students} must not contain duplicate students.
+ */
+ public void setStudents(List students) {
+ requireAllNonNull(students);
+ if (!studentsAreUnique(students)) {
+ throw new DuplicateStudentException();
+ }
+
+ internalList.setAll(students);
+ }
+
+ /**
+ * Returns the backing list as an unmodifiable {@code ObservableList}.
+ */
+ public ObservableList asUnmodifiableObservableList() {
+ return internalUnmodifiableList;
+ }
+
+ @Override
+ public Iterator iterator() {
+ return internalList.iterator();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof UniqueStudentList // instanceof handles nulls
+ && internalList.equals(((UniqueStudentList) other).internalList));
+ }
+
+ @Override
+ public int hashCode() {
+ return internalList.hashCode();
+ }
+
+ /**
+ * Returns true if {@code students} contains only unique students.
+ */
+ private boolean studentsAreUnique(List students) {
+ for (int i = 0; i < students.size() - 1; i++) {
+ for (int j = i + 1; j < students.size(); j++) {
+ if (students.get(i).isSameStudent(students.get(j))) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/seedu/address/model/student/exceptions/DuplicateStudentException.java b/src/main/java/seedu/address/model/student/exceptions/DuplicateStudentException.java
new file mode 100644
index 00000000000..044991d29fd
--- /dev/null
+++ b/src/main/java/seedu/address/model/student/exceptions/DuplicateStudentException.java
@@ -0,0 +1,11 @@
+package seedu.address.model.student.exceptions;
+
+/**
+ * Signals that the operation will result in duplicate students (Students are considered duplicates
+ * if they have the same identity).
+ */
+public class DuplicateStudentException extends RuntimeException {
+ public DuplicateStudentException() {
+ super("Operation would result in duplicate students");
+ }
+}
diff --git a/src/main/java/seedu/address/model/student/exceptions/StudentNotFoundException.java b/src/main/java/seedu/address/model/student/exceptions/StudentNotFoundException.java
new file mode 100644
index 00000000000..2b41e9e0296
--- /dev/null
+++ b/src/main/java/seedu/address/model/student/exceptions/StudentNotFoundException.java
@@ -0,0 +1,6 @@
+package seedu.address.model.student.exceptions;
+
+/**
+ * Signals that the operation is unable to find the specified student.
+ */
+public class StudentNotFoundException extends RuntimeException {}
diff --git a/src/main/java/seedu/address/model/tag/Tag.java b/src/main/java/seedu/address/model/tag/Tag.java
index b0ea7e7dad7..9e7a02a45cf 100644
--- a/src/main/java/seedu/address/model/tag/Tag.java
+++ b/src/main/java/seedu/address/model/tag/Tag.java
@@ -4,12 +4,14 @@
import static seedu.address.commons.util.AppUtil.checkArgument;
/**
- * Represents a Tag in the address book.
+ * Represents a Tag in the mathutoring.
* 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 MESSAGE_EXCEED_MAX_LETTERS =
+ "A tag should not contains more than 20 letters. Please try again.";
public static final String VALIDATION_REGEX = "\\p{Alnum}+";
public final String tagName;
@@ -22,9 +24,23 @@ public class Tag {
public Tag(String tagName) {
requireNonNull(tagName);
checkArgument(isValidTagName(tagName), MESSAGE_CONSTRAINTS);
+ checkArgument(isMoreThanMaxLetters(tagName), MESSAGE_EXCEED_MAX_LETTERS);
this.tagName = tagName;
}
+ /**
+ * Returns true if a given tag length is not exceed 20 letters long.
+ *
+ * @param test Tag that user want to add in.
+ * @return Boolean value if a given tag length is greater than 20 letters.
+ */
+ public static boolean isMoreThanMaxLetters(String test) {
+ if (test.length() > 20) {
+ return false;
+ }
+ return true;
+ }
+
/**
* Returns true if a given string is a valid tag name.
*/
diff --git a/src/main/java/seedu/address/model/tag/TagContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/tag/TagContainsKeywordsPredicate.java
new file mode 100644
index 00000000000..32d55686ec2
--- /dev/null
+++ b/src/main/java/seedu/address/model/tag/TagContainsKeywordsPredicate.java
@@ -0,0 +1,56 @@
+package seedu.address.model.tag;
+
+import java.util.List;
+import java.util.function.Predicate;
+
+import seedu.address.model.student.Student;
+
+/**
+ * Tests that a {@code Student}'s {@code tag} matches any of the keywords given.
+ */
+public class TagContainsKeywordsPredicate implements Predicate {
+ private final List keywords;
+
+ public TagContainsKeywordsPredicate(List keywords) {
+ this.keywords = keywords;
+ }
+
+ @Override
+ public boolean test(Student student) {
+ return keywords.stream().anyMatch(keyword -> isMatch(student, keyword));
+ }
+
+ /**
+ * Returns if any keyword matches with any of the student's tag.
+ *
+ * @param student Student.
+ * @param keyword Keyword provided by the user.
+ * @return Boolean value, if any keyword matches with any of the student's tag given.
+ */
+ public boolean isMatch(Student student, String keyword) {
+ String lowerCaseKeyword = keyword.toLowerCase();
+ String lowerCaseTagName = "";
+
+ for (Tag tag : student.getTags()) {
+ lowerCaseTagName = tag.tagName.toLowerCase();
+
+ if (lowerCaseTagName.equals(lowerCaseKeyword)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Checks if an object is equal to the TagContainsKeywordsPredicate.
+ *
+ * @param other The object that need to compare with.
+ * @return Boolean value if the object and the TagContainsKeywordsPredicate are the same.
+ */
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof TagContainsKeywordsPredicate // instanceof handles nulls
+ && keywords.equals(((TagContainsKeywordsPredicate) other).keywords)); // state check
+ }
+}
diff --git a/src/main/java/seedu/address/model/task/Task.java b/src/main/java/seedu/address/model/task/Task.java
new file mode 100644
index 00000000000..17998fa4e0c
--- /dev/null
+++ b/src/main/java/seedu/address/model/task/Task.java
@@ -0,0 +1,152 @@
+package seedu.address.model.task;
+
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+
+import java.time.LocalDateTime;
+import java.util.Objects;
+
+import seedu.address.model.student.Name;
+
+//@@author astraxq
+/**
+ * Represents a Task in the mathutoring.
+ * Guarantees: immutable; fields are validated; details are present and not null;
+ */
+public class Task implements Comparable {
+
+ public static final String MESSAGE_CONSTRAINTS =
+ "Name of tasks should only contain alphanumeric characters and spaces, and it should not be blank";
+
+ public static final String VALIDATION_REGEX = "[\\p{Alnum}][\\p{Alnum} ]*";
+
+
+ // Identity field(s)
+ public final Name taskName;
+
+ // Data field(s)
+ private TaskStatus status;
+
+ private LocalDateTime creationDate;
+
+ /**
+ * Constructs a {@code Task}.
+ * Every field must be present and not null.
+ *
+ * @param taskName A valid Task name.
+ */
+ public Task(Name taskName) {
+ this(taskName, TaskStatus.INPROGRESS);
+ }
+
+ /**
+ * Every field must be present and not null.
+ */
+ public Task(Name taskName, TaskStatus status) {
+ this(taskName, status, LocalDateTime.now());
+ }
+
+ /**
+ * Every field must be present and not null.
+ */
+ public Task(Name taskName, TaskStatus status, LocalDateTime creationDate) {
+ requireAllNonNull(taskName, status, creationDate);
+ this.taskName = taskName;
+ this.status = status;
+ this.creationDate = creationDate;
+ }
+ //@@author astraxq
+ //@@author toh-xinyi
+ /**
+ * Marks the task as completed.
+ */
+ public void markTaskAsComplete() {
+ status = TaskStatus.COMPLETE;
+ }
+
+ /**
+ * Marks the task as late.
+ */
+ public void markTaskAsLate() {
+ status = TaskStatus.LATE;
+ }
+
+ /**
+ * Marks the task as in progress.
+ */
+ public void markTaskAsInProgress() {
+ status = TaskStatus.INPROGRESS;
+ }
+ //@@author toh-xinyi
+ //@@author astraxq
+ public Name getName() {
+ return taskName;
+ }
+
+ public TaskStatus getStatus() {
+ return status;
+ }
+ //@@author astraxq
+
+ //@@author toh-xinyi
+ public LocalDateTime getCreationDate() {
+ return creationDate;
+ }
+ //@@author toh-xinyi
+
+ //@@author astraxq
+ /**
+ * Returns true if both tasks have the same name.
+ * This defines a weaker notion of equality between two tasks.
+ */
+ public boolean isSameTask(Task otherTask) {
+ if (otherTask == this) {
+ return true;
+ }
+
+ return otherTask != null
+ && otherTask.getName().equals(getName());
+ }
+
+ /**
+ * Returns true if a given string is a valid task name.
+ */
+ public static boolean isValidTaskName(String test) {
+ return test.matches(VALIDATION_REGEX);
+ }
+
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof Task // instanceof handles nulls
+ && taskName.equals(((Task) other).taskName)); // state check
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(taskName, status, creationDate);
+ }
+ //@@author astraxq
+
+ //@@author toh-xinyi
+ @Override
+ public int compareTo(Task other) {
+ if (this.status.equals(other.status)) {
+ return this.creationDate.compareTo(other.creationDate);
+ }
+ return this.status.compareTo(other.status);
+ }
+ //@@author toh-xinyi
+
+ //@@author astraxq
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append(getName())
+ .append("; Status: ")
+ .append(getStatus());
+
+ return builder.toString();
+ }
+}
+//@@author astraxq
diff --git a/src/main/java/seedu/address/model/task/TaskStatus.java b/src/main/java/seedu/address/model/task/TaskStatus.java
new file mode 100644
index 00000000000..ce8bfb9df3a
--- /dev/null
+++ b/src/main/java/seedu/address/model/task/TaskStatus.java
@@ -0,0 +1,25 @@
+package seedu.address.model.task;
+
+/**
+ * Represents a TaskStatus that holds the possible task statuses available.
+ */
+public enum TaskStatus {
+ INPROGRESS,
+ LATE,
+ COMPLETE;
+
+ public static final String MESSAGE_CONSTRAINTS =
+ "Task status should only be any of the following three: INPROGRESS, LATE, COMPLETE";
+
+ /**
+ * Returns true if a given string is a valid task status.
+ */
+ public static boolean isValidTaskStatus(String status) {
+ try {
+ TaskStatus.valueOf(status);
+ return true;
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/model/task/UniqueTaskList.java b/src/main/java/seedu/address/model/task/UniqueTaskList.java
new file mode 100644
index 00000000000..8bb274ac5ae
--- /dev/null
+++ b/src/main/java/seedu/address/model/task/UniqueTaskList.java
@@ -0,0 +1,200 @@
+package seedu.address.model.task;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import seedu.address.model.task.exceptions.DuplicateTaskException;
+import seedu.address.model.task.exceptions.TaskNotFoundException;
+
+//@@author astraxq
+/**
+ * A list of tasks that enforces uniqueness between its elements and does not allow nulls.
+ * A task is considered unique by comparing using {@code task#isSameTask(otherTask)}. As such, adding and updating of
+ * tasks uses Task#isSameTask(otherTask) for equality so as to ensure that the task being added or updated is
+ * unique in terms of identity in the UniqueTaskList. However, the removal of a task uses task#equals(Object) so
+ * as to ensure that the task with exactly the same fields will be removed.
+ *
+ * Supports a minimal set of list operations.
+ *
+ * @see Task#isSameTask(Task)
+ */
+public class UniqueTaskList implements Iterable {
+ private final ObservableList internalList = FXCollections.observableArrayList();
+ private final ObservableList internalUnmodifiableList =
+ FXCollections.unmodifiableObservableList(internalList);
+
+ /**
+ * Returns true if the list contains an equivalent task as the given argument.
+ */
+ public boolean contains(Task toCheck) {
+ requireNonNull(toCheck);
+ return internalList.stream().anyMatch(toCheck::isSameTask);
+ }
+
+ /**
+ * Adds a task to the list.
+ * The task must not already exist in the list.
+ */
+ public void add(Task toAdd) {
+ requireNonNull(toAdd);
+ if (contains(toAdd)) {
+ throw new DuplicateTaskException();
+ }
+ internalList.add(toAdd);
+ Collections.sort(internalList);
+ }
+
+ /**
+ * Replaces the task {@code target} in the list with {@code editedTask}.
+ * {@code target} must exist in the list.
+ * The task identity of {@code editedTask} must not be the same as another existing task in the list.
+ */
+ public void setTask(Task target, Task editedTask) {
+ requireAllNonNull(target, editedTask);
+
+ int index = internalList.indexOf(target);
+ if (index == -1) {
+ throw new TaskNotFoundException();
+ }
+
+ if (!target.isSameTask(editedTask) && contains(editedTask)) {
+ throw new DuplicateTaskException();
+ }
+
+ internalList.set(index, editedTask);
+ Collections.sort(internalList);
+ }
+
+ /**
+ * Removes the equivalent task from the list.
+ * The task must exist in the list.
+ */
+ public void remove(Task toRemove) {
+ requireNonNull(toRemove);
+ if (!internalList.remove(toRemove)) {
+ throw new TaskNotFoundException();
+ }
+ }
+ //@@author astraxq
+
+ //@@author toh-xinyi
+ /**
+ * Marks the given task from the list as complete.
+ * The task must exist in the list.
+ */
+ public void markTaskAsComplete(Task toMark) {
+ requireNonNull(toMark);
+ if (!contains(toMark)) {
+ throw new TaskNotFoundException();
+ }
+ toMark.markTaskAsComplete();
+ Collections.sort(internalList);
+ }
+
+ /**
+ * Marks the given task from the list as in progress.
+ * The task must exist in the list.
+ */
+ public void markTaskAsInProgress(Task toMark) {
+ requireNonNull(toMark);
+ if (!contains(toMark)) {
+ throw new TaskNotFoundException();
+ }
+ toMark.markTaskAsInProgress();
+ Collections.sort(internalList);
+ }
+
+ /**
+ * Marks the given task from the list as late.
+ * The task must exist in the list.
+ */
+ public void markTaskAsLate(Task toMark) {
+ requireNonNull(toMark);
+ if (!contains(toMark)) {
+ throw new TaskNotFoundException();
+ }
+ toMark.markTaskAsLate();
+ Collections.sort(internalList);
+ }
+ //@@author toh-xinyi
+
+ //@@author astraxq
+ public void setTasks(UniqueTaskList replacement) {
+ requireNonNull(replacement);
+ internalList.setAll(replacement.internalList);
+ }
+
+ /**
+ * Replaces the contents of this list with {@code tasks}.
+ * {@code tasks} must not contain duplicate students.
+ */
+ public void setTasks(List tasks) {
+ requireAllNonNull(tasks);
+ if (!tasksAreUnique(tasks)) {
+ throw new DuplicateTaskException();
+ }
+
+ internalList.setAll(tasks);
+ }
+
+ /**
+ * Gets the internal list for task list panel to get all tasks for a specific student.
+ *
+ * @return All tasks in list.
+ */
+ public ObservableList getInternalList() {
+ return internalList;
+ }
+
+ /**
+ * Returns the backing list as an unmodifiable {@code ObservableList}.
+ */
+ public ObservableList asUnmodifiableObservableList() {
+ return internalUnmodifiableList;
+ }
+
+ @Override
+ public Iterator iterator() {
+ return internalList.iterator();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof UniqueTaskList // instanceof handles nulls
+ && internalList.equals(((UniqueTaskList) other).internalList));
+ }
+
+ @Override
+ public int hashCode() {
+ return internalList.hashCode();
+ }
+
+ /**
+ * Returns true if {@code tasks} contains only unique tasks.
+ */
+ private boolean tasksAreUnique(List tasks) {
+ for (int i = 0; i < tasks.size() - 1; i++) {
+ for (int j = i + 1; j < tasks.size(); j++) {
+ if (tasks.get(i).isSameTask(tasks.get(j))) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ public int size() {
+ return internalList.size();
+ }
+ public Task get(int index) {
+ return internalList.get(index);
+ }
+}
+//@@author astraxq
diff --git a/src/main/java/seedu/address/model/task/exceptions/DuplicateTaskException.java b/src/main/java/seedu/address/model/task/exceptions/DuplicateTaskException.java
new file mode 100644
index 00000000000..67034465c47
--- /dev/null
+++ b/src/main/java/seedu/address/model/task/exceptions/DuplicateTaskException.java
@@ -0,0 +1,13 @@
+package seedu.address.model.task.exceptions;
+
+//@@author astraxq
+/**
+ * Signals that the operation will result in duplicate Tasks (Tasks are considered duplicates if they have the same
+ * identity).
+ */
+public class DuplicateTaskException extends RuntimeException {
+ public DuplicateTaskException() {
+ super("Operation would result in duplicate tasks");
+ }
+}
+//@@author astraxq
diff --git a/src/main/java/seedu/address/model/task/exceptions/TaskNotFoundException.java b/src/main/java/seedu/address/model/task/exceptions/TaskNotFoundException.java
new file mode 100644
index 00000000000..8563cfe6e1c
--- /dev/null
+++ b/src/main/java/seedu/address/model/task/exceptions/TaskNotFoundException.java
@@ -0,0 +1,8 @@
+package seedu.address.model.task.exceptions;
+
+//@@author astraxq
+/**
+ * Signals that the operation is unable to find the specified task.
+ */
+public class TaskNotFoundException extends RuntimeException {}
+//@@author astraxq
diff --git a/src/main/java/seedu/address/model/util/SampleDataUtil.java b/src/main/java/seedu/address/model/util/SampleDataUtil.java
index 1806da4facf..bfb656adef0 100644
--- a/src/main/java/seedu/address/model/util/SampleDataUtil.java
+++ b/src/main/java/seedu/address/model/util/SampleDataUtil.java
@@ -4,46 +4,47 @@
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.Mathutoring;
+import seedu.address.model.ReadOnlyMathutoring;
+import seedu.address.model.student.Address;
+import seedu.address.model.student.Email;
+import seedu.address.model.student.Name;
+import seedu.address.model.student.Phone;
+import seedu.address.model.student.Student;
import seedu.address.model.tag.Tag;
+
/**
- * Contains utility methods for populating {@code AddressBook} with sample data.
+ * Contains utility methods for populating {@code Mathutoring} 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"),
+ public static Student[] getSampleStudents() {
+ return new Student[] {
+ new Student(new Name("Alex Yeoh"), new Phone("87438807"), new Email("alexyeoh@example.com"),
+ new Address("Blk 30 Geylang Street 29, #06-40"), new Phone("93377418"),
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"),
+ new Student(new Name("Bernice Yu"), new Phone("99272758"), new Email("berniceyu@example.com"),
+ new Address("Blk 30 Lorong 3 Serangoon Gardens, #07-18"), new Phone("94597742"),
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"),
+ new Student(new Name("Charlotte Oliveiro"), new Phone("93210283"), new Email("charlotte@example.com"),
+ new Address("Blk 11 Ang Mo Kio Street 74, #11-04"), new Phone("81547632"),
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"),
+ new Student(new Name("David Li"), new Phone("91031282"), new Email("lidavid@example.com"),
+ new Address("Blk 436 Serangoon Gardens Street 26, #16-43"), new Phone("94256541"),
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"),
+ new Student(new Name("Irfan Ibrahim"), new Phone("92492021"), new Email("irfan@example.com"),
+ new Address("Blk 47 Tampines Street 20, #17-35"), new Phone("88814569"),
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"),
+ new Student(new Name("Roy Balakrishnan"), new Phone("92624417"), new Email("royb@example.com"),
+ new Address("Blk 45 Aljunied Street 85, #11-31"), new Phone("88187718"),
getTagSet("colleagues"))
};
}
- public static ReadOnlyAddressBook getSampleAddressBook() {
- AddressBook sampleAb = new AddressBook();
- for (Person samplePerson : getSamplePersons()) {
- sampleAb.addPerson(samplePerson);
+ public static ReadOnlyMathutoring getSampleMathutoring() {
+ Mathutoring sampleAb = new Mathutoring();
+ for (Student sampleStudent : getSampleStudents()) {
+ sampleAb.addStudent(sampleStudent);
}
return sampleAb;
}
diff --git a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java b/src/main/java/seedu/address/storage/JsonAdaptedPerson.java
deleted file mode 100644
index a6321cec2ea..00000000000
--- a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java
+++ /dev/null
@@ -1,109 +0,0 @@
-package seedu.address.storage;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-import seedu.address.commons.exceptions.IllegalValueException;
-import seedu.address.model.person.Address;
-import seedu.address.model.person.Email;
-import seedu.address.model.person.Name;
-import seedu.address.model.person.Person;
-import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
-
-/**
- * Jackson-friendly version of {@link Person}.
- */
-class JsonAdaptedPerson {
-
- public static final String MISSING_FIELD_MESSAGE_FORMAT = "Person's %s field is missing!";
-
- private final String name;
- private final String phone;
- private final String email;
- private final String address;
- private final List tagged = new ArrayList<>();
-
- /**
- * Constructs a {@code JsonAdaptedPerson} with the given person details.
- */
- @JsonCreator
- public JsonAdaptedPerson(@JsonProperty("name") String name, @JsonProperty("phone") String phone,
- @JsonProperty("email") String email, @JsonProperty("address") String address,
- @JsonProperty("tagged") List tagged) {
- this.name = name;
- this.phone = phone;
- this.email = email;
- this.address = address;
- if (tagged != null) {
- this.tagged.addAll(tagged);
- }
- }
-
- /**
- * Converts a given {@code Person} into this class for Jackson use.
- */
- public JsonAdaptedPerson(Person source) {
- name = source.getName().fullName;
- phone = source.getPhone().value;
- email = source.getEmail().value;
- address = source.getAddress().value;
- tagged.addAll(source.getTags().stream()
- .map(JsonAdaptedTag::new)
- .collect(Collectors.toList()));
- }
-
- /**
- * Converts this Jackson-friendly adapted person object into the model's {@code Person} object.
- *
- * @throws IllegalValueException if there were any data constraints violated in the adapted person.
- */
- public Person toModelType() throws IllegalValueException {
- final List personTags = new ArrayList<>();
- for (JsonAdaptedTag tag : tagged) {
- personTags.add(tag.toModelType());
- }
-
- if (name == null) {
- throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName()));
- }
- if (!Name.isValidName(name)) {
- throw new IllegalValueException(Name.MESSAGE_CONSTRAINTS);
- }
- final Name modelName = new Name(name);
-
- if (phone == null) {
- throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName()));
- }
- if (!Phone.isValidPhone(phone)) {
- throw new IllegalValueException(Phone.MESSAGE_CONSTRAINTS);
- }
- final Phone modelPhone = new Phone(phone);
-
- if (email == null) {
- throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.class.getSimpleName()));
- }
- if (!Email.isValidEmail(email)) {
- throw new IllegalValueException(Email.MESSAGE_CONSTRAINTS);
- }
- final Email modelEmail = new Email(email);
-
- if (address == null) {
- throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName()));
- }
- if (!Address.isValidAddress(address)) {
- throw new IllegalValueException(Address.MESSAGE_CONSTRAINTS);
- }
- final Address modelAddress = new Address(address);
-
- final Set modelTags = new HashSet<>(personTags);
- return new Person(modelName, modelPhone, modelEmail, modelAddress, modelTags);
- }
-
-}
diff --git a/src/main/java/seedu/address/storage/JsonAdaptedScore.java b/src/main/java/seedu/address/storage/JsonAdaptedScore.java
new file mode 100644
index 00000000000..a7d52a00db5
--- /dev/null
+++ b/src/main/java/seedu/address/storage/JsonAdaptedScore.java
@@ -0,0 +1,80 @@
+package seedu.address.storage;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import seedu.address.commons.exceptions.IllegalValueException;
+import seedu.address.model.score.Date;
+import seedu.address.model.score.Label;
+import seedu.address.model.score.Score;
+import seedu.address.model.score.ScoreValue;
+
+/**
+ * Jackson-friendly version of {@link Score}.
+ */
+class JsonAdaptedScore {
+
+ public static final String MISSING_FIELD_MESSAGE_FORMAT = "Student's %s field is missing!";
+
+ private final String label;
+ private final String value;
+ private final String date;
+
+ /**
+ * Constructs a {@code JsonAdaptedScore} with the given score details.
+ */
+ @JsonCreator
+ public JsonAdaptedScore(@JsonProperty("label") String label, @JsonProperty("value") String value,
+ @JsonProperty("date") String date) {
+ this.label = label;
+ this.value = value;
+ this.date = date;
+ }
+
+ /**
+ * Converts a given {@code Score} into this class for Jackson use.
+ */
+ public JsonAdaptedScore(Score source) {
+ label = source.getLabel().label;
+ value = String.valueOf(source.getValue().value);
+ date = String.valueOf(source.getDate().date);
+ }
+
+ /**
+ * Converts this Jackson-friendly adapted score object into the model's {@code Score} object.
+ *
+ * @throws IllegalValueException if there were any data constraints violated in the adapted score.
+ */
+ public Score toModelType() throws IllegalValueException {
+
+ if (label == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Label.class.getSimpleName()));
+ }
+ if (!Label.isValidLabel(label)) {
+ throw new IllegalValueException(Label.MESSAGE_CONSTRAINTS);
+ }
+ final Label modelLabel = new Label(label);
+
+ if (value == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT,
+ ScoreValue.class.getSimpleName()));
+ }
+ if (!ScoreValue.isValidScoreValue(value)) {
+ throw new IllegalValueException(ScoreValue.MESSAGE_CONSTRAINTS);
+ }
+ final ScoreValue modelScoreValue = new ScoreValue(value);
+
+ if (date == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Date.class.getSimpleName()));
+ }
+ if (!Date.isValidDate(date)) {
+ throw new IllegalValueException(Date.MESSAGE_CONSTRAINTS);
+ }
+ if (!Date.isFutureDate(date)) {
+ throw new IllegalValueException(Date.MESSAGE_INVALID_DATE);
+ }
+ final Date modelDate = new Date(date);
+
+ return new Score(modelLabel, modelScoreValue, modelDate);
+ }
+}
diff --git a/src/main/java/seedu/address/storage/JsonAdaptedStudent.java b/src/main/java/seedu/address/storage/JsonAdaptedStudent.java
new file mode 100644
index 00000000000..0d99797bb1c
--- /dev/null
+++ b/src/main/java/seedu/address/storage/JsonAdaptedStudent.java
@@ -0,0 +1,167 @@
+package seedu.address.storage;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import seedu.address.commons.exceptions.IllegalValueException;
+import seedu.address.model.score.Score;
+import seedu.address.model.student.Address;
+import seedu.address.model.student.Email;
+import seedu.address.model.student.Name;
+import seedu.address.model.student.Phone;
+import seedu.address.model.student.Student;
+import seedu.address.model.tag.Tag;
+import seedu.address.model.task.Task;
+
+/**
+ * Jackson-friendly version of {@link Student}.
+ */
+class JsonAdaptedStudent {
+
+ public static final String MESSAGE_DUPLICATE_SCORE = "Score list contains duplicate score(s).";
+ public static final String MESSAGE_DUPLICATE_TASK = "Task list contains duplicate task(s).";
+ public static final String MISSING_FIELD_MESSAGE_FORMAT = "Student's %s field is missing!";
+
+ private final String name;
+ private final String phone;
+ private final String email;
+ private final String address;
+ private final String parentPhone;
+ private final List tagged = new ArrayList<>();
+ private final List taskList = new ArrayList<>();
+
+ private final List scores = new ArrayList<>();
+ /**
+ * Constructs a {@code JsonAdaptedStudent} with the given student details.
+ */
+ @JsonCreator
+ public JsonAdaptedStudent(@JsonProperty("name") String name, @JsonProperty("phone") String phone,
+ @JsonProperty("email") String email, @JsonProperty("address") String address,
+ @JsonProperty("parentPhone") String parentPhone,
+ @JsonProperty("tagged") List tagged,
+ @JsonProperty("taskList") List taskList,
+ @JsonProperty("scores") List scores) {
+ this.name = name;
+ this.phone = phone;
+ this.email = email;
+ this.address = address;
+ this.parentPhone = parentPhone;
+
+ if (tagged != null) {
+ this.tagged.addAll(tagged);
+ }
+ if (taskList != null) {
+ this.taskList.addAll(taskList);
+ }
+ if (scores != null) {
+ this.scores.addAll(scores);
+ }
+ }
+
+ /**
+ * Converts a given {@code Student} into this class for Jackson use.
+ */
+ public JsonAdaptedStudent(seedu.address.model.student.Student source) {
+ name = source.getName().fullName;
+ phone = source.getPhone().value;
+ email = source.getEmail().value;
+ address = source.getAddress().value;
+ parentPhone = source.getParentPhone().value;
+ tagged.addAll(source.getTags().stream()
+ .map(JsonAdaptedTag::new)
+ .collect(Collectors.toList()));
+ taskList.addAll(source.getTaskListAsObservableList().stream()
+ .map(JsonAdaptedTask::new)
+ .collect(Collectors.toList()));
+ scores.addAll(source.getScoreListAsObservableList().stream()
+ .map(JsonAdaptedScore::new)
+ .collect(Collectors.toList()));
+ }
+
+ /**
+ * Converts this Jackson-friendly adapted student object into the model's {@code Student} object.
+ *
+ * @throws IllegalValueException if there were any data constraints violated in the adapted student.
+ */
+ public seedu.address.model.student.Student toModelType() throws IllegalValueException {
+ final List personTags = new ArrayList<>();
+ for (JsonAdaptedTag tag : tagged) {
+ personTags.add(tag.toModelType());
+ }
+
+ if (name == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName()));
+ }
+ if (!Name.isValidName(name)) {
+ throw new IllegalValueException(Name.MESSAGE_CONSTRAINTS);
+ }
+ final Name modelName = new Name(name);
+
+ if (phone == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName()));
+ }
+ if (!Phone.isValidPhone(phone)) {
+ throw new IllegalValueException(Phone.MESSAGE_CONSTRAINTS);
+ }
+ if (!Phone.isMoreThanMaxDigits(phone)) {
+ throw new IllegalValueException(Phone.MESSAGE_EXCEED_MAX_DIGITS);
+ }
+ final Phone modelPhone = new Phone(phone);
+
+ if (email == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.class.getSimpleName()));
+ }
+ if (!Email.isValidEmail(email)) {
+ throw new IllegalValueException(Email.MESSAGE_CONSTRAINTS);
+ }
+ final Email modelEmail = new Email(email);
+
+ if (address == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName()));
+ }
+ if (!Address.isValidAddress(address)) {
+ throw new IllegalValueException(Address.MESSAGE_CONSTRAINTS);
+ }
+ final Address modelAddress = new Address(address);
+
+ if (parentPhone == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName()));
+ }
+
+ if (!Phone.isValidPhone(parentPhone)) {
+ throw new IllegalValueException(Phone.MESSAGE_CONSTRAINTS);
+ }
+ if (!Phone.isMoreThanMaxDigits(parentPhone)) {
+ throw new IllegalValueException(Phone.MESSAGE_EXCEED_MAX_DIGITS);
+ }
+ final Phone modelParentPhone = new Phone(parentPhone);
+
+ final Set modelTags = new HashSet<>(personTags);
+
+ Student student = new Student(modelName, modelPhone, modelEmail, modelAddress, modelParentPhone, modelTags);
+
+ for (JsonAdaptedTask jsonAdaptedTask : taskList) {
+ Task task = jsonAdaptedTask.toModelType();
+ if (student.hasTask(task)) {
+ throw new IllegalValueException(MESSAGE_DUPLICATE_TASK);
+ }
+ student.addTask(task);
+ }
+
+ for (JsonAdaptedScore jsonAdaptedScore : scores) {
+ Score score = jsonAdaptedScore.toModelType();
+ if (student.hasScore(score)) {
+ throw new IllegalValueException(MESSAGE_DUPLICATE_SCORE);
+ }
+ student.addScore(score);
+ }
+
+ return student;
+ }
+}
diff --git a/src/main/java/seedu/address/storage/JsonAdaptedTag.java b/src/main/java/seedu/address/storage/JsonAdaptedTag.java
index 0df22bdb754..e7b519ad49e 100644
--- a/src/main/java/seedu/address/storage/JsonAdaptedTag.java
+++ b/src/main/java/seedu/address/storage/JsonAdaptedTag.java
@@ -42,7 +42,9 @@ public Tag toModelType() throws IllegalValueException {
if (!Tag.isValidTagName(tagName)) {
throw new IllegalValueException(Tag.MESSAGE_CONSTRAINTS);
}
+ if (!Tag.isMoreThanMaxLetters(tagName)) {
+ throw new IllegalValueException(Tag.MESSAGE_EXCEED_MAX_LETTERS);
+ }
return new Tag(tagName);
}
-
}
diff --git a/src/main/java/seedu/address/storage/JsonAdaptedTask.java b/src/main/java/seedu/address/storage/JsonAdaptedTask.java
new file mode 100644
index 00000000000..8247b9bb831
--- /dev/null
+++ b/src/main/java/seedu/address/storage/JsonAdaptedTask.java
@@ -0,0 +1,84 @@
+package seedu.address.storage;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import seedu.address.commons.exceptions.IllegalValueException;
+import seedu.address.model.student.Name;
+import seedu.address.model.task.Task;
+import seedu.address.model.task.TaskStatus;
+
+/**
+ * Jackson-friendly version of {@link Task}.
+ */
+public class JsonAdaptedTask {
+
+ public static final String MISSING_FIELD_MESSAGE_FORMAT = "Task's %s field is missing!";
+ public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_DATE_TIME;
+ public static final String DATE_MESSAGE_CONSTRAINTS = "Wrong format of date.";
+
+ private final String taskName;
+ private final String status;
+ private final String creationDate;
+
+ /**
+ * Constructs a {@code JsonAdaptedTask} with the given task details.
+ */
+ @JsonCreator
+ public JsonAdaptedTask(@JsonProperty("taskName") String taskName, @JsonProperty("status") String status,
+ @JsonProperty("creationDate") String creationDate) {
+ this.taskName = taskName;
+ this.status = status;
+ this.creationDate = creationDate;
+ }
+
+ /**
+ * Converts a given {@code Task} into this class for Jackson use.
+ */
+ public JsonAdaptedTask(Task source) {
+ taskName = source.getName().fullName;
+ status = source.getStatus().toString();
+ creationDate = source.getCreationDate().toString();
+ }
+
+ /**
+ * Converts this Jackson-friendly adapted task object into the model's {@code Task} object.
+ *
+ * @throws IllegalValueException if there were any data constraints violated in the adapted task.
+ */
+ public Task toModelType() throws IllegalValueException {
+ if (taskName == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName()));
+ }
+ if (!Task.isValidTaskName(taskName)) {
+ throw new IllegalValueException(Task.MESSAGE_CONSTRAINTS);
+ }
+ final Name modelTaskName = new Name(taskName);
+
+ if (status == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT,
+ TaskStatus.class.getSimpleName()));
+ }
+ if (!TaskStatus.isValidTaskStatus(status)) {
+ throw new IllegalValueException(TaskStatus.MESSAGE_CONSTRAINTS);
+ }
+ final TaskStatus modelTaskStatus = TaskStatus.valueOf(status);
+
+ if (creationDate == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT,
+ LocalDateTime.class.getSimpleName()));
+ }
+ try {
+ LocalDateTime.parse(creationDate, FORMATTER);
+ } catch (DateTimeParseException e) {
+ throw new IllegalValueException(DATE_MESSAGE_CONSTRAINTS);
+ }
+ LocalDateTime modelCreationDate = LocalDateTime.parse(creationDate, FORMATTER);
+
+ return new Task(modelTaskName, modelTaskStatus, modelCreationDate);
+ }
+}
diff --git a/src/main/java/seedu/address/storage/JsonAddressBookStorage.java b/src/main/java/seedu/address/storage/JsonMathutoringStorage.java
similarity index 57%
rename from src/main/java/seedu/address/storage/JsonAddressBookStorage.java
rename to src/main/java/seedu/address/storage/JsonMathutoringStorage.java
index dfab9daaa0d..92357eb5d08 100644
--- a/src/main/java/seedu/address/storage/JsonAddressBookStorage.java
+++ b/src/main/java/seedu/address/storage/JsonMathutoringStorage.java
@@ -12,47 +12,47 @@
import seedu.address.commons.exceptions.IllegalValueException;
import seedu.address.commons.util.FileUtil;
import seedu.address.commons.util.JsonUtil;
-import seedu.address.model.ReadOnlyAddressBook;
+import seedu.address.model.ReadOnlyMathutoring;
/**
- * A class to access AddressBook data stored as a json file on the hard disk.
+ * A class to access Mathutoring data stored as a json file on the hard disk.
*/
-public class JsonAddressBookStorage implements AddressBookStorage {
+public class JsonMathutoringStorage implements MathutoringStorage {
- private static final Logger logger = LogsCenter.getLogger(JsonAddressBookStorage.class);
+ private static final Logger logger = LogsCenter.getLogger(JsonMathutoringStorage.class);
private Path filePath;
- public JsonAddressBookStorage(Path filePath) {
+ public JsonMathutoringStorage(Path filePath) {
this.filePath = filePath;
}
- public Path getAddressBookFilePath() {
+ public Path getMathutoringFilePath() {
return filePath;
}
@Override
- public Optional readAddressBook() throws DataConversionException {
- return readAddressBook(filePath);
+ public Optional readMathutoring() throws DataConversionException {
+ return readMathutoring(filePath);
}
/**
- * Similar to {@link #readAddressBook()}.
+ * Similar to {@link #readMathutoring()}.
*
* @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 {
+ public Optional readMathutoring(Path filePath) throws DataConversionException {
requireNonNull(filePath);
- Optional jsonAddressBook = JsonUtil.readJsonFile(
- filePath, JsonSerializableAddressBook.class);
- if (!jsonAddressBook.isPresent()) {
+ Optional jsonMathutoring = JsonUtil.readJsonFile(
+ filePath, JsonSerializableMathutoring.class);
+ if (!jsonMathutoring.isPresent()) {
return Optional.empty();
}
try {
- return Optional.of(jsonAddressBook.get().toModelType());
+ return Optional.of(jsonMathutoring.get().toModelType());
} catch (IllegalValueException ive) {
logger.info("Illegal values found in " + filePath + ": " + ive.getMessage());
throw new DataConversionException(ive);
@@ -60,21 +60,21 @@ public Optional readAddressBook(Path filePath) throws DataC
}
@Override
- public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException {
- saveAddressBook(addressBook, filePath);
+ public void saveMathutoring(ReadOnlyMathutoring mathutoring) throws IOException {
+ saveMathutoring(mathutoring, filePath);
}
/**
- * Similar to {@link #saveAddressBook(ReadOnlyAddressBook)}.
+ * Similar to {@link #saveMathutoring(ReadOnlyMathutoring)}.
*
* @param filePath location of the data. Cannot be null.
*/
- public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException {
- requireNonNull(addressBook);
+ public void saveMathutoring(ReadOnlyMathutoring mathutoring, Path filePath) throws IOException {
+ requireNonNull(mathutoring);
requireNonNull(filePath);
FileUtil.createIfMissing(filePath);
- JsonUtil.saveJsonFile(new JsonSerializableAddressBook(addressBook), filePath);
+ JsonUtil.saveJsonFile(new JsonSerializableMathutoring(mathutoring), 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/JsonSerializableMathutoring.java b/src/main/java/seedu/address/storage/JsonSerializableMathutoring.java
new file mode 100644
index 00000000000..730793ed6d8
--- /dev/null
+++ b/src/main/java/seedu/address/storage/JsonSerializableMathutoring.java
@@ -0,0 +1,62 @@
+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.Mathutoring;
+import seedu.address.model.ReadOnlyMathutoring;
+import seedu.address.model.student.Student;
+
+
+/**
+ * An Immutable Mathutoring that is serializable to JSON format.
+ */
+@JsonRootName(value = "mathutoring")
+public class JsonSerializableMathutoring {
+
+ public static final String MESSAGE_DUPLICATE_STUDENT = "Students list contains duplicate student(s).";
+
+ private final List students = new ArrayList<>();
+
+
+ /**
+ * Constructs a {@code JsonSerializableMathutoring} with the given students.
+ */
+ @JsonCreator
+ public JsonSerializableMathutoring(@JsonProperty("students") List students) {
+ this.students.addAll(students);
+ }
+
+ /**
+ * Converts a given {@code ReadOnlyMathutoring} into this class for Jackson use.
+ *
+ * @param source future changes to this will not affect the created {@code JsonSerializableMathutoring}.
+ */
+ public JsonSerializableMathutoring(ReadOnlyMathutoring source) {
+ students.addAll(source.getStudentList().stream().map(JsonAdaptedStudent::new).collect(Collectors.toList()));
+ }
+
+ /**
+ * Converts this mathutoring into the model's {@code Mathutoring} object.
+ *
+ * @throws IllegalValueException if there were any data constraints violated.
+ */
+ public Mathutoring toModelType() throws IllegalValueException {
+ Mathutoring mathutoring = new Mathutoring();
+ for (JsonAdaptedStudent jsonAdaptedStudent : students) {
+ Student student = jsonAdaptedStudent.toModelType();
+ if (mathutoring.hasStudent(student)) {
+ throw new IllegalValueException(MESSAGE_DUPLICATE_STUDENT);
+ }
+ mathutoring.addStudent(student);
+ }
+ return mathutoring;
+ }
+
+}
diff --git a/src/main/java/seedu/address/storage/AddressBookStorage.java b/src/main/java/seedu/address/storage/MathutoringStorage.java
similarity index 51%
rename from src/main/java/seedu/address/storage/AddressBookStorage.java
rename to src/main/java/seedu/address/storage/MathutoringStorage.java
index 4599182b3f9..44dfe36aba4 100644
--- a/src/main/java/seedu/address/storage/AddressBookStorage.java
+++ b/src/main/java/seedu/address/storage/MathutoringStorage.java
@@ -5,41 +5,43 @@
import java.util.Optional;
import seedu.address.commons.exceptions.DataConversionException;
-import seedu.address.model.ReadOnlyAddressBook;
+import seedu.address.model.Mathutoring;
+import seedu.address.model.ReadOnlyMathutoring;
+
/**
- * Represents a storage for {@link seedu.address.model.AddressBook}.
+ * Represents a storage for {@link Mathutoring}.
*/
-public interface AddressBookStorage {
+public interface MathutoringStorage {
/**
* Returns the file path of the data file.
*/
- Path getAddressBookFilePath();
+ Path getMathutoringFilePath();
/**
- * Returns AddressBook data as a {@link ReadOnlyAddressBook}.
+ * Returns Mathutoring data as a {@link ReadOnlyMathutoring}.
* 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;
+ Optional readMathutoring() throws DataConversionException, IOException;
/**
- * @see #getAddressBookFilePath()
+ * @see #getMathutoringFilePath()
*/
- Optional readAddressBook(Path filePath) throws DataConversionException, IOException;
+ Optional readMathutoring(Path filePath) throws DataConversionException, IOException;
/**
- * Saves the given {@link ReadOnlyAddressBook} to the storage.
- * @param addressBook cannot be null.
+ * Saves the given {@link ReadOnlyMathutoring} to the storage.
+ * @param mathutoring cannot be null.
* @throws IOException if there was any problem writing to the file.
*/
- void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException;
+ void saveMathutoring(ReadOnlyMathutoring mathutoring) throws IOException;
/**
- * @see #saveAddressBook(ReadOnlyAddressBook)
+ * @see #saveMathutoring(ReadOnlyMathutoring)
*/
- void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException;
+ void saveMathutoring(ReadOnlyMathutoring mathutoring, Path filePath) throws IOException;
}
diff --git a/src/main/java/seedu/address/storage/Storage.java b/src/main/java/seedu/address/storage/Storage.java
index beda8bd9f11..c8342d16dfd 100644
--- a/src/main/java/seedu/address/storage/Storage.java
+++ b/src/main/java/seedu/address/storage/Storage.java
@@ -5,14 +5,15 @@
import java.util.Optional;
import seedu.address.commons.exceptions.DataConversionException;
-import seedu.address.model.ReadOnlyAddressBook;
+import seedu.address.model.ReadOnlyMathutoring;
import seedu.address.model.ReadOnlyUserPrefs;
import seedu.address.model.UserPrefs;
+
/**
* API of the Storage component
*/
-public interface Storage extends AddressBookStorage, UserPrefsStorage {
+public interface Storage extends MathutoringStorage, UserPrefsStorage {
@Override
Optional readUserPrefs() throws DataConversionException, IOException;
@@ -21,12 +22,12 @@ public interface Storage extends AddressBookStorage, UserPrefsStorage {
void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException;
@Override
- Path getAddressBookFilePath();
+ Path getMathutoringFilePath();
@Override
- Optional readAddressBook() throws DataConversionException, IOException;
+ Optional readMathutoring() throws DataConversionException, IOException;
@Override
- void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException;
+ void saveMathutoring(ReadOnlyMathutoring addressBook) throws IOException;
}
diff --git a/src/main/java/seedu/address/storage/StorageManager.java b/src/main/java/seedu/address/storage/StorageManager.java
index 6cfa0162164..746ce118528 100644
--- a/src/main/java/seedu/address/storage/StorageManager.java
+++ b/src/main/java/seedu/address/storage/StorageManager.java
@@ -7,24 +7,25 @@
import seedu.address.commons.core.LogsCenter;
import seedu.address.commons.exceptions.DataConversionException;
-import seedu.address.model.ReadOnlyAddressBook;
+import seedu.address.model.ReadOnlyMathutoring;
import seedu.address.model.ReadOnlyUserPrefs;
import seedu.address.model.UserPrefs;
+
/**
- * Manages storage of AddressBook data in local storage.
+ * Manages storage of Mathutoring data in local storage.
*/
public class StorageManager implements Storage {
private static final Logger logger = LogsCenter.getLogger(StorageManager.class);
- private AddressBookStorage addressBookStorage;
+ private MathutoringStorage mathutoringStorage;
private UserPrefsStorage userPrefsStorage;
/**
- * Creates a {@code StorageManager} with the given {@code AddressBookStorage} and {@code UserPrefStorage}.
+ * Creates a {@code StorageManager} with the given {@code MathutoringStorage} and {@code UserPrefStorage}.
*/
- public StorageManager(AddressBookStorage addressBookStorage, UserPrefsStorage userPrefsStorage) {
- this.addressBookStorage = addressBookStorage;
+ public StorageManager(MathutoringStorage mathutoringStorage, UserPrefsStorage userPrefsStorage) {
+ this.mathutoringStorage = mathutoringStorage;
this.userPrefsStorage = userPrefsStorage;
}
@@ -46,33 +47,33 @@ public void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException {
}
- // ================ AddressBook methods ==============================
+ // ================ Mathutoring methods ==============================
@Override
- public Path getAddressBookFilePath() {
- return addressBookStorage.getAddressBookFilePath();
+ public Path getMathutoringFilePath() {
+ return mathutoringStorage.getMathutoringFilePath();
}
@Override
- public Optional readAddressBook() throws DataConversionException, IOException {
- return readAddressBook(addressBookStorage.getAddressBookFilePath());
+ public Optional readMathutoring() throws DataConversionException, IOException {
+ return readMathutoring(mathutoringStorage.getMathutoringFilePath());
}
@Override
- public Optional readAddressBook(Path filePath) throws DataConversionException, IOException {
+ public Optional readMathutoring(Path filePath) throws DataConversionException, IOException {
logger.fine("Attempting to read data from file: " + filePath);
- return addressBookStorage.readAddressBook(filePath);
+ return mathutoringStorage.readMathutoring(filePath);
}
@Override
- public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException {
- saveAddressBook(addressBook, addressBookStorage.getAddressBookFilePath());
+ public void saveMathutoring(ReadOnlyMathutoring mathutoring) throws IOException {
+ saveMathutoring(mathutoring, mathutoringStorage.getMathutoringFilePath());
}
@Override
- public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException {
+ public void saveMathutoring(ReadOnlyMathutoring mathutoring, Path filePath) throws IOException {
logger.fine("Attempting to write to data file: " + filePath);
- addressBookStorage.saveAddressBook(addressBook, filePath);
+ mathutoringStorage.saveMathutoring(mathutoring, filePath);
}
}
diff --git a/src/main/java/seedu/address/storage/UserPrefsStorage.java b/src/main/java/seedu/address/storage/UserPrefsStorage.java
index 29eef178dbc..2c620d47b11 100644
--- a/src/main/java/seedu/address/storage/UserPrefsStorage.java
+++ b/src/main/java/seedu/address/storage/UserPrefsStorage.java
@@ -9,7 +9,7 @@
import seedu.address.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/address/ui/ExportProgressWindow.java b/src/main/java/seedu/address/ui/ExportProgressWindow.java
new file mode 100644
index 00000000000..97f91f3373a
--- /dev/null
+++ b/src/main/java/seedu/address/ui/ExportProgressWindow.java
@@ -0,0 +1,147 @@
+package seedu.address.ui;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.logging.Logger;
+
+import javafx.fxml.FXML;
+import javafx.scene.control.Button;
+import javafx.scene.control.TextArea;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+import javafx.stage.FileChooser;
+import javafx.stage.Stage;
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.logic.Logic;
+import seedu.address.model.student.Student;
+
+/**
+ * Controller for an export student's progress page
+ */
+public class ExportProgressWindow extends UiPart {
+
+ public static final String MESSAGE_SUCCESS = "%1$s's progress report exported in %2$s";
+ private static final Logger logger = LogsCenter.getLogger(ExportProgressWindow.class);
+ private static final String FXML = "ExportProgressWindow.fxml";
+
+ private Student student;
+
+ private Stage root;
+ private Logic logic;
+
+ private String defaultFileName;
+
+ @FXML
+ private Button saveAsButton;
+
+ @FXML
+ private TextArea resultDisplay;
+ @FXML
+ private ImageView exportImage;
+
+ /**
+ * Creates a new ExportProgressWindow.
+ *
+ * @param root Stage to use as the root of the ExportProgressWindow.
+ */
+ public ExportProgressWindow(Stage root, Student student, Logic logic) {
+ super(FXML, root);
+ this.root = root;
+ this.student = student;
+ this.logic = logic;
+ this.exportImage.setImage(new Image(this.getClass().getResourceAsStream("/images/export.png")));
+ if (this.student != null) {
+ resultDisplay.setText("Export " + this.student.getName().fullName + "'s progress");
+ saveAsButton.setDisable(false);
+ }
+ }
+
+ /**
+ * Creates a new ExportProgressWindow.
+ */
+ public ExportProgressWindow(Student student, Logic logic) {
+ this(new Stage(), student, logic);
+ }
+
+ /**
+ * Shows the export progress window.
+ * @throws IllegalStateException
+ *
+ *
+ * if this method is called on a thread other than the JavaFX Application Thread.
+ *
+ *
+ * if this method is called during animation or layout processing.
+ *
+ *
+ * if this method is called on the primary stage.
+ *
+ *
+ * if {@code dialogStage} is already showing.
+ *