diff --git a/.gitignore b/.gitignore
index 2873e189e1..433b106574 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,3 +15,10 @@ bin/
/text-ui-test/ACTUAL.TXT
text-ui-test/EXPECTED-UNIX.TXT
+/data/exerciselist.txt
+/data/exercisegoallist.txt
+/data/favourites.txt
+/data/habits.txt
+/data/sleep.txt
+
+META-INF/MANIFEST.MF
diff --git a/build.gradle b/build.gradle
index ea82051fab..d677524a36 100644
--- a/build.gradle
+++ b/build.gradle
@@ -29,11 +29,11 @@ test {
}
application {
- mainClass.set("seedu.duke.Duke")
+ mainClass.set("Main")
}
shadowJar {
- archiveBaseName.set("duke")
+ archiveBaseName.set("Wellness360")
archiveClassifier.set("")
}
@@ -44,3 +44,7 @@ checkstyle {
run{
standardInput = System.in
}
+
+tasks.withType(JavaExec) {
+ enableAssertions = true
+}
diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml
index c35db7c7e6..958314713c 100644
--- a/config/checkstyle/checkstyle.xml
+++ b/config/checkstyle/checkstyle.xml
@@ -205,7 +205,7 @@
-->
diff --git a/data/sleep.txt b/data/sleep.txt
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/docs/AboutUs.md b/docs/AboutUs.md
index 0f072953ea..860ac72183 100644
--- a/docs/AboutUs.md
+++ b/docs/AboutUs.md
@@ -1,9 +1,9 @@
# About us
-Display | Name | Github Profile | Portfolio
---------|:----:|:--------------:|:---------:
- | John Doe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
- | Don Joe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
- | Ron John | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
- | John Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
- | Don Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
+Display | Name | Github Profile | Portfolio
+--------|:--------------:|:--------------:|:---------:
+ | Ari Lim Ee Lik | [Github](https://github.com/genexus85) | [Portfolio](https://www.linkedin.com/in/ari-lim/)
+ | Damien Wee | [Github](https://github.com/damiwee) | [Portfolio](https://www.linkedin.com/in/damien-wee-jl/)
+ | Davian Kho Yong Quan | [Github](https://github.com/Daviancold) | [Portfolio](https://daviancold.netlify.app)
+ | Lim Jing Hao | [Github](https://github.com/JingHaoooo) | [Portfolio](https://www.linkedin.com/in/jing-hao-lim-684326290/)
+ | Ong Kan Wu | [Github](https://github.com/OKW32) | [Portfolio](https://www.linkedin.com/in/ong-kan-wu-4a869726a/)
diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md
index 64e1f0ed2b..beccde99c7 100644
--- a/docs/DeveloperGuide.md
+++ b/docs/DeveloperGuide.md
@@ -1,38 +1,1826 @@
-# Developer Guide
+# Wellness360 Developer Guide
+
+## Table of Contents
+- [Wellness360 Developer Guide](#wellness360-developer-guide)
+- [Table of contents](#table-of-contents)
+- [Acknowledgements](#acknowledgements)
+- [Setting up, getting started](#setting-up-getting-started)
+ - [Setting up the project in your computer](#setting-up-the-project-in-your-computer)
+ - [Before writing code](#before-writing-code)
+- [Design & implementation](#design--implementation)
+ 1. [Architecture](#architecture)
+ 2. [Ui component](#ui-component)
+ - [Description](#description)
+ - [Design Considerations](#design-considerations)
+ - [Implementation](#implementation)
+ - [Class Diagram](#class-diagram)
+ 3. [Command parser component](#command-parser-component)
+ - [Description](#description-1)
+ - [Design Considerations](#design-considerations-1)
+ - [Implementation](#implementation-1)
+ - [Class Diagram](#class-diagram-1)
+ - [Sequence Diagram](#sequence-diagram)
+ 4. [Storage component](#storage-component)
+ - [Description](#description-2)
+ - [Design Considerations](#design-considerations-2)
+ - [Implementation](#implementation-2)
+ - [Class Diagram](#class-diagram-2)
+ - [Sequence Diagram](#sequence-diagram-)
+ 5. [Reflection component](#reflection-component)
+ - [Description](#description-3)
+ - [Design Considerations](#design-considerations-3)
+ - [Implementation](#implementation-3)
+ - [Class Diagram](#class-diagram-3)
+ - [Sequence Diagram](#sequence-diagram-1)
+ 6. [Habit tracker component](#habit-tracker-component)
+ - [Description](#description-4)
+ - [Design Considerations](#design-considerations-4)
+ - [Implementation](#implementation-4)
+ - [Class Diagram](#class-diagram-4)
+ - [Sequence Diagram](#sequence-diagram-2)
+ 7. [Sleep tracker component](#sleep-tracker-component)
+ - [Description](#description-5)
+ - [Design Considerations](#design-considerations-5)
+ - [Implementation](#implementation-5)
+ - [Class Diagram](#class-diagram-5)
+ - [Sequence Diagram](#sequence-diagram-3)
+ 8. [Focus timer component](#focus-timer-component)
+ - [Design Considerations](#design-considerations-6)
+ - [Implementation](#implementation-6)
+ - [Focus Class Diagram](#focus-class-diagram)
+ - [Focus State Transition Diagram](#focus-state-transition-diagram)
+ - [Focus Sequence Diagram](#focus-sequence-diagram)
+ 9. [Fitness Motivator component](#fitness-motivator-component)
+ - [Description](#description-6)
+ - [Design Considerations](#design-considerations-7)
+ - [Implementation](#implementation-7)
+ - [Class Diagram](#class-diagram-6)
+ - [Sequence Diagram](#sequence-diagram-4)
+- [Appendix: Requirements](#appendix-requirements)
+ - [Product Scope](#product-scope)
+ - [User Stories](#user-stories)
+ - [Non Functional Requirements](#non-functional-requirements)
+ - [Glossary](#glossary)
+- [Appendix: Instructions for manual testing](#instructions-for-manual-testing)
+ - [Reflection component](#reflection-component-1)
+ - [Habit Tracker component](#habit-tracker-component-1)
+ - [Sleep Tracker component](#sleep-tracker-component-1)
+ - [Focus Timer component](#focus-timer-component-1)
+ - [Fitness Motivator component](#fitness-motivator-component-1)
+
+
## Acknowledgements
-{list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well}
+Reference to AB-3 Developer Guide
+* [Source URL](https://se-education.org/addressbook-level3/DeveloperGuide.html#documentation-logging-testing-configuration-dev-ops)
+* Used as template to structure this DeveloperGuide
+
+Reference to AB-3 diagrams code
+* [Source URL](https://github.com/se-edu/addressbook-level3/tree/master/docs/diagrams)
+* Used as reference to understand PlantUML syntax
+
+## Setting up, getting started
+
+### Setting up the project in your computer
+Fork this repo, and clone it on your local machine.
+
+You are recommended to use Intellij IDEA:
+
+1. Configure the JDK: Follow the guide [[se-edu/guides] IDEA: Configuring the JDK](https://se-education.org/guides/tutorials/intellijJdk.html) to ensure Intellij is configured to use JDK 11.
+2. Import the project as a Gradle project: Follow the guide [[se-edu/guides] IDEA: Importing a Gradle project](https://se-education.org/guides/tutorials/intellijImportGradleProject.html) to import the project into IDEA.
+3. Verify the setup:
+ * Run the main class in IntelliJ and try a few commands.
+ * Run the tests to ensure they all pass.
+
+### Before writing code
+1. Configure the coding style: [[se-edu/guides] IDEA: Configuring the code style](https://se-education.org/guides/tutorials/intellijCodeStyle.html)
+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.
+3. Read the developer guide to understand the design.
+
+
## Design & implementation
-{Describe the design and implementation of the product. Use UML diagrams and short code snippets where applicable.}
+### Architecture
+
+
+The Architecture Diagram given above explains the high-level design of the App.
+
+Given below is a quick overview of main components and how they interact with each other.
+
+The bulk of the app’s work is done by the following four components:
+
+* Ui: Handles user interactions, including prompting for input and handling outputs to console.
+* Parser: Parses user input to identify command and parameters, if any.
+* Logic: The command executor.
+* Storage: Reads data from, and writes data to, the respective text files for each feature where applicable.
+
+### UI component
+#### Description
+`Ui` component is in charge of reading in user input and printing output.
+
+The `Ui` class is created to standardise the output formatting of messages to be printed across all features. These formatting include the printing of messages and lists, reducing code repetition.
+
+#### Design Considerations
+
+- #### User Design Considerations
+ - Users are able to understand what is going on with relative ease, when they use the Wellness360 chat bot.
+ - Standardised output creates a cohesive look and an overall more immersive experience for the user.
+- #### Developer Design Considerations
+ - Static Attributes: All methods are static, allowing their usage anywhere within the codebase.
+ - SRP: Classes adhere to the Single Responsibility Principle. For example, the `Ui` class is solely responsible for managing printing-related operations.
+ - Readability and Maintainability: Descriptive naming, comments, and documentation for clarity. Since printing is a highly used task, it prevents highly-repetitive code as well.
+
+#### Implementation
+
+#### Class Diagram
+
+
+- `Ui` class
+ - Overview
+ - This class is a collection of methods that standardise the printing of messages to the CLI.
+ - Attributes:
+ - `SEP`: A private static string constant that stores a separation line.
+ - Methods:
+ - `greetUser()`: A public static method that prints a welcome message when Wellness360 is started.
+ - `promptUserInput()`: A public static method that prints a "You: " message before the input cursor, to signal where the input will go.
+ - `sayGoodbye()`: A public static method that prints a farewell message when Wellness360 is terminated by the user.
+ - `printMessagewithSepNewLine(String message)`: A public static method used to print a message with a separation line.
+ - `printMessageWithoutNewLine(String message)`: A public static method used to print a message without a separation line.
+ - `printList(ArrayList list, String message)`: A public static method to print a list of type `ArrayList`, along with a message and a separation line.
+
+### Command parser component
+
+#### Description
+The Parser component is responsible for interpreting user input commands and converting them into executable commands for the Wellness360 system.
+It facilitates the interaction between the user interface and the underlying functionality of the system.
+
+#### Design Considerations
+* ##### User Design Considerations
+ * User commands should be clear and intuitive, following a consistent syntax across different features.
+ * Error messages should be informative and guide users on how to correct their input if it's invalid.
+
+* #### Developer Design Considerations
+ * Single Responsibility Principle (SRP): Each parser class should have a single responsibility, such as parsing commands related to a specific feature.
+ * Modularity: Parsers should be modular and easily extendable to accommodate new features or commands.
+ * Exception Handling: Ensure robust error handling to gracefully manage invalid user input or system errors.
+
+#### Implementation
+
+##### Class Diagram
+
+
+* `Parser` class:
+ * Overview
+ * The Parser class is responsible for determining the appropriate command to execute based on user input.
+ * Methods
+ * `determineCommand()`: Determines the command to execute based on user input and delegates to specific feature command parsers.
+ * Dependencies
+ * HabitCommandParser, FitnessCommandParser, ReflectionCommandParser, FocusCommandParser, SleepCommandParser: To further parse user input to determine the command specific to the feature
+
+* `HabitCommandParser` class:
+ * Overview
+ * The `HabitCommandParser` class parses user input related to habits and creates corresponding command objects.
+ * Methods
+ * `determineHabitCommand()`: Determines the habit-related command to execute based on user input.
+ * Dependencies
+ * HabitTracker: For storing and managing habits.
+ * Command: To represent executable habit-related commands.
+
+* `FitnessCommandParser` class:
+ * Overview
+ * The `FitnessCommandParser` class parses user input related to fitness and creates corresponding command objects.
+ * Methods
+ * `determineFitnessCommand()`: Determines the fitness-related command to execute based on user input.
+ * Dependencies
+ * FitnessMotivator: For managing fitness-related tasks.
+ * Command: To represent executable fitness-related commands.
+
+* `ReflectionCommandParser` class:
+ * Overview
+ * The `ReflectionCommandParser` class parses user input related to reflection and creates corresponding command objects.
+ * Methods
+ * `determineReflectionCommand()`: Determines the reflection-related command to execute based on user input.
+ * Dependencies
+ * ReflectionManager: For handling reflection-related operations.
+ * Command: To represent executable reflection-related commands.
+
+* `FocusCommandParser` class:
+ * Overview
+ * The `FocusCommandParser` class parses user input related to focus and creates corresponding command objects.
+ * Methods
+ * `determineFocusCommand():` Determines the focus-related command to execute based on user input.
+ * Dependencies
+ * FocusTimer: For managing focus-related tasks.
+ * Command: To represent executable focus-related commands.
+
+* `SleepCommandParser` class:
+ * Overview
+ * The `SleepCommandParser` class parses user input related to sleep and creates corresponding command objects.
+ * Methods
+ * `determineSleepCommand()`: Determines the sleep-related command to execute based on user input.
+ * Dependencies
+ * SleepTracker: For managing sleep-related data.
+ * Command: To represent executable sleep-related commands.
+
+
+##### Sequence Diagram
+
+
+
+When the User enters a command, the Main class receives the user input and forwards it to the Parser class.
+The Parser class then activates and determines the appropriate command parser based on the user input.
+
+If the user input corresponds to a HabitCommand, the Parser activates the HabitCommandParser class,
+which creates a command object representing the habit-related command.
+The command object is returned to the Parser, which then returns it to the Main class for execution.
+
+Similarly, if the user input corresponds to a FitnessCommand, FocusCommand, ReflectionCommand, or SleepCommand,
+the Parser activates the corresponding command parser (FitnessCommandParser, FocusCommandParser, ReflectionCommandParser,
+SleepCommandParser respectively), creates the corresponding command object, and returns it to the Main class for execution.
+
+
+
+### Storage component
+#### Description
+The Storage component deals with the storage of all the data collected from the other components in the hard disk
+in the form of text files so that data can be loaded from the hard disk when Wellness360 starts up.
+
+#### Design Considerations
+* ##### User Design Considerations
+ * File path for storage of data should all be in the folder /data for ease of locating the storage files
+ * Storage data can be easily read with the use of comma separated values
+
+* #### Developer Design Considerations
+ * SRP: Classes adhere to the Single Responsibility Principle. For example, Storage class is specifically for creating/
+ writing/reading of files, while HabitTrackerStorage/SleepTrackerStorage deals with the processing of data for storage
+ and reading for their individual components
+ * Readability and Maintainability: Ensure there are no magic numbers used. For example, use of constants for
+ information position in the text file.
+ * Exception Handling: Graceful error handling with meaningful messages.
+
+
+
+#### Implementation
+
+##### Class Diagram
+
+
+* `Storage` class:
+ * Overview
+ * The `Storage` class is in charge of saving/loading of data.
+ * Methods
+ * `loadDataFromFile(String filepath)`: Stores lines in text file into an array of Strings.
+ * `createFolder(String filepath)`: Creates text file and folder to store file in.
+ * `saveTasksToFile(String filepath, ArrayList data)`: Write data into text file
+ * `isFileCreated(String filepath)`: Checks if file has been created
+
+* `HabitTrackerStorage` class:
+ * Overview
+ * The `HabitTrackerStorage` class converts data into comma separated values and stores it in a text file.
+ * Attributes
+ * `HABIT_FILE_PATH`: File path for storage of habit list
+ * `COMMA_SEPARATION`: Separator to be placed between each data value
+ * `DATA_SIZE`: Number of values in a data point (Habit)
+ * Methods
+ * `saveHabitListToFile(ArrayList habitList)`: Saves habit list into a text file.
+ * `loadHabitListFromFile()`: Loads habit list from a text file.
+ * Dependencies
+ * Storage: reading/writing/creating files
+ * UML Notes:
+ * It relies on `Storage` class for reading and writing of files
+
+* `SleepTrackerStorage`
+ * Overview
+ * The `SleepTrackerStorage` class converts data into comma separated values and stores it in a text file.
+ * Attributes
+ * `SLEEP_FILE_PATH`: File path for storage of sleep cycles
+ * `ERROR_MESSAGE`: Message to be print when file is corrupted
+ * `HOURS_POS`: position of hours parameter in comma separated values
+ * `DATE_POS`: position of date parameter in comma separated valuees
+ * Methods
+ * `saveSleepListToFile(ArrayList sleepList)`: Saves sleep list into a text file.
+ * `loadSleepListFromFile()`: Loads sleep list from a text file.
+ * Dependencies
+ * Storage: reading/writing/creating files
+ * UML Notes:
+ * It relies on `Storage` class for reading and writing of files
+
+
+
+##### Sequence Diagram
+
+##### Loading Data Sequence Diagram
+
+
+* Note: HabitTrackerStorage works in a similar way as SleepTrackerStorage
+
+When `Main` starts, `SleepTracker` object is created. `SleepTrackerStorage loadSleepListFromFile()` method is called to
+Begin loading sleep cycles from text file. `SleepCycleList` is returned to `SleepTracker` class and Sleep Tracker class
+is successfully instantiated.
+
+##### Saving Data Sequence Diagram
+
+
+* Note: HabitTrackerStorage works in a similar way as SleepTrackerStorage
+
+When `SleepTracker` executes `saveSleepListToFile` method from `SleepTrackerStorage` class, `ArrayList` is
+created and after successfully converting sleepCycleList to String format, SleepTrackerStorage then calls
+`saveTasksToFile` from `Storage` class to write data into text file.
+
+### Reflection component
+#### Description
+The Reflection component provides users with random reflections questions to reflect on about their personal lives and goals.
+This component aims to contribute to the goal of improving user's wellness.
+
+#### Design Considerations
+* ##### User Design Considerations
+ * The sets of 5 questions are randomly generated each time. They are randomised to allow users to reflect on different areas.
+ * Users can save questions they liked into their favourites list for future review.
+ * Users can also delete questions from their favourites list if the question is no longer relevant to them.
+ * Help menu is available for new users to guide them on how to use the reflection feature.
+ * Error messages with guidance messages will be printed to console if command input by user is invalid.
+
+
+* #### Developer Design Considerations
+ * Modularity: Organized into packages for separation of concerns. Each package contains related classes, such as ReflectionManager and ReflectionList in the reflection package, and ReflectionCommandParser in the parser package.
+ * Abstraction: Use of abstract classes and interfaces for flexibility and extensibility. Abstract classes (ReflectionList) provide a common structure and behavior for subclasses (FavoriteReflectionsList, ReflectionQuestionBank), promoting code reuse and ensuring consistent functionality across different implementations.
+ * Encapsulation: Private access modifiers and encapsulated methods ensure data integrity. Methods like addReflectionQuestion() and removeReflectionQuestion() in ReflectionList encapsulate the manipulation of the reflection list, ensuring data integrity and promoting a clear interface for interacting with the list.
+ * Exception Handling: Graceful error handling with meaningful messages.
+ * Command Design Pattern: Encapsulation of actions as objects for decoupling.
+ * Dependency Injection: Dependencies injected via constructors for loose coupling.
+ * SRP: Classes adhere to the Single Responsibility Principle. For example, the ReflectionManager class is responsible for managing reflection-related operations, while the ReflectionCommandParser class handles parsing and determining reflection commands.
+ * Readability and Maintainability: Descriptive naming, comments, and documentation for clarity.
+
+
+
+#### Implementation
+
+##### Class Diagram
+
+
+
+Note that certain details described below have been omitted from Class Diagram for simplicity and to improve readability.
+ * `ReflectionManager` class
+ * Overview
+ * The `ReflectionManager` class oversees reflection-related operations, managing reflection questions and favorites.
+ * Attributes:
+ * `HELP_MENU_INSTRUCTIONS`: Array containing instructions for the help menu.
+ * `FAVOURITE_QUESTIONS_FILE_PATH`: Path to the file storing favorite reflection questions.
+ * `fiveRandomQuestions`: ArrayList holding five random reflection questions.
+ * `questionBank`: Instance of `ReflectionQuestionBank` managing reflection questions.
+ * `favoriteReflectionsList`: Instance of `FavoriteReflectionsList` managing favorite reflection questions.
+ * Methods:
+ * `printFiveRandomQuestions()`: Prints five random reflection questions.
+ * `saveReflectionQuestion(int reflectionId)`: Saves a reflection question to favorites based on ID.
+ * `unsaveReflectionQuestion(int reflectionId)`: Removes a reflection question from favorites based on ID.
+ * `printFavourites()`: Prints the list of favorite reflection questions.
+ * `printHelpMenu()`: Prints the help menu with available reflection commands.
+ * Dependencies:
+ * Storage: Utilized for data storage operations.
+ * Ui: Utilized for user interface interactions.
+ * UML notes:
+ * `ReflectionManager` contains exactly one `ReflectionQuestionBank` class and one `FavoriteReflectionsList` class.
+ * It relies on `Storage` class for file operations and `Ui` class for user interactions.
+
+ * `ReflectionQuestion` class
+ * Overview:
+ * This class represents a reflection question
+ * Attributes:
+ * `question`: Private attribute holding the text of the reflection question.
+ * Methods:
+ * `toString()`: Overriden method that retrieves the text of the reflection question.
+ * UML notes:
+ * `ReflectionList` and `FavoriteReflectionsList` may contain 0 or more `ReflectionQuestion` instances.
+ * `ReflectionQuestionBank` contains exactly 41 of `ReflectionQuestion` instances.
+ * When a `ReflectionList`/ `FavoriteReflectionsList`/ `ReflectionQuestionBank` object is destroyed, its associated `ReflectionQuestion` instances are also destroyed, reflecting a "whole-part" relationship.
+
+ * `ReflectionList` class
+ * Overview:
+ * The `ReflectionList` class serves as an abstract representation of a list of reflection questions within the Reflection Management System. It provides basic operations for managing reflection questions, such as adding, removing, and retrieving questions from the list.
+ * Attributes:
+ * `reflectionList`: Protected attribute representing the list of reflection questions. It is an ArrayList of `ReflectionQuestion` objects.
+ * Methods:
+ * `addReflectionQuestion(ReflectionQuestion reflectionQuestion)`: Adds a reflection question to the list.
+ * `removeReflectionQuestion(ReflectionQuestion reflectionQuestion)`: Removes a reflection question from the list.
+ * `getSize()`: Retrieves the size of the reflection list.
+ * `getReflectionList()`: Retrieves the list of reflection questions.
+ * UML notes:
+ * `FavoriteReflectionsList` inherits from `ReflectionList`
+ * `ReflectionQuestionBank` inherits from `ReflectionList`
+ * `ReflectionList` may contain 0 or more instances of `ReflectionQuestion`. The association is of type composition.
+
+ * `FavoriteReflectionsList` class
+ * Overview:
+ * The `FavoriteReflectionsList` class extends the `ReflectionList` class and represents a specialized list of favorite reflection questions within the Reflection Management System. It inherits functionality from `ReflectionList`.
+ * Attributes:
+ * Inherits from the superclass `ReflectionList`.
+ * Methods:
+ * `get(int favouritesId)`: Retrieves a favorite reflection question by its index in the list.
+ * Dependencies:
+ * Inherits functionality from the `ReflectionList` class.
+ * UML notes:
+ * `FavoriteReflectionsList` inherits from `ReflectionList`.
+ * `ReflectionManager` contains a single instance of `FavoriteReflectionsList`.
+ * `FavoriteReflectionsList` may contain 0 or more instances of `ReflectionQuestion`.
+
+ * `ReflectionQuestionBank` class
+ * Overview:
+ * The `ReflectionQuestionBank` class represents a bank of reflection questions within the Reflection Management System. It extends the ReflectionList class to inherit basic list management functionalities and provides additional methods for initializing the bank with predefined questions and retrieving random sets of questions.
+ * Attributes:
+ * Inherits from the superclass `ReflectionList`.
+ * `REFLECTION_QUESTIONS`: Static array containing predefined reflection questions that used for setup of question bank.
+ * Methods:
+ * `setUpReflectionBank()`: Initializes the reflection question bank with predefined questions.
+ * `getFiveRandomQuestions()`: Retrieves five random reflection questions from the bank.
+ * Dependencies:
+ * Inherits functionality from the `ReflectionList` class.
+ * UML notes:
+ * `ReflectionManager` contains a single instance of `ReflectionQuestionBank`
+ * `ReflectionQuestionBank` inherits from `ReflectionList`
+ * `ReflectionQuestionBank` contains 41 instances of `ReflectionQuestion`
+
+ * `ReflectionCommandParser`
+ * Overview: Parses reflection-related commands and create different reflection command objects based on user input.
+ * Method: `determineReflectionCommand(ReflectionManager reflectionManager, String commandArgs)`
+
+ * Reflection command classes
+ * `GetReflectionQuestionsCommand`: Retrieve random reflection questions.
+ * Command format: `reflect get`
+ * `ListFavouriteReflectionsCommand`: List favorite reflection items.
+ * Command format: `reflect list`
+ * `ReflectionHelpCommand`: Display help menu for reflection commands.
+ * Command format: `reflect help`
+ * `SaveToFavouritesCommand`: Save a reflection item to favorites.
+ * Command format: `reflect save `
+ * `UnsaveFromFavouritesCommand`: Unsave a reflection item from favorites.
+ * Command format:`reflect unsave `
+
+
+
+##### Sequence Diagram
+
+* Note that `PlaceholderReflectionCommand` can refer to any of the reflection commands as mentioned above, as all of them follow the same call pattern.
+
+When `Main` starts, `scanner` and `ReflectionManager` objects are created. Upon receiving user input, the input will first be
+determined if it is a command related to the reflection feature. If it is, it will be further parsed by `ReflectionCommandParser` to determine
+the command. The corresponding reflection command object is then created and is returned to `Main`, where `execute` will then be called
+and the corresponding method in `ReflectionManager` is invoked.
+
+
+
+### Habit tracker component
+
+#### Description
+The habit tracker component aims to provide user with a tool to track and cultivate good habits.
+
+#### Design Considerations
+* ##### User Design Considerations
+ * User can add new habits in the habit tracker that they wish to cultivate.
+ * User can increase the count of a habit after they completed the habit for the day, allowing them to keep track of the total number of times they completed the habit.
+ * User can also delete the habit if they no longer want to cultivate that habit.
+ * User are able to set the priority of the habits (HIGH, MED, LOW), to prioritise their time on more important habits.
+ * User can also sort the habits according to their priority for better visualization.
+ * A help menu is also provided for users to guide them on how to use the habit tracker.
+ * Error messages with guidance messages will be printed to console if command input by user is invalid.
+
+* #### Developer Design Considerations
+ * Modularity: Encapsulate related functionalities within classes to promote re-usability and maintainability.
+ * Exception Handling: Use of custom exceptions to differentiate between various types of errors, and to handle them appropriately with error messages
+ * Data Encapsulation: Control the access to a class's internal attributes, accessible only through getters and setters
+ * Interface Segregation: Segregation of command interface to represent different command types for specific use cases.
+ * Readability and Maintainability: Descriptive naming, robust documentation for code clarity.
+
+
+
+#### Implementation
+
+##### Class Diagram
+
+
+* `HabitTracker` class
+ * Overview
+ * The `HabitTracker` class manages the habit tracker list which contains the habits.
+ * Attributes:
+ * `habitList`: Private attribute representing the list of habits. It is an ArrayList of Habit objects.
+ * Methods:
+ * `HabitTracker()`: Constructs a HabitTracker object.
+ * `getNumberOfHabits()`: Returns the number of habits in the habitList.
+ * `addHabit(Habit newHabit)`: Adds a new Habit object into the habitList.
+ * `listHabits()`: Prints a list of all habits in habitList .
+ * `isValidHabitID(int habitID)`: Check if a habit ID is valid by comparing with the size of habitList.
+ * `updateHabitCount(int habitID, String updatedCount)`: Update the habit count of a habit.
+ * `deleteHabit(int habitID)`: Delete a habit from habitList.
+ * `setPriorityLevel(int habitID, String priority)`: Set the priority of a habit.
+ * `sortHabits()`: Sort the habits in habitList according to the habits' priorities.
+ * `clearHabitList()`: Delete all habits from habitList.
+ * Dependencies:
+ * HabitTrackerStorage: Utilized for data storage operations.
+ * Ui: Utilized for user interface interactions.
+ * UML notes:
+ * `HabitTracker` can contain any number of `Habit` class objects.
+ * It relies on `HabitTrackerStorage` class for file operations and `Ui` class for user interactions.
+
+* `Habit` class
+ * Overview:
+ * This class represents a Habit.
+ * Attributes:
+ * `description`: Private attribute holding the habit description.
+ * `habitCount`: Private attribute holding the total count the user have completed the habit.
+ * `priority`: Private attribute representing the priority level of a habit.
+ * Methods:
+ * `Habit(String description)`: Constructs a habit object.
+ * `Habit(String description, int habitCount, Priority priority)`: Constructs a habit object.
+ * `getDescription()`: Get the description of the habit.
+ * `getHabitCount()`: Get the habit count of the habit.
+ * `getPriority()`: Get the priority of the habit.
+ * `toString()`: Method that formats the attributes of the habit for printing.
+ * Dependencies:
+ * Enum Priority: Utilized to allow only specific priority levels (`HIGH, MED, LOW`) for a habit.
+ * UML notes:
+ * `HabitTracker` can contain any number of `Habit` instances.
+ * When a `HabitTracker` object is destroyed, its associated `Habit` instances are also destroyed, showcasing a composition relationship.
+
+* `HabitCommandParser`
+ * Overview: Parses habit-tracker commands and create different habit command objects based on user input.
+ * Method: `determineHabitCommand(HabitTracker habitTracker, String commandArgs)`
+
+* Habit command classes
+ * `AddHabitCommand`: Add a new habit to the habit-tracker.
+ * Command format: `habit add `
+ * `DeleteHabitCommand`: Delete a habit from the habit-tracker.
+ * Command format: `habit delete /id `
+ * `ListHabitsCommand`: Prints a list of all existing habits.
+ * Command format: `habit list`
+ * `UpdateHabitCountCommand`: Increase habit count after completing a habit.
+ * Command format: `habit update /id /by `
+ * `SetPriorityCommand`: Set priority level of habit.
+ * Command format: `habit set /id /priority `
+ * `SortHabitsCommand`: Sort habit list according to priority level.
+ * Command format: `habit sort`
+ * `HabitHelpCommand`: Display a help menu of the habit-tracker commands.
+ * Command format: `habit help`
+
+
+
+##### Sequence Diagram
+
+* Note that `PlaceholderHabitCommand` can refer to any of the habit commands.
+
+When `Main` starts, `scanner` and `HabitTracker` objects are created. Upon receiving user input, the input will first be
+determined if it is a command related to the habit tracker feature. If it is, it will be further parsed by `HabitCommandParser` to determine
+the command. The corresponding habit command object is then created and is returned to `Main`, where `execute` will then be called
+and the corresponding method in `HabitTracker` is invoked.
+
+### Sleep tracker component
+
+#### Description
+
+The Sleep tracker component allow users to keep track of the number of hours they have slept, so that users will know
+when their lacking sleep or getting more sleep hours as the day progresses. With information on the user's sleep cycle,
+one will be able to understand what is their optimal sleep cycle. With this users can have better sleep, improving
+user's wellness.
+
+#### Design Considerations
+
+* ##### User Design Considerations
+ * Users can add hours slept on a specific date
+ * Users can update hours slept on a specific date in case of mistakes in adding of sleep hours
+ * Users can list out all sleep hours tracked or get number of hours slept on a specific date
+ * Users can delete sleep cycles with deletion methods of:
+ * Deleting a sleep cycle that is of a certain date
+ * Deleting sleep cycles before a certain date
+ * Deleting sleep cycles between certain dates
+ * Error messages with guidance messages will be printed to console if command input by user is invalid.
+
+
+* ##### Developer Design Considerations
+ * SRP: Ease of scalability is achieved as classes adhere to the Single Responsibility Principle. For example,
+ the SleepTracker class is responsible for managing sleep tracker related commands, SleepCommandParser class handles
+ parsing and determining which sleep command is being called and SleepCycleList is responsible for storing sleep
+ cycles and methods that can be called to edit it's content.
+ * Readability and Maintainability: Descriptive naming, use of Enumerations and JavaDoc for clarity.
+ For example, use of enumerations for deleteMode.
+ * Encapsulation: Private access modifiers and encapsulated methods ensure data integrity. Methods like addSleepCycle
+ and deleteSleepCycle in SleepCycleList encapsulate the manipulation of the reflection list, ensuring data integrity
+ and promoting a clear interface for interacting with the list.
+ * Exception Handling: Extensive coverage of exceptions in sleepCommand classes to ensure all errors are handled
+ properly
+
+#### Implementation
+
+
+
+##### Class Diagram
+
+
+* `SleepTracker` class
+ * Overview
+ * The `SleepTracker` class oversees sleep-related operations, managing sleep cycles.
+ * Attributes:
+ * `sleepCycleList`: Instance of `SleepCycleList` managing sleep cycles.
+ * Methods:
+ * `listSleepCycles()`: List out all sleep cycles.
+ * `addSleepCycle(SleepCycle sleepCycleToAdd)`: Add sleep cycle.
+ * `updateSleepCycle(LocalDate date, double newHours)`: Change hours slept for specific date.
+ * `getSleepCycle(LocalDate date)`: Get number of hours slept for specific date.
+ * `deleteSleepCycle(LocalDate date)`: Delete sleep cycle for specific date.
+ * `deleteSleepCyclesBefore(LocalDate date)`: Delete sleep cycle before specific date.
+ * `deleteSleepCyclesBetween(LocalDate startDate, LocalDate endDate)`: Delete sleep cycles between 2 dates.
+ * `saveSleepCycles()`: Save sleep cycle list into a text file.
+ * Dependencies:
+ * `SleepTrackerStorage`: Utilized for sleep cycle data storage operations
+ * UML Notes:
+ * `SleepTracker` contains only 1 `SleepCycleList`
+ * It relies on `SleepTrackerStorage` class for file operations
+
+* `SleepCycle` class
+ * Overview:
+ * This class represents a sleep cycle
+ * Attributes:
+ * `hoursSlept`: Number of hours slept
+ * `dateOfSleep`: Date that user slept on
+ * Methods:
+ * `getHoursSlept()`: Get Hours slept for this sleep cycle.
+ * `getDateOfSleep()`: Get date slept for this sleep cycle.
+ * `setHoursOfSleep(double newHours)`: Set Hours slept for this sleep cycle to a new duration.
+ * `compareTo(SleepCycle: SleepCycle)`: Comparison between sleep cycles.
+ * `toString()`: String format for what needs to be printed out for a sleep cycle.
+ * `SleepCycleList` may contain 0 or more instances of `SleepCycle`.
+ * UML Notes:
+ * When a `SleepCycleList` object is destroyed, its associated `SleepCycle` instances are also destroyed,
+ reflecting a "whole part" relationship.
+
+* `SleepCycleList` class
+ * Overview
+ * The `SleepCycleList` class contains all sleep cycles added by the user.
+ * Attributes:
+ * `totalHrsSlept`: Accumulated number of hours slept from all sleep cycles.
+ * `numberOfCycles`: Number of sleep cycles in sleepCycleList.
+ * Methods:
+ * `listSleepCycles()`: List out all sleep cycles.
+ * `addSleepCycle(SleepCycle sleepCycleToAdd)`: Add sleep cycle.
+ * `updateSleepCycle(LocalDate date, double newHours)`: Change hours slept for specific date.
+ * `getSleepCycle(LocalDate date)`: Get number of hours slept for specific date.
+ * `deleteSleepCycle(LocalDate date)`: Delete sleep cycle for specific date.
+ * `deleteSleepCyclesBefore(LocalDate date)`: Delete sleep cycle before specific date.
+ * `deleteSleepCyclesBetween(LocalDate startDate, LocalDate endDate)`: Delete sleep cycles between 2 dates.
+ * `getNumberOfCycles()`: Get number of sleep cycles in sleepCycleList.
+ * `getTotalHrsSlept()`: Get total number of hours slept.
+ * `getSleepCycleList`: Get list of sleep cycles.
+ * Dependencies:
+ * Ui: Utilized for user interface interactions
+ * UML Notes:
+ * `SleepTracker` contains a single instance of `SleepCycleList`.
+ * `SleepCycleList` may contain 0 or more instances of `SleepCycle`.
+ * It relies on `Ui` class for user interaction.
+
+* `SleepCommandParser` class
+ * Overview:
+ * Parses sleep-related commands and create different sleep command objects based on user input.
+ * Method:
+ * determineSleepCommand(SleepTracker sleepTracker, String commandArgs)
+
+* Sleep command classes
+ * `AddSleepCommand`: Add a sleep cycle.
+ * Command format: `sleep add`
+ * `DeleteSleepCommand`: Delete sleep cycles.
+ * Delete sleep cycle matching date command format: `sleep delete /date `
+ * Delete sleep cycle before date command format: `sleep delete /before `
+ * Delete sleep cycles between 2 dates command format: `sleep delete /from `
+ * `GetSleepCommand`: Get number of hours slept on a specific date.
+ * Command format: `sleep get `
+ * `ListSleepcommand`: Get information on all the sleep.
+ * Command format: `sleep list`
+ * `SaveSleepCommand`: Save current sleep cycles added/deleted/updated into a text file.
+ * Command format:`sleep save`
+ * `UpdateSleepCommand`: Change number of hours slept on a specific date.
+ * Command format: `sleep update /new `
+ * `HelpSleepCommand`: Help page for sleep commands.
+ * Command format: `sleep help`
+
+##### Sequence Diagram
+
+
+
+* Note that PlaceholderReflectionCommand can refer to any of the reflection commands as mentioned above,
+as all of them follow the same call pattern.
+
+When main starts, scanner and SleepTracker objects are created. Upon receiving user input, the input will first
+be determined if it is a command related to the sleep tracker feature. If it is, it will be further parsed by
+SleepTrackerParser to determine the command. The corresponding sleep command object is then created and is
+returned to Main, where execute will then be called and the corresponding method in SleepTracker is invoked.
+
+
+
+### Focus timer component
+The focus timer component provides users with a countdown timer and a count up timer, which enables the user to set a
+goal to focus entirely on their work. This component aims to allow users to keep track of their time, improving their
+productivity and well-being.
+
+### Design Considerations
+* #### User Design Considerations
+ * Users will be able to choose between a count up timer and a countdown timer.
+ * Users can start, pause, resume, stop the timer at any point in time.
+ * Users will also be able to navigate to other functions while running the timer concurrently.
+ * Error messages will inform users the current status of the timer and reason the error appeared.
+ * A help menu is also provided for users to guide them on how to use the focus timer.
+
+* #### Developer Design Considerations
+ * The `Focus Timer` component is a wrapper class for both `CountupTimer` and `CountdownTimer`, which contains
+utility logic to identify state and manage the different timers.
+ * Modularity: Encapsulate related functionalities within classes to promote re-usability and maintainability.
+ * Exception Handling: Use of custom exceptions to differentiate between various types of errors, and to handle them appropriately with error messages.
+ * Readability and Maintainability: Descriptive naming, robust documentation for code clarity.
+
+
+
+#### Implementation
+
+#### Focus Class Diagram
+
+* `FocusTimer` object
+ * Overview:
+ * The `FocusTimer` class is a facade class that sits between the component internals and users of the component
+ such that all access to the component like countdown timer and count up timer happens through the Facade class.
+ * Attributes:
+ * `countupTimer`: Count up timer object.
+ * `countdownTimer`: Count down timer object.
+ * `timerMode`: Indicates the timer mode to be operating.
+ * Methods:
+ * `getStartTiming()`: Gets the current running state of the timer.
+ * `switchTimer()`: Changes the timer mode between count up and count down timer.
+ * `getPausedStatus()`: Gets the current pause status of the timer.
+ * `setStartTiming()`: Start the timer.
+ * `setStopTiming()`: Stop the timer.
+ * `setPauseTiming()`: Pause the timer.
+ * `setResumeTiming()`: Resume the timer.
+ * `checkTime()`: Get the total time elapsed or time remaining in the timer.
+ * `setDuration()`: Change countdown timer duration.
+ * UML notes:
+ * When a `FocusTimer` object is destroyed, its associated `CountdownTimer` and `CountupTimer` are also destroyed, showcasing a composition relationship.
+
+* `CountupTimer` and `CountdownTimer` object
+ * Dependencies:
+ * `Ui` object: Utilized for user interface interactions.
+
+* Focus timer command classes
+ * `SwitchTimerCommand`: Switch between Count up timer and Count down timer.
+ * Command format: `focus switch`
+ * `StartTimerCommand`: Start the current timer.
+ * Command format: `focus start`
+ * `StopTimerCommand`: Stop the current timer.
+ * Command format: `focus stop`
+ * `SetPauseCommand`: Pause the current timer.
+ * Command format: `focus pause`
+ * `SetResumeCommand`: Resume the current timer.
+ * Command format: `focus resume`
+ * `CheckTimeCommand`: Check the time elapsed/remaining for the current timer, depending on the timer currently in use.
+ * Command format: `focus check`
+ * `SetTimingCommand`: Set the desired timing in minutes for count down timer.
+ * Command format: `focus set [minutes]`
+ * `FocusHelpCommand`: Display a help menu of the focus timer commands.
+ * Command format: `focus help`
+
+#### Focus State transition Diagram
+There are many commands for the focus timer feature. However, some commands logically cannot be executed in cetain
+states. For example, the command `focus pause` cannot be used if the timer hasn't started. Another example would be
+the `focus switch` command to switch between the timer could not be used if the current timer mode is running. To aid
+the understanding of the logic, we will use state transition diagram.
+
+
+* The black circle in the diagram represents the starting point of focus timer.
+* The labels of the arrows represents the commands.
+#### Focus Sequence Diagram
+
+* Note that `PlaceholderFocusCommand` can refer to any of the focus commands as mentioned above, since all of them
+follows the same call pattern.
+When `Main` starts, `scanner` and `FocusTimer` objects are created. Upon receiving user input, the input will first be
+determined if it is a command related to the focus timer feature.
+If it is, it will be further parsed by `FocusCommandParser` to determine the command.
+The corresponding focus command object is then created and is returned to `Main`, where `execute` will then be called
+and the corresponding method in `FocusTimer` is invoked.
+
+
+### Fitness Motivator component
+#### Description
+The Fitness Motivator provides users with a list of exercises, and give these users the ability to add and track their
+fitness goals. This component aims to contribute to the goal of improving the user's wellness, mainly their physical
+well-being.
+
+#### Design Considerations
+
+- #### User Design Considerations
+ - Users are able to generate a list of 5 different exercises that target 5 different parts of the body: The arms, chest, abs, back and legs. The list is randomly generated each time, to allow for the mixing of exercises.
+ - Users can also choose to generate exercises that generate a single part of the body, should they choose to target that part of the body for exercise.
+ - Users are given the freedom of adding, editing and deleting exercises to the list, to increase the number of different exercises that can be done.
+ - What good are exercises if there is no way of keeping track of progress? The goal tracker does this by generating 5 exercises for the user to do that day. It then allows the user to mark the exercise as done or not done, along with a progress bar for the user to track their progress.
+ - Error messages helps guide the user with usage of the fitness tracker, to ensure ease of usage.
+- #### Developer Design Considerations
+ - _Modularity_: All related classes are grouped together into packages. Command parsers are placed in the parser package, with individual command execution further placed into the fitnesscommands package. The execution of fitness logic are all grouped into the fitness package.
+ - _Abstraction_: The command interface is used to specify methods to be implemented in every single exercise command, creating a pre-written template and behaviour for all command classes.
+ - _Encapsulation_: The usage of private attributes and the use of helper methods help ensure data integrity. Get methods in the Exercise class and ExerciseList class ensure that the data is manipulated in the way that was intended by the developer and thus protecting the data.
+ - _Inheritance_: Commands with similar classes utilise inheritance to reduce code repetition of similar methods.
+ - _Exception Handling_: Exception Handling prevents the code from reaching an unknown or unpredictable state, which could break the program.
+ - _Design Pattern_: To Be Continued
+ - _Code Readability_: Proper coding convention, Java Docs and comments were added for clarity so that other developers can more easily review our code.
+
+
+
+
+#### Implementation
+#### Class Diagram
+
+
+
+Note that certain details described below have been omitted from Class Diagram for simplicity and to improve readability.
+- `FitnessMotivator` Class
+ - Overview
+ - The `FitnessMotivator` class manages fitness related operations.
+ - Attributes:
+ - `DATA_FILE_PATH`: A string that represents the path to the save file for the fitness motivator
+ - `GOALS_FILE_PATH`: A string that represents the path to the save file for the fitness motivator
+ - `REQUIRED_NUM_OF_PARAMETERS`: The number of parameters needed for the `add` command.
+ - `allExercise`: An instance of `ExerciseList`.
+ - `dailyGoals`: An instance of `ExerciseGoalList`.
+ - Methods:
+ - `fiveRandomExercises()`: Generates five random integers within the index of each `ExerciseType`, then returns an array of exercises, where each exercise belongs to a different `ExerciseType`.
+ - `getExercises()`: Uses `fiveRandomExercises()`, then prints the exercises generated.
+ - `getTypeExercises(ExerciseType type)`: Prints all the exercises belonging to the queried `ExerciseType`.
+ - `addExercises(String[] commandArgs)`: Add the user-specified exercise into `allExercises`.
+ - `deleteExercises(String[] commandArgs)`: Delete the user-specified exercise by using its `ExerciseType` and index to remove it from the list.
+ - `newGoals()`: Uses `fiveRandomExercises()`, then converts them to `ExerciseGoal` objects and adds them to the dailyGoals list.
+ - `goalStatus()`: If the list of goals is not empty, it prints out all the currently set `ExerciseGoal` and its status, otherwise it shows a message that says there are no goals.
+ - `toggleGoal(int index)`: Toggles the status of the user-specified `ExerciseGoal` based on its index.
+ - `printHelp()`: Prints out a list of commands and briefly describes what each command does.
+ - Dependencies:
+ - Ui: Utilised for user interface interactions.
+ - Enum ExerciseType: Utilised to allow only specified types of exercises.
+ - UiMessageConstants: A class with static string constants, where each string stores a different message for printing.
+ - UML Notes:
+ - `FitnessMotivator` class only contains one `ExerciseList`, and one `ExerciseGoalList`.
+- `ExerciseList` Class
+ - Overview:
+ - The `ExerciseList` class directly manipulates the list of `Exercises`, and provides methods to do so.
+ - Attributes:
+ - `allExercises`: A private instance of an `ArrayList` of `Exercises`.
+ - Methods:
+ - `ExerciseList()`: A public constructor method, it checks if a local save file exists. If it does not, it creates a new file and initialises it with data, otherwise it will simply load the file.
+ - `initialiseSingleList(String[] list, ExerciseType type)`: A private helper method used to read an array of strings and convert it into exercises to be added into the list.
+ - `initialiseData()`: A private helper method used to initialise all 5 list by calling `initialiseSingleList` five times.
+ - `parseData(ArrayList data)`: A private helper method used to further process the `ArrayList` of strings read by the Storage class.
+ - `add(Exercise exercise)`: A public helper method used to add an `Exercise` object into `allExercises`.
+ - `get(ExerciseType type, int index)`: A public helper method used to query for an `Exercise` Object that matches the n-th `Exercise` with the matching `ExerciseType`, where n = index.
+ - `getType(ExerciseType type)`: A public helper method used to query for all of the `Exercise` objects that match the `ExerciseType`.
+ - `size(ExerciseType type)`: A public helper method that returns the total number of `Exercise` objects that match the `ExerciseType`.
+ - `newExercise(String[] parameters)`: A public helper method that creates an `Exercise` Object from an array of strings.
+ - `findExercise(ExerciseType type, String nameQuery)`: A public helper method that searches for an `Exercise` object within `allExercises` using `ExerciseType` and a string which represents the name being searched.
+ - `remove(Exercise exercise)`: A public helper method that removes a specified `Exercise` Object from `ExerciseList`.
+ - Dependencies:
+ - Storage: Utilised for persistent memory storage.
+ - Enum ExerciseType: Utilised to allow only specified types of exercises.
+ - ExerciseBank: Utilised for storage of initialisation data.
+ - UML Notes:
+ - `ExerciseList` contains at least 25 instances of `Exercise`.
+ - When `FitnessMotivator` is destroyed, the `ExerciseList` instance is destroyed, reflecting a "whole-part" relationship.
+ - `ExerciseList` is a parent class to `ExerciseGoalList`.
+- `Exercise` Class
+ - Overview:
+ - The `Exercise` class stores the basic data of each exercise, such as its name, its type and the number of sets and reps to do.
+ - Attributes:
+ - `exerciseName`: A private string storing the name of the exercise.
+ - `exerciseType`: A private `ExerciseType` object storing one of five types of exercises.
+ - `sets`: A private string storing the number of sets to be done.
+ - `reps`: A private string storing the number of reps to be done.
+ - Methods:
+ - `getType()`: A public helper method to obtain the `ExerciseType` of the exercise.
+ - `getExerciseName()`: A public helper method to obtain the name of the exercise.
+ - `getSets()`: A public helper method to get the number of sets to be done per exercise.
+ - `getReps()`: A public helper method to get the number of reps to be done per exercise.
+ - `toString()`: An overriden public method used to specify the string format of the `Exercise` object.
+ - Dependencies:
+ - Enum ExerciseType: Utilised to allow only specified types of exercises.
+ - UML Notes:
+ - `ExerciseList` contains at least 25 instances of `Exercise`.
+ - When `ExerciseList` or `FitnessMotivator` is destroyed, the `Exercise` instances are destroyed as well, reflecting a "whole-part" relationship.
+ - `Exercise` is the parent class of `ExerciseGoal`.
+- `ExerciseBank` Class
+ - Overview:
+ - The `ExerciseBank` class stores a collection of static string constants for initialisation.
+ - Attributes:
+ - `INIT_ARM_EXERCISES` : A public static array of `String` storing arm exercises, along with the exercise name, sets and reps to be done.
+ - `INIT_CHEST_EXERCISES`: A public static array of `String` storing chest exercises, along with the exercise name, sets and reps to be done.
+ - `INIT_ABS_EXERCISES`: A public static array of `String` storing abs exercises, along with the exercise name, sets and reps to be done.
+ - `INIT_BACK_EXERCISES`: A public static array of `String` storing back exercises, along with the exercise name, sets and reps to be done.
+ - `INIT_LEGS_EXERCISES`: A public static array of `String` storing leg exercises, along with the exercise name, sets and reps to be done.
+ - UML Notes:
+ - Used exclusively by `ExerciseList`.
+- `ExerciseGoalList` Class
+ - Overview:
+ - The `ExerciseGoalList` class inherits from the class `ExerciseList`, but storing `ExerciseGoal` objects instead of `Exercise` objects. It also implements additional methods.
+ - Attributes:
+ - `NUMBER_OF_GOALS`: A private static constant integer value representing the maximum number of goals in the `ExerciseGoalList`.
+ - `goals`: A private `ArrayList` of `ExerciseGoal` objects.
+ - Methods:
+ - `parseData(ArrayList data)`: A private helper method used to further process the `ArrayList` of strings read by the Storage class. In this class, the data is initialised into `ExerciseGoal` Objects instead of `Exercise` Objects.
+ - `isEmpty()`: A public helper method used to check if the `ExerciseGoalList` is empty.
+ - `clear()`: A public helper method used to reset the `ExerciseGoalList` by deleting all `ExerciseGoal` objects within.
+ - `findExercise(int index)`: A public helper method used to find an `ExerciseGoal` based on its index.
+ - `saveGoals()`: A public helper method used to save the current list to a local storage.
+ - `newExercise(String[] parameters)`: An overriden public method from the parent class, it is used to create a new `ExerciseGoal` object, with its `isDone` attribute initialised to `false`.
+ - `add(Exercise exercise, boolean isDone)`: A public method overloaded from the parent class, it adds a pre-existing `Exercise`, converts it with a declared `isDone` parameter, before adding it into the list and saving locally.
+ - `toString()`: An overriden public method used to specify the string format of the `ExerciseGoalList` object.
+ - Dependencies:
+ - Storage: Utilised for persistent memory storage.
+ - Enum ExerciseType: Utilised to allow only specified types of exercises.
+ - UML Notes:
+ - `ExerciseGoalList` is the child class of `ExerciseList`.
+ - When `ExercisegoalList` or `FitnessMotivator` is destroyed, the `ExerciseGoal` instances are destroyed as well, reflecting a "whole-part" relationship.
+ - `ExerciseGoalList` contains either 5 or 0 instances of `Exercise`.
+- `ExerciseGoal` Class
+ - Overview:
+ - The `ExerciseGoal` class inherits from the class `Exercise`, but adds an attribute to store the completion status of the exercise goal.
+ - Attributes:
+ - All attributes of the `Exercise` class.
+ - `isDone`: A private boolean storing the completion state of the `ExerciseGoal` object.
+ - Methods:
+ - `toggle()`: A public helper method used to toggle the state of the `ExerciseGoal` object.
+ - `toString()`: An overriden public method used to specify the string format of the `ExerciseGoal` object.
+ - Dependencies:
+ - Enum ExerciseType: Utilised to allow only specified types of exercises.
+ - UML Notes:
+ - `ExerciseGoal` is the child class of `Exercise`.
+ - `ExerciseGoalList` contains either 5 or 0 instances of `Exercise`.
+ - When `ExercisegoalList` or `FitnessMotivator` is destroyed, the `ExerciseGoal` instances are destroyed as well, reflecting a "whole-part" relationship.
+- `UiMessageConstants` Class
+ - Overview:
+ - The `UiMessageConstants` class stores all constant `String` variables used in the various printing methods in `FitnessMotivator`.
+ - Attributes:
+ - `NEW_GOAL_MESSAGE`: A public static `String` constant storing the new goal command message.
+ - `EMPTY_GOAL_MESSAGE`: A public static `String` constant storing the empty goal message.
+ - `GOAL_MESSAGE`: A public static `String` constant storing the goal command message.
+ - `GOAL_STATUS_MESSAGE`: A public static `String` constant storing the goal status message.
+ - `HELP_MESSAGE`: A public static `String` constant storing the help message.
+ - `ADD_EXERCISE_MESSAGE`: A public static `String` constant storing the add exercise message.
+ - `DELETE_EXERCISE_MESSAGE`: A public static `String` constant storing the delete exercise message.
+ - `HELP_MENU_INSTRUCTIONS`: A public static `String` constant storing all possible commands and a brief description of each command.
+ - UML Notes:
+ - Used exclusively by `FitnessMotivator`.
+- `ExerciseType` Enumeration
+ - Overview:
+ - The `ExerciseType` enumeration restricts the number of different types of exercises to five parts of the body, for easier sorting and management of all exercises.
+ - Attributes:
+ - `ARMS`: Exercises that work the Arms
+ - `CHEST`: Exercises that work the Chest
+ - `ABS`: Exercises that work the Abs
+ - `BACK`: Exercises that work the Back
+ - `LEGS`: Exercises that work the Legs
+ - Methods:
+ - `toString()`: An overriden public method used to specify the string format of the `ExerciseType` Enumeration.
+ - Dependencies:
+ - Does not require any dependencies.
+ - UML Notes:
+ - It is used everywhere, as every feature in the FitnessMotivator uses/parses `Exercise` objects, which will always use `ExerciseGoal` objects.
+- Fitness command classes
+ - `GetExercisesCommand`:
+ - Without parameters, the command retrieves 5 random exercises from each `ExerciseType` and prints it.
+ - Command format: `fitness get`
+ - With parameters, the command retrieves all the exercises from the specified `ExerciseType` and prints it.
+ - Command format: `fitness get arms`
+ - `AddExerciseCommand`: Add a user specified exercise into the list.
+ - CommandFormat: `fitness add /type /name /sets /reps `
+ - `DeleteExerciseCommand`: Deletes a user specified `Exercise` from the list.
+ - CommandFormat: `fitness delete `
+ - `GoalExerciseCommand`:
+ - Without parameters, the command tries to retrieve the status of the list of `ExerciseGoal`.
+ - Command format: `fitness goal`
+ - With parameters, there are only 2 possible parameters.
+ - This command creates or overwrites the existing goals with new goals.
+ - Command format: `fitness goal new`
+ - This command marks/unmarks the `ExerciseGoal` as completed or uncompleted.
+ - Command format: `fitness goal `
+ - `HelpExerciseCommand`: Prints a list of executable commands for Fitness Motivator
+ - Command format: `fitness help`
+
+##### Sequence Diagram
+
+
+
+- Note that `PlaceholderFitnessCommand` can refer to any of the fitness commands mentioned above, as all of them follow the same call pattern.
+
+When `Main` starts, `scanner` and `FitnessMotivator` objects are created. Upon receiving user input, the input will first be determined if it is a command related to the habit tracker feature. if it is, it will be further parsed by `FitnessCommandParser` to determine the command. The corresponding fitness command object is then created and is returend to `Main`, where `execute()` will then be called and the corresponding method in `FitnessMotivator` is invoked.
+
+
+
+## Appendix: Requirements
## Product scope
### Target user profile
-{Describe the target user profile}
+* Wellness360 is a wellness app.
+* It is meant for stressed Engineering Students who prefer CLI over GUI and want to keep track of their overall wellbeing.
### Value proposition
-{Describe the value proposition: what problem does it solve?}
+* Our app offers comprehensive tracking and management tools, providing personalized support to alleviate stress and enhance overall well-being.
+* It can help you take control of your mental and physical health effortlessly, so you can focus on your studies with peace of mind.
## User Stories
-|Version| As a ... | I want to ... | So that I can ...|
-|--------|----------|---------------|------------------|
-|v1.0|new user|see usage instructions|refer to them when I forget how to use the application|
-|v2.0|user|find a to-do item by name|locate a to-do without having to go through the entire list|
+| Version | As a ... | I want to ... | So that I can ... |
+|---------|----------|---------------------------------------------------------------------------------------|---------------------------------------------------------------------|
+| v1.0 | user | load and save data to a text file | have persistent memory storage. |
+| v1.0 | user | enter the command as one long string | so that I can execute different commands tied to different features |
+| v1.0 | user | get new random reflection questions | have a variety of questions to think about. |
+| v1.0 | user | save my favorite questions | refer to them and focus my attention on these questions |
+| v1.0 | user | print my favorite reflections questions | access and read my favorites list. |
+| v1.0 | user | view all sleep cycles that I have added before | see how many hours I have slept thus far. |
+| v1.0 | user | add habits | stay focused and organized in my studies. |
+| v1.0 | user | list out all my habits | keep track of them. |
+| v1.0 | user | update the count for a habit | keep track of how many times I have completed a habit |
+| v1.0 | user | start the focus timer | start the clock to time my focus session. |
+| v1.0 | user | stop the focus timer | see the total duration of the entire focus session. |
+| v1.0 | user | see exercise tips | know what exercises I can do to stay fit. |
+| v2.0 | user | have daily exercise goals | track my progress |
+| v2.0 | user | add custom exercises | view them later on |
+| v2.0 | user | resume the timer | continue my existing timer after using the command pause. |
+| v2.0 | user | have access to a menu option | know how to use the reflection feature if I am lost. |
+| v2.0 | user | remove reflection questions that are no longer relevant to me from my favourites list | focus on the questions that matter. |
+| v2.0 | user | sort the habit tracker list based on the habits' priority | easily view the habits with higher priority. |
+| v2.0 | user | set different priority levels for my habits | prioritize my time on certain habits. |
+| v2.0 | user | pause the timer after starting it | pause during breaks or an emergency and resume it afterwards. |
+| v2.0 | user | set the desired duration for countdown timer | focus for that specific timing. |
+| v2.0 | user | use a help command to view the format of the various habit tracker commands | utilize the habit tracker with ease. |
+| v2.0 | user | load and save my habits to a local text file | access it again without losing the data. |
+| v2.0 | user | delete habits from the habit tracker | remove habits that I no longer want to track. |
+| v2.0 | user | check the total time elapsed | keep track of how long the session have been running. |
+| v2.0 | user | start a countdown timer | set a specific timing to focus on my tasks. |
+| v2.0 | user | delete sleep cycles | remove sleep cycles that I do not want to track. |
+| v2.0 | user | add custom exercises | view them later on. |
+| v2.0 | user | delete exercises from the fitness motivator | keep the list of exercises updated. |
+| v2.0 | user | create exercise goals | work towards a daily goal. |
+| v2.0 | user | mark exercise goals | keep track of what goals are done and what goals are not. |
+
## Non-Functional Requirements
-{Give non-functional requirements}
+* Should work on any mainstream OS as long as it has Java 11 or above installed.
+* 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.
## Glossary
-* *glossary item* - Definition
+* Mainstream OS: Windows, Linux, Unix, MacOS
## Instructions for manual testing
-{Give instructions on how to do a manual product testing e.g., how to load sample data to be used for testing}
+### Reflection component
+#### Get reflection questions
+1. Testcase: `reflect get`
+
+ Expected outcome: Get 5 random questions
+ ```
+ ________________________________________________________________________________________________________________
+ Generated Questions:
+ 1. How do you prioritize self-care and well-being in your daily life?
+ 2. Reflect on your communication style. In what ways do you excel, and where do you see room for improvement?
+ 3. What role does creativity play in your life, and how do you nurture it?
+ 4. What steps are you taking to advance your skills and knowledge in your field?
+ 5. Describe a moment when you stepped outside of your comfort zone. What did you discover about yourself?
+ ________________________________________________________________________________________________________________
+ ```
+2. Testcase: `get`
+
+ Expected outcome: Unknown command
+
+ ```
+ ________________________________________________________________________________________________________________
+ ERROR MSG: Unknown Wellness360 command
+ ________________________________________________________________________________________________________________
+ ```
+
+3. Testcase: `get reflect`
+
+ Expected outcome: Unknown command
+
+ ```
+ ________________________________________________________________________________________________________________
+ ERROR MSG: Unknown Wellness360 command
+ ________________________________________________________________________________________________________________
+ ```
+
+4. Testcase: `reflect get 10`
+ Expected outcome: No additional parameters allowed warning
+ ```
+ ________________________________________________________________________________________________________________
+ ERROR MSG: Additional parameters for 'reflect get' command are not allowed.
+ ________________________________________________________________________________________________________________
+ ```
+
+
+#### View list of saved favourite questions
+1. Testcase: Calling `reflect list` on an empty favourites list
+
+ Expected outcome: Empty list message if no questions saved
+ ```
+ ________________________________________________________________________________________________________________
+ No reflection questions saved to favourites
+ ________________________________________________________________________________________________________________
+ ```
+
+2. Testcase: Calling `reflect list` on a non-empty favourites list
+
+ Expected outcome: List of saved questions if list is not empty
+ ```
+ ________________________________________________________________________________________________________________
+ Favourites list:
+ 1. Describe a memorable shared experience with someone you care about. What made it special?
+ 2. Reflect on a time when you faced a significant challenge at work. How did you overcome it?
+ 3. In what ways do you seek to grow and develop within your current role or industry?
+ 4. Describe a recent moment when you felt inspired by something or someone in your environment.
+ ________________________________________________________________________________________________________________
+ ```
+
+3. Testcase: `reflect list 10`
+ Expected outcome: No additional parameters allowed warning
+ ```
+ ________________________________________________________________________________________________________________
+ ERROR MSG: Additional parameters for 'reflect list' command are not allowed.
+ ________________________________________________________________________________________________________________
+ ```
+
+#### Save favourite question
+1. Testcase: Calling `reflect save 1` without generating questions first (`reflect get`)
+
+ Expected outcome: Error message prompting you to generate questions first.
+ ```
+ ________________________________________________________________________________________________________________
+ ERROR MSG: No questions generated yet. Generate questions using 'reflect get' command first.
+ ________________________________________________________________________________________________________________
+ ```
+2. Testcase: Calling `reflect save 1 ` AFTER generating questions first (`reflect get`)
+
+ Expected outcome: save question successful
+
+ ```
+ ________________________________________________________________________________________________________________
+ You:reflect get
+ ________________________________________________________________________________________________________________
+ Generated Questions:
+ 1. Reflect on a time when you took a creative risk. What did you learn from the experience?
+ 2. Reflect on your career goals. Are they still aligned with your passions and values, or have they evolved?
+ 3. Describe a moment when you stepped outside of your comfort zone. What did you discover about yourself?
+ 4. Describe a recent moment when you felt inspired by something or someone in your environment.
+ 5. How do you express gratitude toward the people who enrich your life?
+ ________________________________________________________________________________________________________________
+ You:reflect save 1
+ ________________________________________________________________________________________________________________
+ Got it. Added reflection question to favourites:
+ Reflect on a time when you took a creative risk. What did you learn from the experience?
+ ________________________________________________________________________________________________________________
+ ```
+
+#### Unsave favourite question
+1. Testcase: Calling `reflect unsave 1` on an empty list
+
+ Expected outcome: Error message prompting you to check list of saved questions
+ ```
+ ________________________________________________________________________________________________________________
+ ERROR MSG: Key in valid favourite reflection ID, key in 'reflect list' command to view range of questions in your favourites list.
+ ________________________________________________________________________________________________________________
+ ```
+2. Testcase: Calling `reflect unsave 1` on a non-empty list
+
+ Expected outcome: unsave successful
+ ```
+ ________________________________________________________________________________________________________________
+ Got it. Unsaved reflection question from favourites:
+ Reflect on a time when you took a creative risk. What did you learn from the experience?
+ ________________________________________________________________________________________________________________
+ ```
+
+### Habit Tracker component
+#### Add Habit
+1. Testcase: `habit add`
+
+ Expected outcome: Error message prompting you that the habit description cannot be empty.
+ ```
+ ________________________________________________________________________________________________________________
+ ERROR MSG: Habit Description cannot be left empty.
+ ________________________________________________________________________________________________________________
+ ```
+2. Testcase: `habit add vacuum the floor`
+
+ Expected outcome: add habit successful.
+ ```
+ ________________________________________________________________________________________________________________
+ Great! You have added a new habit:
+ 'vacuum the floor' was successfully added!
+ ________________________________________________________________________________________________________________
+ ```
+
+#### Delete Habit
+1. Testcase: `habit delete /id`
+
+ Expected outcome: Error message prompting you to provide a habit ID.
+ ```
+ ________________________________________________________________________________________________________________
+ ERROR MSG: Please provide a habit ID.
+ Use Format: habit delete
+ ________________________________________________________________________________________________________________
+ ```
+2. Testcase: `habit delete /id w`
+
+ Expected outcome: Error message prompting you to provide a valid habit ID.
+ ```
+ ________________________________________________________________________________________________________________
+ ERROR MSG: Please provide a valid habit ID.
+ ________________________________________________________________________________________________________________
+ ```
+3. Testcase: `habit delete /id 2` with one habit in the list.
+
+ Expected outcome: Error message prompting you to provide a valid habit ID.
+ ```
+ ________________________________________________________________________________________________________________
+ ERROR MSG: Please provide a valid habit ID.
+ ________________________________________________________________________________________________________________
+ ```
+4. Testcase: `habit delete /id 1` with one habit in the list.
+
+ Expected outcome: Habit deleted successfully.
+ ```
+ ________________________________________________________________________________________________________________
+ Got it! I've removed this habit:
+ [LOW] vacuum the floor [count: 0]
+ Now you have 0 habits left in the list.
+ ________________________________________________________________________________________________________________
+ ```
+
+#### List habits
+1. Testcase: `habit list` with empty list.
+
+ Expected outcome: Show empty list message.
+ ```
+ ________________________________________________________________________________________________________________
+ Here is the list of all your habits!
+
+ ________________________________________________________________________________________________________________
+ ```
+2. Testcase: `habit list` with 2 habits in the list.
+
+ Expected outcome: Prints a list of current habits.
+ ```
+ ________________________________________________________________________________________________________________
+ Here is the list of all your habits!
+ 1. [LOW] vacuum the floor [count: 0]
+ 2. [LOW] complete leetcode daily question [count: 0]
+ ________________________________________________________________________________________________________________
+ ```
+
+#### Update habit count
+1. Testcase: `habit update` or `habit update /id` or `habit update /id /by`
+
+ Expected outcome: Error message displaying incorrect formatting.
+ ```
+ ________________________________________________________________________________________________________________
+ ERROR MSG: Incorrect update command formatting
+ Use Format: habit update /id /by
+ Note: for , use '+1' to increase by 1, '-1' to decrease by 1
+ ________________________________________________________________________________________________________________
+ ```
+2. Testcase: `habit update /id 3 /by +2` with 2 habits in the list.
+
+ Expected outcome: Error message prompting user to provide valid habit ID.
+
+ ```
+ ________________________________________________________________________________________________________________
+ ERROR MSG: Please provide a valid habit ID.
+ ________________________________________________________________________________________________________________
+ ```
+3. Testcase: `habit update /id 1 /by -1` with at least 1 habit in the list, and a zero habit count for the first habit.
+
+ Expected outcome: Error message informing user to not decrement habit count to below zero.
+
+ ```
+ ________________________________________________________________________________________________________________
+ ERROR MSG: You cannot decrement a habit count to below zero
+ ________________________________________________________________________________________________________________
+ ```
+4. Testcase: `habit update /id 1 /by +1` with at least 1 habit in the list, and a zero habit count for the first habit.
+
+ Expected outcome: Increase habit count successfully.
+
+ ```
+ ________________________________________________________________________________________________________________
+ Good Job! You have completed your habit!
+ The count for your habit has been updated:
+ 1. [LOW] vacuum the floor [count: 1]
+ ________________________________________________________________________________________________________________
+ ```
+
+#### Set habit priority level
+1. Testcase: `habit set` or `habit set /id` or `habit set /id /priority`
+
+ Expected outcome: Error message displaying incorrect formatting.
+ ```
+ ________________________________________________________________________________________________________________
+ ERROR MSG: Incorrect set priority command formatting
+ Use Format: habit set /id /priority
+ Note: for , there are 3 levels --> low, med, high
+ ________________________________________________________________________________________________________________
+ ```
+2. Testcase: `habit set /id 3 /priority low` with 2 habits in the list.
+
+ Expected outcome: Error message prompting user to provide valid habit ID.
+
+ ```
+ ________________________________________________________________________________________________________________
+ ERROR MSG: Please provide a valid habit ID.
+ ________________________________________________________________________________________________________________
+ ```
+3. Testcase: `habit set /id 1 /priority very high` with at least 1 habit in the list.
+
+ Expected outcome: Error message displaying invalid priority level.
+
+ ```
+ ________________________________________________________________________________________________________________
+ ERROR MSG: Invalid priority level!
+ ________________________________________________________________________________________________________________
+ ```
+4. Testcase: `habit set /id 1 /priority high` with at least 1 habit in the list.
+
+ Expected outcome: Priority level set to high successfully.
+
+ ```
+ ________________________________________________________________________________________________________________
+ The priority for your habit has been updated:
+ 1. [HIGH] vacuum the floor [count: 0]
+ ________________________________________________________________________________________________________________
+ ```
+
+#### Sort habit list according to priority
+1. Testcase: `habit sort` without any habits in the list.
+
+ Expected outcome: Message informing user that he have no habits to sort.
+ ```
+ ________________________________________________________________________________________________________________
+ You have no habits to sort.
+ ________________________________________________________________________________________________________________
+ ```
+
+2. Testcase: `habit sort` with at least 1 habit in the list.
+
+ Expected outcome: habit sorted successfully.
+ ```
+ ________________________________________________________________________________________________________________
+ Habits have been sorted according to priority.
+ Use `habit list` to view the updated list.
+ ________________________________________________________________________________________________________________
+ ```
+
+### Sleep Tracker component
+#### Add Sleep Cycle
+1. Testcase: `sleep add`
+
+ Expected outcome: Error message prompting proper format use
+ ~~~
+ ________________________________________________________________________________________________________________
+ ERROR MSG: Please use proper format:
+ sleep add /date
+ ________________________________________________________________________________________________________________
+ ~~~
+
+2. Testcase: `sleep add -7.5 /date 07/04/2024`
+
+ Expected outcome: Error message prompting hours must be between valid range
+ ~~~
+ ________________________________________________________________________________________________________________
+ ERROR MSG: Number of hours must be between 0 and 24
+ E.g: 7.5
+ ________________________________________________________________________________________________________________
+ ~~~
+
+3. Testcase: `sleep add 12 /date <>`
+
+ Expected outcome: Error message prompting date must be earlier than today's date
+ ~~~
+ ________________________________________________________________________________________________________________
+ ERROR MSG: U can only add sleep cycles before today's date
+
+ ________________________________________________________________________________________________________________
+ ~~~
+
+4. Testcase: `sleep add 13 /date 07/04/2024`
+
+ Expected outcome: Success add message
+ ~~~
+ ________________________________________________________________________________________________________________
+ --- SleepCycle for 07/04/2024 has been added ---
+ ________________________________________________________________________________________________________________
+ ~~~
+
+#### Get Sleep Cycle
+1. Testcase: `sleep get <>`
+
+ Expected outcome: Error message to indicate sleep cycle not recorded in sleep list
+ ~~~
+ ________________________________________________________________________________________________________________
+ No entry found for the date.
+ ________________________________________________________________________________________________________________
+ ~~~
+
+2. Testcase: `sleep get 07/04/2024`
+
+ Expected outcome: Success get message
+ ~~~
+ ________________________________________________________________________________________________________________
+ Hours slept on 07/04/2024: 7.0
+ ________________________________________________________________________________________________________________
+ ~~~
+
+#### List Sleep Cycles
+
+1. Testcase: `sleep list <>`
+
+ Expected outcome: Error message to prompt proper sleep list format usage
+ ~~~
+ ________________________________________________________________________________________________________________
+ ERROR MSG: Please use proper format:
+ sleep list
+ ________________________________________________________________________________________________________________
+ ~~~
+
+2. Testcase: `sleep list`
+
+ Expected outcome: Success sleep list message
+ ~~~
+ ________________________________________________________________________________________________________________
+ Total hrs slept: 7.0
+ 1. 07/04/2024: 7.0
+ ________________________________________________________________________________________________________________
+ ~~~
+
+#### Update Sleep Cycle
+
+1. Testcase: `sleep update <> /new 5.5`
+
+ Expected outcome: Error message to indicate date not tracked
+ ~~~
+ ________________________________________________________________________________________________________________
+ No entry found for the date.
+ ________________________________________________________________________________________________________________
+ ~~~
+
+2. Testcase: `sleep update 07/04/2024 /new -4.5`
+
+ Expected outcome: Error message to indicate improper hours used
+ ~~~
+ ________________________________________________________________________________________________________________
+ ERROR MSG: Number of hours must be between 0 and 24
+ E.g: 7.5
+ ________________________________________________________________________________________________________________
+ ~~~
+
+3. Testcase: `sleep update 07/04/2024 /new 5.5`
+
+ Expected outcome: Success update sleep message
+ ~~~
+ ________________________________________________________________________________________________________________
+ Hours of sleep for 07/04/2024 has been updated from 7.0 to 5.5
+ ________________________________________________________________________________________________________________
+ ~~~
+
+#### Delete Sleep Cycle
+1. Testcase: `sleep delete`
+
+ Expected outcome: Error message to prompt proper delete sleep cycle formats
+ ~~~
+ ________________________________________________________________________________________________________________
+ ERROR MSG: Please use proper format:
+ sleep delete /date
+ OR
+ sleep delete /before
+ OR
+ sleep delete /from /to
+ ________________________________________________________________________________________________________________
+ ~~~
+
+2. Testcase: `sleep delete /date <>`
+
+ Expected outcome: Error message to indicate no entry for the date
+ ~~~
+ ________________________________________________________________________________________________________________
+ No entry for sleep cycle on 27/01/2001
+ ________________________________________________________________________________________________________________
+ ~~~
+
+3. Testcase: `sleep delete /date 05/04/2012`
+
+ Expected outcome: Delete success message
+ ~~~
+ ________________________________________________________________________________________________________________
+ Sleep cycle for 05/04/2012 has been removed from list
+ ________________________________________________________________________________________________________________
+ ~~~
+
+4. Testcase: `sleep delete /before 27/01/2001`
+
+ Expected outcome: Shows number of Sleep cycles deleted
+ ~~~
+ ________________________________________________________________________________________________________________
+ A total of 0 sleep cycles have been deleted
+ ________________________________________________________________________________________________________________
+ ~~~
+
+5. Testcase: `sleep delete /from 29/01/2002 /to 29/12/2002`
+
+ Expected outcome: Error message to indicate invalid date range
+ ~~~
+ ________________________________________________________________________________________________________________
+ ERROR MSG: Start date must be before end date
+ ________________________________________________________________________________________________________________
+ ~~~
+
+6. Testcase: `sleep delete /from 29/01/2002 /to 29/12/2002`
+
+ Expected outcome: Shows number of sleep cycles deleted
+ ~~~
+ ________________________________________________________________________________________________________________
+ A total of 0 sleep cycles have been deleted
+ ________________________________________________________________________________________________________________
+ ~~~
+
+#### Save Sleep Cycle
+1. Testcase: `sleep save <>`
+
+ Expected outcome: Error message to prompt proper sleep list format usage
+ ~~~
+ ________________________________________________________________________________________________________________
+ ERROR MSG: Please use proper format:
+ sleep save
+ ________________________________________________________________________________________________________________
+ ~~~
+
+2. Testcase: `sleep save`
+
+ Expected outcome: Success sleep list message
+ ~~~
+ ________________________________________________________________________________________________________________
+ Saved list to storage file
+ ________________________________________________________________________________________________________________
+ ~~~
+
+### Focus Timer component
+#### Switch timer mode
+1. Testcase: `focus switch` when no timer is running
+
+ Expected outcome: Message indicating that timer has been switched.
+ ~~~
+ ________________________________________________________________________________________________________________
+ Switched to Count down timer
+ ________________________________________________________________________________________________________________
+ ~~~
+2. Testcase: `focus switch` when timer has been set to run by the user(`focus start`)
+
+ Expected outcome: Error message indicating that a timer is currently running.
+ ~~~
+ ________________________________________________________________________________________________________________
+ ERROR MSG: Unable to change as timer is running.
+ ________________________________________________________________________________________________________________
+ ~~~
+
+#### Start a timer
+1. Testcase: `focus start` when timer is not running
+ Expected outcome: Message indicating that timer has started successfully.
+
+ Count up timer:
+ ~~~
+ ________________________________________________________________________________________________________________
+ Your session has started. Time to grind!
+ ________________________________________________________________________________________________________________
+ ~~~
+ Count down timer:
+ ~~~
+ ________________________________________________________________________________________________________________
+ Countdown timer started!
+ Duration set: 1 minute(s) 0 second(s)
+ ________________________________________________________________________________________________________________
+ ~~~
+2. Testcase: `focus start` when timer is running
+
+ Expected outcome: Error message informing users that timer has already started.
+ ~~~
+ ________________________________________________________________________________________________________________
+ ERROR MSG: Error! Clock has already started.
+ ________________________________________________________________________________________________________________
+ ~~~
+
+#### Stop a timer
+1. Testcase: `focus stop` when timer is currently running
+
+ Expected outcome: Timer stopped successfully.
+
+ Count up timer:
+ ~~~
+ ________________________________________________________________________________________________________________
+ Your focus session has ended.
+ Total time spent: 0 hours, 3 minutes, 42 seconds
+ To start a new session, use focus start
+ ________________________________________________________________________________________________________________
+ ~~~
+ Count down timer:
+ ~~~
+ ________________________________________________________________________________________________________________
+ Countdown timer stopped.
+ ________________________________________________________________________________________________________________
+ ~~~
+2. Testcase: `focus stop` when timer is not running
+
+ Expected outcome: Error message informing users that timer is not running.
+
+ ~~~
+ ________________________________________________________________________________________________________________
+ ERROR MSG: Error! Clock is not running.
+ ________________________________________________________________________________________________________________
+ ~~~
+
+#### Check time for focus timer
+1. Testcase: `focus check` when timer has started
+
+ Expected outcome: Message informing users the total time elapsed or time remaining.
+
+ Count up timer:
+ ~~~
+ ________________________________________________________________________________________________________________
+ Total time elapsed:
+ 0 hours, 0 minutes, 5 seconds
+ ________________________________________________________________________________________________________________
+ ~~~
+ Count down timer:
+ ~~~
+ ________________________________________________________________________________________________________________
+ Remaining time:
+ 0 minutes 56 seconds left.
+ ________________________________________________________________________________________________________________
+ ~~~
+2. Testcase: `focus check` when timer is not running
+
+ Expected outcome: Error message informing users that timer is not running.
+
+ ~~~
+ ________________________________________________________________________________________________________________
+ ERROR MSG: Timer have not started. Please use focus start.
+ ________________________________________________________________________________________________________________
+ ~~~
+
+#### Pause time for focus timer
+1. Testcase: `focus pause` when timer has started
+
+ Expected outcome: Message informing users that timer has been paused.
+
+ Count up timer:
+ ~~~
+ ________________________________________________________________________________________________________________
+ Count up timer paused.
+ ________________________________________________________________________________________________________________
+ ~~~
+ Count down timer:
+ ~~~
+ ________________________________________________________________________________________________________________
+ Timer paused.
+ Remaining time: 0 minutes 56 seconds
+ ________________________________________________________________________________________________________________
+ ~~~
+2. Testcase: `focus pause` when timer is not running
+
+ Expected outcome: Error message informing users that timer has been resumed.
+
+ ~~~
+ ________________________________________________________________________________________________________________
+ ERROR MSG: Timer is already paused or Timer hasn't started.
+ ________________________________________________________________________________________________________________
+ ~~~
+
+#### Resume time for focus timer
+1. Testcase: `focus resume` when timer has started and has been paused.
+
+ Expected outcome: Message informing users the total time elapsed or time remaining.
+
+ Count up timer:
+ ~~~
+ ________________________________________________________________________________________________________________
+ Count up timer resumed
+ ________________________________________________________________________________________________________________
+ ~~~
+ Count down timer:
+ ~~~
+ ________________________________________________________________________________________________________________
+ Countdown timer resumed.
+ ________________________________________________________________________________________________________________
+ ~~~
+2. Testcase: `focus resume` when timer is not running
+
+ Expected outcome: Error message informing users that timer is not running or has already been resumed.
+
+ ~~~
+ ________________________________________________________________________________________________________________
+ ERROR MSG: Timer is already resumed or Timer hasn't started.
+ ________________________________________________________________________________________________________________
+ ~~~
+
+#### Set time for focus timer
+1. Testcase: `focus set 10`
+
+ Expected outcome: Message showing that count down timer has been set to 10 minutes.
+ ~~~
+ ________________________________________________________________________________________________________________
+ Countdown duration has been set to 10 minute(s)
+ ________________________________________________________________________________________________________________
+ ~~~
+2. Testcase: `focus set 0`
+
+ Expected outcome: Error message prompting user to input a duration more than 0.
+ ~~~
+ ________________________________________________________________________________________________________________
+ Duration cannot be less than 1.
+ ________________________________________________________________________________________________________________
+ ~~~
+3. Testcase: `focus set w`
+
+ Expected outcome: Error message prompting user to input a valid duration.
+ ~~~
+ ________________________________________________________________________________________________________________
+ ERROR MSG: Invalid duration.
+ ________________________________________________________________________________________________________________
+ ~~~
+
+### Fitness Motivator component
+#### Get 5 different exercises
+1. Testcase: Calling `fitness get` when the jar is first executed.
+
+ Expected outcome: A list of 5 random different exercises.
+ ```
+ ________________________________________________________________________________________________________________
+ These are some of the exercises you can do! LETS GET STRONK MY G
+
+ 1. Arms: Preacher Curls, 3 sets & 8 reps
+ 2. Chest: Cable Flies, 3 sets & 8 reps
+ 3. Abs: Flutter Kicks, 4 sets & 20 reps
+ 4. Back: Weighted Pull Ups, 3 sets & 6 reps
+ 5. Legs: Leg Extensions, 3 sets & 10 reps
+
+ ________________________________________________________________________________________________________________
+ ```
+
+#### Get a list of specific type of exercises
+1. Testcase: Calling `fitness get ` with an invalid exercise type.
+
+ Expected outcome: Error message prompts you to use the accepted types of exercises.
+ ```
+ ________________________________________________________________________________________________________________
+ ERROR MSG: Hmm...Invalid type of exercise...
+ Only the following exercise types are allowed: Arms, Chest, Abs, Back and Legs!
+ ________________________________________________________________________________________________________________
+ ```
+
+#### Add an exercise
+1. Testcase: Calling `fitness add` with incorrect exercise type in the parameter.
+
+ Expected outcome: Error message prompts you to use the accepted types of exercises.
+ ```
+ ________________________________________________________________________________________________________________
+ ERROR MSG: Hmm...Invalid type of exercise...
+ Only the following exercise types are allowed: Arms, Chest, Abs, Back and Legs!
+ ________________________________________________________________________________________________________________
+ ```
+
+#### Create a new Goal
+1. Testcase: Calling `fitness goal new` when the jar is first executed
+
+ Expected outcome: 5 randomly generated exercise goals with a status icon in front of the respective exercises
+ ```
+ ________________________________________________________________________________________________________________
+ Lets get working on today's exercises!
+
+ 1. [ ] Arms: Skullcrushers, 3 sets & 8 reps
+ 2. [ ] Chest: Diamond Push-up, 3 sets & 15 reps
+ 3. [ ] Abs: Weighted Sit-Ups, 3 sets & 20 reps
+ 4. [ ] Back: Lateral Rows, 3 sets & 8 reps
+ 5. [ ] Legs: Leg Press, 3 sets & 8 reps
+
+ ________________________________________________________________________________________________________________
+ ```
+
+#### Mark a goal as done
+1. Testcase: Calling `fitness goal first` when the goals have been created.
+
+ Expected outcome:
+ ```
+ ________________________________________________________________________________________________________________
+ ERROR MSG: Are you trying to create a new goal? You can try 'goal new'!
+ You can also do 'goal ' to mark and unmark exercises!
+ ________________________________________________________________________________________________________________
+
+ ```
diff --git a/docs/README.md b/docs/README.md
index bbcc99c1e7..5a7e2af4f0 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,8 +1,15 @@
-# Duke
+# Wellness360
-{Give product intro here}
+Wellness360, a wellness app meant for stressed Engineering Students who prefer CLI over GUI and want to keep track of
+their overall well-being.
Useful links:
* [User Guide](UserGuide.md)
* [Developer Guide](DeveloperGuide.md)
* [About Us](AboutUs.md)
+* Project Profile Page
+ * [Ari Lim Ee Lik](team/genexus85.md)
+ * [Damien Wee](team/damiwee.md)
+ * [Davian Kho Yong Quan](team/daviancold.md)
+ * [Lim Jing Hao](team/jinghaoooo.md)
+ * [Ong Kan Wu](team/okw32.md)
diff --git a/docs/UserGuide.md b/docs/UserGuide.md
index abd9fbe891..0195c51455 100644
--- a/docs/UserGuide.md
+++ b/docs/UserGuide.md
@@ -1,42 +1,1132 @@
-# User Guide
+# Wellness360 User Guide
## Introduction
-{Give a product intro}
+Wellness360 is a wellness app. It is meant for stressed Engineering Students who prefer CLI over GUI and want to keep track of their overall wellbeing. Our app offers comprehensive tracking and management tools, providing personalized support to alleviate stress and enhance overall well-being. It can help you take control of your mental and physical health effortlessly, so you can focus on your studies with peace of mind.
+
+## Table of Contents
+- [Wellness360 User Guide](#wellness360-user-guide)
+ - [Starting Wellness360](#quick-start)
+ - [Features](#features)
+ - [Command Format](#features)
+ - [Usage](#usage)
+ - Reflection Manager
+ - [`reflect get` - Get reflection questions](#get-reflection-questions-reflect-get)
+ - [`reflect save` - Save favourite reflection question](#save-favourite-reflection-question-reflect-save)
+ - [`reflect unsave` - Unsave favourite reflection question](#unsave-favourite-reflection-question-reflect-unsave)
+ - [`reflect list` - View favourite reflection questions](#view-favourite-reflection-questions-reflect-list)
+ - [`reflect help` - View reflection help menu](#view-reflection-help-menu-reflect-help)
+ - Habit Tracker
+ - [`habit add` - Add a new habit](#add-a-new-habit-habit-add)
+ - [`habit list` - List out all habits](#list-out-all-habits-habit-list)
+ - [`habit update` - Update habit count after completing a habit](#update-habit-count-after-completing-a-habit-habit-update)
+ - [`habit delete` - Delete a habit](#delete-a-habit-habit-delete)
+ - [`habit set` - Set priority of habit](#set-priority-of-habit-habit-set)
+ - [`habit sort` - Sort habit tracker list](#sort-habit-tracker-list-habit-sort)
+ - [`habit help` - View habit tracker help menu](#view-habit-tracker-help-menu-habit-help)
+ - Sleep Tracker
+ - [`sleep add` - Add a new sleep cycle](#add-a-new-sleep-cycle-sleep-add)
+ - [`sleep list` - List out all sleep cycles](#list-out-all-sleep-cycles-sleep-list)
+ - [`sleep get` - Get hours slept on specific date](#get-hours-slept-on-specific-date-sleep-get)
+ - [`sleep update` - Update hours slept on specific date](#update-hours-slept-on-specific-date)
+ - [`sleep delete` - Delete sleep cycles](#delete-sleep-cycles-sleep-delete)
+ - [Delete Sleep Cycle of a specific date](#delete-sleep-cycle-of-a-specific-date)
+ - [Delete Sleep Cycles before a specific date](#delete-sleep-cycles-before-a-specific-date)
+ - [Delete Sleep Cycles within a range of dates](#delete-sleep-cycles-within-a-range-of-dates)
+ - [`sleep save` - Save sleep cycles](#save-sleep-cycles-sleep-save)
+ - [`sleep help` - View sleep tracker help menu](#view-sleep-tracker-help-menu-sleep-help)
+ - Focus Timer
+ - [`focus switch` - Switch focus timer mode](#switch-focus-timer-mode-focus-switch)
+ - [`focus start` - Start a new focus timer](#start-a-new-focus-timer-focus-start)
+ - [`focus stop` - Stop the current focus timer](#stop-the-current-focus-timer-focus-stop)
+ - [`focus pause` - Pause the current focus timer](#pause-the-current-focus-timer-focus-pause)
+ - [`focus resume` - Resume the current focus timer](#resume-the-current-focus-timer-focus-resume)
+ - [`focus check` - Check time for focus timer](#check-time-for-focus-timer-focus-check)
+ - [`focus set` - Set focus time duration](#set-focus-timer-duration-focus-set)
+ - [`focus help` - View focus timer help menu](#view-focus-timer-help-menu-focus-help)
+ - Fitness Motivator
+ - [`fitness get` - Get a pre-loaded list of different exercises](#get-a-list-of-exercises-fitness-get)
+ - [`fitness add` - Add new exercises into the list](#add-exercises-to-the-list-fitness-add)
+ - [`fitness delete` - Delete exercises from the list](#delete-exercises-from-the-list-fitness-delete)
+ - [`fitness goal` - Set exercise goals for the day](#set-exercise-goals-for-the-day-fitness-goal)
+ - [`fitness help` - View Fitness Motivator help menu](#view-fitness-motivator-help-menu-fitness-help)
+ - [`exit` - Exit application](#exit-application-exit)
+ - [Command Summary](#command-summary)
+ - [FAQ](#faq)
-## Quick Start
-{Give steps to get started quickly}
+## Quick Start
1. Ensure that you have Java 11 or above installed.
-1. Down the latest version of `Duke` from [here](http://link.to/duke).
+2. Download the latest version of `Wellness360` from [here](https://github.com/AY2324S2-CS2113-T11-1/tp/releases).
+3. To start `Wellness360` using the `jar` file, go to the containing folder for Wellness360.
+Then, on your terminal of choice, run:
+```
+-$ java -jar Wellness360.jar
+```
-## Features
+## Features
-{Give detailed description of each feature}
+Wellness360 comes with many features for you to manage your well-being using CLI. The 5 main features are Habit
+Tracker, Sleep Tracker, Self Reflection, Focus Timer, and Fitness Motivation.
-### Adding a todo: `todo`
-Adds a new item to the list of todo items.
+## Command Format
+A command has the general structure:
+```
+
+```
+- Arguments and parameters are not compulsory for all commands. Refer to respective feature commands for specifics.
+- Feature, command and arguments are not case-sensitive to make keying in commands more user-friendly.
-Format: `todo n/TODO_NAME d/DEADLINE`
+## Usage
-* The `DEADLINE` can be in a natural language format.
-* The `TODO_NAME` cannot contain punctuation.
+### Get reflection questions: `reflect get`
+Allows user to generate a set of 5 random unique reflection questions from
+a question bank for users to view and reflect on. The questions come
+from 5 main categories: personal growth and development, relationships
+and social connections, career and professional development, health
+and well-being and lastly creativity and personal expression.
+The questions are meant to be randomized for users to think about
+various aspects of their lives. It is not guaranteed to get a
+question from each category. One or more questions may come from the
+same category.
+
+Format:
+```
+reflect get
+```
+* No additional parameters are allowed, otherwise an error message will be shown.
Example of usage:
+```
+reflect get
+```
+Expected outcome:
+Note that the questions are randomized.
+```
+________________________________________________________________________________________________________________
+1. How do you overcome creative blocks or periods of stagnation?
+2. Reflect on a recent professional success. What factors contributed to your achievement?
+3. What are your biggest strengths, and how can you leverage them more effectively in your daily life?
+4. How do you prioritize self-care and well-being in your daily life?
+5. Reflect on a time when you took a creative risk. What did you learn from the experience?
+________________________________________________________________________________________________________________
+```
-`todo n/Write the rest of the User Guide d/next week`
+### Save favourite reflection question: `reflect save`
+Allows user to save reflection question to favourites after viewing generated questions. This allows the user
+to review the question another time. The favourites list is stored in memory as a text file.
+New users will have an empty favourites list file on load, but existing users can load back favourite questions from
+past sessions.
-`todo n/Refactor the User Guide to remove passive voice d/13/04/2020`
+Format:
+```
+reflect save [QUESTION_ID]
+```
-## FAQ
+* Questions that can be saved correspond to the most recent list of generated questions.
+* Users need to generate questions before attempting to save it to favourites, otherwise an error message will prompt user to generate questions first.
+* `QUESTION_ID` only accepts integers between 1 and 5 inclusive as each list of newly generated questions only contains 5 questions.
+* Saving duplicate questions are not allowed, any attempts to do so will result in an error message reminding users that duplicate questions are not allowed.
+
+Example of usage (After generating questions first):
+```
+reflect save 1
+```
+Expected outcome:
+```
+________________________________________________________________________________________________________________
+Got it. Added reflection question to favourites:
+How do you overcome creative blocks or periods of stagnation?
+________________________________________________________________________________________________________________
+```
+
+### Unsave favourite reflection question: `reflect unsave`
+Allows user to unsave reflection question from favourites list if the question is no longer relevant to the user. The favourites list is stored in memory as a text file.
+New users will have an empty favourites list file on load, but existing users can load back favourite questions from
+past sessions.
+
+Format:
+```
+reflect unsave [QUESTION_ID]
+```
+
+* Questions that can be unsaved correspond to the reflection questions favourites list.
+* If user attempts to unsave a question from an empty favourites list, an error message will prompt user to check range of questions in his favourites list again.
+
+Example of usage:
+```
+reflect unsave 1
+```
+Expected outcome:
+```
+________________________________________________________________________________________________________________
+Got it. Unsaved reflection question from favourites:
+How do you overcome creative blocks or periods of stagnation?
+________________________________________________________________________________________________________________
+```
+
+### View favourite reflection questions: `reflect list`
+Allow the user to view favourite reflection questions that have been saved.
+
+Format:
+
+```
+reflect list
+```
+* No additional parameters are allowed, otherwise an error message will be shown.
+
+Example of usage:
+```
+reflect list
+```
+
+Expected outcome:
+```
+________________________________________________________________________________________________________________
+Favourites list:
+1. How do you overcome creative blocks or periods of stagnation?
+2. How do you prioritize self-care and well-being in your daily life?
+3. Reflect on a time when you took a creative risk. What did you learn from the experience?
+________________________________________________________________________________________________________________
+```
+
+### View reflection help menu: `reflect help`
+Allows new users to check what commands are available for reflection feature and their formats.
+
+Format:
+```
+reflect help
+```
+
+* No additional parameters are allowed, otherwise an error message will be shown.
+
+Example of usage:
+```
+reflect help
+```
+
+Expected outcome:
+```
+________________________________________________________________________________________________________________
+Commands for reflection feature:
+1. reflect get: Get 5 random reflection questions
+2. reflect save : Save reflection question by id to favourites list
+3. reflect unsave : Unsave reflection question by id from favourites list
+4. reflect list: Retrieve questions from favourites list
+5. reflect help: Get help menu for reflect commands
+________________________________________________________________________________________________________________
+```
+
+### Add a new habit: `habit add`
+Allow the user to add new habits into the habit tracker.
+
+Format:
+```
+habit add [HABIT_DESCRIPTION]
+```
+
+Example of usage:
+```
+habit add vacuum and mop the floor
+```
+Expected outcome:
+```
+________________________________________________________________________________________________________________
+Great! You have added a new habit:
+ 'vacuum and mop the floor' was successfully added!
+________________________________________________________________________________________________________________
+```
+
+### List out all habits: `habit list`
+Prints a list of all the habits that the user has added into the habit tracker.
+
+Format:
+```
+habit list
+```
+
+* Extraneous parameters will be ignored. For eg, `habit list 123` will be taken as `habit list`
+
+
+Example of usage:
+```
+habit list
+```
+
+Expected outcome:
+```
+________________________________________________________________________________________________________________
+Here is the list of all your habits!
+ 1. [LOW] vacuum and mop the floor [count: 2]
+ 2. [LOW] complete leetcode daily question [count: 3]
+________________________________________________________________________________________________________________
+```
+
+### Update habit count after completing a habit: `habit update`
+Allow the user to update the number of times they have completed a habit. The user will be able to increase the
+count after they have completed the habit during the day. If the user has accidentally increased the count,
+they can decrease the count too.
+
+Format:
+```
+habit update /id [HABIT_ID] /by [INCREMENT_COUNT]
+```
+
+* `HABIT_ID` and `INCREMENT_COUNT` have to be numerical.
+* `INCREMENT_COUNT` can be set to 0, but the habit count will remain the same.
+* The habit count can only be incremented to a maximum of `2147483647`, which represents the maximum range of an
+integer. Any exceeding count will result in an error message due to integer overflow.
+
+Example of usage (increasing count):
+```
+habit update /id 2 /by +1
+```
+* For increasing count, you can omit the positive sign `+` in front, and just type the command as `/by 1`
+
+Expected outcome:
+```
+________________________________________________________________________________________________________________
+Good Job! You have completed your habit!
+The count for your habit has been updated:
+ 2. [LOW] complete leetcode daily question [count: 4]
+________________________________________________________________________________________________________________
+```
+
+Example of usage (decreasing count):
+```
+habit update /id 2 /by -2
+```
+* For decreasing count, you must include a negative sign `-` in front of the count.
+
+Expected outcome:
+```
+________________________________________________________________________________________________________________
+The count for your habit has been updated:
+ 2. [LOW] complete leetcode daily question [count: 2]
+________________________________________________________________________________________________________________
+```
+
+### Delete a habit: `habit delete`
+Delete a habit from the habit tracker.
+
+Format:
+```
+habit delete /id
+```
+
+* `HABIT_ID` have to be numerical.
+
+Example of usage:
+```
+habit delete /id 1
+```
+Expected outcome:
+```
+________________________________________________________________________________________________________________
+Got it! I've removed this habit:
+ [LOW] vacuum and mop the floor [count: 2]
+Now you have 1 habit left in the list.
+________________________________________________________________________________________________________________
+```
+
+### Set priority of habit: `habit set`
+Set the priority of a habit (`HIGH`, `MED`, `LOW`). Priority of habits are initialised at LOW by default.
+
+Format:
+```
+habit set /id /priority
+```
+
+* `HABIT_ID` have to be numerical.
+* For `PRIORITY_LEVEL`, user can set as `HIGH`, `MED` OR `LOW` (not case-sensitive).
+
+Example of usage:
+```
+habit set /id 1 /priority HIGH
+```
+Expected outcome:
+```
+________________________________________________________________________________________________________________
+The priority for your habit has been updated:
+ 1. [HIGH] complete leetcode daily question [count: 4]
+________________________________________________________________________________________________________________
+```
+
+### Sort habit tracker list: `habit sort`
+Sort the habits in the habit tracker list according to the habits' priority.
+Habits with higher priority will be placed higher than those with lower priority.
+
+Format:
+```
+habit sort
+```
+
+* Extraneous parameters will be ignored. For eg, `habit sort 123` will be taken as `habit sort`.
+
+Example of usage:
+```
+habit sort
+```
+Expected outcome:
+```
+________________________________________________________________________________________________________________
+Habits have been sorted according to priority.
+Use `habit list` to view the updated list.
+________________________________________________________________________________________________________________
+```
+
+### View habit tracker help menu: `habit help`
+Allows new users to check what commands are available for habit tracker feature and their formats.
+
+Format:
+```
+habit help
+```
+
+* Extraneous parameters will be ignored. For eg, `habit help 123` will be taken as `habit help`.
+
+Example of usage:
+```
+habit help
+```
+Expected outcome:
+```
+________________________________________________________________________________________________________________
+Commands for habit tracker feature:
+1. habit add : Add a new habit
+2. habit list: List out all existing habits
+3. habit update /id /by : Increase habit count after completing a habit
+4. habit delete /id : Delete a habit
+5. habit set /id /priority : Set priority level for habits (HIGH, MED, LOW)
+6. habit sort: Sort habit list according to priority level
+________________________________________________________________________________________________________________
+```
+
+### Add a new sleep cycle: `sleep add`
+Allow the user to add new sleep Cycles into the sleep tracker. Older sleep cycles will be deleted when total hrs reaches
+the `MAX_VALUE` for `double`.
+
+Format:
+```
+sleep add [HOURS_SLEPT] /date [DATE_SLEPT]
+```
+
+* `HOURS_SLEPT` will be rounded down to nearest 1 d.p
+* `DATE_SLEPT` must be of format dd/MM/yyyy
+
+Example of usage:
+```
+sleep add 7 /date 18/03/2024
+```
+Expected outcome:
+```
+________________________________________________________________________________________________________________
+--- SleepCycle for 18/03/2024 has been added ---
+________________________________________________________________________________________________________________
+```
+
+### List out all sleep cycles: `sleep list`
+Prints a list of all the sleep cycles that the user has added into the sleep tracker.
+
+Format:
+```
+sleep list
+```
+
+Example of usage:
+```
+sleep list
+```
+Expected outcome:
+```
+________________________________________________________________________________________________________________
+Total hrs slept: 15.0
+1. 27/01/2012: 7.0
+2. 30/01/2012: 8.0
+________________________________________________________________________________________________________________
+```
+
+### Get hours slept on specific date: `sleep get`
+Prints number of hours slept on specific date.
+
+Format:
+```
+sleep get [DATE_OF_SLEEP]
+```
+
+* `DATE_OF_SLEEP` must be of format dd/MM/yyyy
+
+Example of usage:
+```
+sleep get 27/01/2012
+```
+Expected outcome:
+```
+________________________________________________________________________________________________________________
+Hours slept on 27/01/2012: 7.0
+________________________________________________________________________________________________________________
+```
+
+### Update hours slept on specific date: `sleep update`
+Updates number of hours slept on specfic date.
+
+Format:
+```
+sleep update [DATE_OF_SLEEP] /new [HOURS_OF_SLEEP]
+```
+
+* `DATE_OF_SLEEP` must be of format dd/MM/yyyy
+
+Example of usage:
+```
+sleep update 27/01/2012 /new 9
+```
+Expected outcome:
+```
+________________________________________________________________________________________________________________
+Hours of sleep for 27/01/2012 has been updated from 7.0 to 9.0
+________________________________________________________________________________________________________________
+```
+
+### Delete sleep cycles: `sleep delete`
+
+#### Delete Sleep Cycle of a specific date:
+
+Format:
+```
+sleep delete /date [DATE_OF_SLEEP]
+```
+
+* `DATE_OF_SLEEP` must be of format dd/MM/yyyy
+
+Example of usage:
+```
+sleep delete /date 27/01/2012
+```
+Expected outcome:
+```
+________________________________________________________________________________________________________________
+Sleep cycle for 27/01/2012 has been removed from list
+________________________________________________________________________________________________________________
+```
+Format:
+
+#### Delete Sleep Cycles before a specific date:
+
+Format:
+```
+sleep delete /before [DATE_OF_SLEEP]
+```
+
+* `DATE_OF_SLEEP` must be of format dd/MM/yyyy
+
+Example of usage:
+```
+sleep delete /date 27/01/2012
+```
+Expected outcome:
+```
+________________________________________________________________________________________________________________
+A total of 2 sleep cycles have been deleted
+________________________________________________________________________________________________________________
+```
+
+#### Delete Sleep Cycles within a range of dates:
+
+Format:
+```
+sleep delete /from [START_DATE] /to [END_DATE]
+```
+
+* `START_DATE` and `END_DATE` must be of format dd/MM/yyyy
+
+Example of usage:
+```
+sleep delete /from 27/01/2012 /to 27/02/2012
+```
+Expected outcome:
+```
+________________________________________________________________________________________________________________
+A total of 5 sleep cycles have been deleted
+________________________________________________________________________________________________________________
+```
+
+### Save sleep cycles: `sleep save`
+Allow user to save sleep cycles in a text file located in `FILE_PATH: data/sleep.txt`
+
+Format:
+```
+sleep save
+```
+
+* The `sleep` and `save` are case-sensitive.
+* Use lower casing for this command.
+
+Example of usage:
+```
+sleep save
+```
+Expected outcome:
+```
+________________________________________________________________________________________________________________
+Saved list to storage file
+________________________________________________________________________________________________________________
+```
+
+### View sleep tracker help menu: `sleep help`
+Allows new users to check what commands are available for sleep tracker feature and their formats.
+
+Format:
+```
+sleep help
+```
+
+Example of usage:
+```
+sleep help
+```
+Expected outcome:
+```
+________________________________________________________________________________________________________________
+Commands for sleep tracker feature:
+1. sleep add [HOURS_SLEPT] /date [DATE_SLEPT]: Add a new sleep cycle
+2. sleep list: List out all sleep cycles
+3. sleep get [DATE_OF_SLEEP]: Get hours slept on a specific date
+4. sleep update [DATE_OF_SLEEP] /new [HOURS_OF_SLEEP]: Updates hours slept on a specific date
+5. sleep delete /date [DATE_OF_SLEEP]: Delete Sleep Cycle of a specific date
+6. sleep delete /before [DATE_OF_SLEEP]: Delete Sleep Cycles before a specific date
+7. sleep delete /from [START_DATE] /to [END_DATE]: Delete Sleep Cycles within a range of dates
+8. sleep save: Allow user to save sleep cycles in a text file located in FILE_PATH: data/sleep.txt
+________________________________________________________________________________________________________________
+```
+
+### Switch focus timer mode: `focus switch`
+Focus timer offers 2 kind of timer for the user. Using `focus switch` command allows user to choose
+between count up timer and count down timer.
+
+Format:
+~~~
+focus switch
+~~~
+
+Expected outcome:
+~~~
+________________________________________________________________________________________________________________
+Switched to Count down timer
+________________________________________________________________________________________________________________
+~~~
+* Outcome depends on the current timer mode.
+* `focus switch` will be disable if a timer is currently running.
+* Extraneous parameters for this command will be ignored. For example: `focus switch 123` will be taken as `focus switch`.
+
+### Start a new focus timer: `focus start`
+Allow the user to start a new focus timer session. The user will be able
+to start the timer whenever they want and the application will keep track of the time.
+In addition, only 1 timer will run at a time, thus multiple uses of `focus start` is not allowed.
-**Q**: How do I transfer my data to another computer?
+Format:
+~~~
+focus start
+~~~
+* Extraneous parameters for this command will be ignored. For example: `focus start 123` will be taken as `focus start`.
-**A**: {your answer here}
+Expected outcome:
+* Count up timer.
+~~~
+________________________________________________________________________________________________________________
+Your session has started. Time to grind!
+________________________________________________________________________________________________________________
+~~~
+
+* Count down timer.
+~~~
+________________________________________________________________________________________________________________
+Countdown timer started!
+Duration set: 1 minute(s) 0 second(s)
+________________________________________________________________________________________________________________
+~~~
+
+### Stop the current focus timer: `focus stop`
+>
NOTE:
+> * Countdown timer will automatically stop when the duration expires.
+>
+Allow users to stop a timer that is currently running. The users will be able to see the total
+time elapsed upon a successful stop.
+
+Format:
+~~~
+focus stop
+~~~
+
+* Extraneous parameters for this command will be ignored. For example: `focus stop 123` will be taken as `focus stop`.
+
+Expected outcome:
+* Count up timer
+~~~
+________________________________________________________________________________________________________________
+Your focus session has ended.
+ Time spent: X hours, X minutes, XX seconds
+To start a new session, use focus start
+________________________________________________________________________________________________________________
+~~~
+
+* Count down timer
+* Timer will automatically stop if the duration expires. However, users will still be able to stop it manually
+~~~
+________________________________________________________________________________________________________________
+3 seconds left
+________________________________________________________________________________________________________________
+________________________________________________________________________________________________________________
+2 seconds left
+________________________________________________________________________________________________________________
+________________________________________________________________________________________________________________
+1 seconds left
+________________________________________________________________________________________________________________
+________________________________________________________________________________________________________________
+Count down timer completed!
+________________________________________________________________________________________________________________
+~~~
+
+>
+ Warning:
+> * The countdown timer will display the above messages when there are 3 seconds left on the timer, which may disrupt user input. Please wait until the timer is up before trying to input new commands into the CLI, as this is part of the countdown timer feature.
+
+### Pause the current focus timer: `focus pause`
+Allow users to pause the timer momentarily while the timer is running.
+
+Format:
+~~~
+focus pause
+~~~
+
+* Extraneous parameters for this command will be ignored. For example: `focus pause 123` will be taken as `focus pause`.
+
+Expected outcome:
+* Count up timer
+~~~
+________________________________________________________________________________________________________________
+Count up timer paused.
+________________________________________________________________________________________________________________
+~~~
+
+* Count down timer
+~~~
+________________________________________________________________________________________________________________
+Timer paused.
+Remaining time: 0 minutes 56 seconds
+________________________________________________________________________________________________________________
+~~~
+### Resume the current focus timer: `focus resume`
+Allow users to resume the paused timer.
+
+* Extraneous parameters for this command will be ignored. For example: `focus resume 123` will be taken as `focus resume`.
+
+Format:
+~~~
+focus resume
+~~~
+
+Expected outcome:
+* Count up timer
+~~~
+________________________________________________________________________________________________________________
+Count up timer resumed
+________________________________________________________________________________________________________________
+~~~
+
+* Count down timer
+~~~
+________________________________________________________________________________________________________________
+Countdown timer resumed.
+________________________________________________________________________________________________________________
+~~~
+
+### Check time for focus timer: `focus check`
+Allow users to check the total time elapsed or total time remaining, depending on the mode of the timer.
+
+Format:
+~~~
+focus check
+~~~
+
+* Extraneous parameters for this command will be ignored. For example: `focus check 123` will be taken as `focus check`.
+
+Expected outcome:
+* Count up timer
+~~~
+________________________________________________________________________________________________________________
+Total time elapsed:
+0 hours, 0 minutes, 8 seconds
+________________________________________________________________________________________________________________
+~~~
+
+* Count down timer
+~~~
+________________________________________________________________________________________________________________
+Remaining time:
+0 minutes 54 seconds left.
+________________________________________________________________________________________________________________
+~~~
+
+### Set focus timer duration: `focus set`
+>
Tip:
+> * Using `focus set` command only affects count down timer.
+>
+
+Allow users to set the desired countdown timer duration in minutes for the session.
+
+Format:
+~~~
+focus set [minutes]
+~~~
+* Input `minutes` must be in numerical form and can be more than 60.
+* Example: *120 minutes implies 2 hours*
+* Input `minutes` cannot be more than `MAX_VALUE: 2,147,483,647`.
+* Extraneous parameters for this command will be ignored. For example: `focus set 10 123` will be taken as `focus set 10`.
+
+Example of usage:
+~~~
+focus set 10
+~~~
+
+Expected outcome:
+~~~
+________________________________________________________________________________________________________________
+Countdown duration has been set to 10 minute(s)
+________________________________________________________________________________________________________________
+
+~~~
+
+### View focus timer help menu: `focus help`
+Allows new users to check what commands are available for focus timer feature and their formats.
+
+Format:
+~~~
+focus help
+~~~
+
+* Extraneous parameters will be ignored. For eg, `focus help 123` will be taken as `focus help`.
+
+Example of usage:
+~~~
+focus help
+~~~
+
+Expected outcome:
+~~~
+________________________________________________________________________________________________________________
+Commands for focus timer feature:
+1. focus switch: Switch between count up timer and count down timer
+2. focus start: Start the timer
+3. focus stop: Stop the timer
+4. focus pause: Pause the timer
+5. focus resume: Resume the timer
+6. focus set [minutes]: Set the desired countdown timer duration in minutes
+________________________________________________________________________________________________________________
+~~~
+
+### Get a list of exercises: `fitness get`
+Prints a list of exercises. If no parameters are specified, 5 different exercises from 5 different types of exercises
+targeting different body parts. If the exercise type is specified, only exercises matching that type will be shown.
+
+Format:
+```
+fitness get [EXERCISE_TYPE]
+```
+
+* Running the command with no parameters will generate different exercises with each repeated command, but there should be one exercise from
+ each type
+* The `[EXERCISE_TYPE]` parameter is optional, and only supports the following types:
+ * Arms, Chest, Abs, Back, Legs (Not Case Sensitive)
+
+Example of usage (getting exercises of a certain type):
+~~~
+fitness get
+~~~
+
+Expected outcome:
+```
+________________________________________________________________________________________________________________
+These are some of the exercises you can do! LETS GET STRONK MY G
+
+1. Arms: Skullcrushers, 3 sets & 8 reps
+2. Chest: Cable Flies, 3 sets & 8 reps
+3. Abs: Crunches, 5 sets & 20 reps
+4. Back: Lateral Rows, 3 sets & 8 reps
+5. Legs: Leg Press, 3 sets & 8 reps
+
+________________________________________________________________________________________________________________
+```
+Example of usage:
+~~~
+fitness get arms
+~~~
+
+Expected outcome:
+```
+________________________________________________________________________________________________________________
+Here are the Arms exercises as requested!
+
+1. Arms: Cable Triceps Push down, 3 sets & 8 reps
+2. Arms: Barbell Curls, 3 sets & 8 reps
+3. Arms: Preacher Curls, 3 sets & 8 reps
+4. Arms: Skullcrushers, 3 sets & 8 reps
+5. Arms: Lateral Raises, 3 sets & 8 reps
+________________________________________________________________________________________________________________
+
+```
+
+
+### Add exercises to the list: `fitness add`
+Allows the user to add their own exercises to the list of exercises.
+
+Format:
+```
+fitness add /type [EXERCISE_TYPE] /name [EXERCISE_NAME] /sets [NUMBER_OF_SETS] /reps [NUMBER_OF_REPS]
+```
+
+* The `[EXERCISE_TYPE]` parameter only supports the following types:
+ * Arms, Chest, Abs, Back, Legs (Not Case Sensitive)
+
+Example of usage:
+```
+fitness add /type Arms /name Tricep Dips /sets 8 /reps 10
+```
+
+Expected outcome:
+```
+________________________________________________________________________________________________________________
+I have added the following exercise into our list!
+Arms: Tricep Dips, 8 sets & 10 reps
+________________________________________________________________________________________________________________
+```
+
+### Delete exercises from the list: `fitness delete`
+Allows the user to delete exercises from the list of exercises.
+
+Format:
+```
+fitness delete [EXERCISE_TYPE] [INDEX]
+```
+
+* The `[EXERCISE_TYPE]` parameter only supports the following types:
+ * Arms, Chest, Abs, Back, Legs (Not Case Sensitive)
+* Use the `fitness get [EXERCISE_TYPE]` command to find out the indexes of the respective exercises
+* The printed list of exercises reflects the list of exercises left that are of the same type as the exercise deleted.
+
+Example of usage:
+```
+fitness delete arms 6
+```
+Expected Outcome:
+```
+________________________________________________________________________________________________________________
+ I have deleted the exercise. Here are the exercises left in the list!
+
+1. Arms: Cable Triceps Push down, 3 sets & 8 reps
+2. Arms: Barbell Curls, 3 sets & 8 reps
+3. Arms: Preacher Curls, 3 sets & 8 reps
+4. Arms: Skullcrushers, 3 sets & 8 reps
+5. Arms: Lateral Raises, 3 sets & 8 reps
+________________________________________________________________________________________________________________
+
+```
+
+### Set exercise goals for the day: `fitness goal`
+Allows the user to create, track and mark a list of exercise goals.
+
+Formats:
+```
+fitness goal
+fitness goal new
+fitness goal [INDEX]
+```
+
+* Running the command with no parameters will generate different exercises goals with each repeated command.
+* Use the `fitness goal` command to find out the indexes of the respective exercises goals.
+* Use the `fitness goal new` command to get a new randomised list of five exercise goals.
+* Running `fitness goal new` will always override any previously set goals.
+* Use the `fitness goal [INDEX]` command to mark/unmark a specific goal.
+* Only `new` and `[INDEX]` are accepted as parameters, where `[INDEX]` is an integer value. The parameters are optional as well.
+
+Example of usage :
+```
+fitness goal
+```
+
+Expected outcome (If there are no current goals):
+```
+________________________________________________________________________________________________________________
+There are no goals set :(
+
+You can set one by doing 'goal new'!
+________________________________________________________________________________________________________________
+```
+
+Expected outcome (If there are goals already set):
+```
+________________________________________________________________________________________________________________
+Lets get working on today's exercises!
+
+1. [ ] Arms: Skullcrushers, 3 sets & 8 reps
+2. [ ] Chest: Wide Arm Push-up, 3 sets & 15 reps
+3. [ ] Abs: Weighted Sit-Ups, 3 sets & 20 reps
+4. [ ] Back: Weighted Pull Ups, 3 sets & 6 reps
+5. [ ] Legs: Calf Raises, 3 sets & 10 reps
+
+________________________________________________________________________________________________________________
+```
+
+Example of usage :
+```
+fitness goal new
+```
+
+Expected outcome:
+```
+________________________________________________________________________________________________________________
+Lets get working on today's exercises!
+
+1. [ ] Arms: Skullcrushers, 3 sets & 8 reps
+2. [ ] Chest: Wide Arm Push-up, 3 sets & 15 reps
+3. [ ] Abs: Weighted Sit-Ups, 3 sets & 20 reps
+4. [ ] Back: Weighted Pull Ups, 3 sets & 6 reps
+5. [ ] Legs: Calf Raises, 3 sets & 10 reps
+
+________________________________________________________________________________________________________________
+```
+
+Example of usage:
+```
+fitness goal 3
+```
+
+Expected outcome:
+```
+________________________________________________________________________________________________________________
+I see there are changes. I hope you are making progress...
+
+1. [ ] Arms: Skullcrushers, 3 sets & 8 reps
+2. [ ] Chest: Wide Arm Push-up, 3 sets & 15 reps
+3. [X] Abs: Weighted Sit-Ups, 3 sets & 20 reps
+4. [ ] Back: Weighted Pull Ups, 3 sets & 6 reps
+5. [ ] Legs: Calf Raises, 3 sets & 10 reps
+
+________________________________________________________________________________________________________________
+```
+
+### View Fitness Motivator help menu: `fitness help`
+Allows the user to see a list of available commands that can be executed under the FitnessMotivator.
+
+Format:
+```
+fitness help
+```
+
+Example of usage:
+```
+fitness help
+```
+Expected Outcome:
+```
+________________________________________________________________________________________________________________
+Here is a list of possible commands you can use with the Fitness Motivator!
+
+1. fitness get: Get 5 random exercises each of different type.
+2. fitness get : Get a full list of exercises belonging to the exercise type
+3. fitness add /type /name /sets /reps :
+add an exercise to the list of exercises
+4. fitness delete : Delete the exercise from the list of exercise.
+The index is based on the index when you run 'fitness get
+5. fitness goal: Retrieves the status of all current goals, if it exists
+6. fitness goal new: Overwrites current goals with new set of goals if it exists,
+otherwise creates a brand new set of goals
+7. fitness goal : Toggle the status of the goal
+8. fitness help: Get help menu for reflect commands
+________________________________________________________________________________________________________________
+```
+
+### Exit application: `exit`
+Allows user to exit Wellness360 application.
+
+Format:
+
+```
+exit
+```
+
+* No additional parameters are allowed, otherwise an error message will be shown.
+
+Example of usage:
+
+```
+exit
+```
+
+Expected outcome:
+```
+________________________________________________________________________________________________________________
+Goodbye! See you again!
+________________________________________________________________________________________________________________
+```
## Command Summary
-{Give a 'cheat sheet' of commands here}
+This section serves as a cheatsheet for commands.
+
+| **Command** | **Description** |
+|---------------------------------------------------------------------------------------------------------|----------------------------------------------|
+| `reflect get` | Get 5 random reflection questions |
+| `reflect save [QUESTION_ID]` | Save favourite reflection question |
+| `reflect unsave [QUESTION_ID]` | Unsave favourite reflection question |
+| `reflect list` | View favourite reflection questions |
+| `reflect help` | View reflection help menu |
+| `habit add [HABIT_DESCRIPTION]` | Add a new habit |
+| `habit list` | List out all habits |
+| `habit update /id [HABIT_ID] /by [INCREMENT_COUNT]` | Update habit count after completing a habit |
+| `habit delete /id [HABIT_ID]` | Delete a habit |
+| `habit set /id [HABIT_ID] /priority [PRIORITY_LEVEL]` | Set priority of habit |
+| `habit sort` | Sort habit tracker list |
+| `habit help` | View habit tracker help menu |
+| `sleep add [HOURS_SLEPT] /date [DATE_SLEPT]` | Add a new sleep cycle |
+| `sleep list ` | List out all sleep cycles |
+| `sleep get [DATE_OF_SLEEP]` | Get hours slept on specific date |
+| `sleep update [DATE_OF_SLEEP] /new [HOURS_OF_SLEEP]` | Update hours slept on specific date |
+| `sleep delete /date [DATE_OF_SLEEP]` | Delete Sleep Cycle of a specific date |
+| `sleep delete /before [DATE_OF_SLEEP]` | Delete Sleep Cycles before a specific date |
+| `sleep delete /from [START_DATE] /to [END_DATE]` | Delete Sleep Cycles within a range of dates |
+| `sleep save` | Save sleep cycles |
+| `sleep help` | View sleep tracker help menu |
+| `focus switch` | Switch focus timer mode |
+| `focus start` | Start a new focus timer |
+| `focus stop` | Stop the current focus timer |
+| `focus pause` | Pause the current focus timer |
+| `focus resume` | Resume the current focus timer |
+| `focus check` | Check time for focus timer |
+| `focus set [minutes]` | Set focus time duration |
+| `focus help` | View focus timer help menu |
+| `fitness get`, `fitness get [EXERCISE_TYPE]` | Get a pre-loaded list of different exercises |
+| `fitness add /type [EXERCISE_TYPE] /name [EXERCISE_NAME] /sets [NUMBER_OF_SETS] /reps [NUMBER_OF_REPS]` | Add new exercises into the list |
+| `fitness delete [EXERCISE_TYPE] [INDEX]` | Delete exercises from the list |
+| `fitness goal`, `fitness goal new`, `fitness goal [INDEX]` | Set exercise goals for the day |
+| `fitness help` | View Fitness Motivator help menu |
+| `exit` | Exit application |
+
+
+## FAQ
+
+**Q**: How do I transfer my data to another computer?
+
+**A**: After first time usage, if any data was saved during the session, it will be stored as text files under docs folder. Copy it over to
+another computer if you wish.
+
+**Q**: Can I directly edit the text files that are meant as data storage?
+
+**A**: Absolutely not. Do not edit the text files directly. Only interact with the storage text files via commands when running the jar file.
+
+**Q**: What will happen if I accidentally edited the text files?
-* Add todo `todo n/TODO_NAME d/DEADLINE`
+**A**: Behaviour of application might become unpredictable as the text files may become corrupted. We recommend manually deleting corrupted text files and restarting the jar file.
+New text files will be created to replace the missing files, and you can continue using the app as usual. Note that by deleting the text files, any past data stored on it would be wiped.
+To prevent loss of data and having to remove corrupted files, DO NOT attempt to edit the text files directly as mentioned previously.
\ No newline at end of file
diff --git a/docs/diagrams/Ui/UiClassDiagram.png b/docs/diagrams/Ui/UiClassDiagram.png
new file mode 100644
index 0000000000..1b76e7b717
Binary files /dev/null and b/docs/diagrams/Ui/UiClassDiagram.png differ
diff --git a/docs/diagrams/Ui/UiClassDiagram.puml b/docs/diagrams/Ui/UiClassDiagram.puml
new file mode 100644
index 0000000000..a44694cf99
--- /dev/null
+++ b/docs/diagrams/Ui/UiClassDiagram.puml
@@ -0,0 +1,49 @@
+@startuml
+
+skinparam classAttributeIconSize 0
+hide circle
+
+package ui {
+ class Ui {
+ - {static} SEP: String
+ + {static} greetUser(): void
+ + {static} promptUserInput(): void
+ + {static} sayGoodbye(): void
+ + {static} printMessageWithSepNewLine(message: String): void
+ + {static} printMessageWithoutNewLine(message: String): void
+ + {static} printList(list: ArrayList, message: String): void
+ }
+}
+
+package fitness {
+ class FitnessMotivator {
+ }
+}
+
+package focus {
+ class FocusTimer {
+ }
+}
+
+package habit {
+ class HabitTracker {
+ }
+}
+
+package reflection {
+ class ReflectionManager {
+ }
+}
+
+package sleep {
+ class SleepTracker {
+ }
+}
+
+FitnessMotivator ..> Ui : uses >
+focus ..> Ui : uses >
+habit ..> Ui : uses >
+reflection ..> Ui : uses >
+sleep ..> Ui : uses >
+
+@enduml
\ No newline at end of file
diff --git a/docs/diagrams/architecture/architecture.png b/docs/diagrams/architecture/architecture.png
new file mode 100644
index 0000000000..ea11f5ae3f
Binary files /dev/null and b/docs/diagrams/architecture/architecture.png differ
diff --git a/docs/diagrams/architecture/architecture.puml b/docs/diagrams/architecture/architecture.puml
new file mode 100644
index 0000000000..f95c52f27b
--- /dev/null
+++ b/docs/diagrams/architecture/architecture.puml
@@ -0,0 +1,32 @@
+@startuml
+
+hide circle
+hide attributes
+hide methods
+
+skinparam class {
+ FontSize 20
+ AttributeFontSize 20
+ MethodFontSize 20
+}
+
+skinparam defaultFontColor white
+
+Package " "<>{
+ Class Ui #green
+ Class Logic #blue
+ Class Storage #goldenrod
+ Class Parser #grey
+}
+
+Class User #red
+Class File #lightgreen
+
+Parser -[#grey]> Logic
+Ui -[#green]-> Parser
+Logic -[#blue]-> Storage
+Logic -[#blue]-> Ui
+
+Storage .[#goldenrod].>File
+User ..> Ui #red
+@enduml
\ No newline at end of file
diff --git a/docs/diagrams/fitness/FitnessClassDiagram.png b/docs/diagrams/fitness/FitnessClassDiagram.png
new file mode 100644
index 0000000000..48fdbdffb7
Binary files /dev/null and b/docs/diagrams/fitness/FitnessClassDiagram.png differ
diff --git a/docs/diagrams/fitness/FitnessClassDiagram.puml b/docs/diagrams/fitness/FitnessClassDiagram.puml
new file mode 100644
index 0000000000..45d6f3aab1
--- /dev/null
+++ b/docs/diagrams/fitness/FitnessClassDiagram.puml
@@ -0,0 +1,128 @@
+@startuml
+
+skinparam classAttributeIconSize 0
+hide circle
+
+package parser {
+ class FitnessCommandParser {
+ {Static} determineFitnessCommand(fitnessMotivator: FitnessMotivator, commandArgs: String): Command
+ }
+
+ class Parser {
+ }
+}
+
+package commands {
+ interface "<>\nCommand" as Command {
+ + execute(): void
+ + isExit(): boolean
+ }
+
+ class ExerciseCommand implements Command {
+ }
+ note top of ExerciseCommand: Could represent: \n1.GetExerciseCommand \n2.GoalExerciseCommand \n3.HelpExerciseCommand \n4.DeleteExerciseCommand \n5.AddExerciseCommand
+}
+
+package fitness {
+ package exercise {
+
+ class ExerciseList {
+ - allExercises: ArrayList
+ + ExerciseList()
+ + add(exercise: Exercise): void
+ + get(type: ExerciseType, index: int): Exercise
+ + getType(type: ExerciseType): ArrayList
+ + size(type: ExerciseType): int
+ + newExercise(parameters: String[]): Exercise
+ + findExercise(type: ExerciseType, nameQuery: String): Exercise
+ + remove(exercise: Exercise): void
+ }
+
+ class Exercise {
+ - ExerciseName: String
+ - exerciseType: ExerciseType
+ - sets: String
+ - reps: String
+ + toString(): String
+ }
+
+ class ExerciseBank {
+ }
+
+ }
+
+ package goals {
+ class ExerciseGoal {
+ - isDone: boolean
+ + toggle(): void
+ + toString(): String
+ }
+
+ class ExerciseGoalList {
+ - goals: ArrayList
+ + isEmpty(): void
+ + clear(): void
+ + findExercise(index: int): ExerciseGoal
+ + saveGoals(): void
+ + newExercise(parameters: String[]): ExerciseGoal
+ + add(exercise: Exercise, isDone: boolean): void
+ }
+ }
+
+ class FitnessMotivator {
+ + allExercises: ExerciseList
+ + dailyGoals : ExerciseGoalList
+ - fiveRandomExercise(): Exercise[]
+ + getExercises(): String
+ + addExercises(commandArgs: String[]): void
+ + getTypeExercises(type: ExerciseType): void
+ + deleteExercise(commandArgs: String[]): void
+ + newGoal(): void
+ + goalStatus(): void
+ + toggleStatus(index: int): void
+ + printHelp(): void
+ }
+
+ class UiMessageConstants {
+ }
+}
+
+enum "<>\nExerciseType" as ExerciseType {
+ ARMS
+ CHEST
+ ABS
+ BACK
+ LEGS
+ }
+
+class Storage {
+}
+
+class Ui {
+}
+
+Parser ..> FitnessCommandParser : uses >
+FitnessCommandParser ..> ExerciseCommand : creates >
+
+ExerciseCommand --> FitnessMotivator : uses >
+
+FitnessMotivator *--> "1" ExerciseList : contains >
+FitnessMotivator *--> "1" ExerciseGoalList : contains >
+
+ExerciseGoal --|> Exercise : extends >
+ExerciseGoalList --|> ExerciseList : extends >
+FitnessMotivator ..> UiMessageConstants : uses >
+ExerciseList ..> ExerciseBank : uses >
+
+ExerciseList *--> "25..*" Exercise : contains >
+ExerciseGoalList *--> "0..5" ExerciseGoal : contains >
+
+ExerciseList ..> ExerciseType : uses >
+FitnessMotivator ..> ExerciseType : uses >
+Exercise ..> ExerciseType : uses >
+
+ExerciseList ..> Storage : uses >
+
+FitnessMotivator ..> Ui : uses >
+
+@enduml
diff --git a/docs/diagrams/fitness/FitnessSequenceDiagram.png b/docs/diagrams/fitness/FitnessSequenceDiagram.png
new file mode 100644
index 0000000000..4d0aea7bf0
Binary files /dev/null and b/docs/diagrams/fitness/FitnessSequenceDiagram.png differ
diff --git a/docs/diagrams/fitness/FitnessSequenceDiagram.puml b/docs/diagrams/fitness/FitnessSequenceDiagram.puml
new file mode 100644
index 0000000000..9d95b9bf5a
--- /dev/null
+++ b/docs/diagrams/fitness/FitnessSequenceDiagram.puml
@@ -0,0 +1,45 @@
+@startuml
+
+autonumber
+
+participant ":Main" as Main
+participant ":Scanner" as Scanner
+participant ":FitnessMotivator" as FitnessMotivator
+participant "<>\nParser" as Parser
+participant "<>\nFitnessCommandParser" as FitnessCommandParser
+participant ":PlaceholderFitnessCommand" as PlaceholderFitnessCommand
+
+hide footbox
+
+activate Main
+
+create Scanner
+Main -> Scanner
+activate Scanner
+
+create FitnessMotivator
+Main -> FitnessMotivator
+activate FitnessMotivator
+
+loop until isExit
+
+ Main -> Scanner: nextLine()
+ Scanner --> Main: userInput
+ Main -> Parser: userInput
+ alt userInput is FitnessCommand
+ Parser -> FitnessCommandParser: determineFitnessCommand()
+ alt command is PlaceholderFitnessCommand
+ create PlaceholderFitnessCommand
+ FitnessCommandParser -> PlaceholderFitnessCommand: command, params
+ activate PlaceholderFitnessCommand
+ PlaceholderFitnessCommand --> FitnessCommandParser: command object
+ FitnessCommandParser --> Parser: command object
+ Parser --> Main: command object
+ Main -> PlaceholderFitnessCommand: execute()
+ PlaceholderFitnessCommand -> FitnessMotivator: execute()
+ destroy PlaceholderFitnessCommand
+ end
+ end
+end
+
+@enduml
diff --git a/docs/diagrams/focus/FocusClassDiagram.png b/docs/diagrams/focus/FocusClassDiagram.png
new file mode 100644
index 0000000000..54fd8d27bb
Binary files /dev/null and b/docs/diagrams/focus/FocusClassDiagram.png differ
diff --git a/docs/diagrams/focus/FocusClassDiagram.puml b/docs/diagrams/focus/FocusClassDiagram.puml
new file mode 100644
index 0000000000..e7a2b63f03
--- /dev/null
+++ b/docs/diagrams/focus/FocusClassDiagram.puml
@@ -0,0 +1,69 @@
+@startuml
+'https://plantuml.com/class-diagram
+skinparam classAttributeIconSize 0
+hide circle
+
+interface "<>\nCommand" as Command {
+execute()
+isExit()
+}
+class PlaceholderFocusCommand implements Command {
+}
+note top of PlaceholderFocusCommand: PlaceholderFocusCommand = StartTimerCommand,\n StopTimer command, etc
+class FocusCommandParser {
+}
+
+class Parser {
+}
+
+class Ui {
+}
+
+class FocusTimer {
+ - countupTimer: CountupTimer
+ - countdownTimer : CountdownTimer
+ - timerMode: boolean
+
+ + getStartStatus() : boolean
+ + switchTimer() : boolean
+ + getPausedStatus() : boolean
+ + setStartTiming()
+ + setStopTiming()
+ + setPauseTiming()
+ + setResumeTiming()
+ + checkTime()
+ + setDuration()
+
+}
+
+class CountupTimer {
+ - startTiming: LocalDateTime
+ - stoptiming: LocalDateTime
+ - currentTime: LocalDateTime
+ - isStarted: boolean
+ - isPaused: boolean
+
+}
+
+class CountdownTimer {
+ - minutes: int
+ - seconds: int
+ - inputMinutes: int
+ - isStarted: AtomicBoolean
+ - isRunning: AtomicBoolean
+ - stopwatch: Timer
+ - timerTask: TimerTask
+}
+
+
+FocusCommandParser .> PlaceholderFocusCommand: creates >
+PlaceholderFocusCommand ->"1" FocusTimer : controls >
+CountupTimer "1"<-* FocusTimer : manages <
+FocusTimer *-->"1" CountdownTimer : manages >
+Ui <. CountupTimer : uses <
+CountdownTimer .> Ui : uses >
+Parser ..> FocusCommandParser : creates >
+
+
+
+@enduml
\ No newline at end of file
diff --git a/docs/diagrams/focus/FocusSequenceDiagram.png b/docs/diagrams/focus/FocusSequenceDiagram.png
new file mode 100644
index 0000000000..dd5b3ecf04
Binary files /dev/null and b/docs/diagrams/focus/FocusSequenceDiagram.png differ
diff --git a/docs/diagrams/focus/FocusSequenceDiagram.puml b/docs/diagrams/focus/FocusSequenceDiagram.puml
new file mode 100644
index 0000000000..85dac351c0
--- /dev/null
+++ b/docs/diagrams/focus/FocusSequenceDiagram.puml
@@ -0,0 +1,43 @@
+@startuml
+'https://plantuml.com/sequence-diagram
+
+autonumber
+hide footbox
+participant ":Main" as Main
+participant ":Scanner" as Scanner
+participant ":FocusTimer" as FocusTimer
+participant "<>\nParser" as Parser
+participant "<>\nFocusCommandParser" as FocusCommandParser
+participant ":PlaceholderFocusCommand" as PlaceholderFocusCommand
+
+activate Main
+
+create Scanner
+Main -> Scanner
+activate Scanner
+
+create FocusTimer
+Main -> FocusTimer
+activate FocusTimer
+
+loop until isExit
+ Main -> Scanner : nextLine()
+ Main <-- Scanner : userInput
+ Main -> Parser : userInput
+ alt userInput is FocusCommand
+ Parser -> FocusCommandParser : DetermineFocusCommand
+ alt command is PlaceholderFocusCommand
+ create PlaceholderFocusCommand
+ FocusCommandParser -> PlaceholderFocusCommand : focusCommand()
+ activate PlaceholderFocusCommand
+ PlaceholderFocusCommand --> FocusCommandParser : command object
+ FocusCommandParser --> Parser : command object
+ Parser --> Main : command object
+ Main -> PlaceholderFocusCommand : execute()
+ PlaceholderFocusCommand --> FocusTimer : execute()
+ destroy PlaceholderFocusCommand
+ end
+ end
+end
+
+@enduml
\ No newline at end of file
diff --git a/docs/diagrams/focus/FocusStateDiagram.png b/docs/diagrams/focus/FocusStateDiagram.png
new file mode 100644
index 0000000000..396feca906
Binary files /dev/null and b/docs/diagrams/focus/FocusStateDiagram.png differ
diff --git a/docs/diagrams/focus/FocusStateDiagram.puml b/docs/diagrams/focus/FocusStateDiagram.puml
new file mode 100644
index 0000000000..c59d131740
--- /dev/null
+++ b/docs/diagrams/focus/FocusStateDiagram.puml
@@ -0,0 +1,17 @@
+@startuml
+'https://plantuml.com/class-diagram
+
+hide empty description
+
+[*] --> StateReady
+StateReady --> StateReady: switch timer
+StateReady --> StateTimerRunning: start
+StateTimerRunning -up-> StateReady: stop
+StateTimerRunning -up-> StatePaused: pause
+StatePaused --> StateTimerRunning: resume
+StatePaused --> StateReady: stop
+StatePaused --> StatePaused: check
+StateTimerRunning --> StateTimerRunning: check
+
+
+@enduml
\ No newline at end of file
diff --git a/docs/diagrams/habit/HabitClassDiagram.png b/docs/diagrams/habit/HabitClassDiagram.png
new file mode 100644
index 0000000000..b7cc596b72
Binary files /dev/null and b/docs/diagrams/habit/HabitClassDiagram.png differ
diff --git a/docs/diagrams/habit/HabitSequenceDiagram.png b/docs/diagrams/habit/HabitSequenceDiagram.png
new file mode 100644
index 0000000000..36dbb46416
Binary files /dev/null and b/docs/diagrams/habit/HabitSequenceDiagram.png differ
diff --git a/docs/diagrams/habit/HabitSequenceDiagram.puml b/docs/diagrams/habit/HabitSequenceDiagram.puml
new file mode 100644
index 0000000000..d277e70875
--- /dev/null
+++ b/docs/diagrams/habit/HabitSequenceDiagram.puml
@@ -0,0 +1,47 @@
+@startuml
+'https://plantuml.com/sequence-diagram
+
+autonumber
+
+participant ":Main" as Main
+participant ":Scanner" as Scanner
+participant ":HabitTracker" as HabitTracker
+participant "<>\nParser" as Parser
+participant "<>\nHabitCommandParser" as HabitCommandParser
+participant ":PlaceholderHabitCommand" as PlaceholderHabitCommand
+
+hide footbox
+
+activate Main
+
+create Scanner
+Main -> Scanner
+activate Scanner
+
+create HabitTracker
+Main -> HabitTracker
+activate HabitTracker
+
+loop until isExit
+ Main -> Scanner: nextLine()
+ Scanner --> Main: userInput
+ Main -> Parser: userInput
+
+ alt userInput is HabitCommand
+ Parser -> HabitCommandParser: determineHabitCommand()
+ alt command is PlaceholderHabitCommand
+ create PlaceholderHabitCommand
+ HabitCommandParser -> PlaceholderHabitCommand: command, params
+ activate PlaceholderHabitCommand
+ PlaceholderHabitCommand --> HabitCommandParser: command object
+ HabitCommandParser --> Parser: command object
+ Parser --> Main: command object
+ Main -> PlaceholderHabitCommand: execute()
+ PlaceholderHabitCommand -> HabitTracker: execute()
+ destroy PlaceholderHabitCommand
+ end
+ end
+end
+
+
+@enduml
diff --git a/docs/diagrams/habit/HabitTracker.puml b/docs/diagrams/habit/HabitTracker.puml
new file mode 100644
index 0000000000..de87d21382
--- /dev/null
+++ b/docs/diagrams/habit/HabitTracker.puml
@@ -0,0 +1,101 @@
+@startuml
+'https://plantuml.com/class-diagram
+
+skinparam NoteFontSize 20
+
+skinparam class {
+ FontSize 20
+ AttributeFontSize 20
+ MethodFontSize 20
+}
+
+skinparam classAttributeIconSize 0
+hide circle
+
+
+package parser {
+ class HabitCommandParser {
+ }
+
+ class Parser {
+ }
+}
+
+package command {
+ interface "<>\nCommand" as Command {
+ + execute(): void
+ + isExit(): boolean
+ }
+
+ class HabitCommand implements Command {
+ }
+ note top of HabitCommand: Could represent: \n1. AddHabitCommand \n2. DeleteHabitCommand \n3. ListHabitsCommand \n4. SetPriorityCommand \n5. SortHabitsCommand \n6. UpdateHabitCountCommand \n7. HabitHelpCommand
+}
+
+Package habit {
+ class HabitTracker {
+ - habitList: ArrayList
+
+ + HabitTracker()
+ + getNumberOfHabits(): int
+ + addHabit(newHabit: Habit): void
+ + listHabits(): void
+ + isValidHabitID(habitID: int): boolean
+ + updateHabitCount(habitID: int, updatedCount: String): void
+ + deleteHabit(habitID: int): void
+ + setPriorityLevel(habitID: int, priority String): void
+ + sortHabits(): void
+ + clearHabitList(): void
+ }
+
+ class Habit {
+ - description: String
+ - habitCount: int
+ - priority: Priority
+
+ + Habit(description: String)
+ + Habit(description: String, habitCount: int, priority: Priority)
+ + getDescription(): String
+ + getHabitCount(): int
+ + getPriority(): Priority
+ + updateCount(updatedCount: String): int
+ + setPriority(priority: String): void
+ }
+
+ enum Priority {
+ HIGH
+ MED
+ LOW
+ }
+}
+
+Package storage {
+ class Storage {
+ }
+
+ class HabitTrackerStorage {
+ + saveHabitListToFile(habitList: ArrayList): void
+ + loadHabitListFromFile(): ArrayList
+ }
+}
+
+class Ui {
+}
+
+HabitTracker *-- "*" Habit : contains >
+Habit ..> Priority : uses >
+
+HabitTracker ..> HabitTrackerStorage : uses >
+HabitTrackerStorage ..> Storage : uses >
+HabitTracker ..> Ui : uses >
+
+Parser ..> HabitCommandParser : creates >
+HabitCommandParser .DOWN.> HabitCommand : creates >
+
+HabitCommand --> HabitTracker : uses >
+
+
+
+
+@enduml
+
diff --git a/docs/diagrams/parser/CommandParserClass.png b/docs/diagrams/parser/CommandParserClass.png
new file mode 100644
index 0000000000..d7633098e4
Binary files /dev/null and b/docs/diagrams/parser/CommandParserClass.png differ
diff --git a/docs/diagrams/parser/CommandParserClass.puml b/docs/diagrams/parser/CommandParserClass.puml
new file mode 100644
index 0000000000..2d126ec2a2
--- /dev/null
+++ b/docs/diagrams/parser/CommandParserClass.puml
@@ -0,0 +1,50 @@
+
+
+@startuml
+
+skinparam NoteFontSize 20
+
+skinparam class {
+ FontSize 20
+ AttributeFontSize 20
+ MethodFontSize 20
+}
+
+skinparam classAttributeIconSize 0
+hide circle
+
+class Parser {
+ + determineCommand(sleepTracker: SleepTracker, reflection: ReflectionManager, habitTracker: HabitTracker, focusTimer: FocusTimer, fitnessMotivator: FitnessMotivator, userInput: String): Command
+}
+
+class HabitCommandParser {
+ + determineHabitCommand(habitTracker: HabitTracker, commandArgs: String): Command
+}
+
+class FitnessCommandParser {
+ + determineFitnessCommand(fitnessMotivator: FitnessMotivator, commandArgs: String): Command
+}
+
+class FocusCommandParser {
+ + determineFocusCommand(focusTimer: FocusTimer, commandArgs: String): Command
+}
+
+class ReflectionCommandParser {
+ + determineReflectionCommand(reflectionManager: ReflectionManager, commandArgs: String): Command
+}
+
+class SleepCommandParser {
+ + determineSleepCommand(sleepTracker: SleepTracker, commandArgs: String): Command
+}
+
+
+
+Parser ---> HabitCommandParser : uses >
+Parser --> FitnessCommandParser : uses >
+Parser ---> FocusCommandParser : uses >
+Parser --> ReflectionCommandParser : uses >
+Parser ---> SleepCommandParser : uses >
+
+
+
+@enduml
diff --git a/docs/diagrams/parser/CommandParserSequence.png b/docs/diagrams/parser/CommandParserSequence.png
new file mode 100644
index 0000000000..71883d8063
Binary files /dev/null and b/docs/diagrams/parser/CommandParserSequence.png differ
diff --git a/docs/diagrams/parser/CommandParserSequence.puml b/docs/diagrams/parser/CommandParserSequence.puml
new file mode 100644
index 0000000000..2fb9791f3a
--- /dev/null
+++ b/docs/diagrams/parser/CommandParserSequence.puml
@@ -0,0 +1,70 @@
+@startuml
+
+participant ":Main" as Main
+participant "<>\nParser" as Parser
+participant "<>\nHabitCommandParser" as HabitCommandParser
+participant "<>\nFitnessCommandParser" as FitnessCommandParser
+participant "<>\nFocusCommandParser" as FocusCommandParser
+participant "<>\nReflectionCommandParser" as ReflectionCommandParser
+participant "<>\nSleepCommandParser" as SleepCommandParser
+participant ":Command" as Command
+
+Main -> Parser: userInput
+
+hide footbox
+
+activate Parser
+
+alt if userInput is HabitCommand
+ Parser -> HabitCommandParser: determineHabitCommand
+ activate HabitCommandParser
+ HabitCommandParser -> Command: create Command object
+ activate Command
+ Command --> HabitCommandParser: Command object
+ deactivate Command
+ HabitCommandParser --> Parser: return Command
+ deactivate HabitCommandParser
+else if userInput is FitnessCommand
+ Parser -> FitnessCommandParser: determineFitnessCommand
+ activate FitnessCommandParser
+ FitnessCommandParser -> Command: create Command object
+ activate Command
+ Command --> FitnessCommandParser: Command object
+ deactivate Command
+ FitnessCommandParser --> Parser: return Command
+ deactivate FitnessCommandParser
+else if userInput is FocusCommand
+ Parser -> FocusCommandParser: determineFocusCommand
+ activate FocusCommandParser
+ FocusCommandParser -> Command: create Command object
+ activate Command
+ Command --> FocusCommandParser: Command object
+ deactivate Command
+ FocusCommandParser --> Parser: return Command
+ deactivate FocusCommandParser
+else if userInput is ReflectionCommand
+ Parser -> ReflectionCommandParser: determineReflectionCommand
+ activate ReflectionCommandParser
+ ReflectionCommandParser -> Command: create Command object
+ activate Command
+ Command --> ReflectionCommandParser: Command object
+ deactivate Command
+ ReflectionCommandParser --> Parser: return Command
+ deactivate ReflectionCommandParser
+else if userInput is SleepCommand
+ Parser -> SleepCommandParser: determineSleepCommand
+ activate SleepCommandParser
+ SleepCommandParser -> Command: create Command object
+ activate Command
+ Command --> SleepCommandParser: Command object
+ deactivate Command
+ SleepCommandParser --> Parser: return Command
+ deactivate SleepCommandParser
+end
+
+deactivate Parser
+
+Parser -> Main: return Command
+deactivate Parser
+
+@enduml
diff --git a/docs/diagrams/reflection/Reflection.puml b/docs/diagrams/reflection/Reflection.puml
new file mode 100644
index 0000000000..a4b269462a
--- /dev/null
+++ b/docs/diagrams/reflection/Reflection.puml
@@ -0,0 +1,95 @@
+@startuml
+scale 2.0
+skinparam NoteFontSize 20
+
+skinparam class {
+ FontSize 20
+ AttributeFontSize 20
+ MethodFontSize 20
+ ArrowFontSize 20
+}
+
+skinparam classAttributeIconSize 0
+hide circle
+
+package parser {
+ class ReflectionCommandParser {
+ }
+
+ class Parser {
+ }
+}
+
+package command {
+ interface "<>\nCommand" as Command {
+ + execute(): void
+ + isExit(): boolean
+ }
+
+
+ class ReflectionCommand implements Command {
+ }
+ note top of ReflectionCommand: Could represent: \n1.GetReflectionQuestionsCommand \n2.ListFavouriteReflectionsCommand \n3.ReflectionHelpCommand \n4.SaveToFavouritesCommand \n5.UnsaveFromFavouritesCommand
+}
+package reflection {
+ abstract class "{abstract}\nReflectionList" as ReflectionList {
+ - reflectionList: ArrayList
+
+ + ReflectionList()
+ + addReflectionQuestion(reflectionQuestion: ReflectionQuestion): void
+ + removeReflectionQuestion(reflectionQuestion: ReflectionQuestion): void
+ + getSize(): int
+ + getReflectionList(): ArrayList
+ }
+
+ class FavoriteReflectionsList {
+ + FavoriteReflectionsList()
+ + get(favouritesId: int): ReflectionQuestion
+ }
+
+ class ReflectionQuestionBank {
+ - setUpReflectionBank(): void
+ + ReflectionQuestionBank()
+ + getFiveRandomQuestions(): ArrayList
+ }
+
+ class ReflectionQuestion {
+ - question: String
+ + ReflectionQuestion(question: String)
+ + toString(): String
+ }
+
+ class ReflectionManager {
+
+ + ReflectionManager()
+ + printFiveRandomQuestions(): void
+ + saveReflectionQuestion(reflectionId: int): void
+ + unsaveReflectionQuestion(reflectionId: int): void
+ + printFavourites(): void
+ + printHelpMenu(): void
+ }
+}
+
+class Storage {
+}
+
+class Ui {
+}
+
+ReflectionList *-- "*" ReflectionQuestion : contains
+FavoriteReflectionsList --|> ReflectionList
+ReflectionQuestionBank --|> ReflectionList
+ReflectionQuestionBank *-- "41" ReflectionQuestion : contains >
+FavoriteReflectionsList *-- "*" ReflectionQuestion : contains >
+
+ReflectionManager ..> Storage : uses >
+ReflectionManager ..> Ui : uses >
+ReflectionManager *-- "1" FavoriteReflectionsList : contains >
+ReflectionManager *-- "1" ReflectionQuestionBank : contains >
+
+Parser ..> ReflectionCommandParser : creates >
+ReflectionCommandParser .DOWN.> ReflectionCommand : creates >
+
+ReflectionCommand --> ReflectionManager: uses
+
+@enduml
diff --git a/docs/diagrams/reflection/ReflectionClassDiagram.png b/docs/diagrams/reflection/ReflectionClassDiagram.png
new file mode 100644
index 0000000000..2629656a04
Binary files /dev/null and b/docs/diagrams/reflection/ReflectionClassDiagram.png differ
diff --git a/docs/diagrams/reflection/ReflectionSequenceDiagram.png b/docs/diagrams/reflection/ReflectionSequenceDiagram.png
new file mode 100644
index 0000000000..917aa70dfb
Binary files /dev/null and b/docs/diagrams/reflection/ReflectionSequenceDiagram.png differ
diff --git a/docs/diagrams/reflection/ReflectionSequenceDiagram.puml b/docs/diagrams/reflection/ReflectionSequenceDiagram.puml
new file mode 100644
index 0000000000..cc42e52cb5
--- /dev/null
+++ b/docs/diagrams/reflection/ReflectionSequenceDiagram.puml
@@ -0,0 +1,49 @@
+@startuml
+'https://plantuml.com/sequence-diagram
+
+autonumber
+
+participant ":Main" as Main
+participant ":Scanner" as Scanner
+participant ":ReflectionManager" as ReflectionManager
+participant "<>\nParser" as Parser
+participant "<>\nReflectionCommandParser" as ReflectionCommandParser
+participant ":PlaceholderReflectionCommand" as PlaceholderReflectionCommand
+
+hide footbox
+
+activate Main
+
+create Scanner
+Main -> Scanner
+activate Scanner
+
+create ReflectionManager
+Main -> ReflectionManager
+activate ReflectionManager
+
+loop until isExit
+
+ Main -> Scanner: nextLine()
+ Scanner --> Main: userInput
+ Main -> Parser: userInput
+ alt userInput is ReflectionCommand
+ Parser -> ReflectionCommandParser: determineReflectionCommand()
+ alt command is PlaceholderReflectionCommand
+ create PlaceholderReflectionCommand
+ ReflectionCommandParser -> PlaceholderReflectionCommand
+ activate PlaceholderReflectionCommand
+ PlaceholderReflectionCommand --> ReflectionCommandParser: command object
+ ReflectionCommandParser --> Parser: command object
+ Parser --> Main: command object
+ Main -> PlaceholderReflectionCommand: execute()
+ PlaceholderReflectionCommand -> ReflectionManager: execute()
+ destroy PlaceholderReflectionCommand
+ end
+ end
+end
+
+
+@enduml
+
+@enduml
\ No newline at end of file
diff --git a/docs/diagrams/sleep/Sleep.puml b/docs/diagrams/sleep/Sleep.puml
new file mode 100644
index 0000000000..e915a31e9c
--- /dev/null
+++ b/docs/diagrams/sleep/Sleep.puml
@@ -0,0 +1,95 @@
+@startuml
+'https://plantuml.com/sequence-diagram
+
+skinparam classAttributeIconSize 0
+hide circle
+
+package parser {
+ class SleepCommandParser {
+
+ +determineSleepCommand(sleepTracker: SleepTracker, commandArgs: String)
+ }
+
+ class Parser {
+ }
+}
+
+package command {
+ interface "<>\nCommand" as Command {
+ + execute(): void
+ + isExit(): boolean
+ }
+
+ class SleepCommand implements Command {
+ }
+ note top of SleepCommand: Could represent: \n1.AddSleepCommand \n2.DeleteSleepCommand \n3.GetSleepCommand \n4.ListSleepCommand \n5.SaveSleepCommand \n6.UpdateSleepCommand \n7.HelpSleepCommand
+
+
+}
+
+package sleep {
+ interface "<>\nComparable" as Comparable {
+ }
+
+ class SleepCycle implements Comparable {
+ - hoursSlept: double
+ - dateOfSleep: LocalDate
+
+ + getHoursSlept(): double
+ + getDateOfSleep(): LocalDate
+ + setHoursOfSleep(newHours: double)
+ + compareTo(sleepCycle: SleepCycle)
+ +toString(): String
+ }
+
+ class SleepCycleList {
+ - totalHrsSlept: double
+ - numberOfCycles: int
+
+ +addSleepCycle(sleepCycle: SleepCycle, isPrint: boolean)
+ +deleteSleepCycle(date: LocalDate)
+ +deleteSleepCyclesBefore(date: LocalDate)
+ +deleteSleepCyclesBetween(startDate: LocalDate, endDate: LocalDate)
+ +listSleepCycles()
+ +getSleepCycle(date: LocalDate, isPrint: boolean): int
+ +updateSleepCycle(date: LocalDate, newHours: double)
+ +getNumberOfCycles(): int
+ +getTotalHrsSlept(): double
+ +getSleepCycleList(): ArrayList
+
+ }
+
+ class SleepTracker {
+ +listSleepCycles()
+ +addSleepCycle(sleepCycleToAdd: SleepCycle)
+ +updateSleepCycle(date: LocalDate, newHours: double)
+ +getSleepCycle(date: LocalDate)
+ +deleteSleepCycle(date: LocalDate)
+ +deleteSleepCyclesBefore(date: LocalDate)
+ +deleteSleepCyclesBetween(startDate:LocalDate, endDate:LocalDate)
+ +saveSleepCycles()
+ }
+}
+
+
+class SleepTrackerStorage {
+}
+
+class Ui {
+}
+
+Parser ..> SleepCommandParser: creates >
+
+SleepCommandParser .DOWN.> SleepCommand : creates >
+SleepCommandParser ..> SleepTracker
+
+SleepCommand --> SleepTracker : uses >
+
+
+SleepCycleList ..> Ui : uses >
+SleepTracker *--> "1" SleepCycleList: contains >
+SleepTracker ..> SleepTrackerStorage : uses >
+
+SleepCycleList *-- "*" SleepCycle : contains
+
+@enduml
\ No newline at end of file
diff --git a/docs/diagrams/sleep/SleepDiagram.png b/docs/diagrams/sleep/SleepDiagram.png
new file mode 100644
index 0000000000..aacbe63cd0
Binary files /dev/null and b/docs/diagrams/sleep/SleepDiagram.png differ
diff --git a/docs/diagrams/sleep/SleepSequenceDiagram.png b/docs/diagrams/sleep/SleepSequenceDiagram.png
new file mode 100644
index 0000000000..fd337a9664
Binary files /dev/null and b/docs/diagrams/sleep/SleepSequenceDiagram.png differ
diff --git a/docs/diagrams/sleep/SleepSequenceDiagram.puml b/docs/diagrams/sleep/SleepSequenceDiagram.puml
new file mode 100644
index 0000000000..27e51929d3
--- /dev/null
+++ b/docs/diagrams/sleep/SleepSequenceDiagram.puml
@@ -0,0 +1,46 @@
+@startuml
+'https://plantuml.com/sequence-diagram
+
+autonumber
+
+participant ":Main" as Main
+participant ":Scanner" as Scanner
+participant ":SleepTracker" as SleepTracker
+participant "<>\nParser" as Parser
+participant "<>\nSleepCommandParser" as SleepCommandParser
+participant ":PlaceHolderSleepCommand" as PlaceholderSleepCommand
+
+activate Main
+
+create Scanner
+Main -> Scanner
+activate Scanner
+
+create SleepTracker
+Main -> SleepTracker
+activate SleepTracker
+
+loop until isExit
+
+ Main -> Scanner: nextLine()
+ Scanner --> Main: userInput
+ Main -> Parser: userInput
+ alt userInput is SleepCommand
+ Parser -> SleepCommandParser: determineSleepCommand()
+ alt command is PlaceholderSleepCommand
+ create PlaceholderSleepCommand
+ SleepCommandParser -> PlaceholderSleepCommand: command, params
+ activate PlaceholderSleepCommand
+ PlaceholderSleepCommand --> SleepCommandParser: command object
+ SleepCommandParser --> Parser: command object
+ Parser --> Main: command object
+ Main -> PlaceholderSleepCommand: execute()
+ PlaceholderSleepCommand -> SleepTracker: execute()
+ destroy PlaceholderSleepCommand
+ end
+ end
+end
+
+@enduml
+
+@enduml
\ No newline at end of file
diff --git a/docs/diagrams/storage/StorageClassDiagram.png b/docs/diagrams/storage/StorageClassDiagram.png
new file mode 100644
index 0000000000..de7313c4ca
Binary files /dev/null and b/docs/diagrams/storage/StorageClassDiagram.png differ
diff --git a/docs/diagrams/storage/StorageClassDiagram.puml b/docs/diagrams/storage/StorageClassDiagram.puml
new file mode 100644
index 0000000000..e1e412ce8f
--- /dev/null
+++ b/docs/diagrams/storage/StorageClassDiagram.puml
@@ -0,0 +1,36 @@
+@startuml
+'https://plantuml.com/sequence-diagram
+
+skinparam classAttributeIconSize 0
+hide circle
+
+class Storage {
+ +loadDataFromFile(filePath: String): ArrayList
+ +createFolder(filePath: String)
+ +saveTasksToFile(filePath:String, data:ArrayList)
+ +isFileCreated(filePath: String): boolean
+}
+
+class HabitTrackerStorage {
+ <> -HABIT_FILE_PATH: String
+ <> -COMMA_SEPARATION: String
+ <> -DATA_SIZE: int
+
+ +saveHabitListToFile(habitList: ArrayList)
+ +loadHabitListFromFile(): ArrayList
+}
+
+class SleepTrackerStorage {
+ <> -SLEEP_FILE_PATH: String
+ <> -ERROR_MESSAGE: String
+ <> -HOURS_POS: int
+ <> -DATE_POS: int
+
+ +saveSleepListToFile(sleepList: ArrayList)
+ +loadSleepListFromFile(): SleepCycleList
+}
+
+HabitTrackerStorage ..> Storage : uses >
+SleepTrackerStorage ..> Storage : uses >
+
+@enduml
\ No newline at end of file
diff --git a/docs/diagrams/storage/StorageSequenceDiagramSleep.png b/docs/diagrams/storage/StorageSequenceDiagramSleep.png
new file mode 100644
index 0000000000..72b811af11
Binary files /dev/null and b/docs/diagrams/storage/StorageSequenceDiagramSleep.png differ
diff --git a/docs/diagrams/storage/StorageSequenceDiagramSleep.puml b/docs/diagrams/storage/StorageSequenceDiagramSleep.puml
new file mode 100644
index 0000000000..148d704f71
--- /dev/null
+++ b/docs/diagrams/storage/StorageSequenceDiagramSleep.puml
@@ -0,0 +1,50 @@
+@startuml
+'https://plantuml.com/sequence-diagram
+
+autonumber
+
+participant ":Main" as Main
+participant ":SleepTracker" as SleepTracker
+participant "<>\nSleepTrackerStorage" as SleepTrackerStorage
+participant "<>\nStorage" as Storage
+participant ":SleepCycleList" as SleepCycleList
+participant ":SleepCycle" as SleepCycle
+
+activate Main
+Create SleepTracker
+activate SleepTracker
+Main -> SleepTracker: null
+SleepTracker -> SleepTrackerStorage: calls loadSleepListFromFile()
+activate SleepTrackerStorage
+alt isFileCreated
+ SleepTrackerStorage -> Storage: calls createFolder(SLEEP_FILE_PATH)
+ activate Storage
+ Storage --> SleepTrackerStorage
+ deactivate Storage
+end
+SleepTrackerStorage -> Storage: loadDataFromFile
+activate Storage
+Storage --> SleepTrackerStorage: return ArrayList
+deactivate Storage
+Create SleepCycleList
+SleepTrackerStorage -> SleepCycleList: SleepCycleList()
+activate SleepCycleList
+SleepCycleList --> SleepTrackerStorage
+deactivate SleepCycleList
+loop until end of ArrayList
+Create SleepCycle
+SleepTrackerStorage -> SleepCycle: hour, date
+activate SleepCycle
+SleepCycle --> SleepTrackerStorage: return sleep cycle to be added
+deactivate SleepCycle
+SleepTrackerStorage -> SleepCycleList: calls addSleepCycle(sleep cycle to be added)
+activate SleepCycleList
+SleepCycleList --> SleepTrackerStorage
+deactivate SleepCycleList
+end
+
+SleepTrackerStorage --> SleepTracker: return sleep cycle list
+deactivate SleepTrackerStorage
+SleepTracker --> Main: return Sleep Tracker
+deactivate SleepTracker
+@enduml
\ No newline at end of file
diff --git a/docs/diagrams/storage/StorageSequenceSleep.png b/docs/diagrams/storage/StorageSequenceSleep.png
new file mode 100644
index 0000000000..d546f20d6f
Binary files /dev/null and b/docs/diagrams/storage/StorageSequenceSleep.png differ
diff --git a/docs/diagrams/storage/StorageSequenceSleep.puml b/docs/diagrams/storage/StorageSequenceSleep.puml
new file mode 100644
index 0000000000..a15817c2fb
--- /dev/null
+++ b/docs/diagrams/storage/StorageSequenceSleep.puml
@@ -0,0 +1,34 @@
+@startuml
+'https://plantuml.com/sequence-diagram
+!pragma sequenceDiagramActive false
+
+autonumber
+
+participant ":SleepTracker" as SleepTracker
+participant "<>\nSleepTrackerStorage" as SleepTrackerStorage
+participant "<>\nStorage" as Storage
+participant ":ArrayList" as ArrayList
+
+activate SleepTracker
+SleepTracker -> SleepTrackerStorage: calls saveSleepListToFile(ArrayList sleepList)
+activate SleepTrackerStorage
+create ArrayList
+SleepTrackerStorage -> ArrayList: ArrayList()
+activate ArrayList
+ArrayList --> SleepTrackerStorage: ArrayList object
+deactivate ArrayList
+loop until end of
+SleepTrackerStorage -> ArrayList: calls add(String)
+activate ArrayList
+ArrayList --> SleepTrackerStorage
+deactivate ArrayList
+end
+SleepTrackerStorage -> Storage: Call saveTasksToFile(SLEEP_FILE_PATH, ArrayList)
+activate Storage
+Storage --> SleepTrackerStorage
+deactivate Storage
+SleepTrackerStorage --> SleepTracker
+deactivate SleepTrackerStorage
+
+
+@enduml
diff --git a/docs/team/damiwee.md b/docs/team/damiwee.md
new file mode 100644
index 0000000000..69bb14eaa7
--- /dev/null
+++ b/docs/team/damiwee.md
@@ -0,0 +1,61 @@
+# Damien Wee Joon Long's Project Portfolio Page
+
+## Overview
+Wellness360 is a wellness app meant for stressed Engineering Students who prefer CLI over GUI
+and want to keep track of their overall wellbeing. Our app offers comprehensive tracking and management
+tools, providing personalized support to alleviate stress and enhance overall well-being with features
+including a Fitness Tracker, Focus Timer, Habit Tracker, Self Reflection and Sleep Tracker.
+
+Given below is the summary of my contributions.
+
+* Code contribution: [Reposense](https://nus-cs2113-ay2324s2.github.io/tp-dashboard/?search=damiwee&breakdown=true&sort=groupTitle%20dsc&sortWithin=title&since=2024-02-23&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other)
+* Feature: `Storage` [#21](https://github.com/AY2324S2-CS2113-T11-1/tp/issues/21)
+ * What it does: Common class shared by developers to store and load data from a text file. No parsing is done.
+ * Justification: Different developers parse their data in different ways. By creating a generic and basic template, it can be used on its own or other developers can use inheritance to add functionality based on their own use case.
+ * Highlights: Handles exceptions such as file not found by creating new file and other IO exceptions.
+* Feature: `ReflectionManager` [#6](https://github.com/AY2324S2-CS2113-T11-1/tp/issues/6) [#39](https://github.com/AY2324S2-CS2113-T11-1/tp/issues/39) [#45](https://github.com/AY2324S2-CS2113-T11-1/tp/issues/45) [#73](https://github.com/AY2324S2-CS2113-T11-1/tp/issues/73) [#74](https://github.com/AY2324S2-CS2113-T11-1/tp/issues/74)
+ * What it does: Allow user to conduct self reflection.
+ * Justification: [Refer to developer guide for design considerations](https://ay2324s2-cs2113-t11-1.github.io/tp/DeveloperGuide.html#reflection-component)
+ * Highlights: User friendly and intuitive commands.
+* User guide contributions:
+ * [Introduction](https://ay2324s2-cs2113-t11-1.github.io/tp/UserGuide.html#wellness360-user-guide)
+ * [Quick start guide](https://ay2324s2-cs2113-t11-1.github.io/tp/UserGuide.html#quick-start)
+ * [Features summary](https://ay2324s2-cs2113-t11-1.github.io/tp/UserGuide.html#features)
+ * [General command format guide](https://ay2324s2-cs2113-t11-1.github.io/tp/UserGuide.html#features)
+ * `ReflectionManger`
+ * [`reflect get` command](https://ay2324s2-cs2113-t11-1.github.io/tp/UserGuide.html#get-reflection-questions-reflect-get)
+ * [`reflect save` command](https://ay2324s2-cs2113-t11-1.github.io/tp/UserGuide.html#save-favourite-reflection-question-reflect-save)
+ * [`reflect unsave` command](https://ay2324s2-cs2113-t11-1.github.io/tp/UserGuide.html#unsave-favourite-reflection-question-reflect-unsave)
+ * [`reflect list` command](https://ay2324s2-cs2113-t11-1.github.io/tp/UserGuide.html#view-favourite-reflection-questions-reflect-list)
+ * [`reflect help` command](https://ay2324s2-cs2113-t11-1.github.io/tp/UserGuide.html#view-reflection-help-menu-reflect-help)
+ * [`exit`](https://ay2324s2-cs2113-t11-1.github.io/tp/UserGuide.html#exit-application-exit)
+ * [Command Summary](https://ay2324s2-cs2113-t11-1.github.io/tp/UserGuide.html#command-summary)
+ * [FAQ](https://ay2324s2-cs2113-t11-1.github.io/tp/UserGuide.html#faq)
+* Developer guide contributions:
+ * [Acknowledgements](https://ay2324s2-cs2113-t11-1.github.io/tp/DeveloperGuide.html#acknowledgements)
+ * [Set up guide](https://ay2324s2-cs2113-t11-1.github.io/tp/DeveloperGuide.html#setting-up-getting-started)
+ * [Architecture](https://ay2324s2-cs2113-t11-1.github.io/tp/DeveloperGuide.html#architecture)
+ * [Reflection component](https://ay2324s2-cs2113-t11-1.github.io/tp/DeveloperGuide.html#reflection-component)
+ * [Appendix - Product scope](https://ay2324s2-cs2113-t11-1.github.io/tp/DeveloperGuide.html#product-scope)
+ * [Appendix - User stories](https://ay2324s2-cs2113-t11-1.github.io/tp/DeveloperGuide.html#user-stories)
+ * [Appendix - Non functional requirements](https://ay2324s2-cs2113-t11-1.github.io/tp/DeveloperGuide.html#non-functional-requirements)
+ * [Appendix - Glossary](https://ay2324s2-cs2113-t11-1.github.io/tp/DeveloperGuide.html#glossary)
+ * [Manual testing of reflection component](https://ay2324s2-cs2113-t11-1.github.io/tp/DeveloperGuide.html#reflection-component-1)
+* Team based task contributions:
+ * Labels and milestones creation
+ * [Involved in creating and managing issues](https://github.com/AY2324S2-CS2113-T11-1/tp/issues?q=is%3Aissue+is%3Aclosed)
+* Community:
+ * [(damiwee) PR review comments count: 37](https://nus-cs2113-ay2324s2.github.io/dashboards/contents/tp-comments.html)
+ * PE Dry Run 12 Bugs reported (top 10%) [#86](https://github.com/AY2324S2-CS2113-W14-4/tp/issues/86)
+ [#83](https://github.com/AY2324S2-CS2113-W14-4/tp/issues/83)
+ [#80](https://github.com/AY2324S2-CS2113-W14-4/tp/issues/80)
+ [#77](https://github.com/AY2324S2-CS2113-W14-4/tp/issues/77)
+ [#75](https://github.com/AY2324S2-CS2113-W14-4/tp/issues/75)
+ [#74](https://github.com/AY2324S2-CS2113-W14-4/tp/issues/74)
+ [#70](https://github.com/AY2324S2-CS2113-W14-4/tp/issues/70)
+ [#69](https://github.com/AY2324S2-CS2113-W14-4/tp/issues/69)
+ [#68](https://github.com/AY2324S2-CS2113-W14-4/tp/issues/68)
+ [#67](https://github.com/AY2324S2-CS2113-W14-4/tp/issues/67)
+ [#65](https://github.com/AY2324S2-CS2113-W14-4/tp/issues/65)
+ [#62](https://github.com/AY2324S2-CS2113-W14-4/tp/issues/62)
+ * DG review [#37](https://github.com/nus-cs2113-AY2324S2/tp/pull/37/files#diff-1a95edf069a4136e9cb71bee758b0dc86996f6051f0d438ec2c424557de7160b)
\ No newline at end of file
diff --git a/docs/team/daviancold.md b/docs/team/daviancold.md
new file mode 100644
index 0000000000..1d2f72104e
--- /dev/null
+++ b/docs/team/daviancold.md
@@ -0,0 +1,44 @@
+# Davian Kho Yong Quan's Project Portfolio Page
+
+## Overview
+Wellness360 is a wellness app meant for stressed Engineering Students who prefer CLI over GUI
+and want to keep track of their overall wellbeing. Our app offers comprehensive tracking and management
+tools, providing personalized support to alleviate stress and enhance overall well-being with features
+including a Fitness Tracker, Focus Timer, Habit Tracker, Self Reflection and Sleep Tracker.
+
+### Summary of Contributions
+* Code contribution: [Reposense](https://nus-cs2113-ay2324s2.github.io/tp-dashboard/?search=daviancold&breakdown=true&sort=groupTitle%20dsc&sortWithin=title&since=2024-02-23&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other&tabOpen=true&tabType=authorship&tabAuthor=Daviancold&tabRepo=AY2324S2-CS2113-T11-1%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false)
+* Setting up main java class: [#26](https://github.com/AY2324S2-CS2113-T11-1/tp/pull/26)
+ * What it does: Provides a standard template for everyone to pass in their feature handler object
+ * Justification: Since everyone's feature fundamentally requires a text input and a text response, with a common command `exit` that shuts the chatbot down, it is easier to use a template that handles everyone's feature handler. This makes the code more readable as well.
+* Releasing [Wellness360 v2.0](https://github.com/AY2324S2-CS2113-T11-1/tp/releases/tag/v2.0)
+* Feature: `FitnessMotivator` [#10](https://github.com/AY2324S2-CS2113-T11-1/tp/issues/10) [#18](https://github.com/AY2324S2-CS2113-T11-1/tp/issues/18) [#91](https://github.com/AY2324S2-CS2113-T11-1/tp/issues/91) [#92](https://github.com/AY2324S2-CS2113-T11-1/tp/issues/92) [#107](https://github.com/AY2324S2-CS2113-T11-1/tp/issues/107) [#109](https://github.com/AY2324S2-CS2113-T11-1/tp/issues/109) [#126](https://github.com/AY2324S2-CS2113-T11-1/tp/issues/126)
+ * What it does: Allows user to set exercise goals, and to view, create and delete exercises that can be done.
+ * Justification: [Refer to developer guide for design considerations](https://ay2324s2-cs2113-t11-1.github.io/tp/DeveloperGuide.html)
+ * Highlights: User friendly and intuitive commands.
+* User Guide contributions:
+* `FitnessMotivator`
+ * [`fitness get` command](https://ay2324s2-cs2113-t11-1.github.io/tp/UserGuide.html#get-a-list-of-exercises-fitness-get)
+ * [`fitness add` command](https://ay2324s2-cs2113-t11-1.github.io/tp/UserGuide.html#add-exercises-to-the-list-fitness-add)
+ * [`fitness delete` command](https://ay2324s2-cs2113-t11-1.github.io/tp/UserGuide.html#delete-exercises-from-the-list-fitness-delete)
+ * [`fitness goal` command](https://ay2324s2-cs2113-t11-1.github.io/tp/UserGuide.html#set-exercise-goals-for-the-day-fitness-goal)
+ * [`fitness help` command](https://ay2324s2-cs2113-t11-1.github.io/tp/UserGuide.html#view-fitness-motivator-help-menu-fitness-help)
+* Developer guide contributions:
+ * [Fitness Motivator component](https://ay2324s2-cs2113-t11-1.github.io/tp/DeveloperGuide.html#fitness-motivator-component)
+ * [Manual testing of Fitness Motivator component](https://ay2324s2-cs2113-t11-1.github.io/tp/DeveloperGuide.html#fitness-motivator-component-1)
+* Team based task contributions:
+ * [Involved in creating and managing issues](https://github.com/AY2324S2-CS2113-T11-1/tp/issues?q=is%3Aissue+is%3Aclosed+author%3ADaviancold)
+* Community:
+ * PE Dry run bugs reported:
+ [#121](https://github.com/AY2324S2-CS2113-W13-3/tp/issues/121)
+ [#126](https://github.com/AY2324S2-CS2113-W13-3/tp/issues/126)
+ [#130](https://github.com/AY2324S2-CS2113-W13-3/tp/issues/130)
+ [#136](https://github.com/AY2324S2-CS2113-W13-3/tp/issues/136)
+ [#145](https://github.com/AY2324S2-CS2113-W13-3/tp/issues/145)
+ [#148](https://github.com/AY2324S2-CS2113-W13-3/tp/issues/148)
+ [#151](https://github.com/AY2324S2-CS2113-W13-3/tp/issues/151)
+ [#155](https://github.com/AY2324S2-CS2113-W13-3/tp/issues/155)
+ [#157](https://github.com/AY2324S2-CS2113-W13-3/tp/issues/157)
+ [#161](https://github.com/AY2324S2-CS2113-W13-3/tp/issues/161)
+ * Developer guide review:
+ [#66](https://github.com/nus-cs2113-AY2324S2/tp/pull/66/files/e7ea92bbcca43bd71129a2d98f160f28587c4f12)
\ No newline at end of file
diff --git a/docs/team/genexus85.md b/docs/team/genexus85.md
new file mode 100644
index 0000000000..51dd204f35
--- /dev/null
+++ b/docs/team/genexus85.md
@@ -0,0 +1,45 @@
+# Ari Lim Ee Lik's Project Portfolio Page
+
+## Overview
+Wellness360 is a wellness app meant for stressed Engineering Students who prefer CLI over GUI
+and want to keep track of their overall wellbeing. Our app offers comprehensive tracking and management
+tools, providing personalized support to alleviate stress and enhance overall well-being with features
+including a Fitness Tracker, Focus Timer, Habit Tracker, Self Reflection and Sleep Tracker.
+
+Given below is the summary of my contributions.
+
+* Code contribution: [Reposense](https://nus-cs2113-ay2324s2.github.io/tp-dashboard/?search=genexus85&breakdown=true&sort=groupTitle%20dsc&sortWithin=title&since=2024-02-23&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other)
+* Feature: `Command Parser` [#13](https://github.com/AY2324S2-CS2113-T11-1/tp/issues/13)
+ * What it does: Common parser class that will delegate the user input to the specific feature command parser to determine the command for execution.
+ * Justification: By having a common parser class, it will make it easier to determine which feature command parser to receive the user input for parsing.
+ * Highlights: Handles exceptions such as unknown command exception.
+* Feature: `Habit Tracker` [#9](https://github.com/AY2324S2-CS2113-T11-1/tp/issues/9) [#11](https://github.com/AY2324S2-CS2113-T11-1/tp/issues/11) [#12](https://github.com/AY2324S2-CS2113-T11-1/tp/issues/12) [#64](https://github.com/AY2324S2-CS2113-T11-1/tp/issues/64) [#70](https://github.com/AY2324S2-CS2113-T11-1/tp/issues/70) [#71](https://github.com/AY2324S2-CS2113-T11-1/tp/issues/71)
+ * What it does: Allow user to track their habits, with commands to `add`, `list`, `delete`, `update habit count`, `set priority` and `sort` habits.
+ * Justification: [Refer to developer guide for design considerations](https://ay2324s2-cs2113-t11-1.github.io/tp/DeveloperGuide.html#design-considerations-4)
+ * Highlights: User friendly and intuitive commands.
+* User guide contributions:
+ * `Habit Tracker`
+ * [`habit add` command](https://ay2324s2-cs2113-t11-1.github.io/tp/UserGuide.html#add-a-new-habit-habit-add)
+ * [`habit list` command](https://ay2324s2-cs2113-t11-1.github.io/tp/UserGuide.html#list-out-all-habits-habit-list)
+ * [`habit update` command](https://ay2324s2-cs2113-t11-1.github.io/tp/UserGuide.html#update-habit-count-after-completing-a-habit-habit-update)
+ * [`habit delete` command](https://ay2324s2-cs2113-t11-1.github.io/tp/UserGuide.html#delete-a-habit-habit-delete)
+ * [`habit set` command](https://ay2324s2-cs2113-t11-1.github.io/tp/UserGuide.html#set-priority-of-habit-habit-set)
+ * [`habit sort` command](https://ay2324s2-cs2113-t11-1.github.io/tp/UserGuide.html#sort-habit-tracker-list-habit-sort)
+ * [`habit help` command](https://ay2324s2-cs2113-t11-1.github.io/tp/UserGuide.html#view-habit-tracker-help-menu-habit-help)
+* Developer guide contributions:
+ * [Command parser component](https://ay2324s2-cs2113-t11-1.github.io/tp/DeveloperGuide.html#command-parser-component)
+ * [Habit tracker component](https://ay2324s2-cs2113-t11-1.github.io/tp/DeveloperGuide.html#habit-tracker-component)
+ * [Manual testing of habit component](https://ay2324s2-cs2113-t11-1.github.io/tp/DeveloperGuide.html#habit-tracker-component-1)
+* Team based task contributions:
+ * [Involved in creating and managing issues](https://github.com/AY2324S2-CS2113-T11-1/tp/issues?q=is%3Aissue+is%3Aclosed)
+* Community:
+ * PE Dry Run 8 Bugs reported [#145](https://github.com/AY2324S2-CS2113-T12-4/tp/issues/145)
+ [#151](https://github.com/AY2324S2-CS2113-T12-4/tp/issues/151)
+ [#155](https://github.com/AY2324S2-CS2113-T12-4/tp/issues/155)
+ [#164](https://github.com/AY2324S2-CS2113-T12-4/tp/issues/164)
+ [#166](https://github.com/AY2324S2-CS2113-T12-4/tp/issues/166)
+ [#171](https://github.com/AY2324S2-CS2113-T12-4/tp/issues/171)
+ [#174](https://github.com/AY2324S2-CS2113-T12-4/tp/issues/174)
+ [#178](https://github.com/AY2324S2-CS2113-T12-4/tp/issues/178)
+
+ * DG review [#13](https://github.com/nus-cs2113-AY2324S2/tp/pull/13/files#diff-1a95edf069a4136e9cb71bee758b0dc86996f6051f0d438ec2c424557de7160b)
\ No newline at end of file
diff --git a/docs/team/jinghaoooo.md b/docs/team/jinghaoooo.md
new file mode 100644
index 0000000000..d98d94e140
--- /dev/null
+++ b/docs/team/jinghaoooo.md
@@ -0,0 +1,38 @@
+# Lim Jing Hao's Project Portfolio Page
+
+## Overview
+Wellness360 is a wellness app meant for stressed Engineering Students who prefer CLI over GUI
+and want to keep track of their overall well-being. Our app offers comprehensive tracking and management
+tools, providing personalized support to alleviate stress and enhance overall well-being with features
+including a Fitness Tracker, Focus Timer, Habit Tracker, Self Reflection and Sleep Tracker.
+
+### Summary of Team Project Contributions
+
+* Code contribution: [Reposense](https://nus-cs2113-ay2324s2.github.io/tp-dashboard/?search=jinghaoooo&breakdown=true&sort=groupTitle%20dsc&sortWithin=title&since=2024-02-23&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other)
+* Setting up exception packages: [#27](https://github.com/AY2324S2-CS2113-T11-1/tp/issues/27)
+ * What it does: Provides each feature with it's own exception class so that it can better characterised to each unique feature.
+ * Justification: Every feature has it's own set of exceptions to be thrown. Thus, downcasting wellness360Exception class to the different feature exception class will improve it's functionality and coding quality.
+* Setting up gradle assertion [#81](https://github.com/AY2324S2-CS2113-T11-1/tp/pull/81)
+* Releasing [Wellness360 v1.0](https://github.com/AY2324S2-CS2113-T11-1/tp/releases/tag/v1.0)
+* Feature: `FocusTimer` [#15](https://github.com/AY2324S2-CS2113-T11-1/tp/issues/15) [#19](https://github.com/AY2324S2-CS2113-T11-1/tp/issues/19) [#20](https://github.com/AY2324S2-CS2113-T11-1/tp/issues/20) [#62](https://github.com/AY2324S2-CS2113-T11-1/tp/issues/62) [#63](https://github.com/AY2324S2-CS2113-T11-1/tp/issues/63) [#68](https://github.com/AY2324S2-CS2113-T11-1/tp/issues/68) [#69](https://github.com/AY2324S2-CS2113-T11-1/tp/issues/69) [#78](https://github.com/AY2324S2-CS2113-T11-1/tp/issues/78)
+ * What it does: Allow users to use a focus timer, be it count up timer or count down timer.
+ * Justification: [Refer to developer guide for design considerations](https://ay2324s2-cs2113-t11-1.github.io/tp/DeveloperGuide.html#design-considerations-6)
+ * Highlights: User friendly and intuitive commands.
+* User Guide contributions:
+* `FocusTimer`
+ * [`focus switch` command](https://ay2324s2-cs2113-t11-1.github.io/tp/UserGuide.html#switch-focus-timer-mode-focus-switch)
+ * [`focus start` command](https://ay2324s2-cs2113-t11-1.github.io/tp/UserGuide.html#start-a-new-focus-timer-focus-start)
+ * [`focus stop` command](https://ay2324s2-cs2113-t11-1.github.io/tp/UserGuide.html#stop-the-current-focus-timer-focus-stop)
+ * [`focus pause` command](https://ay2324s2-cs2113-t11-1.github.io/tp/UserGuide.html#pause-the-current-focus-timer-focus-pause)
+ * [`focus resume` command](https://ay2324s2-cs2113-t11-1.github.io/tp/UserGuide.html#resume-the-current-focus-timer-focus-resume)
+ * [`focus check` command](https://ay2324s2-cs2113-t11-1.github.io/tp/UserGuide.html#check-time-for-focus-timer-focus-check)
+ * [`focus set` command](https://ay2324s2-cs2113-t11-1.github.io/tp/UserGuide.html#set-focus-timer-duration-focus-set)
+ * [`focus help` command](https://ay2324s2-cs2113-t11-1.github.io/tp/UserGuide.html#view-focus-timer-help-menu-focus-help)
+* Developer guide contributions:
+ * [Focus timer component](https://ay2324s2-cs2113-t11-1.github.io/tp/DeveloperGuide.html#focus-timer-component)
+ * [Manual testing of focus timer component](https://ay2324s2-cs2113-t11-1.github.io/tp/DeveloperGuide.html#focus-timer-component-1)
+* Team based task contributions:
+ * [Involved in creating and managing issues](https://github.com/AY2324S2-CS2113-T11-1/tp/issues?q=is%3Aissue+is%3Aclosed)
+* Community:
+ * PE Dry run bugs reported: [#82](https://github.com/AY2324S2-CS2113-T13-2/tp/issues/82) [#84](https://github.com/AY2324S2-CS2113-T13-2/tp/issues/84) [#85](https://github.com/AY2324S2-CS2113-T13-2/tp/issues/85) [#88](https://github.com/AY2324S2-CS2113-T13-2/tp/issues/88) [#90](https://github.com/AY2324S2-CS2113-T13-2/tp/issues/90) [#94](https://github.com/AY2324S2-CS2113-T13-2/tp/issues/94)
+ * Developer guide review: [#33](https://github.com/nus-cs2113-AY2324S2/tp/pull/33) [#54](https://github.com/nus-cs2113-AY2324S2/tp/pull/54)
\ No newline at end of file
diff --git a/docs/team/johndoe.md b/docs/team/johndoe.md
deleted file mode 100644
index ab75b391b8..0000000000
--- a/docs/team/johndoe.md
+++ /dev/null
@@ -1,6 +0,0 @@
-# John Doe - Project Portfolio Page
-
-## Overview
-
-
-### Summary of Contributions
diff --git a/docs/team/okw32.md b/docs/team/okw32.md
new file mode 100644
index 0000000000..ee735ed9c8
--- /dev/null
+++ b/docs/team/okw32.md
@@ -0,0 +1,39 @@
+# Ong Kan Wu's Project Portfolio Page
+
+## Overview
+Wellness360 is a wellness app meant for stressed Engineering Students who prefer CLI over GUI
+and want to keep track of their overall well-being. Our app offers comprehensive tracking and management
+tools, providing personalized support to alleviate stress and enhance overall well-being with features
+including a Fitness Tracker, Focus Timer, Habit Tracker, Self Reflection and Sleep Tracker.
+
+### Summary of Team Project Contributions
+
+* Code contribution: [Reposense](https://nus-cs2113-ay2324s2.github.io/tp-dashboard/?search=okw32&breakdown=true&sort=groupTitle%20dsc&sortWithin=title&since=2024-02-23&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other)
+
+* Feature: `Ui` [#22](https://github.com/AY2324S2-CS2113-T11-1/tp/issues/22)
+ * What it does: Common class shared by developers to display text and format output
+ * Justification: This abstraction simplifies and standardizes UI development by encapsulating common tasks like display programme's output
+* Feature: `Sleep Tracker` [#7](https://github.com/AY2324S2-CS2113-T11-1/tp/issues/7) [#8](https://github.com/AY2324S2-CS2113-T11-1/tp/issues/8) [#17](https://github.com/AY2324S2-CS2113-T11-1/tp/issues/17) [#57](https://github.com/AY2324S2-CS2113-T11-1/tp/issues/57) [#58](https://github.com/AY2324S2-CS2113-T11-1/tp/issues/58) [#59](https://github.com/AY2324S2-CS2113-T11-1/tp/issues/59) [#86](https://github.com/AY2324S2-CS2113-T11-1/tp/issues/86)
+ * User guide contributions: Allow users to keep track of sleep cycles
+ * Justification: [Refer to developer guide for design considerations](https://ay2324s2-cs2113-t11-1.github.io/tp/DeveloperGuide.html)
+ * Highlights: User friendly and intuitive commands.
+* User Guide contributions:
+* `SleepTracker`
+ * [`sleep add` command](https://ay2324s2-cs2113-t11-1.github.io/tp/UserGuide.html#add-a-new-sleep-cycle-sleep-add)
+ * [`sleep list` command](https://ay2324s2-cs2113-t11-1.github.io/tp/UserGuide.html#list-out-all-sleep-cycles-sleep-list)
+ * [`sleep get` command](https://ay2324s2-cs2113-t11-1.github.io/tp/UserGuide.html#get-hours-slept-on-specific-date-sleep-get)
+ * [`sleep update` command](https://ay2324s2-cs2113-t11-1.github.io/tp/UserGuide.html#update-hours-slept-on-specific-date)
+ * [`sleep delete` command](https://ay2324s2-cs2113-t11-1.github.io/tp/UserGuide.html#delete-sleep-cycles-sleep-delete)
+ * [`sleep save` command](https://ay2324s2-cs2113-t11-1.github.io/tp/UserGuide.html#save-sleep-cycles-sleep-save)
+ * [`sleep help` command](https://ay2324s2-cs2113-t11-1.github.io/tp/UserGuide.html#view-sleep-tracker-help-menu-sleep-help)
+* Developer guide contributions:
+ * [Storage component](https://ay2324s2-cs2113-t11-1.github.io/tp/DeveloperGuide.html#storage-component)
+ * [Sleep tracker component](https://ay2324s2-cs2113-t11-1.github.io/tp/DeveloperGuide.html#sleep-tracker-component)
+* Team based task contributions:
+ * [Involved in creating and managing issues](https://github.com/AY2324S2-CS2113-T11-1/tp/issues?q=is%3Aissue+is%3Aclosed)
+* Community:
+ * [Individual Project Coding Standard Review](https://github.com/nus-cs2113-AY2324S2/ip/pull/173)
+ * [Individual Project Code Quality Review](https://github.com/nus-cs2113-AY2324S2/ip/pull/139)
+ * Team Project Reviews:
+ * [Review 1](https://github.com/AY2324S2-CS2113-T11-1/tp/pull/88#discussion_r1536734904)
+ * [Review 2](https://github.com/AY2324S2-CS2113-T11-1/tp/pull/119#discussion_r1544293656)
\ No newline at end of file
diff --git a/resources/question_bank.txt b/resources/question_bank.txt
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/src/main/java/Main.java b/src/main/java/Main.java
new file mode 100644
index 0000000000..91d6c4ee62
--- /dev/null
+++ b/src/main/java/Main.java
@@ -0,0 +1,47 @@
+import commands.Command;
+import exceptions.Wellness360Exception;
+import fitness.FitnessMotivator;
+import focus.FocusTimer;
+import habit.HabitTracker;
+import parser.Parser;
+import reflection.ReflectionManager;
+import ui.Ui;
+
+import java.util.Scanner;
+import sleep.SleepTracker;
+
+
+public class Main {
+ private static Scanner scanner = new Scanner(System.in);
+ private static boolean isExit = false;
+
+ public static void main(String[] args) {
+
+ SleepTracker sleepTracker = new SleepTracker();
+ ReflectionManager reflectionManager = new ReflectionManager();
+ HabitTracker habitTracker = new HabitTracker();
+ FocusTimer focusTimer = new FocusTimer();
+ FitnessMotivator fitnessMotivator = new FitnessMotivator();
+
+ Ui.greetUser();
+
+ while (!isExit) {
+
+ Ui.promptUserInput();
+
+ String userInput = scanner.nextLine();
+
+ //execute user command if it is valid else throw exception
+ try {
+ Command userCommand = Parser.determineCommand(sleepTracker, reflectionManager,
+ habitTracker, focusTimer, fitnessMotivator, userInput);
+ userCommand.execute();
+ isExit = userCommand.isExit();
+ } catch (Wellness360Exception e) {
+ Ui.printMessageWithSepNewLine(e.getMessage());
+ }
+ }
+
+ Ui.sayGoodbye();
+ }
+}
diff --git a/src/main/java/commands/Command.java b/src/main/java/commands/Command.java
new file mode 100644
index 0000000000..8f21833b19
--- /dev/null
+++ b/src/main/java/commands/Command.java
@@ -0,0 +1,9 @@
+package commands;
+
+import exceptions.Wellness360Exception;
+
+public interface Command {
+
+ void execute() throws Wellness360Exception;
+ boolean isExit();
+}
diff --git a/src/main/java/commands/ExitCommand.java b/src/main/java/commands/ExitCommand.java
new file mode 100644
index 0000000000..2bb1ac407b
--- /dev/null
+++ b/src/main/java/commands/ExitCommand.java
@@ -0,0 +1,28 @@
+package commands;
+
+import exceptions.Wellness360Exception;
+import focus.FocusTimer;
+
+public class ExitCommand implements Command {
+ FocusTimer focustimer;
+
+ public ExitCommand( FocusTimer timer, String commandArgs) throws Wellness360Exception {
+ if (!commandArgs.isEmpty()) {
+ throw new Wellness360Exception("Unknown command");
+ }
+ focustimer = timer;
+ }
+
+ @Override
+ public void execute() {
+ if(focustimer.timerMode) {
+ focustimer.setStopTiming();
+ }
+ }
+
+
+ @Override
+ public boolean isExit() {
+ return true;
+ }
+}
diff --git a/src/main/java/commands/fitnesscommands/AddExerciseCommand.java b/src/main/java/commands/fitnesscommands/AddExerciseCommand.java
new file mode 100644
index 0000000000..2ef191bef2
--- /dev/null
+++ b/src/main/java/commands/fitnesscommands/AddExerciseCommand.java
@@ -0,0 +1,77 @@
+package commands.fitnesscommands;
+
+import java.util.Arrays;
+import commands.Command;
+import exceptions.FitnessException;
+import fitness.ExerciseType;
+import fitness.FitnessMotivator;
+
+import static commands.fitnesscommands.ErrorMessageConstants.ILLEGAL_TYPE_ERROR_MESSAGE;
+import static commands.fitnesscommands.ErrorMessageConstants.INCORRECT_SETS_REPS_ERROR_MESSAGE;
+import static commands.fitnesscommands.ErrorMessageConstants.INSUFFICIENT_ADD_PARAMS_ERROR_MESSAGE;
+import static fitness.FitnessMotivator.REQUIRED_NUM_OF_PARAMETERS;
+
+public class AddExerciseCommand implements Command {
+
+ private FitnessMotivator fitnessMotivator;
+ private String[] commandArgs;
+
+ public AddExerciseCommand(FitnessMotivator fitnessMotivator, String commandArgs)
+ throws FitnessException {
+ this.fitnessMotivator = fitnessMotivator;
+ this.commandArgs = checkCommandArgs(commandArgs);
+ }
+
+ /**
+ * Validates the command argument given for the fitness add command.
+ *
+ * @param commandArgs A string of arguments
+ * @return A split array of strings of size 4 if there are no issues found with the string
+ * input
+ *
+ * @throws FitnessException Thrown when improper command arguments are found
+ * */
+ private String[] checkCommandArgs(String commandArgs) throws FitnessException {
+ String[] processCommandArgs = commandArgs.split("/type|/name|/sets|/reps", 5);
+
+ // Handles insufficient parameters entered
+ if (processCommandArgs.length != REQUIRED_NUM_OF_PARAMETERS + 1) {
+ throw new FitnessException(INSUFFICIENT_ADD_PARAMS_ERROR_MESSAGE);
+ }
+
+ // Remove redundant first string in the string array
+ String[] tempCommandArgs = Arrays.copyOfRange(processCommandArgs, 1, 5);
+
+ // String Cleaning
+ tempCommandArgs[0] = tempCommandArgs[0].trim();
+ tempCommandArgs[1] = tempCommandArgs[1].trim();
+ tempCommandArgs[2] = tempCommandArgs[2].trim();
+ tempCommandArgs[3] = tempCommandArgs[3].trim();
+
+ // Handles the case where non-integer values are entered in parameters that should only
+ // be integers
+ if (!tempCommandArgs[2].matches("\\d+") ||
+ !tempCommandArgs[3].matches("\\d+")) {
+ throw new FitnessException(INCORRECT_SETS_REPS_ERROR_MESSAGE);
+ }
+
+ // Checks that the entered type belongs to one of the ExerciseType Enum
+ try {
+ String exerciseTypeString = tempCommandArgs[0].toUpperCase().trim();
+ ExerciseType.valueOf(exerciseTypeString);
+ } catch (IllegalArgumentException e) {
+ throw new FitnessException(ILLEGAL_TYPE_ERROR_MESSAGE);
+ }
+ return tempCommandArgs;
+ }
+
+ @Override
+ public void execute() throws FitnessException {
+ fitnessMotivator.addExercises(commandArgs);
+ }
+
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+}
diff --git a/src/main/java/commands/fitnesscommands/DeleteExerciseCommand.java b/src/main/java/commands/fitnesscommands/DeleteExerciseCommand.java
new file mode 100644
index 0000000000..bb9d96b98a
--- /dev/null
+++ b/src/main/java/commands/fitnesscommands/DeleteExerciseCommand.java
@@ -0,0 +1,79 @@
+package commands.fitnesscommands;
+
+import commands.Command;
+import exceptions.FitnessException;
+import fitness.FitnessMotivator;
+import fitness.ExerciseType;
+
+import static commands.fitnesscommands.ErrorMessageConstants.ILLEGAL_TYPE_ERROR_MESSAGE;
+import static commands.fitnesscommands.ErrorMessageConstants.INVALID_DELETE_INDEX_ERROR_MESSAGE;
+import static commands.fitnesscommands.ErrorMessageConstants.INSUFFICIENT_DELETE_PARAMS_ERROR_MESSAGE;
+
+public class DeleteExerciseCommand implements Command {
+
+ public static final int REQUIRED_DELETE_PARAMS = 2;
+ private FitnessMotivator fitnessMotivator;
+ private String[] commandArgs;
+
+ public DeleteExerciseCommand(FitnessMotivator fitnessMotivator, String commandArgs)
+ throws FitnessException {
+ this.fitnessMotivator = fitnessMotivator;
+ this.commandArgs = checkCommandArgs(commandArgs);
+ }
+
+ /**
+ * Validates the command argument given for the fitness delete command.
+ *
+ * @param commandArgs A string of arguments
+ * @return A split array of strings of size 2 if there are no issues found with the string
+ * input
+ *
+ * @throws FitnessException Thrown when improper command arguments are found
+ * */
+ private String[] checkCommandArgs(String commandArgs) throws FitnessException {
+ String[] tempCommandArgs = commandArgs.split(" ");
+
+ // Handles insufficient parameters entered
+ if (tempCommandArgs.length != REQUIRED_DELETE_PARAMS) {
+ throw new FitnessException(INSUFFICIENT_DELETE_PARAMS_ERROR_MESSAGE);
+ }
+
+ // String Cleaning
+ tempCommandArgs[0] = tempCommandArgs[0].trim().toUpperCase();
+ tempCommandArgs[1] = tempCommandArgs[1].trim();
+
+ // Checks that the entered type belongs to one of the ExerciseType Enum
+ try {
+ ExerciseType.valueOf(tempCommandArgs[0]);
+ } catch (IllegalArgumentException e) {
+ throw new FitnessException(ILLEGAL_TYPE_ERROR_MESSAGE);
+ }
+
+ ExerciseType type = ExerciseType.valueOf(tempCommandArgs[0]);
+
+ int maxIndex = fitnessMotivator.allExercises.size(type);
+
+ // Handles the case where non-integer values are entered in parameters
+ if (!tempCommandArgs[1].matches("\\d+")) {
+ throw new FitnessException(INVALID_DELETE_INDEX_ERROR_MESSAGE);
+ }
+
+ // Handles the case where the integer entered is out of bounds
+ int index = Integer.parseInt(tempCommandArgs[1]);
+ if (index > maxIndex || index <= 0) {
+ throw new FitnessException(INVALID_DELETE_INDEX_ERROR_MESSAGE);
+ }
+
+ return tempCommandArgs;
+ }
+
+ @Override
+ public void execute() throws FitnessException {
+ fitnessMotivator.deleteExercise(commandArgs);
+ }
+
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+}
diff --git a/src/main/java/commands/fitnesscommands/ErrorMessageConstants.java b/src/main/java/commands/fitnesscommands/ErrorMessageConstants.java
new file mode 100644
index 0000000000..65d1a33a45
--- /dev/null
+++ b/src/main/java/commands/fitnesscommands/ErrorMessageConstants.java
@@ -0,0 +1,47 @@
+package commands.fitnesscommands;
+
+/**
+ * Class that stores static constants of error messages
+ * */
+public class ErrorMessageConstants {
+ public static final String INSUFFICIENT_ADD_PARAMS_ERROR_MESSAGE =
+ "Forgetting something? Key in the correct parameters please!" +
+ System.lineSeparator() + System.lineSeparator() +
+ "Example: fitness add , , , ";
+
+ public static final String INSUFFICIENT_DELETE_PARAMS_ERROR_MESSAGE =
+ "Key in the correct parameters please!" +
+ System.lineSeparator() + System.lineSeparator() +
+ "Example: fitness delete ";
+
+ public static final String ILLEGAL_TYPE_ERROR_MESSAGE = "Hmm...Invalid type of exercise..." +
+ System.lineSeparator() + "Only the following exercise types are allowed: " +
+ "Arms, Chest, Abs, Back and Legs!";
+
+ public static final String INCORRECT_SETS_REPS_ERROR_MESSAGE =
+ "Did you enter your Sets and Reps correctly? :(";
+
+ public static final String ADDITIONAL_PARAMS_ERROR_MESSAGE =
+ "Additional parameters detected. Don't be extra please.";
+
+ public static final String INVALID_DELETE_INDEX_ERROR_MESSAGE =
+ "Please use a valid delete index. " +
+ "Use 'fitness get [Exercise_Type]' to get the indexes!";
+
+ public static final String ILLEGAL_GOAL_PARAMS_ERROR_MESSAGE =
+ "Are you trying to create a new goal? You can try 'fitness goal new'!" +
+ System.lineSeparator() +
+ "You can also do 'fitness goal ' to mark and unmark exercise goals!" +
+ System.lineSeparator() +
+ "Do 'fitness goal' to check the index of each exercise goal.";
+
+ public static final String COMMAND_SUGGESTION_MESSAGE =
+ "Unknown command. Did you try any of the following command types?"
+ + System.lineSeparator() +
+ "- 'fitness get'" + System.lineSeparator() +
+ "- 'fitness add'" + System.lineSeparator() +
+ "- 'fitness goal'" + System.lineSeparator() +
+ "- 'fitness delete'" + System.lineSeparator() +
+ "Do 'fitness help' to see what are the exact commands and " +
+ "parameters which are accepted!";
+}
diff --git a/src/main/java/commands/fitnesscommands/GetExercisesCommand.java b/src/main/java/commands/fitnesscommands/GetExercisesCommand.java
new file mode 100644
index 0000000000..698a5d14ff
--- /dev/null
+++ b/src/main/java/commands/fitnesscommands/GetExercisesCommand.java
@@ -0,0 +1,62 @@
+package commands.fitnesscommands;
+
+import commands.Command;
+import exceptions.FitnessException;
+import fitness.ExerciseType;
+import fitness.FitnessMotivator;
+
+import static commands.fitnesscommands.ErrorMessageConstants.ILLEGAL_TYPE_ERROR_MESSAGE;
+
+public class GetExercisesCommand implements Command {
+
+ private FitnessMotivator fitnessMotivator;
+ private ExerciseType exerciseType;
+
+ public GetExercisesCommand (FitnessMotivator fitnessMotivator, String commandArgs)
+ throws FitnessException {
+ this.fitnessMotivator = fitnessMotivator;
+ exerciseType = checkCommandArgs(commandArgs);
+ }
+
+ /**
+ * Validates the command argument given for the fitness get command. In this command, the
+ * argument is optional.
+ *
+ * @param commandArgs An argument string
+ * @return null if there is no argument provided, otherwise returns an Object of type
+ * ExerciseType for further processing
+ *
+ * @throws FitnessException Thrown when improper command arguments are found
+ * */
+ private ExerciseType checkCommandArgs(String commandArgs) throws FitnessException {
+ String exerciseTypeString = commandArgs.toUpperCase().trim();
+
+ // Checks if the arguments entered is empty
+ if (exerciseTypeString.isEmpty() || exerciseTypeString.isBlank()) {
+ return null;
+ }
+
+ // Checks that the entered type belongs to one of the ExerciseType Enum. If it is, return
+ // the type, else throws an error
+ try {
+ return ExerciseType.valueOf(exerciseTypeString);
+ } catch (IllegalArgumentException e) {
+ throw new FitnessException(ILLEGAL_TYPE_ERROR_MESSAGE);
+ }
+ }
+
+
+ @Override
+ public void execute() throws FitnessException {
+ if (exerciseType == null) {
+ fitnessMotivator.getExercises();
+ } else {
+ fitnessMotivator.getTypeExercises(exerciseType);
+ }
+ }
+
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+}
diff --git a/src/main/java/commands/fitnesscommands/GoalExerciseCommand.java b/src/main/java/commands/fitnesscommands/GoalExerciseCommand.java
new file mode 100644
index 0000000000..ee97026b6c
--- /dev/null
+++ b/src/main/java/commands/fitnesscommands/GoalExerciseCommand.java
@@ -0,0 +1,67 @@
+package commands.fitnesscommands;
+
+import commands.Command;
+import exceptions.FitnessException;
+import fitness.FitnessMotivator;
+
+import static commands.fitnesscommands.ErrorMessageConstants.ILLEGAL_GOAL_PARAMS_ERROR_MESSAGE;
+
+public class GoalExerciseCommand implements Command {
+
+ private FitnessMotivator fitnessMotivator;
+ private String commandArgs;
+
+ public GoalExerciseCommand(FitnessMotivator fitnessMotivator, String commandArgs) {
+ this.fitnessMotivator = fitnessMotivator;
+ this.commandArgs = commandArgs;
+ }
+
+ /**
+ * Validates the command argument given for the fitness goal command. In this command, the
+ * argument is optional.
+ *
+ * @param commandArgs An argument string
+ * @return null if there is no argument provided, otherwise returns a String for
+ * further processing
+ *
+ * @throws FitnessException Thrown when improper command arguments are found
+ * */
+ private String checkCommandArgs(String commandArgs) throws FitnessException {
+ if (commandArgs.isBlank() || commandArgs.isEmpty()) {
+ return null;
+ }
+ commandArgs = commandArgs.trim();
+ if (commandArgs.equalsIgnoreCase("new")) {
+ return "new";
+ }
+ if (commandArgs.matches("^[1-5]$")) {
+ return commandArgs;
+ }
+ throw new FitnessException(ILLEGAL_GOAL_PARAMS_ERROR_MESSAGE);
+ }
+
+ @Override
+ public void execute() throws FitnessException {
+ String parsedCommand = checkCommandArgs(commandArgs);
+
+ if (parsedCommand == null) {
+ // 'fitness goal' command
+ fitnessMotivator.goalStatus();
+ } else {
+ if (parsedCommand.contentEquals("new")) {
+ // 'fitness goal new' command
+ fitnessMotivator.newGoals();
+ }
+ if (parsedCommand.matches("^[1-5]$")) {
+ // 'fitness goal ' command
+ int exerciseIndex = Integer.parseInt(parsedCommand);
+ fitnessMotivator.toggleGoal(exerciseIndex);
+ }
+ }
+ }
+
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+}
diff --git a/src/main/java/commands/fitnesscommands/HelpExerciseCommand.java b/src/main/java/commands/fitnesscommands/HelpExerciseCommand.java
new file mode 100644
index 0000000000..5f3d5250cd
--- /dev/null
+++ b/src/main/java/commands/fitnesscommands/HelpExerciseCommand.java
@@ -0,0 +1,34 @@
+package commands.fitnesscommands;
+
+import commands.Command;
+import exceptions.FitnessException;
+import fitness.FitnessMotivator;
+
+import static commands.fitnesscommands.ErrorMessageConstants.ADDITIONAL_PARAMS_ERROR_MESSAGE;
+
+public class HelpExerciseCommand implements Command {
+
+ private FitnessMotivator fitnessMotivator;
+
+ public HelpExerciseCommand (FitnessMotivator fitnessMotivator, String commandArgs)
+ throws FitnessException {
+ this.fitnessMotivator = fitnessMotivator;
+ checkCommandArgs(commandArgs);
+ }
+
+ private void checkCommandArgs(String commandArgs) throws FitnessException {
+ if (!commandArgs.isBlank()) {
+ throw new FitnessException(ADDITIONAL_PARAMS_ERROR_MESSAGE);
+ }
+ }
+
+ @Override
+ public void execute() throws FitnessException {
+ fitnessMotivator.printHelp();
+ }
+
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+}
diff --git a/src/main/java/commands/focuscommands/CheckTimeCommand.java b/src/main/java/commands/focuscommands/CheckTimeCommand.java
new file mode 100644
index 0000000000..bda26d437d
--- /dev/null
+++ b/src/main/java/commands/focuscommands/CheckTimeCommand.java
@@ -0,0 +1,44 @@
+package commands.focuscommands;
+
+import commands.Command;
+import exceptions.FocusException;
+import focus.FocusTimer;
+
+/**
+ * Represents the command to check the current time for focus timer object.
+ */
+public class CheckTimeCommand implements Command {
+ FocusTimer focustimer;
+
+ /**
+ * Constructs a StopTimerCommand with the concerned focus timer.
+ *
+ * @param timer FocusTimer object to be stopped.
+ */
+ public CheckTimeCommand(FocusTimer timer) {
+ focustimer = timer;
+ }
+
+ /**
+ * Executes the command by checking the current timing of the timer.
+ *
+ * @throws FocusException If the status of focus timer is not currently running.
+ */
+ @Override
+ public void execute() throws FocusException {
+ if(!focustimer.getStartStatus()) {
+ throw new FocusException("Timer have not started. Please use focus start.");
+ }
+ focustimer.checkTime();
+ }
+
+ /**
+ * Determines whether the command is an exit message.
+ *
+ * @return Returns false since this is not an exit command.
+ */
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+}
diff --git a/src/main/java/commands/focuscommands/FocusHelpCommand.java b/src/main/java/commands/focuscommands/FocusHelpCommand.java
new file mode 100644
index 0000000000..9ae8f9f4ae
--- /dev/null
+++ b/src/main/java/commands/focuscommands/FocusHelpCommand.java
@@ -0,0 +1,39 @@
+package commands.focuscommands;
+
+import commands.Command;
+import ui.Ui;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+
+/**
+ * Represents a command to display the focus timer help menu
+ */
+public class FocusHelpCommand implements Command {
+
+ private static final String[] HELP_MENU_INSTRUCTIONS = {
+ "focus switch: Switch between count up timer and count down timer",
+ "focus start: Start the timer",
+ "focus stop: Stop the timer",
+ "focus pause: Pause the timer",
+ "focus resume: Resume the timer",
+ "focus check: Check the time elapsed/remaining for the current timer, " +
+ "depending on the timer currently in use.",
+ "focus set [minutes]: Set the desired countdown timer duration in minutes"
+ };
+
+ @Override
+ public void execute() {
+ ArrayList helpMenuInstructionsList = new ArrayList<>(Arrays.asList(HELP_MENU_INSTRUCTIONS));
+
+ assert helpMenuInstructionsList.size() == 7 : "Help menu should have 6 instructions";
+
+ Ui.printList(helpMenuInstructionsList, "Commands for focus timer feature:");
+ }
+
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+}
diff --git a/src/main/java/commands/focuscommands/SetPauseCommand.java b/src/main/java/commands/focuscommands/SetPauseCommand.java
new file mode 100644
index 0000000000..06811f6632
--- /dev/null
+++ b/src/main/java/commands/focuscommands/SetPauseCommand.java
@@ -0,0 +1,53 @@
+package commands.focuscommands;
+
+import commands.Command;
+import exceptions.FocusException;
+import focus.FocusTimer;
+
+/**
+ * Represents the command to pause the current timer for focus timer object.
+ */
+public class SetPauseCommand implements Command {
+ public FocusTimer focusTimer;
+
+ /**
+ * Constructs a StopTimerCommand with the concerned focus timer.
+ *
+ * @param timer FocusTimer object to be stopped.
+ */
+ public SetPauseCommand(FocusTimer timer) {
+ focusTimer = timer;
+ }
+
+ /**
+ * Checks whether the timer is currently running or is currently being paused.
+ *
+ * @return true if timer is currently paused or hasn't started. False otherwise.
+ */
+ public boolean timerNotStartedOrPaused() {
+ return (focusTimer.getPausedStatus() || !focusTimer.getStartStatus());
+ }
+
+ /**
+ * Executes the command by pausing the timing of the timer.
+ *
+ * @throws FocusException If the status of focus timer is not currently running or is in paused state.
+ */
+ @Override
+ public void execute() throws FocusException {
+ if (timerNotStartedOrPaused()) {
+ throw new FocusException("Timer is already paused or Timer hasn't started.");
+ }
+ focusTimer.setPauseTiming();
+ }
+
+ /**
+ * Determines whether the command is an exit message.
+ *
+ * @return Returns false since this is not an exit command.
+ */
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+}
diff --git a/src/main/java/commands/focuscommands/SetResumeCommand.java b/src/main/java/commands/focuscommands/SetResumeCommand.java
new file mode 100644
index 0000000000..719b50cac2
--- /dev/null
+++ b/src/main/java/commands/focuscommands/SetResumeCommand.java
@@ -0,0 +1,52 @@
+package commands.focuscommands;
+
+import commands.Command;
+import exceptions.FocusException;
+import focus.FocusTimer;
+
+/**
+ * Represents the command to resume the current timer for focus timer object.
+ */
+public class SetResumeCommand implements Command {
+ public FocusTimer focusTimer;
+
+ /**
+ * Constructs a StopTimerCommand with the concerned focus timer.
+ *
+ * @param timer FocusTimer object to be stopped.
+ */
+ public SetResumeCommand(FocusTimer timer) {
+ focusTimer = timer;
+ }
+
+ /**
+ * Checks whether the timer is currently running or is currently being paused.
+ *
+ * @return true if timer has been started and is currently in the paused state.
+ */
+ public boolean timerStartedAndPaused() {
+ return (focusTimer.getStartStatus() && focusTimer.getPausedStatus());
+ }
+
+ /**
+ * Executes the command by resuming the timing of the timer.
+ *
+ * @throws FocusException If the status of focus timer is not currently running or is not in paused state.
+ */
+ public void execute() throws FocusException {
+ if (!timerStartedAndPaused()) {
+ throw new FocusException("Timer is already resumed or Timer hasn't started.");
+ }
+ focusTimer.setResumeTiming();
+ }
+
+ /**
+ * Determines whether the command is an exit message.
+ *
+ * @return Returns false since this is not an exit command.
+ */
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+}
diff --git a/src/main/java/commands/focuscommands/SetTimingCommand.java b/src/main/java/commands/focuscommands/SetTimingCommand.java
new file mode 100644
index 0000000000..71e2872821
--- /dev/null
+++ b/src/main/java/commands/focuscommands/SetTimingCommand.java
@@ -0,0 +1,50 @@
+package commands.focuscommands;
+
+import commands.Command;
+import exceptions.FocusException;
+import focus.FocusTimer;
+
+/**
+ * Represents the command to set duration for focus timer object countdown timer.
+ */
+public class SetTimingCommand implements Command {
+ private FocusTimer focusTimer;
+ private int desiredDuration;
+
+ /**
+ * Constructs a SetTimingCommand with the concerned focus timer and input timing.
+ *
+ * @throws FocusException If the input duration is invalid
+ */
+ public SetTimingCommand(FocusTimer timer, String userInput) throws FocusException {
+ focusTimer = timer;
+ String[] parts = userInput.trim().split("\\s", 2);
+
+ try {
+ this.desiredDuration = Integer.parseInt(parts[0]);
+ } catch (NumberFormatException e) {
+ String invalidIndex = "Invalid duration.";
+ throw new FocusException(invalidIndex);
+ }
+ }
+
+ /**
+ * Executes the command by resuming the timing of the timer.
+ *
+ */
+ @Override
+ public void execute() {
+ focusTimer.setDuration(desiredDuration);
+ }
+
+ /**
+ * Determines whether the command is an exit message.
+ *
+ * @return Returns false since this is not an exit command.
+ */
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+
+}
diff --git a/src/main/java/commands/focuscommands/StartTimerCommand.java b/src/main/java/commands/focuscommands/StartTimerCommand.java
new file mode 100644
index 0000000000..8c74707a5b
--- /dev/null
+++ b/src/main/java/commands/focuscommands/StartTimerCommand.java
@@ -0,0 +1,44 @@
+package commands.focuscommands;
+
+import commands.Command;
+import exceptions.FocusException;
+import focus.FocusTimer;
+
+/**
+ * Represents the command to start the timer for focus timer object.
+ */
+public class StartTimerCommand implements Command {
+ public FocusTimer focusTimer;
+
+ /**
+ * Constructs a StartTimerCommand with the concerned focus timer.
+ *
+ * @param timer FocusTimer object to be started.
+ */
+ public StartTimerCommand(FocusTimer timer) {
+ focusTimer = timer;
+ }
+
+ /**
+ * Executes the command by setting the start timing of the timer.
+ *
+ * @throws FocusException If the status of focus timer is already running.
+ */
+ @Override
+ public void execute() throws FocusException {
+ if (focusTimer.getStartStatus()) {
+ throw new FocusException("Error! Clock has already started.");
+ }
+ focusTimer.setStartTiming();
+ }
+
+ /**
+ * Determines whether the command is an exit message.
+ *
+ * @return Returns false since this is not an exit command.
+ */
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+}
diff --git a/src/main/java/commands/focuscommands/StopTimerCommand.java b/src/main/java/commands/focuscommands/StopTimerCommand.java
new file mode 100644
index 0000000000..54b39da7a9
--- /dev/null
+++ b/src/main/java/commands/focuscommands/StopTimerCommand.java
@@ -0,0 +1,44 @@
+package commands.focuscommands;
+
+import commands.Command;
+import exceptions.FocusException;
+import focus.FocusTimer;
+
+/**
+ * Represents the command to stop the timer for focus timer object.
+ */
+public class StopTimerCommand implements Command {
+ public FocusTimer focusTimer;
+
+ /**
+ * Constructs a StopTimerCommand with the concerned focus timer.
+ *
+ * @param timer FocusTimer object to be stopped.
+ */
+ public StopTimerCommand(FocusTimer timer) {
+ focusTimer = timer;
+ }
+
+ /**
+ * Executes the command by setting the stop timing of the timer.
+ *
+ * @throws FocusException If the status of focus timer is not currently running.
+ */
+ @Override
+ public void execute() throws FocusException {
+ if (!focusTimer.getStartStatus()) {
+ throw new FocusException("Error! Clock is not running.");
+ }
+ focusTimer.setStopTiming();
+ }
+
+ /**
+ * Determines whether the command is an exit message.
+ *
+ * @return Returns false since this is not an exit command.
+ */
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+}
diff --git a/src/main/java/commands/focuscommands/SwitchTimerCommand.java b/src/main/java/commands/focuscommands/SwitchTimerCommand.java
new file mode 100644
index 0000000000..4c6bc517ce
--- /dev/null
+++ b/src/main/java/commands/focuscommands/SwitchTimerCommand.java
@@ -0,0 +1,45 @@
+package commands.focuscommands;
+
+import commands.Command;
+import exceptions.FocusException;
+import focus.FocusTimer;
+
+/**
+ * Represents the command to switch between count up timer and count down timer of focus timer.
+ */
+public class SwitchTimerCommand implements Command {
+
+ public FocusTimer focusTimer;
+
+ /**
+ * Constructs a StopTimerCommand with the concerned focus timer.
+ *
+ * @param timer FocusTimer object to be stopped.
+ */
+ public SwitchTimerCommand(FocusTimer timer) {
+ focusTimer = timer;
+ }
+
+ /**
+ * Executes the command by switching the mode of the timer.
+ *
+ * @throws FocusException If the status of focus timer is not currently running.
+ */
+ @Override
+ public void execute() throws FocusException {
+ if (focusTimer.getStartStatus()) {
+ throw new FocusException("Unable to change as timer is running.");
+ }
+ focusTimer.switchTimer();
+ }
+
+ /**
+ * Determines whether the command is an exit message.
+ *
+ * @return Returns false since this is not an exit command.
+ */
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+}
diff --git a/src/main/java/commands/habitcommands/AddHabitCommand.java b/src/main/java/commands/habitcommands/AddHabitCommand.java
new file mode 100644
index 0000000000..4a4a08dfab
--- /dev/null
+++ b/src/main/java/commands/habitcommands/AddHabitCommand.java
@@ -0,0 +1,47 @@
+package commands.habitcommands;
+
+import commands.Command;
+import exceptions.HabitException;
+import habit.Habit;
+import habit.HabitTracker;
+
+/**
+ * Represents a command to add new habits.
+ */
+public class AddHabitCommand implements Command {
+ private HabitTracker habitTracker;
+ private Habit newHabit;
+
+ /**
+ * Constructs a AddHabitCommand object with user input.
+ *
+ * @param habitTracker The HabitTracker instance to be used for storing the habits.
+ * @param habitCommandArgs User input for the add habit command.
+ * @throws HabitException If there are any formatting issues.
+ */
+ public AddHabitCommand(HabitTracker habitTracker, String habitCommandArgs) throws HabitException {
+ this.habitTracker = habitTracker;
+
+ if (habitCommandArgs.isEmpty()) {
+ throw new HabitException("Habit Description cannot be left empty.");
+ }
+
+ this.newHabit = new Habit(habitCommandArgs.trim());
+ }
+
+ /**
+ * Execute the command to add a new habit into the habit tracker.
+ *
+ * @throws HabitException If there are any formatting issues.
+ */
+ @Override
+ public void execute() throws HabitException {
+ habitTracker.addHabit(newHabit);
+ }
+
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+}
+
diff --git a/src/main/java/commands/habitcommands/DeleteHabitCommand.java b/src/main/java/commands/habitcommands/DeleteHabitCommand.java
new file mode 100644
index 0000000000..7680036bef
--- /dev/null
+++ b/src/main/java/commands/habitcommands/DeleteHabitCommand.java
@@ -0,0 +1,55 @@
+package commands.habitcommands;
+
+import commands.Command;
+import exceptions.HabitException;
+import habit.HabitTracker;
+
+/**
+ * Represents a command to add new habits.
+ */
+public class DeleteHabitCommand implements Command{
+
+ private static final int REQUIRED_PARAMETERS = 2;
+ private HabitTracker habitTracker;
+ private int habitID;
+
+ /**
+ * Constructs a DeleteHabitCommand object with user input.
+ *
+ * @param habitTracker The HabitTracker instance to be used for storing the habits.
+ * @param habitCommandArgs User input for the delete habit command.
+ * @throws HabitException If there are any formatting issues.
+ */
+ public DeleteHabitCommand(HabitTracker habitTracker, String habitCommandArgs) throws HabitException {
+ this.habitTracker = habitTracker;
+
+ String[] parts = habitCommandArgs.trim().split("\\s+");
+
+ if (parts.length != REQUIRED_PARAMETERS) {
+ throw new HabitException("Please provide a habit ID.\n" +
+ "Use Format: habit delete /id ");
+ }
+
+ try {
+ this.habitID = Integer.parseInt(parts[1].trim());
+ } catch (NumberFormatException e) {
+ throw new HabitException("Please provide a valid habit ID.");
+ }
+ }
+
+ /**
+ * Execute the command to delete a habit from the habit tracker.
+ *
+ * @throws HabitException If there are any formatting issues.
+ */
+ @Override
+ public void execute() throws HabitException {
+ habitTracker.deleteHabit(habitID);
+ }
+
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+
+}
diff --git a/src/main/java/commands/habitcommands/HabitHelpCommand.java b/src/main/java/commands/habitcommands/HabitHelpCommand.java
new file mode 100644
index 0000000000..77f0779f99
--- /dev/null
+++ b/src/main/java/commands/habitcommands/HabitHelpCommand.java
@@ -0,0 +1,36 @@
+package commands.habitcommands;
+
+import commands.Command;
+import ui.Ui;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Represents a command to display the habit tracker help menu
+ */
+public class HabitHelpCommand implements Command {
+ private static final String[] HELP_MENU_INSTRUCTIONS = {
+ "habit add : Add a new habit",
+ "habit list: List out all existing habits",
+ "habit update /id /by : Increase habit count after completing a habit",
+ "habit delete /id : Delete a habit",
+ "habit set /id /priority : Set priority level for habits (HIGH, MED, LOW)",
+ "habit sort: Sort habit list according to priority level",
+ };
+
+ /**
+ * Execute the command to display the habit tracker help menu
+ */
+ public void execute() {
+ ArrayList helpMenuInstructionsList = new ArrayList<>(Arrays.asList(HELP_MENU_INSTRUCTIONS));
+
+ assert helpMenuInstructionsList.size() == 6 : "Help menu should have 6 instructions";
+
+ Ui.printList(helpMenuInstructionsList, "Commands for habit tracker feature:");
+ }
+
+ public boolean isExit() {
+ return false;
+ }
+}
diff --git a/src/main/java/commands/habitcommands/ListHabitsCommand.java b/src/main/java/commands/habitcommands/ListHabitsCommand.java
new file mode 100644
index 0000000000..911a6f4e33
--- /dev/null
+++ b/src/main/java/commands/habitcommands/ListHabitsCommand.java
@@ -0,0 +1,33 @@
+package commands.habitcommands;
+
+import commands.Command;
+import habit.HabitTracker;
+
+/**
+ * Represents a command to display a list of all habits
+ */
+public class ListHabitsCommand implements Command {
+ private HabitTracker habitTracker;
+
+ /**
+ * Constructs a ListHabitsCommand object.
+ *
+ * @param habitTracker The HabitTracker instance to be used for storing the habits.
+ */
+ public ListHabitsCommand(HabitTracker habitTracker) {
+ this.habitTracker = habitTracker;
+ }
+
+ /**
+ * Execute the command to list out all the habits in the habit tracker.
+ */
+ @Override
+ public void execute() {
+ habitTracker.listHabits();
+ }
+
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+}
diff --git a/src/main/java/commands/habitcommands/SetPriorityCommand.java b/src/main/java/commands/habitcommands/SetPriorityCommand.java
new file mode 100644
index 0000000000..388623ea62
--- /dev/null
+++ b/src/main/java/commands/habitcommands/SetPriorityCommand.java
@@ -0,0 +1,73 @@
+package commands.habitcommands;
+
+import commands.Command;
+import exceptions.HabitException;
+import habit.HabitTracker;
+
+/**
+ * Represents a command to set the priority of a habit.
+ */
+public class SetPriorityCommand implements Command {
+ private static final int REQUIRED_PARAMETERS = 3;
+ private HabitTracker habitTracker;
+ private String priority;
+ private int habitID;
+
+ /**
+ * Constructs a SetPriorityCommand object with user input
+ *
+ * @param habitTracker The HabitTracker instance to be used for storing the habits.
+ * @param habitCommandArgs User input for the set priority command.
+ * @throws HabitException If there are any formatting issues, or if there is an
+ * invalid habit ID or priority level
+ */
+ public SetPriorityCommand(HabitTracker habitTracker, String habitCommandArgs) throws HabitException {
+ this.habitTracker = habitTracker;
+
+ String[] parts = habitCommandArgs.trim().split("/id | /priority");
+
+ if (parts.length != REQUIRED_PARAMETERS) {
+ throw new HabitException("Incorrect set priority command formatting\n" +
+ "Use Format: habit set /id /priority \n" +
+ "Note: for , there are 3 levels --> low, med, high");
+ }
+
+ try {
+ this.habitID = Integer.parseInt(parts[1].trim());
+ } catch (NumberFormatException e) {
+ throw new HabitException("Please provide a valid habit ID.");
+ }
+
+ String priorityLevel = parts[2].trim().toLowerCase();
+ if (!isValidPriorityLevel(priorityLevel)) {
+ throw new HabitException("Invalid priority level!");
+ }
+
+ this.priority = priorityLevel;
+ }
+
+ /**
+ * Check if priority level is valid
+ *
+ * @param priorityLevel Priority level input to be checked
+ * @return True if priority level is valid.
+ */
+ private boolean isValidPriorityLevel(String priorityLevel) {
+ return priorityLevel.equals("high") || priorityLevel.equals("med") || priorityLevel.equals("low");
+ }
+
+ /**
+ * Execute the command to set priority of a habit.
+ *
+ * @throws HabitException If there are any formatting issues.
+ */
+ @Override
+ public void execute() throws HabitException {
+ habitTracker.setPriorityLevel(habitID, priority);
+ }
+
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+}
diff --git a/src/main/java/commands/habitcommands/SortHabitsCommand.java b/src/main/java/commands/habitcommands/SortHabitsCommand.java
new file mode 100644
index 0000000000..73434edc78
--- /dev/null
+++ b/src/main/java/commands/habitcommands/SortHabitsCommand.java
@@ -0,0 +1,37 @@
+package commands.habitcommands;
+
+import commands.Command;
+import exceptions.HabitException;
+import habit.HabitTracker;
+
+/**
+ * Represents a command to sort the habits according to their priority.
+ */
+public class SortHabitsCommand implements Command {
+ private HabitTracker habitTracker;
+
+ /**
+ * Constructs a SortHabitsCommand object.
+ *
+ * @param habitTracker The HabitTracker instance to be used for storing the habits.
+ */
+ public SortHabitsCommand(HabitTracker habitTracker) {
+ this.habitTracker = habitTracker;
+ }
+
+ /**
+ * Execute the command to sort the habits according to their priority.
+ *
+ * @throws HabitException If there are any formatting issues.
+ */
+ @Override
+ public void execute() throws HabitException {
+ habitTracker.sortHabits();
+ }
+
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+
+}
diff --git a/src/main/java/commands/habitcommands/UpdateHabitCountCommand.java b/src/main/java/commands/habitcommands/UpdateHabitCountCommand.java
new file mode 100644
index 0000000000..6f1e7b1026
--- /dev/null
+++ b/src/main/java/commands/habitcommands/UpdateHabitCountCommand.java
@@ -0,0 +1,57 @@
+package commands.habitcommands;
+
+import commands.Command;
+import exceptions.HabitException;
+import habit.HabitTracker;
+
+/**
+ * Represents a command to update a habit count.
+ */
+public class UpdateHabitCountCommand implements Command {
+ private static final int REQUIRED_PARAMETERS = 3;
+ private HabitTracker habitTracker;
+ private int habitID;
+ private String updatedCount;
+
+ /**
+ * Constructs a UpdateHabitCountCommand object with user input.
+ *
+ * @param habitTracker The HabitTracker instance to be used for storing the habits.
+ * @param habitCommandArgs User input for the update habit count command.
+ * @throws HabitException If there are any formatting issues.
+ */
+ public UpdateHabitCountCommand(HabitTracker habitTracker, String habitCommandArgs) throws HabitException {
+ this.habitTracker = habitTracker;
+
+ String[] parts = habitCommandArgs.trim().split("/id | /by");
+
+ if (parts.length != REQUIRED_PARAMETERS) {
+ throw new HabitException("Incorrect update command formatting\n" +
+ "Use Format: habit update /id /by \n" +
+ "Note: for , use '+1' to increase by 1, '-1' to decrease by 1");
+ }
+
+ try {
+ this.habitID = Integer.parseInt(parts[1].trim());
+ } catch (NumberFormatException e) {
+ throw new HabitException("Please provide a valid habit ID.");
+ }
+
+ this.updatedCount = parts[2].trim();
+ }
+
+ /**
+ * Execute the command to update the habit count of a habit.
+ */
+ @Override
+ public void execute() throws HabitException {
+ habitTracker.updateHabitCount(habitID, updatedCount);
+ }
+
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+
+}
+
diff --git a/src/main/java/commands/reflectcommands/GetReflectionQuestionsCommand.java b/src/main/java/commands/reflectcommands/GetReflectionQuestionsCommand.java
new file mode 100644
index 0000000000..438283c5dc
--- /dev/null
+++ b/src/main/java/commands/reflectcommands/GetReflectionQuestionsCommand.java
@@ -0,0 +1,47 @@
+package commands.reflectcommands;
+
+import commands.Command;
+import exceptions.ReflectException;
+import reflection.ReflectionManager;
+
+/**
+ * A command implementation for retrieving reflection questions.
+ */
+public class GetReflectionQuestionsCommand implements Command {
+
+ private ReflectionManager reflectionManager;
+
+ /**
+ * Constructs a GetReflectionQuestionsCommand.
+ *
+ * @param reflectionManager The ReflectionManager instance to be used for retrieving questions.
+ * @param reflectionCommandArgs The string representing the reflection command arguments.
+ * @throws ReflectException if the command arguments is not empty.
+ */
+ public GetReflectionQuestionsCommand(ReflectionManager reflectionManager, String reflectionCommandArgs)
+ throws ReflectException {
+
+ if (!reflectionCommandArgs.isBlank()) {
+ throw new ReflectException("Additional parameters for 'reflect get' command are not allowed.");
+ }
+ this.reflectionManager = reflectionManager;
+ }
+
+ /**
+ * Executes the command to retrieve and print five random reflection questions.
+ */
+ @Override
+ public void execute() {
+ reflectionManager.printFiveRandomQuestions();
+ }
+
+ /**
+ * Determines if this command represents an exit action.
+ *
+ * @return Always returns false, as this command is not an exit command.
+ */
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+}
diff --git a/src/main/java/commands/reflectcommands/ListFavouriteReflectionsCommand.java b/src/main/java/commands/reflectcommands/ListFavouriteReflectionsCommand.java
new file mode 100644
index 0000000000..7d653df74e
--- /dev/null
+++ b/src/main/java/commands/reflectcommands/ListFavouriteReflectionsCommand.java
@@ -0,0 +1,48 @@
+
+package commands.reflectcommands;
+
+import commands.Command;
+import exceptions.ReflectException;
+import reflection.ReflectionManager;
+
+/**
+ * A command implementation for listing favorite reflection items.
+ */
+public class ListFavouriteReflectionsCommand implements Command {
+ private ReflectionManager reflectionManager;
+
+ /**
+ * Constructs a ListFavouritesCommand.
+ *
+ * @param reflectionManager The ReflectionManager instance to be used for listing favorite items.
+ * @param reflectionCommandArgs The string representing the reflection command arguments.
+ * @throws ReflectException if the command arguments is not empty.
+ */
+ public ListFavouriteReflectionsCommand(ReflectionManager reflectionManager, String reflectionCommandArgs)
+ throws ReflectException {
+
+ if (!reflectionCommandArgs.isBlank()) {
+ throw new ReflectException("Additional parameters for 'reflect list' command are not allowed.");
+ }
+
+ this.reflectionManager = reflectionManager;
+ }
+
+ /**
+ * Executes the command to print the list of favorite reflection items.
+ */
+ @Override
+ public void execute() {
+ reflectionManager.printFavourites();
+ }
+
+ /**
+ * Determines if this command represents an exit action.
+ *
+ * @return Always returns false, as this command is not an exit command.
+ */
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+}
diff --git a/src/main/java/commands/reflectcommands/ReflectionHelpCommand.java b/src/main/java/commands/reflectcommands/ReflectionHelpCommand.java
new file mode 100644
index 0000000000..76fb46f0d6
--- /dev/null
+++ b/src/main/java/commands/reflectcommands/ReflectionHelpCommand.java
@@ -0,0 +1,47 @@
+package commands.reflectcommands;
+
+import commands.Command;
+import exceptions.ReflectException;
+import reflection.ReflectionManager;
+
+/**
+ * Represents a command to display the help menu for reflection commands.
+ */
+public class ReflectionHelpCommand implements Command {
+ private ReflectionManager reflectionManager;
+
+ /**
+ * Constructs a ReflectionHelpCommand with the provided ReflectionManager.
+ *
+ * @param reflectionManager The ReflectionManager to be used.
+ * @param reflectionCommandArgs The string representing the reflection command arguments.
+ * @throws ReflectException if the command arguments is not empty.
+ */
+ public ReflectionHelpCommand(ReflectionManager reflectionManager, String reflectionCommandArgs)
+ throws ReflectException {
+
+ if (!reflectionCommandArgs.isBlank()) {
+ throw new ReflectException("Additional parameters for 'reflect help' command are not allowed.");
+ }
+
+ this.reflectionManager = reflectionManager;
+ }
+
+ /**
+ * Executes the command to print the help menu.
+ */
+ @Override
+ public void execute() {
+ reflectionManager.printHelpMenu();
+ }
+
+ /**
+ * Indicates whether this command is an exit command.
+ *
+ * @return false, as this command is not an exit command.
+ */
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+}
diff --git a/src/main/java/commands/reflectcommands/SaveToFavouritesCommand.java b/src/main/java/commands/reflectcommands/SaveToFavouritesCommand.java
new file mode 100644
index 0000000000..16879f747f
--- /dev/null
+++ b/src/main/java/commands/reflectcommands/SaveToFavouritesCommand.java
@@ -0,0 +1,51 @@
+package commands.reflectcommands;
+
+import commands.Command;
+import exceptions.ReflectException;
+import reflection.ReflectionManager;
+
+/**
+ * A command implementation for saving a reflection item to favorites.
+ */
+public class SaveToFavouritesCommand implements Command {
+ private ReflectionManager reflectionManager;
+ private int reflectionId;
+
+ /**
+ * Constructs a SaveToFavouritesCommand.
+ *
+ * @param reflectionManager The ReflectionManager instance to be used for saving the reflection item.
+ * @param reflectionCommandArgs The ID of the reflection item to be saved.
+ * @throws ReflectException if the provided reflection ID is invalid.
+ */
+ public SaveToFavouritesCommand(ReflectionManager reflectionManager, String reflectionCommandArgs)
+ throws ReflectException {
+
+ this.reflectionManager = reflectionManager;
+ try {
+ reflectionId = Integer.parseInt(reflectionCommandArgs);
+ } catch (NumberFormatException e) {
+ throw new ReflectException("Key in valid favourite reflection ID, between 1 and 5");
+ }
+ }
+
+ /**
+ * Executes the command to save the reflection item to favorites.
+ *
+ * @throws ReflectException if an error occurs during the saving process.
+ */
+ @Override
+ public void execute() throws ReflectException {
+ reflectionManager.saveReflectionQuestion(reflectionId);
+ }
+
+ /**
+ * Determines if this command represents an exit action.
+ *
+ * @return Always returns false, as this command is not an exit command.
+ */
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+}
diff --git a/src/main/java/commands/reflectcommands/UnsaveFromFavouritesCommand.java b/src/main/java/commands/reflectcommands/UnsaveFromFavouritesCommand.java
new file mode 100644
index 0000000000..6c533f45b4
--- /dev/null
+++ b/src/main/java/commands/reflectcommands/UnsaveFromFavouritesCommand.java
@@ -0,0 +1,52 @@
+
+package commands.reflectcommands;
+
+import commands.Command;
+import exceptions.ReflectException;
+import reflection.ReflectionManager;
+
+/**
+ * A command implementation for unsaving a reflection item from favorites.
+ */
+public class UnsaveFromFavouritesCommand implements Command {
+ private ReflectionManager reflectionManager;
+ private int reflectionId;
+
+ /**
+ * Constructs a UnsaveFromFavouritesCommand.
+ *
+ * @param reflectionManager The ReflectionManager instance to be used for saving the reflection item.
+ * @param reflectionCommandArgs The ID of the reflection item to be saved.
+ * @throws ReflectException if the provided reflection ID is invalid.
+ */
+ public UnsaveFromFavouritesCommand(ReflectionManager reflectionManager, String reflectionCommandArgs)
+ throws ReflectException {
+
+ this.reflectionManager = reflectionManager;
+ try {
+ reflectionId = Integer.parseInt(reflectionCommandArgs);
+ } catch (NumberFormatException e) {
+ throw new ReflectException("Key in valid favourite reflection ID. Only numbers are allowed.");
+ }
+ }
+
+ /**
+ * Executes the command to save the reflection item to favorites.
+ *
+ * @throws ReflectException if an error occurs during the saving process.
+ */
+ @Override
+ public void execute() throws ReflectException {
+ reflectionManager.unsaveReflectionQuestion(reflectionId);
+ }
+
+ /**
+ * Determines if this command represents an exit action.
+ *
+ * @return Always returns false, as this command is not an exit command.
+ */
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+}
diff --git a/src/main/java/commands/sleepcommands/AddSleepCommand.java b/src/main/java/commands/sleepcommands/AddSleepCommand.java
new file mode 100644
index 0000000000..a471d3fbf0
--- /dev/null
+++ b/src/main/java/commands/sleepcommands/AddSleepCommand.java
@@ -0,0 +1,72 @@
+package commands.sleepcommands;
+
+import commands.Command;
+import exceptions.SleepException;
+import sleep.SleepCycle;
+import sleep.SleepTracker;
+import date.DateFormat;
+import java.time.LocalDate;
+import java.time.format.DateTimeParseException;
+
+/**
+ * Represents a command to add sleep cycles.
+ */
+public class AddSleepCommand implements Command {
+ private SleepTracker sleepTracker;
+ private SleepCycle sleepCycleToAdd;
+
+ /**
+ * Constructs a new AddSleepCommand object with user input.
+ *
+ * @param sleepTracker Class that contains information and functions to be executed required by Sleep Tracker
+ * @param sleepCommandArgs User input for the add sleep cycles command.
+ * @throws SleepException if there is any formatting issues.
+ */
+ public AddSleepCommand(SleepTracker sleepTracker, String sleepCommandArgs) throws SleepException {
+ String[] userCommand = sleepCommandArgs.trim().split("/date", 2);
+ if (userCommand.length != 2) {
+ throw new SleepException("Please use proper format: " + System.lineSeparator()
+ + "sleep add /date ");
+ }
+ if (userCommand[1].isBlank()) {
+ throw new SleepException("Key in non-empty date");
+ }
+ if (userCommand[0].isBlank()) {
+ throw new SleepException("Key in non-empty number of hours slept");
+ }
+ this.sleepTracker = sleepTracker;
+ double hourSlept;
+ LocalDate dateSlept;
+ try {
+ hourSlept = Math.round(Double.parseDouble(userCommand[0].trim()) * 10) / 10.0;
+ } catch (NumberFormatException e) {
+ throw new SleepException("Key in valid number of hours slept" + System.lineSeparator()
+ + "E.g: 7.5");
+ }
+ if (hourSlept < 0 || hourSlept > 24) {
+ throw new SleepException("Number of hours must be between 0 and 24" + System.lineSeparator()
+ + "E.g: 7.5");
+ }
+ try {
+ dateSlept = DateFormat.convertStringToDate(userCommand[1].trim());
+ } catch (DateTimeParseException e) {
+ throw new SleepException("Key in valid date of sleep" + System.lineSeparator()
+ + "E.g: 22/12/2023");
+ }
+ if (!dateSlept.isBefore(LocalDate.now())) {
+ throw new SleepException("U can only add sleep cycles before today's date" + System.lineSeparator());
+ }
+ assert !sleepCommandArgs.isEmpty() : "Sleep cycle should not be added";
+ sleepCycleToAdd = new SleepCycle(hourSlept, dateSlept);
+ }
+
+ @Override
+ public void execute() throws SleepException {
+ sleepTracker.addSleepCycle(sleepCycleToAdd);
+ }
+
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+}
diff --git a/src/main/java/commands/sleepcommands/DeleteSleepCommand.java b/src/main/java/commands/sleepcommands/DeleteSleepCommand.java
new file mode 100644
index 0000000000..26ff737331
--- /dev/null
+++ b/src/main/java/commands/sleepcommands/DeleteSleepCommand.java
@@ -0,0 +1,157 @@
+package commands.sleepcommands;
+
+import date.DateFormat;
+import exceptions.SleepException;
+import commands.Command;
+import sleep.DeleteMode;
+import sleep.SleepTracker;
+import java.time.LocalDate;
+import java.time.format.DateTimeParseException;
+
+/**
+ * Represents a command to delete sleep cycles.
+ */
+public class DeleteSleepCommand implements Command {
+ private static final String ERROR_MESSAGE = "Please use proper format: " + System.lineSeparator()
+ + "sleep delete /date " + System.lineSeparator()
+ + "OR" + System.lineSeparator()
+ + "sleep delete /before " +System.lineSeparator()
+ + "OR" + System.lineSeparator()
+ + "sleep delete /from /to ";
+ private SleepTracker sleepTracker;
+ private LocalDate startDate;
+ private LocalDate endDate;
+ private DeleteMode deleteMode;
+
+ /**
+ * Constructs a new DeleteSleepCommand object with user input.
+ *
+ * @param sleepTracker Class that contains information and functions to be executed required by Sleep Tracker
+ * @param sleepCommandArgs User input for the delete sleep cycles command.
+ * @throws SleepException if there is any formatting issues.
+ */
+ public DeleteSleepCommand(SleepTracker sleepTracker, String sleepCommandArgs) throws SleepException {
+ if (sleepCommandArgs.isBlank()) {
+ throw new SleepException(ERROR_MESSAGE);
+ }
+ this.sleepTracker = sleepTracker;
+ if (sleepCommandArgs.contains("/date")) {
+ deleteSleep(sleepCommandArgs);
+ } else if (sleepCommandArgs.contains("/before")) {
+ deleteSleepBefore(sleepCommandArgs);
+ } else {
+ deleteSleepBetween(sleepCommandArgs);
+ }
+ }
+
+ /**
+ * Further parse user input when user wants to delete with deleteMode = FIND.
+ *
+ * @param sleepCommandArgs User input for the delete sleep cycles command.
+ * @throws SleepException if there is any formatting issues.
+ */
+ private void deleteSleep(String sleepCommandArgs) throws SleepException {
+ deleteMode = DeleteMode.FIND;
+ String[] userCommand = sleepCommandArgs.trim().split("/date", 2);
+ if (userCommand.length != 2 || !userCommand[0].isBlank()) {
+ throw new SleepException(ERROR_MESSAGE);
+ }
+ if (userCommand[1].isBlank()) {
+ throw new SleepException("Key in non-empty date");
+ }
+ LocalDate date;
+ try {
+ date = DateFormat.convertStringToDate(userCommand[1].trim());
+ } catch (DateTimeParseException e) {
+ throw new SleepException("Key in valid date of sleep cycle to delete" + System.lineSeparator()
+ + "E.g: 22/12/2023");
+ }
+ startDate = date;
+ endDate = date;
+ }
+
+ /**
+ * Further parse user input when user wants to delete with deleteMode = BEFORE.
+ *
+ * @param sleepCommandArgs User input for the delete sleep cycles command.
+ * @throws SleepException if there is any formatting issues.
+ */
+ private void deleteSleepBefore(String sleepCommandArgs) throws SleepException {
+ deleteMode = DeleteMode.BEFORE;
+ String[] userCommand = sleepCommandArgs.trim().split("/before", 2);
+ if (userCommand.length != 2 || !userCommand[0].isBlank()) {
+ throw new SleepException(ERROR_MESSAGE);
+ }
+ if (userCommand[1].isBlank()) {
+ throw new SleepException("Key in non-empty date");
+ }
+ LocalDate date;
+ try {
+ date = DateFormat.convertStringToDate(userCommand[1].trim());
+ } catch (DateTimeParseException e) {
+ throw new SleepException("Key in valid date of sleep cycle to delete" + System.lineSeparator()
+ + "E.g: 22/12/2023");
+ }
+ startDate = date;
+ endDate = date;
+ }
+
+ /**
+ * Further parse user input when user wants to delete with deleteMode = BETWEEN.
+ *
+ * @param sleepCommandArgs User input for the delete sleep cycles command.
+ * @throws SleepException if there is any formatting issues.
+ */
+ private void deleteSleepBetween(String sleepCommandArgs) throws SleepException {
+ deleteMode = DeleteMode.BETWEEN;
+ String[] userCommandStart = sleepCommandArgs.trim().split("/from", 2);
+ if (userCommandStart.length == 1) {
+ throw new SleepException(ERROR_MESSAGE);
+ }
+ String[] userCommandEnd = userCommandStart[1].trim().split("/to", 2);
+ if (userCommandEnd.length == 1) {
+ throw new SleepException(ERROR_MESSAGE);
+ }
+ if (!userCommandStart[0].isBlank() || userCommandStart.length != 2 || userCommandEnd.length != 2) {
+ throw new SleepException(ERROR_MESSAGE);
+ }
+ if (userCommandEnd[0].isBlank()) {
+ throw new SleepException("Key in non-empty start date");
+ }
+ if (userCommandEnd[1].isBlank()) {
+ throw new SleepException("Key in non-empty end date");
+ }
+ try {
+ startDate = DateFormat.convertStringToDate(userCommandEnd[0].trim());
+ endDate = DateFormat.convertStringToDate(userCommandEnd[1].trim());
+ } catch (DateTimeParseException e) {
+ throw new SleepException("Key in valid date of sleep" + System.lineSeparator()
+ + "E.g: 22/12/2023");
+ }
+ if (endDate.isBefore(startDate)) {
+ throw new SleepException("Start date must be before end date");
+ }
+ }
+
+ @Override
+ public void execute() throws SleepException{
+ switch(deleteMode) {
+ case FIND:
+ sleepTracker.deleteSleepCycle(startDate);
+ break;
+ case BEFORE:
+ sleepTracker.deleteSleepCyclesBefore(endDate);
+ break;
+ case BETWEEN:
+ sleepTracker.deleteSleepCyclesBetween(startDate, endDate);
+ break;
+ default:
+ throw new SleepException("Error in detection of deletion mode");
+ }
+ }
+
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+}
diff --git a/src/main/java/commands/sleepcommands/GetSleepCommand.java b/src/main/java/commands/sleepcommands/GetSleepCommand.java
new file mode 100644
index 0000000000..4337aede16
--- /dev/null
+++ b/src/main/java/commands/sleepcommands/GetSleepCommand.java
@@ -0,0 +1,47 @@
+package commands.sleepcommands;
+
+import commands.Command;
+import date.DateFormat;
+import exceptions.SleepException;
+import sleep.SleepTracker;
+import java.time.LocalDate;
+import java.time.format.DateTimeParseException;
+
+/**
+ * Represents a command to get sleep cycles.
+ */
+public class GetSleepCommand implements Command {
+ private SleepTracker sleepTracker;
+ private LocalDate date;
+
+ /**
+ * Constructs a new GetSleepCommand object with user input.
+ *
+ * @param sleepTracker Class that contains information and functions to be executed required by Sleep Tracker
+ * @param sleepCommandArgs User input for the get sleep cycles command.
+ * @throws SleepException if there is any formatting issues.
+ */
+ public GetSleepCommand(SleepTracker sleepTracker, String sleepCommandArgs) throws SleepException {
+ if (sleepCommandArgs.isEmpty()) {
+ throw new SleepException("Please use proper format: " + System.lineSeparator()
+ + "sleep get ");
+ }
+ try {
+ this.sleepTracker = sleepTracker;
+ this.date = DateFormat.convertStringToDate(sleepCommandArgs);
+ } catch (DateTimeParseException e) {
+ throw new SleepException("Key in valid date of sleep cycle to get" + System.lineSeparator()
+ + "E.g: 22/12/2023");
+ }
+ }
+
+ @Override
+ public void execute() {
+ sleepTracker.getSleepCycle(date);
+ }
+
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+}
diff --git a/src/main/java/commands/sleepcommands/HelpSleepCommand.java b/src/main/java/commands/sleepcommands/HelpSleepCommand.java
new file mode 100644
index 0000000000..00c6a40abd
--- /dev/null
+++ b/src/main/java/commands/sleepcommands/HelpSleepCommand.java
@@ -0,0 +1,46 @@
+package commands.sleepcommands;
+
+import commands.Command;
+import exceptions.SleepException;
+
+
+import ui.Ui;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+public class HelpSleepCommand implements Command{
+ private static final String[] HELP_MENU_INSTRUCTIONS = {
+ "sleep add [HOURS_SLEPT] /date [DATE_SLEPT]: Add a new sleep cycle",
+ "sleep list: List out all sleep cycles",
+ "sleep get [DATE_OF_SLEEP]: Get hours slept on a specific date",
+ "sleep update [DATE_OF_SLEEP] /new [HOURS_OF_SLEEP]: Updates hours slept on a specific date",
+ "sleep delete /date [DATE_OF_SLEEP]: Delete Sleep Cycle of a specific date",
+ "sleep delete /before [DATE_OF_SLEEP]: Delete Sleep Cycles before a specific date",
+ "sleep delete /from [START_DATE] /to [END_DATE]: Delete Sleep Cycles within a range of dates",
+ "sleep save: Allow user to save sleep cycles in a text file located in FILE_PATH: data/sleep.txt"
+ };
+
+ public HelpSleepCommand(String reflectionCommandArgs)
+ throws SleepException {
+ if (!reflectionCommandArgs.isBlank()) {
+ throw new SleepException("Additional parameters for 'sleep help' command are not allowed.");
+ }
+ }
+
+
+ /**
+ * Execute the command to display the habit tracker help menu
+ */
+ public void execute() {
+ ArrayList helpMenuInstructionsList = new ArrayList<>(Arrays.asList(HELP_MENU_INSTRUCTIONS));
+
+ assert helpMenuInstructionsList.size() == 8 : "Help menu should have 8 instructions";
+
+ Ui.printList(helpMenuInstructionsList, "Commands for sleep tracker feature:");
+ }
+
+ public boolean isExit() {
+ return false;
+ }
+}
diff --git a/src/main/java/commands/sleepcommands/ListSleepCommand.java b/src/main/java/commands/sleepcommands/ListSleepCommand.java
new file mode 100644
index 0000000000..ec294e52ee
--- /dev/null
+++ b/src/main/java/commands/sleepcommands/ListSleepCommand.java
@@ -0,0 +1,38 @@
+package commands.sleepcommands;
+
+import commands.Command;
+import exceptions.SleepException;
+import sleep.SleepTracker;
+
+/**
+ * Represents a command to list sleep cycles.
+ */
+public class ListSleepCommand implements Command {
+ private SleepTracker sleepTracker;
+
+ /**
+ * Constructs a new ListSleepCommand object with user input.
+ *
+ * @param sleepTracker Class that contains information and functions to be executed required by Sleep Tracker
+ * @param sleepCommandArgs User input for the list sleep cycles command.
+ * @throws SleepException if there is any formatting issues.
+ */
+ public ListSleepCommand(SleepTracker sleepTracker, String sleepCommandArgs) throws SleepException {
+ if (!sleepCommandArgs.isEmpty()) {
+ throw new SleepException("Please use proper format: " + System.lineSeparator()
+ + "sleep list");
+ }
+ assert sleepCommandArgs.isEmpty() : "Sleep cycle should not be listed";
+ this.sleepTracker = sleepTracker;
+ }
+
+ @Override
+ public void execute() {
+ sleepTracker.listSleepCycles();
+ }
+
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+}
diff --git a/src/main/java/commands/sleepcommands/SaveSleepCommand.java b/src/main/java/commands/sleepcommands/SaveSleepCommand.java
new file mode 100644
index 0000000000..e89c6dd7ce
--- /dev/null
+++ b/src/main/java/commands/sleepcommands/SaveSleepCommand.java
@@ -0,0 +1,38 @@
+package commands.sleepcommands;
+
+import exceptions.SleepException;
+import sleep.SleepTracker;
+import commands.Command;
+
+/**
+ * Represents a command to save sleep cycles.
+ */
+public class SaveSleepCommand implements Command{
+ private SleepTracker sleepTracker;
+
+ /**
+ * Constructs a new SaveSleepCommand object with user input.
+ *
+ * @param sleepTracker Class that contains information and functions to be executed required by Sleep Tracker
+ * @param sleepCommandArgs User input for the save sleep cycles command.
+ * @throws SleepException if there is any formatting issues.
+ */
+ public SaveSleepCommand(SleepTracker sleepTracker, String sleepCommandArgs) throws SleepException {
+ if (!sleepCommandArgs.isEmpty()) {
+ throw new SleepException("Please use proper format: " + System.lineSeparator()
+ + "sleep save");
+ }
+ assert sleepCommandArgs.isEmpty() : "Sleep cycle should not be saved";
+ this.sleepTracker = sleepTracker;
+ }
+
+ @Override
+ public void execute() {
+ sleepTracker.saveSleepCycles();
+ }
+
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+}
diff --git a/src/main/java/commands/sleepcommands/UpdateSleepCommand.java b/src/main/java/commands/sleepcommands/UpdateSleepCommand.java
new file mode 100644
index 0000000000..54738c35af
--- /dev/null
+++ b/src/main/java/commands/sleepcommands/UpdateSleepCommand.java
@@ -0,0 +1,63 @@
+package commands.sleepcommands;
+
+import commands.Command;
+import date.DateFormat;
+import exceptions.SleepException;
+import sleep.SleepTracker;
+import java.time.LocalDate;
+import java.time.format.DateTimeParseException;
+
+/**
+ * Represents a command to update sleep cycles.
+ */
+public class UpdateSleepCommand implements Command {
+ private SleepTracker sleepTracker;
+ private LocalDate date;
+ private double hours;
+
+ /**
+ * Constructs a new UpdateSleepCommand object with user input.
+ *
+ * @param sleepTracker Class that contains information and functions to be executed required by Sleep Tracker
+ * @param sleepCommandArgs User input for the update sleep cycles command.
+ * @throws SleepException if there is any formatting issues.
+ */
+ public UpdateSleepCommand(SleepTracker sleepTracker, String sleepCommandArgs) throws SleepException {
+ String[] userCommand = sleepCommandArgs.trim().split("/new", 2);
+ if (userCommand.length != 2) {
+ throw new SleepException("Please use proper format: " + System.lineSeparator()
+ + "sleep update /new ");
+ }
+ if (userCommand[0].isBlank()) {
+ throw new SleepException("Key in non-empty date");
+ }
+ if (userCommand[1].isBlank()) {
+ throw new SleepException("Key in non-empty number of hours to update to");
+ }
+ try {
+ this.sleepTracker = sleepTracker;
+ date = DateFormat.convertStringToDate(userCommand[0].trim());
+ hours = Double.parseDouble(userCommand[1].trim());
+ } catch (DateTimeParseException e) {
+ throw new SleepException("Key in valid date of sleep cycle to be updated" + System.lineSeparator()
+ + "E.g: 22/12/2023");
+ } catch (NumberFormatException e) {
+ throw new SleepException("Key in valid number of hours to update to");
+ }
+ if (hours < 0 || hours > 24) {
+ throw new SleepException("Number of hours must be between 0 and 24" + System.lineSeparator()
+ + "E.g: 7.5");
+ }
+
+ }
+
+ @Override
+ public void execute() {
+ sleepTracker.updateSleepCycle(date, hours);
+ }
+
+ @Override
+ public boolean isExit() {
+ return false;
+ }
+}
diff --git a/src/main/java/date/DateFormat.java b/src/main/java/date/DateFormat.java
new file mode 100644
index 0000000000..ab19dae417
--- /dev/null
+++ b/src/main/java/date/DateFormat.java
@@ -0,0 +1,20 @@
+package date;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * Represents a class to convert string to date and vice versa.
+ * Ensures that only one type of format for date is to be used.
+ */
+public class DateFormat {
+ private static final DateTimeFormatter FORMAT = DateTimeFormatter.ofPattern("dd/MM/yyyy");
+
+ public static LocalDate convertStringToDate(String s) {
+ return LocalDate.parse(s , FORMAT);
+ }
+
+ public static String convertDateToString(LocalDate date) {
+ return date.format(FORMAT);
+ }
+}
diff --git a/src/main/java/exceptions/FitnessException.java b/src/main/java/exceptions/FitnessException.java
new file mode 100644
index 0000000000..260c62e8c5
--- /dev/null
+++ b/src/main/java/exceptions/FitnessException.java
@@ -0,0 +1,7 @@
+package exceptions;
+
+public class FitnessException extends Wellness360Exception {
+ public FitnessException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/exceptions/FocusException.java b/src/main/java/exceptions/FocusException.java
new file mode 100644
index 0000000000..5d446c962d
--- /dev/null
+++ b/src/main/java/exceptions/FocusException.java
@@ -0,0 +1,7 @@
+package exceptions;
+
+public class FocusException extends Wellness360Exception {
+ public FocusException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/exceptions/HabitException.java b/src/main/java/exceptions/HabitException.java
new file mode 100644
index 0000000000..c71cde0696
--- /dev/null
+++ b/src/main/java/exceptions/HabitException.java
@@ -0,0 +1,7 @@
+package exceptions;
+
+public class HabitException extends Wellness360Exception {
+ public HabitException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/exceptions/ReflectException.java b/src/main/java/exceptions/ReflectException.java
new file mode 100644
index 0000000000..8ec7ce377e
--- /dev/null
+++ b/src/main/java/exceptions/ReflectException.java
@@ -0,0 +1,7 @@
+package exceptions;
+
+public class ReflectException extends Wellness360Exception {
+ public ReflectException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/exceptions/SleepException.java b/src/main/java/exceptions/SleepException.java
new file mode 100644
index 0000000000..bb49577009
--- /dev/null
+++ b/src/main/java/exceptions/SleepException.java
@@ -0,0 +1,7 @@
+package exceptions;
+
+public class SleepException extends Wellness360Exception {
+ public SleepException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/exceptions/Wellness360Exception.java b/src/main/java/exceptions/Wellness360Exception.java
new file mode 100644
index 0000000000..e4cfda4ccb
--- /dev/null
+++ b/src/main/java/exceptions/Wellness360Exception.java
@@ -0,0 +1,7 @@
+package exceptions;
+
+public class Wellness360Exception extends Exception {
+ public Wellness360Exception(String message) {
+ super("ERROR MSG: " + message);
+ }
+}
diff --git a/src/main/java/fitness/ExerciseType.java b/src/main/java/fitness/ExerciseType.java
new file mode 100644
index 0000000000..4ff799bda5
--- /dev/null
+++ b/src/main/java/fitness/ExerciseType.java
@@ -0,0 +1,18 @@
+package fitness;
+
+/**
+ * Enumeration Class used for exercise type, to prevent incorrect types from being used
+ * */
+public enum ExerciseType {
+ ARMS,
+ CHEST,
+ ABS,
+ BACK,
+ LEGS;
+
+ @Override
+ public String toString() {
+ String string = super.toString();
+ return string.substring(0, 1).toUpperCase() + string.substring(1).toLowerCase();
+ }
+}
diff --git a/src/main/java/fitness/FitnessMotivator.java b/src/main/java/fitness/FitnessMotivator.java
new file mode 100644
index 0000000000..fe64811141
--- /dev/null
+++ b/src/main/java/fitness/FitnessMotivator.java
@@ -0,0 +1,179 @@
+package fitness;
+
+import fitness.exercise.Exercise;
+import fitness.exercise.ExerciseList;
+import fitness.goals.ExerciseGoalList;
+import ui.Ui;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Random;
+
+import static commands.fitnesscommands.DeleteExerciseCommand.REQUIRED_DELETE_PARAMS;
+import static fitness.UiMessageConstants.ADD_EXERCISE_MESSAGE;
+import static fitness.UiMessageConstants.EMPTY_GOAL_MESSAGE;
+import static fitness.UiMessageConstants.GOAL_MESSAGE;
+import static fitness.UiMessageConstants.NEW_GOAL_MESSAGE;
+import static fitness.UiMessageConstants.GOAL_STATUS_MESSAGE;
+import static fitness.UiMessageConstants.HELP_MESSAGE;
+import static fitness.UiMessageConstants.DELETE_EXERCISE_MESSAGE;
+import static fitness.UiMessageConstants.MIN_EXERCISES_MESSAGE;
+import static fitness.UiMessageConstants.HELP_MENU_INSTRUCTIONS;
+
+
+/**
+ * Contains methods that execute given the respective commands
+ * */
+public class FitnessMotivator {
+
+ public static final String DATA_FILE_PATH = "./data/exerciselist.txt";
+ public static final String GOALS_FILE_PATH = "./data/exercisegoallist.txt";
+
+ // Required Number of parameters for the fitness add command
+ public static final int REQUIRED_NUM_OF_PARAMETERS = 4;
+
+ public ExerciseList allExercises = new ExerciseList();
+ public ExerciseGoalList dailyGoals = new ExerciseGoalList();
+
+ public FitnessMotivator() {}
+
+ private Exercise[] fiveRandomExercises() {
+ Random random = new Random();
+ int randomInt1 = random.nextInt(allExercises.size(ExerciseType.ARMS));
+ int randomInt2 = random.nextInt(allExercises.size(ExerciseType.CHEST));
+ int randomInt3 = random.nextInt(allExercises.size(ExerciseType.ABS));
+ int randomInt4 = random.nextInt(allExercises.size(ExerciseType.BACK));
+ int randomInt5 = random.nextInt(allExercises.size(ExerciseType.LEGS));
+
+ Exercise exercise1 = allExercises.get(ExerciseType.ARMS, randomInt1);
+ Exercise exercise2 = allExercises.get(ExerciseType.CHEST, randomInt2);
+ Exercise exercise3 = allExercises.get(ExerciseType.ABS, randomInt3);
+ Exercise exercise4 = allExercises.get(ExerciseType.BACK, randomInt4);
+ Exercise exercise5 = allExercises.get(ExerciseType.LEGS, randomInt5);
+
+ return new Exercise[]{exercise1, exercise2, exercise3, exercise4, exercise5};
+ }
+
+ /**
+ * Gets one randomised exercise per type, then prints it to the UI.
+ *
+ * @return A string that lists 5 exercises of different type
+ * */
+ public String getExercises() {
+ Exercise[] exercises = fiveRandomExercises();
+
+ String message = "These are some of the exercises you can do! " +
+ "LETS GET STRONK MY G" + System.lineSeparator() + System.lineSeparator() +
+ "1. " + exercises[0] + System.lineSeparator() +
+ "2. " + exercises[1] + System.lineSeparator() +
+ "3. " + exercises[2] + System.lineSeparator() +
+ "4. " + exercises[3] + System.lineSeparator() +
+ "5. " + exercises[4] + System.lineSeparator();
+
+ Ui.printMessageWithSepNewLine(message);
+ return message;
+ }
+
+ /**
+ * Add user created exercise into the existing list
+ *
+ * @param commandArgs A list of Strings that contain parameters for the Exercise object
+ * */
+ public void addExercises(String[] commandArgs) {
+ assert commandArgs.length == REQUIRED_NUM_OF_PARAMETERS :
+ "Something went wrong with parsing fitness add command arguments";
+
+ Exercise newExercise = allExercises.newExercise(commandArgs);
+ allExercises.add(newExercise);
+
+ Ui.printMessageWithSepNewLine(ADD_EXERCISE_MESSAGE + newExercise);
+ }
+
+ /**
+ * Gets all the exercises that belong to the queried type, and prints it to the UI.
+ *
+ * @param type An object of type ExerciseType used for query
+ * */
+ public void getTypeExercises(ExerciseType type) {
+ ArrayList exercisesByType = allExercises.getType(type);
+ String message = "Here are the " + type + " exercises as requested!" +
+ System.lineSeparator();
+ Ui.printList(exercisesByType, message);
+ }
+
+ /**
+ * Delete the exercise specified by the user, and prints the remaining exercises of the same
+ * type to the UI.
+ *
+ * @param commandArgs A list of strings that contain the index and ExerciseType to be deleted.
+ * */
+ public void deleteExercise(String[] commandArgs) {
+ assert commandArgs.length == REQUIRED_DELETE_PARAMS :
+ "Something went wrong with parsing fitness delete command arguments";
+
+ ExerciseType type = ExerciseType.valueOf(commandArgs[0]);
+ int index = Integer.parseInt(commandArgs[1]);
+
+ if (allExercises.size(type) > 1) {
+ allExercises.remove(allExercises.get(type, index - 1));
+ ArrayList exercisesByType = allExercises.getType(type);
+
+ Ui.printList(exercisesByType, DELETE_EXERCISE_MESSAGE);
+ } else {
+ Ui.printMessageWithSepNewLine(MIN_EXERCISES_MESSAGE);
+ }
+ }
+
+ /**
+ * Creates new exercise goals for the user. If goals already exists, it will override the
+ * current goals with a set of new goals, then prints these goals to the Ui. Note that the
+ * goals are 5 random exercises extracted from the list of exercises in the Motivator.
+ * */
+ public void newGoals() {
+ dailyGoals.clear();
+ Exercise[] exercises = fiveRandomExercises();
+
+ for (Exercise e: exercises) {
+ dailyGoals.add(e, false);
+ }
+
+ Ui.printMessageWithSepNewLine(NEW_GOAL_MESSAGE + dailyGoals.toString());
+ }
+
+ /**
+ * Retrieves the status of current goals, then prints it to the Ui.
+ * */
+ public void goalStatus() {
+ if (dailyGoals.isEmpty()) {
+ Ui.printMessageWithSepNewLine(EMPTY_GOAL_MESSAGE);
+ } else {
+ Ui.printMessageWithSepNewLine(GOAL_MESSAGE + dailyGoals.toString());
+ }
+ }
+
+ /**
+ * Gets the goal that the user requested, and toggles the state of the goal. If it is done, it
+ * will be marked as undone, and vice versa. All the goal status is then printed to the Ui.
+ * */
+ public void toggleGoal(int index) {
+ if (dailyGoals.isEmpty()) {
+ Ui.printMessageWithSepNewLine(EMPTY_GOAL_MESSAGE);
+ return;
+ }
+
+ dailyGoals.findExercise(index - 1).toggle();
+ dailyGoals.saveGoals();
+
+ Ui.printMessageWithSepNewLine(GOAL_STATUS_MESSAGE + dailyGoals.toString());
+ }
+
+ /**
+ * Prints a set of commands that the user can refer to in order to use the Fitness Motivator.
+ * */
+ public void printHelp() {
+ ArrayList helpMenuInstructionsList =
+ new ArrayList<>(Arrays.asList(HELP_MENU_INSTRUCTIONS));
+
+ Ui.printList(helpMenuInstructionsList, HELP_MESSAGE);
+ }
+}
diff --git a/src/main/java/fitness/UiMessageConstants.java b/src/main/java/fitness/UiMessageConstants.java
new file mode 100644
index 0000000000..040780f159
--- /dev/null
+++ b/src/main/java/fitness/UiMessageConstants.java
@@ -0,0 +1,51 @@
+package fitness;
+
+/**
+ * Class that stores static constants of FitnessMotivator Ui messages
+ * */
+public class UiMessageConstants {
+
+ public static final String NEW_GOAL_MESSAGE = "Lets get working on today's exercises!" +
+ System.lineSeparator() + System.lineSeparator();
+ public static final String EMPTY_GOAL_MESSAGE = "There are no goals set :(" +
+ System.lineSeparator() + System.lineSeparator() +
+ "You can set one by doing 'fitness goal new'!";
+
+ public static final String GOAL_MESSAGE = "Here are your goals for today. " +
+ "Have you started? Don't be lazy ok?" + System.lineSeparator() + System.lineSeparator();
+
+ public static final String GOAL_STATUS_MESSAGE = "I see there are changes. " +
+ "I hope you are making progress..." + System.lineSeparator() + System.lineSeparator();
+
+ public static final String HELP_MESSAGE =
+ "Here is a list of possible commands you can use with the Fitness Motivator!" +
+ System.lineSeparator();
+
+ public static final String ADD_EXERCISE_MESSAGE =
+ "I have added the following exercise into our list!" + System.lineSeparator();
+
+ public static final String DELETE_EXERCISE_MESSAGE =
+ " I have deleted the exercise. Here are the exercises left in the list!" +
+ System.lineSeparator();
+
+ public static final String MIN_EXERCISES_MESSAGE =
+ " I cannot delete anymore, there needs to be at least one exercise per type :(" +
+ System.lineSeparator();
+
+ public static final String[] HELP_MENU_INSTRUCTIONS = {
+ "fitness get: Get 5 random exercises each of different type.",
+ "fitness get : Get a full list of exercises belonging to the exercise type",
+ "fitness add /type /name /sets /reps : " +
+ System.lineSeparator() +
+ " add an exercise to the list of exercises",
+ "fitness delete : Delete the exercise from the list of exercise." +
+ System.lineSeparator() +
+ " The index is based on the index when you run 'fitness get ",
+ "fitness goal: Retrieves the status of all current goals, if it exists",
+ "fitness goal new: Overwrites current goals with new set of goals if it exists, " +
+ System.lineSeparator() +
+ " otherwise creates a brand new set of goals",
+ "fitness goal : Toggle the status of the goal",
+ "fitness help: Get help menu for reflect commands"
+ };
+}
diff --git a/src/main/java/fitness/exercise/Exercise.java b/src/main/java/fitness/exercise/Exercise.java
new file mode 100644
index 0000000000..e294bf918e
--- /dev/null
+++ b/src/main/java/fitness/exercise/Exercise.java
@@ -0,0 +1,39 @@
+package fitness.exercise;
+
+import fitness.ExerciseType;
+
+public class Exercise {
+
+ protected String exerciseName;
+ protected ExerciseType exerciseType;
+ protected String sets;
+ protected String reps;
+
+ public Exercise(String exerciseName, ExerciseType exerciseType, String sets, String reps) {
+ this.exerciseName = exerciseName;
+ this.exerciseType = exerciseType;
+ this.sets = sets;
+ this.reps = reps;
+ }
+
+ public ExerciseType getType() {
+ return exerciseType;
+ }
+
+ public String getExerciseName() {
+ return exerciseName;
+ }
+
+ public String getSets() {
+ return sets;
+ }
+
+ public String getReps() {
+ return reps;
+ }
+
+ @Override
+ public String toString() {
+ return exerciseType + ": " + exerciseName + ", " + sets + " sets & " + reps + " reps";
+ }
+}
diff --git a/src/main/java/fitness/exercise/ExerciseBank.java b/src/main/java/fitness/exercise/ExerciseBank.java
new file mode 100644
index 0000000000..dad2c7bbd2
--- /dev/null
+++ b/src/main/java/fitness/exercise/ExerciseBank.java
@@ -0,0 +1,27 @@
+package fitness.exercise;
+
+/**
+ * Class that stores static String Array constants of exercises for different body types
+ * */
+public class ExerciseBank {
+ public static final String[] INIT_ARMS_EXERCISES = {
+ "Cable Triceps Push down,Arms,3,8", "Barbell Curls,Arms,3,8", "Preacher Curls,Arms,3,8",
+ "Skullcrushers,Arms,3,8", "Lateral Raises,Arms,3,8"
+ };
+ public static final String[] INIT_CHEST_EXERCISES = {
+ "Bench Press,Chest,3,8", "Incline Bench Press,Chest,3,8", "Diamond Push-up,Chest,3,15",
+ "Cable Flies,Chest,3,8", "Wide Arm Push-up,Chest,3,15"
+ };
+ public static final String[] INIT_ABS_EXERCISES = {
+ "Sit-ups,Abs,3,20", "Russian Twists,Abs,3,20", "Crunches,Abs,5,20",
+ "Flutter Kicks,Abs,4,20", "Weighted Sit-Ups,Abs,3,20"
+ };
+ public static final String[] INIT_BACK_EXERCISES = {
+ "Pull Ups,Back,3,6", "Lateral Rows,Back,3,8", "Deadlift,Back,3,5",
+ "Weighted Pull Ups,Back,3,6", "Cable Rows,Back,3,8"
+ };
+ public static final String[] INIT_LEGS_EXERCISES = {
+ "Weighted Squats,Legs,3,10", "Leg Press,Legs,3,8", "Leg Curl,Legs,3,8",
+ "Leg Extensions,Legs,3,10", "Calf Raises,Legs,3,10"
+ };
+}
diff --git a/src/main/java/fitness/exercise/ExerciseList.java b/src/main/java/fitness/exercise/ExerciseList.java
new file mode 100644
index 0000000000..a1daf5afc5
--- /dev/null
+++ b/src/main/java/fitness/exercise/ExerciseList.java
@@ -0,0 +1,205 @@
+package fitness.exercise;
+
+import fitness.ExerciseType;
+import fitness.FitnessMotivator;
+import storage.Storage;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+
+import static fitness.FitnessMotivator.REQUIRED_NUM_OF_PARAMETERS;
+import static fitness.exercise.ExerciseBank.INIT_ARMS_EXERCISES;
+import static fitness.exercise.ExerciseBank.INIT_CHEST_EXERCISES;
+import static fitness.exercise.ExerciseBank.INIT_ABS_EXERCISES;
+import static fitness.exercise.ExerciseBank.INIT_BACK_EXERCISES;
+import static fitness.exercise.ExerciseBank.INIT_LEGS_EXERCISES;
+
+/**
+ * Represents the list of exercises and includes methods to manipulate the list of exercises
+ * */
+public class ExerciseList {
+
+ private ArrayList allExercises = new ArrayList<>();
+
+ /**
+ * Checks if a save file exists, if it does then load it for use, else create a new data file
+ * and initialise it with the data above.
+ * */
+ public ExerciseList() {
+ if (!Storage.isFileCreated(FitnessMotivator.DATA_FILE_PATH)) {
+ initialiseData();
+ Storage.saveTasksToFile(FitnessMotivator.DATA_FILE_PATH, allExercises);
+ } else {
+ parseData(Storage.loadDataFromFile(FitnessMotivator.DATA_FILE_PATH));
+ }
+ }
+
+ /**
+ * Takes in a pre-saved list of exercises for a single exercise type
+ *
+ * @param list An array of strings that contain information about all exercises under each type
+ * */
+ private void initialiseSingleList(String[] list, ExerciseType type) {
+ for (String s : list) {
+ String[] exerciseDetails = s.split(",");
+ assert exerciseDetails.length == REQUIRED_NUM_OF_PARAMETERS
+ : "Missing Data from Data file!";
+ Exercise exercise = new Exercise(exerciseDetails[0], type,
+ exerciseDetails[2], exerciseDetails[3]);
+ allExercises.add(exercise);
+ }
+ }
+
+ /**
+ * Reads all 5 different string arrays from above and adds it into one ArrayList for use
+ * */
+ private void initialiseData() {
+ initialiseSingleList(INIT_ARMS_EXERCISES, ExerciseType.ARMS);
+ initialiseSingleList(INIT_CHEST_EXERCISES, ExerciseType.CHEST);
+ initialiseSingleList(INIT_ABS_EXERCISES, ExerciseType.ABS);
+ initialiseSingleList(INIT_BACK_EXERCISES, ExerciseType.BACK);
+ initialiseSingleList(INIT_LEGS_EXERCISES, ExerciseType.LEGS);
+ }
+
+ /**
+ * Further parses data read from storage into usable exercise objects, before adding it into
+ * the ArrayList.
+ *
+ * @param data An ArrayList of strings, comprising lines read from the data file
+ * */
+ private void parseData (ArrayList data) {
+ for (String s: data) {
+ String[] parts = s.split(": |, | sets & | reps");
+ if (parts.length == REQUIRED_NUM_OF_PARAMETERS) {
+ allExercises.add(newExercise(parts));
+ }
+ }
+ }
+
+ /**
+ * This method adds an exercise object into the full list of exercises. It also sorts the list
+ * in order of exercise type before saving it into storage.
+ *
+ * @param exercise An Exercise object for ExerciseList
+ */
+ public void add(Exercise exercise) {
+ allExercises.add(exercise);
+
+ // The comparing method extracts the exercise type from each exercise object and then
+ // compares them based on their type
+ Comparator comparator = Comparator.comparing(Exercise::getType);
+
+ // The sort method then sorts the list based on the comparator specified before saving
+ allExercises.sort(comparator);
+ Storage.saveTasksToFile(FitnessMotivator.DATA_FILE_PATH, allExercises);
+ }
+
+
+ /**
+ * This method searches the ArrayList for Exercises that matches the required type, and returns
+ * the n-th item of the queried type, where n is the index.
+ *
+ * @param type The ExerciseType Enum to be queried
+ * @param index The n-th instance of all object that matches the queried ExerciseType
+ *
+ * @return Returns an Object of type Exercise that matches the type and index queried.
+ * */
+ public Exercise get(ExerciseType type, int index) {
+ ArrayList typeExercises = new ArrayList<>();
+ for (Exercise e : allExercises) {
+ assert e != null : "Invalid Exercise Detected";
+ if (e.getType().equals(type)) {
+ typeExercises.add(e);
+ }
+ }
+ return typeExercises.get(index);
+ }
+
+ /**
+ * This method fetches all exercises that have the queried type.
+ *
+ * @param type The ExerciseType to be queried in the list of exercises.
+ *
+ * @return A list of exercises matching the type queried.
+ * */
+ public ArrayList getType(ExerciseType type) {
+ ArrayList exercisesByType = new ArrayList<>();
+ for (Exercise e : allExercises) {
+ assert e != null : "Invalid Exercise Detected";
+ if (e.getType().equals(type)) {
+ exercisesByType.add(e);
+ }
+ }
+ return exercisesByType;
+ }
+
+ /**
+ * Returns the total number of a certain type of exercise
+ *
+ * @param type The ExerciseType Enum to be queried
+ * */
+ public int size(ExerciseType type) {
+ int x = 0;
+ for (Exercise e : allExercises) {
+ if (e.getType().equals(type)) {
+ x++;
+ }
+ }
+ return x;
+ }
+
+ /**
+ * Creates a new Exercise Object using an array of strings
+ *
+ * @param parameters An array of Strings that provide details for the creation of an Exercise
+ * object.
+ * Index 0 - Exercise Type
+ * Index 1 - Exercise Name
+ * Index 2 - Number of Sets
+ * Index 3 - Number of Reps
+ *
+ * @return returns a new Exercise object
+ * */
+ public Exercise newExercise(String[] parameters) {
+ assert parameters.length == REQUIRED_NUM_OF_PARAMETERS
+ : "Incorrect Parameters for a new Exercise Object";
+
+ String type = parameters[0].toUpperCase();
+ ExerciseType exerciseType = ExerciseType.valueOf(type);
+ String exerciseName = parameters[1].trim();
+ String sets = parameters[2].trim();
+ String reps = parameters[3].trim();
+
+ return new Exercise(exerciseName, exerciseType, sets, reps);
+ }
+
+ /**
+ * Helper methods for finding exercises in the list. Overloaded with different parameters to
+ * allow for different searching methods. This method uses the exercise name to search.
+ *
+ * @param type The ExerciseType Enum to be queried
+ * @param nameQuery the name of the exercise to be queried
+ *
+ * @return An object of type Exercise
+ * */
+ public Exercise findExercise(ExerciseType type, String nameQuery) {
+ ArrayList typeExercises = getType(type);
+ for (Exercise e : typeExercises) {
+ if (e.getType().equals(type) && e.getExerciseName().contains(nameQuery)) {
+ return e;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Helper method that helps remove an exercise from the list.
+ *
+ * @param exercise The exercise object to be deleted.
+ * */
+ public void remove(Exercise exercise) {
+ allExercises.remove(exercise);
+ Storage.saveTasksToFile(FitnessMotivator.DATA_FILE_PATH, allExercises);
+ }
+
+}
diff --git a/src/main/java/fitness/goals/ExerciseGoal.java b/src/main/java/fitness/goals/ExerciseGoal.java
new file mode 100644
index 0000000000..4d182d02d4
--- /dev/null
+++ b/src/main/java/fitness/goals/ExerciseGoal.java
@@ -0,0 +1,34 @@
+package fitness.goals;
+
+import fitness.exercise.Exercise;
+import fitness.ExerciseType;
+
+public class ExerciseGoal extends Exercise {
+
+ private boolean isDone;
+
+ public ExerciseGoal(
+ String exerciseName,
+ ExerciseType exerciseType,
+ String sets,
+ String reps,
+ boolean isDone) {
+ super(exerciseName, exerciseType, sets, reps);
+ this.isDone = isDone;
+ }
+
+ public void toggle() {
+ isDone = !isDone;
+ }
+
+ public boolean getStatus() {
+ return isDone;
+ }
+
+ @Override
+ public String toString() {
+ String status = isDone ? "[X] " : "[ ] ";
+ return status + exerciseType + ": " + exerciseName + ", " +
+ sets + " sets & " + reps + " reps";
+ }
+}
diff --git a/src/main/java/fitness/goals/ExerciseGoalList.java b/src/main/java/fitness/goals/ExerciseGoalList.java
new file mode 100644
index 0000000000..3ee960748c
--- /dev/null
+++ b/src/main/java/fitness/goals/ExerciseGoalList.java
@@ -0,0 +1,144 @@
+package fitness.goals;
+
+import fitness.FitnessMotivator;
+import fitness.exercise.Exercise;
+import fitness.exercise.ExerciseList;
+import fitness.ExerciseType;
+import storage.Storage;
+
+import java.util.ArrayList;
+
+import static fitness.FitnessMotivator.GOALS_FILE_PATH;
+import static fitness.FitnessMotivator.REQUIRED_NUM_OF_PARAMETERS;
+
+/**
+ * Represents the list of exercises and includes methods to manipulate the list of exercise goals.
+ * Inherits from the ExerciseList class.
+ * */
+public class ExerciseGoalList extends ExerciseList {
+
+ private static final int NUMBER_OF_GOALS = 5;
+ private ArrayList goals = new ArrayList<>();
+
+ public ExerciseGoalList() {
+ if (Storage.isFileCreated(FitnessMotivator.GOALS_FILE_PATH)) {
+ parseData(Storage.loadDataFromFile(FitnessMotivator.GOALS_FILE_PATH));
+ }
+ }
+
+ /**
+ * Further parses data read from storage into usable ExerciseGoal objects, before adding it into
+ * the ArrayList.
+ *
+ * @param data An ArrayList of strings, comprising lines read from the data file
+ * */
+ private void parseData (ArrayList data) {
+ assert data.size() == NUMBER_OF_GOALS : "There is something wrong with the data file!";
+
+ for (String s : data) {
+ String[] parts = s.split(": |, | sets & | reps");
+
+ // Checks whether the goal is done or not done
+ char status = parts[0].charAt(1);
+ boolean isDone = (status == 'X');
+ parts[0] = parts[0].substring(4);
+
+ if (parts.length == REQUIRED_NUM_OF_PARAMETERS) {
+ add(newExercise(parts), isDone);
+ }
+ }
+ }
+
+ /**
+ * A helper method that returns whether the goal list is empty
+ * */
+ public boolean isEmpty() {
+ return goals.isEmpty();
+ }
+
+ /**
+ * A helper method that deletes everything in the list.
+ * */
+ public void clear() {
+ goals.clear();
+ }
+
+
+ /**
+ * Helper methods for finding exercises in the list. Overloaded with different parameters to
+ * allow for different searching methods.This method uses index to search.
+ *
+ * @param index The n-th exercise of type ExerciseType, where n is the index
+ *
+ * @return An object of type Exercise
+ * */
+ public ExerciseGoal findExercise(int index) {
+ return goals.get(index);
+ }
+
+ /**
+ * Helper method that saves the goal data to a local file
+ * */
+ public void saveGoals() {
+ Storage.saveTasksToFile(GOALS_FILE_PATH, goals);
+ }
+
+ /**
+ * Over-ridden method from parent class 'ExerciseList', this method returns an 'ExerciseGoal'
+ * object instead of an 'Exercise' object. The status of the goal is set not done by default.
+ *
+ * @param parameters An array of Strings that provide details for the creation of an Exercise
+ * object.
+ * Index 0 - Exercise Type
+ * Index 1 - Exercise Name
+ * Index 2 - Number of Sets
+ * Index 3 - Number of Reps
+ *
+ * @return returns a new Exercise object
+ * */
+ @Override
+ public ExerciseGoal newExercise(String[] parameters) {
+ assert parameters.length == REQUIRED_NUM_OF_PARAMETERS
+ : "Incorrect Parameters for a new Exercise Object";
+
+ String type = parameters[0].toUpperCase();
+ ExerciseType exerciseType = ExerciseType.valueOf(type);
+ String exerciseName = parameters[1].trim();
+ String sets = parameters[2].trim();
+ String reps = parameters[3].trim();
+
+ return new ExerciseGoal(exerciseName, exerciseType, sets, reps, false);
+ }
+
+ /**
+ * Over-riden method from parent class 'ExerciseList', this method takes in 'Exercise' objects
+ * and converts them into 'ExerciseGoal' objects before adding them into the goals list. The
+ * status of the goal is set not done by default.
+ *
+ * @param exercise An Exercise object to be converted and added into the list
+ * */
+ public void add(Exercise exercise, boolean isDone) {
+ goals.add(new ExerciseGoal(
+ exercise.getExerciseName(),
+ exercise.getType(),
+ exercise.getSets(),
+ exercise.getReps(),
+ isDone
+ ));
+
+ Storage.saveTasksToFile(FitnessMotivator.GOALS_FILE_PATH, goals);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder message = new StringBuilder();
+ for (int i = 0; i < NUMBER_OF_GOALS; i++) {
+ ExerciseGoal goal = goals.get(i);
+ message.append((i + 1))
+ .append(". ")
+ .append(goal.toString())
+ .append(System.lineSeparator());
+ }
+ return message.toString();
+ }
+}
diff --git a/src/main/java/focus/CountdownTimer.java b/src/main/java/focus/CountdownTimer.java
new file mode 100644
index 0000000000..164a701fe9
--- /dev/null
+++ b/src/main/java/focus/CountdownTimer.java
@@ -0,0 +1,162 @@
+package focus;
+
+import ui.Ui;
+
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Represents the countdown timer for the focus timer function.
+ */
+public class CountdownTimer {
+ private static final int DEFAULT_SECONDS = 0;
+ private static final int DEFAULT_MINUTES = 1;
+ private static final int ONE_SECOND = 1000;
+ private static final int MAX_SECONDS = 59;
+ private static final int TIME_DELAY = 0;
+ private static final int STOP_TIME = 0;
+ private static final int START_WARNING = 3;
+ private static final String ASSERTION_TIMER_NOT_RUNNING = "Timer should not be running";
+ private static final String ASSERTION_INVALID_STOP = "Timer should be running.";
+ private int minutes;
+ private int seconds;
+ private int inputMinutes;
+ private AtomicBoolean isStarted;
+ private AtomicBoolean isRunning;
+ private Timer stopwatch;
+ private TimerTask timerTask;
+
+ /**
+ * Constructs a CountdownTimer by initialising the class with default timings.
+ */
+ public CountdownTimer() {
+ this.minutes = DEFAULT_MINUTES;
+ this.seconds = DEFAULT_SECONDS;
+ this.inputMinutes = DEFAULT_MINUTES;
+ this.isRunning = new AtomicBoolean(false);
+ this.isStarted = new AtomicBoolean(false);
+ }
+
+ private void decreaseMinutes() {
+ this.minutes--;
+ }
+
+ private void decreaseSeconds() {
+ this.seconds--;
+ }
+
+ /**
+ * Starts the countdown timer by running it in the background thread.
+ */
+ public void start() {
+ stopwatch = new Timer();
+ isStarted.set(true);
+ timerTask = new TimerTask() {
+ @Override
+ public void run() {
+ if (!isRunning.get()) {
+ return;
+ }
+ if (minutes == STOP_TIME && seconds == START_WARNING) {
+ System.out.println();
+ }
+ if (minutes == STOP_TIME && seconds <= START_WARNING && seconds != STOP_TIME) {
+ Ui.printMessageWithSepNewLine(seconds + " seconds left");
+ }
+ if (minutes == STOP_TIME && seconds == STOP_TIME) {
+ Ui.printMessageWithSepNewLine("Count down timer completed!");
+ setStop();
+ Ui.promptUserInput();
+ } else if (seconds == STOP_TIME) {
+ decreaseMinutes();
+ seconds = MAX_SECONDS;
+ } else {
+ decreaseSeconds();
+ }
+ }
+ };
+ stopwatch.scheduleAtFixedRate(timerTask, TIME_DELAY, ONE_SECOND);
+ }
+
+ /**
+ * Set the timer to start by initialising the timing to the desired countdown timings.
+ */
+ public void setStart() {
+ assert !isRunning.get() : ASSERTION_TIMER_NOT_RUNNING;
+ isRunning.set(true);
+ minutes = inputMinutes;
+ seconds = DEFAULT_SECONDS;
+ start();
+ Ui.printMessageWithSepNewLine("Countdown timer started! \n"
+ + "Duration set: " + minutes + " minute(s) " + seconds + " second(s)");
+ }
+
+ /**
+ * Set the timer to stop
+ */
+ public void setStop() {
+ assert stopwatch != null : ASSERTION_INVALID_STOP;
+ isRunning.set(false);
+ isStarted.set(false);
+ stopwatch.cancel();
+ stopwatch.purge();
+ }
+
+ /**
+ * Set the timer to pause
+ */
+ public void setPause() {
+ isRunning.set(false);
+ Ui.printMessageWithSepNewLine("Timer paused. \n" +
+ "Remaining time: " + minutes + " minutes " + seconds + " seconds");
+ }
+
+ /**
+ * set the timer to resume
+ */
+ public void setResume() {
+ isRunning.set(true);
+ Ui.printMessageWithSepNewLine("Countdown timer resumed.");
+ }
+
+ /**
+ * Get the current running status of the timer
+ *
+ * @return true if the timer has started. False otherwise.
+ */
+ public boolean getRunningStatus() {
+ return isStarted.get();
+ }
+
+ /**
+ * Get the current paused status of the timer
+ *
+ * @return false if the timer has been paused, true otherwise.
+ */
+ public boolean getPausedStatus() {
+ return isRunning.get();
+ }
+
+ /**
+ * Set the countdown timer duration to the desired duration.
+ *
+ * @param userInput The number of minutes desired from the user.
+ */
+ public void setTimer(int userInput) {
+ if (userInput < 1) {
+ Ui.printMessageWithSepNewLine("Duration cannot be less than 1.");
+ } else {
+ inputMinutes = userInput;
+ Ui.printMessageWithSepNewLine("Countdown duration has been set to " + userInput + " minute(s)");
+ }
+ }
+
+ /**
+ * Check the remaining time in the countdown timer.
+ */
+ public void checkTime() {
+ Ui.printMessageWithSepNewLine("Remaining time: \n" +
+ minutes + " minutes " + seconds + " seconds left.");
+ }
+}
diff --git a/src/main/java/focus/CountupTimer.java b/src/main/java/focus/CountupTimer.java
new file mode 100644
index 0000000000..6f348ce7d6
--- /dev/null
+++ b/src/main/java/focus/CountupTimer.java
@@ -0,0 +1,119 @@
+package focus;
+
+import ui.Ui;
+
+import java.time.Duration;
+import java.time.LocalDateTime;
+
+public class CountupTimer {
+ private static final int MINUTES_DIVISION = 60;
+ private static final int SECONDS_DIVISION = 60;
+ private LocalDateTime startTiming;
+ private LocalDateTime stopTiming;
+ private LocalDateTime currentTime;
+ private boolean isStarted = false;
+ private boolean isPaused = false;
+ private long totalHours = 0;
+ private long totalMinutes = 0;
+ private long totalSeconds = 0;
+
+ /**
+ * Store the current time when the user calls the function as the start timing for the timer.
+ */
+ public void setStartTiming() {
+ assert !isStarted : "Timer should not have started";
+ this.startTiming = LocalDateTime.now();
+ isStarted = true;
+ Ui.printMessageWithSepNewLine("Your session has started. Time to grind!");
+ this.totalHours = 0;
+ this.totalMinutes = 0;
+ this.totalSeconds = 0;
+ }
+
+ /**
+ * Store the current time when the user calls the function as the stop timing for the timer.
+ */
+ public void setStopTiming() {
+ assert isStarted : "Timer should have started";
+ if(!isPaused) {
+ stopTiming = LocalDateTime.now();
+ }
+ isStarted = false;
+ isPaused = false;
+ totalTimeSpent();
+ }
+
+ /**
+ * Set the timer to pause
+ */
+ public void setPause() {
+ currentTime = LocalDateTime.now();
+ Duration timeElapsed = Duration.between(startTiming, currentTime);
+ stopTiming = LocalDateTime.now();
+ isPaused = true;
+ totalHours += timeElapsed.toHours();
+ totalMinutes += timeElapsed.toMinutes() % MINUTES_DIVISION;
+ totalSeconds += timeElapsed.toSeconds() % MINUTES_DIVISION;
+ Ui.printMessageWithSepNewLine("Count up timer paused.");
+ }
+
+ /**
+ * Set the timer to resume.
+ */
+ public void setResume() {
+ this.startTiming = LocalDateTime.now();
+ isPaused = false;
+ Ui.printMessageWithSepNewLine("Count up timer resumed");
+ }
+
+ /**
+ * Get the current paused status of the timer
+ *
+ * @return true if the timer has been paused, false otherwise.
+ */
+ public boolean getPauseStatus() {
+ return isPaused;
+ }
+
+ /**
+ * Retrieves the current status of the clock
+ *
+ * @return The status of the focus timer
+ */
+ public boolean getStartedStatus() {
+ return isStarted;
+ }
+
+ /**
+ * Calculates the total time elapsed between the start timing and stop timing, and prints out
+ * the total time elapsed using Ui class.
+ */
+ public void totalTimeSpent() {
+ if (!isPaused) {
+ Duration timeElapsed = Duration.between(startTiming, stopTiming);
+ totalHours += timeElapsed.toHours();
+ totalMinutes += timeElapsed.toMinutes() % MINUTES_DIVISION;
+ totalSeconds += timeElapsed.toSeconds() % SECONDS_DIVISION;
+ }
+ Ui.printMessageWithSepNewLine("Your focus session has ended.\n" + "Total time spent: " +
+ totalHours + " hours, " + totalMinutes + " minutes, " + totalSeconds + " seconds" + "\n" +
+ "To start a new session, use focus start ");
+ }
+
+ /**
+ * Check to total time elapsed from the start to the current time, and prints out the total time elapsed
+ * using the Ui class.
+ */
+ public void checkTime() {
+ if(!isPaused) {
+ currentTime = LocalDateTime.now();
+ }
+ Duration timeElapsed = Duration.between(startTiming, currentTime);
+ startTiming = currentTime;
+ totalHours += timeElapsed.toHours();
+ totalMinutes += timeElapsed.toMinutes() % MINUTES_DIVISION;
+ totalSeconds += timeElapsed.toSeconds() % SECONDS_DIVISION;
+ Ui.printMessageWithSepNewLine("Total time elapsed: \n" +
+ totalHours + " hours, " + totalMinutes + " minutes, " + totalSeconds + " seconds");
+ }
+}
diff --git a/src/main/java/focus/FocusTimer.java b/src/main/java/focus/FocusTimer.java
new file mode 100644
index 0000000000..7596d6edc3
--- /dev/null
+++ b/src/main/java/focus/FocusTimer.java
@@ -0,0 +1,123 @@
+package focus;
+
+import ui.Ui;
+
+/**
+ * Represents the focus timer for wellness360.
+ */
+public class FocusTimer {
+ public boolean timerMode = false; // false for count-up, true for countdown
+ private CountupTimer countupTimer;
+ private CountdownTimer countdownTimer;
+
+ /**
+ * Constructs a FocusTimer class with instances of CountupTimer and CountdownTimer
+ */
+ public FocusTimer() {
+ this.countupTimer = new CountupTimer();
+ this.countdownTimer = new CountdownTimer();
+ }
+
+ /**
+ * Set the start timing for the timer.
+ */
+ public void setStartTiming() {
+ if (timerMode) {
+ countdownTimer.setStart();
+ } else {
+ countupTimer.setStartTiming();
+ }
+ }
+
+ /**
+ * Set the stop timing for the timer.
+ */
+ public void setStopTiming() {
+ if (timerMode) {
+ countdownTimer.setStop();
+ Ui.printMessageWithSepNewLine("Countdown timer stopped.");
+ } else {
+ countupTimer.setStopTiming();
+ }
+ }
+
+ /**
+ * Check whether the timer has started.
+ *
+ * @return true if the timer has started. False otherwise.
+ */
+ public boolean getStartStatus() {
+ if (timerMode) {
+ return countdownTimer.getRunningStatus();
+ } else {
+ return countupTimer.getStartedStatus();
+ }
+ }
+
+ /**
+ * Switch the timer mode for focus timer between count yp timer and countdown timer.
+ */
+ public void switchTimer() {
+ this.timerMode = !timerMode;
+ if (timerMode) {
+ Ui.printMessageWithSepNewLine("Switched to Count down timer");
+ } else {
+ Ui.printMessageWithSepNewLine("Switched to Count up timer");
+ }
+ }
+
+ /**
+ * Pause the timer that is currently running.
+ */
+ public void setPauseTiming() {
+ if (timerMode) {
+ countdownTimer.setPause();
+ } else {
+ countupTimer.setPause();
+ }
+ }
+
+ /**
+ * Get the pause status of the current timer that is running.
+ *
+ * @return True if the timer is in the paused state. False otherwise.
+ */
+ public boolean getPausedStatus() {
+ if (timerMode) {
+ return !countdownTimer.getPausedStatus();
+ } else {
+ return countupTimer.getPauseStatus();
+ }
+ }
+
+ /**
+ * Resume the timer that is currently running.
+ */
+ public void setResumeTiming() {
+ if (timerMode) {
+ countdownTimer.setResume();
+ } else {
+ countupTimer.setResume();
+ }
+ }
+
+ /**
+ * Set the desired duration for the countdown timer.
+ *
+ * @param commandArgs The desired number of minutes from the user.
+ */
+ public void setDuration(int commandArgs) {
+ countdownTimer.setTimer(commandArgs);
+ }
+
+ /**
+ * Check the total time elapsed for count up timer or time remaining for countdown timer.
+ */
+ public void checkTime() {
+ if (timerMode) {
+ countdownTimer.checkTime();
+ } else {
+ countupTimer.checkTime();
+ }
+ }
+}
diff --git a/src/main/java/habit/Habit.java b/src/main/java/habit/Habit.java
new file mode 100644
index 0000000000..960d58a911
--- /dev/null
+++ b/src/main/java/habit/Habit.java
@@ -0,0 +1,92 @@
+package habit;
+
+import exceptions.HabitException;
+
+/**
+ * Represents a Habit.
+ */
+public class Habit {
+ private String description;
+ private int habitCount;
+ private Priority priority;
+
+ /**
+ * Constructs a habit object with the habit description.
+ *
+ * @param description The habit description to be added.
+ */
+ public Habit(String description) {
+ this.description = description;
+ this.habitCount = 0;
+ this.priority = Priority.LOW;
+ }
+
+ public Habit(String description, int habitCount, Priority priority) {
+ this.description = description;
+ this.habitCount = habitCount;
+ this.priority = priority;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public int getHabitCount() {
+ return habitCount;
+ }
+
+ public Priority getPriority() {
+ return priority;
+ }
+
+ /**
+ * Updates the habit count of a habit.
+ *
+ * @param updatedCount The count to be added to the existing habit count.
+ * @return An integer type of the change in count to be added.
+ * @throws HabitException If the user tries to decrease a habit count to below zero, or use a non-numerical number.
+ */
+ public int updateCount(String updatedCount) throws HabitException {
+ int changeInCount = 0;
+
+ try {
+ changeInCount = Integer.parseInt(updatedCount);
+ if (habitCount + changeInCount < 0) {
+ throw new HabitException("You cannot decrement a habit count to below zero");
+ }
+
+ habitCount += changeInCount;
+
+ } catch (NumberFormatException e) {
+ throw new HabitException("Please enter a valid count\n" +
+ "Use: '+1' to increase count, '-1' to decrease count ");
+ }
+
+ return changeInCount;
+ }
+
+ /**
+ * Sets the priority of a habit.
+ *
+ * @param priority The priority level of a habit from user input.
+ */
+ public void setPriority (String priority) {
+ switch (priority) {
+ case "low":
+ this.priority = Priority.LOW;
+ break;
+ case "med":
+ this.priority = Priority.MED;
+ break;
+ case "high":
+ this.priority = Priority.HIGH;
+ break;
+ default:
+ }
+ }
+
+ public String toString() {
+ return " [" + priority + "] " + description + " [count: " + habitCount + "]";
+ }
+
+}
diff --git a/src/main/java/habit/HabitTracker.java b/src/main/java/habit/HabitTracker.java
new file mode 100644
index 0000000000..22e3bca908
--- /dev/null
+++ b/src/main/java/habit/HabitTracker.java
@@ -0,0 +1,191 @@
+package habit;
+
+import exceptions.HabitException;
+import ui.Ui;
+
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+
+import static storage.habit.HabitTrackerStorage.loadHabitListFromFile;
+import static storage.habit.HabitTrackerStorage.saveHabitListToFile;
+import static ui.Ui.printMessageWithoutNewLine;
+
+/**
+ * Represents the habit tracker which stores all habits.
+ */
+public class HabitTracker {
+ private static ArrayList habitList = new ArrayList<>();
+
+ /**
+ * Loads data from local storage and constructs a new HabitTracker object.
+ */
+ public HabitTracker() {
+ try {
+ habitList = loadHabitListFromFile();
+ } catch (HabitException e) {
+ Ui.printMessageWithSepNewLine(e.getMessage());
+ }
+ }
+
+ public static int getNumberOfHabits() {
+ return habitList.size();
+ }
+
+ /**
+ * Adds a new habit into habitList.
+ *
+ * @param newHabit The habit to be added.
+ */
+ public void addHabit (Habit newHabit) {
+ assert habitList != null : "habitList should not be null";
+ habitList.add(newHabit);
+
+ String addHabitMessage = "Great! You have added a new habit:\n";
+ addHabitMessage += " '" + newHabit.getDescription() + "' was successfully added!";
+ Ui.printMessageWithSepNewLine(addHabitMessage);
+
+ saveHabitListToFile(habitList);
+ }
+
+ /**
+ * List out all habits in habitList.
+ */
+ public void listHabits() {
+ String listHabitsMessage = "Here is the list of all your habits!\n";
+
+ if (habitList.isEmpty()) {
+ listHabitsMessage += " \n";
+ }
+
+ for (int i = 0; i < habitList.size(); i++) {
+ Habit habit = habitList.get(i);
+ listHabitsMessage += " " + (i + 1) + "." + habit + "\n";
+ }
+
+ printMessageWithoutNewLine(listHabitsMessage);
+ }
+
+ public static boolean isValidHabitID(int habitID) {
+ return habitID > 0 && habitID <= habitList.size();
+ }
+
+ /**
+ * Update the habit count for a habit.
+ *
+ * @param habitID The habit ID to be updated.
+ * @param updatedCount The count to be added to the existing habit count.
+ * @throws HabitException If an invalid habit ID is provided.
+ */
+ public void updateHabitCount(int habitID, String updatedCount) throws HabitException {
+ if (!isValidHabitID(habitID)) {
+ throw new HabitException("Please provide a valid habit ID.");
+ }
+
+ Habit habit = habitList.get(habitID - 1);
+ int changeInCount = habit.updateCount(updatedCount);
+
+ String updateHabitCountMessage = "";
+ if (changeInCount > 0) {
+ updateHabitCountMessage += "Good Job! You have completed your habit!\n";
+ }
+
+ updateHabitCountMessage += "The count for your habit has been updated:\n";
+ updateHabitCountMessage += " " + habitID + ". " + habit;
+ Ui.printMessageWithSepNewLine(updateHabitCountMessage);
+
+ saveHabitListToFile(habitList);
+ }
+
+ /**
+ * Delete a habit from the habit tracker list.
+ *
+ * @param habitID The habit ID to be deleted.
+ * @throws HabitException If an invalid habit ID is provided.
+ */
+ public void deleteHabit(int habitID) throws HabitException {
+ if (!isValidHabitID(habitID)) {
+ throw new HabitException("Please provide a valid habit ID.");
+ }
+
+ String deleteHabitMessage = "Got it! I've removed this habit:\n";
+ deleteHabitMessage += " " + habitList.get(habitID - 1) + "\n";
+
+ habitList.remove(habitID - 1);
+
+ deleteHabitMessage += "Now you have " + habitList.size() + " habits left in the list.";
+ Ui.printMessageWithSepNewLine(deleteHabitMessage);
+
+ saveHabitListToFile(habitList);
+ }
+
+ /**
+ * Set the priority level of a habit.
+ *
+ * @param habitID The habit ID of the habit to be updated with a different priority level.
+ * @param priority The priority level that the user wish to set.
+ * @throws HabitException If an invalid habit ID is provided.
+ */
+ public void setPriorityLevel(int habitID, String priority) throws HabitException {
+ if (!isValidHabitID(habitID)) {
+ throw new HabitException("Please provide a valid habit ID.");
+ }
+
+ Habit habit = habitList.get(habitID - 1);
+ habit.setPriority(priority);
+
+ String setPriorityLevelMessage = "";
+ setPriorityLevelMessage += "The priority for your habit has been updated:\n";
+ setPriorityLevelMessage += " " + habitID + ". " + habit;
+ Ui.printMessageWithSepNewLine(setPriorityLevelMessage);
+
+ saveHabitListToFile(habitList);
+ }
+
+ /**
+ * Sort the habits in the habit tracker list according to their priority from HIGH to LOW.
+ *
+ * @throws HabitException If there are any exception errors when sorting.
+ */
+ public void sortHabits() throws HabitException {
+ try {
+ // Define a custom comparator to sort habits based on their priority
+ Comparator habitComparator = Comparator.comparing(habit -> {
+ switch (habit.getPriority()) {
+ case HIGH:
+ return 0;
+ case MED:
+ return 1;
+ case LOW:
+ return 2;
+ default:
+ return 3; // Handles any unexpected case
+ }
+ });
+
+ // Sort the habitList using the custom comparator
+ Collections.sort(habitList, habitComparator);
+ } catch (Exception e) {
+ throw new HabitException(e.getMessage());
+ }
+
+ if (habitList.isEmpty()) {
+ Ui.printMessageWithSepNewLine("You have no habits to sort.");
+ } else {
+ Ui.printMessageWithSepNewLine("Habits have been sorted according to priority." +
+ "\nUse `habit list` to view the updated list.");
+ }
+
+ saveHabitListToFile(habitList);
+ }
+
+ public void clearHabitList() {
+ habitList.clear();
+ saveHabitListToFile(habitList);
+ }
+
+ public ArrayList getHabitList() {
+ return habitList;
+ }
+}
diff --git a/src/main/java/habit/Priority.java b/src/main/java/habit/Priority.java
new file mode 100644
index 0000000000..476f069c96
--- /dev/null
+++ b/src/main/java/habit/Priority.java
@@ -0,0 +1,10 @@
+package habit;
+
+/**
+ * Enumeration representing priority levels of habits.
+ */
+public enum Priority {
+ HIGH,
+ MED,
+ LOW
+}
diff --git a/src/main/java/parser/FitnessCommandParser.java b/src/main/java/parser/FitnessCommandParser.java
new file mode 100644
index 0000000000..dbc8e66b49
--- /dev/null
+++ b/src/main/java/parser/FitnessCommandParser.java
@@ -0,0 +1,41 @@
+package parser;
+
+import commands.Command;
+import commands.fitnesscommands.AddExerciseCommand;
+import commands.fitnesscommands.GetExercisesCommand;
+import commands.fitnesscommands.GoalExerciseCommand;
+import commands.fitnesscommands.HelpExerciseCommand;
+import commands.fitnesscommands.DeleteExerciseCommand;
+import exceptions.FitnessException;
+import fitness.FitnessMotivator;
+
+import static commands.fitnesscommands.ErrorMessageConstants.COMMAND_SUGGESTION_MESSAGE;
+
+public class FitnessCommandParser {
+
+ private static final int COMMAND_LENGTH = 2;
+ public static Command determineFitnessCommand(FitnessMotivator fitnessMotivator, String commandArgs)
+ throws FitnessException {
+
+ String[] userCommand = commandArgs.trim().split("\\s+", 2);
+ String userFitnessCommand = userCommand[0].trim();
+
+ String fitnessCommandArgs =
+ userCommand.length == COMMAND_LENGTH ? userCommand[1].trim() : "";
+
+ switch (userFitnessCommand) {
+ case "get":
+ return new GetExercisesCommand(fitnessMotivator, fitnessCommandArgs);
+ case "add":
+ return new AddExerciseCommand(fitnessMotivator, fitnessCommandArgs);
+ case "goal":
+ return new GoalExerciseCommand(fitnessMotivator, fitnessCommandArgs);
+ case "help":
+ return new HelpExerciseCommand(fitnessMotivator, fitnessCommandArgs);
+ case "delete":
+ return new DeleteExerciseCommand(fitnessMotivator, fitnessCommandArgs);
+ default:
+ throw new FitnessException(COMMAND_SUGGESTION_MESSAGE);
+ }
+ }
+}
diff --git a/src/main/java/parser/FocusCommandParser.java b/src/main/java/parser/FocusCommandParser.java
new file mode 100644
index 0000000000..09815451b2
--- /dev/null
+++ b/src/main/java/parser/FocusCommandParser.java
@@ -0,0 +1,55 @@
+package parser;
+
+import commands.Command;
+
+import commands.focuscommands.CheckTimeCommand;
+import commands.focuscommands.FocusHelpCommand;
+import commands.focuscommands.SetPauseCommand;
+import commands.focuscommands.SetResumeCommand;
+import commands.focuscommands.SetTimingCommand;
+import commands.focuscommands.StartTimerCommand;
+import commands.focuscommands.StopTimerCommand;
+import commands.focuscommands.SwitchTimerCommand;
+import exceptions.FocusException;
+import focus.FocusTimer;
+
+/**
+ * FocusCommandParser class determines the type of command based on the user's input and
+ * maps it to the corresponding command.
+ */
+public class FocusCommandParser {
+ /**
+ * Determines the command based on user input and returns the corresponding command object.
+ *
+ * @param focusTimer The focus timer object for wellness360.
+ * @param commandArgs The command arguments from user's input.
+ * @return Type of command to be executed.
+ * @throws FocusException If the type of command is not part of the list of commands.
+ */
+ public static Command determineFocusCommand(FocusTimer focusTimer, String commandArgs) throws FocusException {
+ String[] userCommand = commandArgs.trim().split("\\s+", 2);
+ String userFocusTimerCommand = userCommand[0];
+ String focusTimerCommandArgs = userCommand.length == 2 ? userCommand[1] : "";
+
+ switch (userFocusTimerCommand) {
+ case "start":
+ return new StartTimerCommand(focusTimer);
+ case "stop":
+ return new StopTimerCommand(focusTimer);
+ case "switch":
+ return new SwitchTimerCommand(focusTimer);
+ case "pause":
+ return new SetPauseCommand(focusTimer);
+ case "resume":
+ return new SetResumeCommand(focusTimer);
+ case "check":
+ return new CheckTimeCommand(focusTimer);
+ case "set":
+ return new SetTimingCommand(focusTimer, focusTimerCommandArgs);
+ case "help":
+ return new FocusHelpCommand();
+ default:
+ throw new FocusException("Unknown Focus Timer command");
+ }
+ }
+}
diff --git a/src/main/java/parser/HabitCommandParser.java b/src/main/java/parser/HabitCommandParser.java
new file mode 100644
index 0000000000..ed6cf07193
--- /dev/null
+++ b/src/main/java/parser/HabitCommandParser.java
@@ -0,0 +1,55 @@
+package parser;
+
+import commands.Command;
+import commands.habitcommands.AddHabitCommand;
+import commands.habitcommands.DeleteHabitCommand;
+import commands.habitcommands.HabitHelpCommand;
+import commands.habitcommands.ListHabitsCommand;
+import commands.habitcommands.SetPriorityCommand;
+import commands.habitcommands.SortHabitsCommand;
+import commands.habitcommands.UpdateHabitCountCommand;
+import exceptions.HabitException;
+import habit.HabitTracker;
+
+/**
+ * Represents the input parser of Habit Tracker.
+ * Parses user input into Habit Tracker commands.
+ * Generates the respective Command objects to be executed, based on the parsed command keyword.
+ */
+public class HabitCommandParser {
+ private static final int COMMAND_LENGTH = 2;
+
+ /**
+ * Parses user input into command and details, and creates the corresponding habit command objects.
+ *
+ * @param habitTracker The HabitTracker instance to be used for storing the habits.
+ * @param commandArgs User input for the habit tracker command.
+ * @return Command object to be executed.
+ * @throws HabitException If the user input is not part of the command list.
+ */
+ public static Command determineHabitCommand(HabitTracker habitTracker, String commandArgs) throws HabitException {
+ String[] userCommand = commandArgs.trim().split("\\s+", 2);
+ String userHabitCommand = userCommand[0].trim();
+
+ String habitCommandArgs = userCommand.length == COMMAND_LENGTH ? userCommand[1].trim() : "";
+
+ switch(userHabitCommand) {
+ case "add":
+ return new AddHabitCommand(habitTracker, habitCommandArgs);
+ case "list":
+ return new ListHabitsCommand(habitTracker);
+ case "update":
+ return new UpdateHabitCountCommand(habitTracker, habitCommandArgs);
+ case "delete":
+ return new DeleteHabitCommand(habitTracker, habitCommandArgs);
+ case "set":
+ return new SetPriorityCommand(habitTracker, habitCommandArgs);
+ case "sort":
+ return new SortHabitsCommand(habitTracker);
+ case "help":
+ return new HabitHelpCommand();
+ default:
+ throw new HabitException("Unknown command");
+ }
+ }
+}
diff --git a/src/main/java/parser/Parser.java b/src/main/java/parser/Parser.java
new file mode 100644
index 0000000000..f723eed16c
--- /dev/null
+++ b/src/main/java/parser/Parser.java
@@ -0,0 +1,52 @@
+package parser;
+
+import commands.Command;
+import commands.ExitCommand;
+import fitness.FitnessMotivator;
+import focus.FocusTimer;
+import habit.HabitTracker;
+import reflection.ReflectionManager;
+import sleep.SleepTracker;
+
+import static parser.FitnessCommandParser.determineFitnessCommand;
+import static parser.HabitCommandParser.determineHabitCommand;
+import static parser.ReflectionCommandParser.determineReflectionCommand;
+import static parser.SleepCommandParser.determineSleepCommand;
+import static parser.FocusCommandParser.determineFocusCommand;
+import exceptions.Wellness360Exception;
+
+public class Parser {
+ private static final int COMMAND_LENGTH = 2;
+
+ public static Command determineCommand(SleepTracker sleepTracker,
+ ReflectionManager reflection,
+ HabitTracker habitTracker,
+ FocusTimer focusTimer,
+ FitnessMotivator fitnessMotivator,
+ String userInput)
+ throws Wellness360Exception {
+
+ userInput = userInput.toLowerCase();
+ String[] userWords = userInput.trim().split("\\s+", 2);
+ String userCommandSection = userWords[0];
+
+ String commandArgs = userWords.length == COMMAND_LENGTH ? userWords[1] : "";
+
+ switch (userCommandSection) {
+ case "reflect":
+ return determineReflectionCommand(reflection, commandArgs);
+ case "habit":
+ return determineHabitCommand(habitTracker, commandArgs);
+ case "sleep":
+ return determineSleepCommand(sleepTracker, commandArgs);
+ case "fitness":
+ return determineFitnessCommand(fitnessMotivator, commandArgs);
+ case "focus":
+ return determineFocusCommand(focusTimer, commandArgs);
+ case "exit":
+ return new ExitCommand(focusTimer, commandArgs);
+ default:
+ throw new Wellness360Exception("Unknown Wellness360 command");
+ }
+ }
+}
diff --git a/src/main/java/parser/ReflectionCommandParser.java b/src/main/java/parser/ReflectionCommandParser.java
new file mode 100644
index 0000000000..877fa6b93b
--- /dev/null
+++ b/src/main/java/parser/ReflectionCommandParser.java
@@ -0,0 +1,48 @@
+package parser;
+
+import commands.Command;
+import commands.reflectcommands.ReflectionHelpCommand;
+import commands.reflectcommands.ListFavouriteReflectionsCommand;
+import commands.reflectcommands.GetReflectionQuestionsCommand;
+import commands.reflectcommands.UnsaveFromFavouritesCommand;
+import commands.reflectcommands.SaveToFavouritesCommand;
+import exceptions.ReflectException;
+import reflection.ReflectionManager;
+
+/**
+ * A parser for handling reflection-related commands.
+ */
+public class ReflectionCommandParser {
+
+ /**
+ * Determines the appropriate reflection command based on user input.
+ *
+ * @param reflectionManager The ReflectionManager instance to be used for executing commands.
+ * @param commandArgs The string containing the user command and its arguments.
+ * @return The Command object corresponding to the user's reflection command.
+ * @throws ReflectException if an error occurs during command parsing or execution.
+ */
+ public static Command determineReflectionCommand(ReflectionManager reflectionManager, String commandArgs)
+ throws ReflectException {
+
+ String[] userCommand = commandArgs.trim().split("\\s+", 2);
+ String userReflectionCommand = userCommand[0];
+
+ String reflectionCommandArgs = userCommand.length == 2 ? userCommand[1] : "";
+
+ switch(userReflectionCommand) {
+ case "get":
+ return new GetReflectionQuestionsCommand(reflectionManager, reflectionCommandArgs);
+ case "save":
+ return new SaveToFavouritesCommand(reflectionManager, reflectionCommandArgs);
+ case "unsave":
+ return new UnsaveFromFavouritesCommand(reflectionManager, reflectionCommandArgs);
+ case "list":
+ return new ListFavouriteReflectionsCommand(reflectionManager, reflectionCommandArgs);
+ case "help":
+ return new ReflectionHelpCommand(reflectionManager, reflectionCommandArgs);
+ default:
+ throw new ReflectException("Unknown reflect command");
+ }
+ }
+}
diff --git a/src/main/java/parser/SleepCommandParser.java b/src/main/java/parser/SleepCommandParser.java
new file mode 100644
index 0000000000..204f356257
--- /dev/null
+++ b/src/main/java/parser/SleepCommandParser.java
@@ -0,0 +1,54 @@
+package parser;
+
+import commands.Command;
+import commands.sleepcommands.AddSleepCommand;
+import commands.sleepcommands.DeleteSleepCommand;
+import commands.sleepcommands.GetSleepCommand;
+import commands.sleepcommands.HelpSleepCommand;
+import commands.sleepcommands.ListSleepCommand;
+import commands.sleepcommands.SaveSleepCommand;
+import commands.sleepcommands.UpdateSleepCommand;
+import exceptions.SleepException;
+import sleep.SleepTracker;
+
+/**
+ * Represents the input parser of Sleep Tracker.
+ * Parses user input into Sleep Tracker commands and details of the command.
+ * Generates the respective Command objects based on the parsed command keyword.
+ */
+public class SleepCommandParser {
+ /**
+ * Parses user input into command and details, and creates the corresponding Command object.
+ *
+ * @param sleepTracker Class that contains information and functions to be executed required by Sleep Tracker
+ * @param commandArgs User input for the sleep tracker command.
+ * @return Command object to be executed.
+ * @throws SleepException If the user input is not part of the command list.
+ */
+ public static Command determineSleepCommand(SleepTracker sleepTracker, String commandArgs)
+ throws SleepException {
+ String[] userCommand = commandArgs.trim().split("\\s+", 2);
+ String userSleepCommand = userCommand[0];
+
+ String sleepCommandArgs = userCommand.length == 2 ? userCommand[1] : "";
+
+ switch(userSleepCommand) {
+ case "help":
+ return new HelpSleepCommand(sleepCommandArgs);
+ case "add":
+ return new AddSleepCommand(sleepTracker, sleepCommandArgs);
+ case "list":
+ return new ListSleepCommand(sleepTracker, sleepCommandArgs);
+ case "get":
+ return new GetSleepCommand(sleepTracker, sleepCommandArgs);
+ case "update":
+ return new UpdateSleepCommand(sleepTracker, sleepCommandArgs);
+ case "delete":
+ return new DeleteSleepCommand(sleepTracker, sleepCommandArgs);
+ case "save":
+ return new SaveSleepCommand(sleepTracker, sleepCommandArgs);
+ default:
+ throw new SleepException("Unknown sleep command");
+ }
+ }
+}
diff --git a/src/main/java/reflection/FavoriteReflectionsList.java b/src/main/java/reflection/FavoriteReflectionsList.java
new file mode 100644
index 0000000000..bbc8e30a8a
--- /dev/null
+++ b/src/main/java/reflection/FavoriteReflectionsList.java
@@ -0,0 +1,24 @@
+package reflection;
+
+/**
+ * Represents a list of favorite reflection questions.
+ */
+public class FavoriteReflectionsList extends ReflectionList {
+
+ /**
+ * Constructs a FavoriteReflectionsList with an empty list of favorites.
+ */
+ public FavoriteReflectionsList() {
+ super();
+ }
+
+ /**
+ * Retrieves a favorite reflection question by its index in the list.
+ *
+ * @param favouritesId The index of the favorite reflection question to retrieve.
+ * @return The reflection question at the specified index.
+ */
+ public ReflectionQuestion get(int favouritesId) {
+ return reflectionList.get(favouritesId);
+ }
+}
diff --git a/src/main/java/reflection/ReflectionList.java b/src/main/java/reflection/ReflectionList.java
new file mode 100644
index 0000000000..c1a0410497
--- /dev/null
+++ b/src/main/java/reflection/ReflectionList.java
@@ -0,0 +1,59 @@
+package reflection;
+
+import java.util.ArrayList;
+
+/**
+ * Represents a list of reflection questions.
+ */
+public abstract class ReflectionList {
+ protected ArrayList reflectionList;
+
+ /**
+ * Constructs a ReflectionList with an empty list of reflections.
+ */
+ public ReflectionList() {
+ this.reflectionList = new ArrayList<>();
+ }
+
+ /**
+ * Adds a reflection question to the list.
+ *
+ * @param reflectionQuestion The reflection question to be added.
+ */
+ public void addReflectionQuestion(ReflectionQuestion reflectionQuestion) {
+ if (!reflectionQuestion.toString().isBlank()) {
+ reflectionList.add(reflectionQuestion);
+ }
+ }
+
+ /**
+ * Removes a reflection question from the list.
+ *
+ * @param reflectionQuestion The reflection question to be removed.
+ */
+ public void removeReflectionQuestion(ReflectionQuestion reflectionQuestion) {
+ if (!reflectionQuestion.toString().isBlank()) {
+ reflectionList.remove(reflectionQuestion);
+ }
+ }
+
+
+ /**
+ * Retrieves the size of the reflection list.
+ *
+ * @return The size of the reflection list.
+ */
+ public int getSize() {
+ return reflectionList.size();
+ }
+
+ /**
+ * Retrieves the list of reflection questions.
+ *
+ * @return The list of reflection questions.
+ */
+ public ArrayList getReflectionList() {
+ return reflectionList;
+ }
+}
+
diff --git a/src/main/java/reflection/ReflectionManager.java b/src/main/java/reflection/ReflectionManager.java
new file mode 100644
index 0000000000..573acadab8
--- /dev/null
+++ b/src/main/java/reflection/ReflectionManager.java
@@ -0,0 +1,130 @@
+package reflection;
+
+import exceptions.ReflectException;
+import storage.Storage;
+import ui.Ui;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Manages reflection-related operations.
+ */
+public class ReflectionManager {
+ private static final String[] HELP_MENU_INSTRUCTIONS = {
+ "reflect get: Get 5 random reflection questions",
+ "reflect save : Save reflection question by id to favourites list",
+ "reflect unsave : Unsave reflection question by id from favourites list",
+ "reflect list: Retrieve questions from favourites list",
+ "reflect help: Get help menu for reflect commands"
+ };
+ private static final String FAVOURITE_QUESTIONS_FILE_PATH = "data/favourites.txt";
+ private ArrayList fiveRandomQuestions;
+ private ReflectionQuestionBank questionBank;
+ private FavoriteReflectionsList favoriteReflectionsList;
+
+ /**
+ * Constructs a ReflectionManager and initializes question bank and favorite reflections list.
+ */
+ public ReflectionManager() {
+ this.questionBank = new ReflectionQuestionBank();
+
+ this.favoriteReflectionsList = new FavoriteReflectionsList();
+ ArrayList favouritesList = Storage.loadDataFromFile(FAVOURITE_QUESTIONS_FILE_PATH);
+
+ for (String fav : favouritesList) {
+ ReflectionQuestion reflectionQuestion = new ReflectionQuestion(fav);
+ this.favoriteReflectionsList.addReflectionQuestion(reflectionQuestion);
+ }
+ }
+
+ /**
+ * Prints five random reflection questions.
+ */
+ public void printFiveRandomQuestions() {
+ try {
+ fiveRandomQuestions = questionBank.getFiveRandomQuestions();
+ Ui.printList(fiveRandomQuestions, "Generated Questions:");
+ } catch (ReflectException e) {
+ Ui.printMessageWithSepNewLine(e.getMessage());
+ }
+ }
+
+ /**
+ * Saves a reflection question to favorites.
+ *
+ * @param reflectionId The ID of the reflection question to save.
+ * @throws ReflectException if an error occurs during saving.
+ */
+ public void saveReflectionQuestion(int reflectionId) throws ReflectException {
+ try {
+ ReflectionQuestion questionToSave = fiveRandomQuestions.get(reflectionId - 1);
+
+ ArrayList currentFavourites = favoriteReflectionsList.getReflectionList();
+
+ // Check for duplicate saved questions
+ for (ReflectionQuestion question: currentFavourites) {
+ String savedQuestion = question.toString();
+ String newQuestionToSave = questionToSave.toString();
+
+ if (newQuestionToSave.equals(savedQuestion)) {
+ throw new ReflectException("Saving duplicate questions are not allowed");
+ }
+ }
+
+ favoriteReflectionsList.addReflectionQuestion(questionToSave);
+ Storage.saveTasksToFile(FAVOURITE_QUESTIONS_FILE_PATH, favoriteReflectionsList.getReflectionList());
+
+ Ui.printMessageWithSepNewLine("Got it. Added reflection question to favourites:\n" + questionToSave);
+
+ } catch (IndexOutOfBoundsException e) {
+ throw new ReflectException("Key in valid favourite reflection ID, between 1 and 5");
+ } catch (NullPointerException e) {
+ throw new ReflectException("No questions generated yet. Generate questions using 'reflect get' " +
+ "command first.");
+ }
+ }
+
+ /**
+ * Unsaves a reflection question from favorites.
+ *
+ * @param reflectionId The ID of the reflection question to be unsaved.
+ * @throws ReflectException if an error occurs during saving.
+ */
+ public void unsaveReflectionQuestion(int reflectionId) throws ReflectException {
+ try {
+ ReflectionQuestion questionToUnsave = favoriteReflectionsList.get(reflectionId - 1);
+
+ favoriteReflectionsList.removeReflectionQuestion(questionToUnsave);
+ Storage.saveTasksToFile(FAVOURITE_QUESTIONS_FILE_PATH, favoriteReflectionsList.getReflectionList());
+
+ Ui.printMessageWithSepNewLine("Got it. Unsaved reflection question from favourites:\n" + questionToUnsave);
+
+ } catch (IndexOutOfBoundsException e) {
+ throw new ReflectException("Key in valid favourite reflection ID, key in 'reflect list' command to " +
+ "view range of questions in your favourites list. ");
+ }
+ }
+
+ /**
+ * Prints the list of favorite reflection questions.
+ */
+ public void printFavourites() {
+ if(favoriteReflectionsList.getReflectionList().isEmpty()) {
+ Ui.printMessageWithSepNewLine("No reflection questions saved to favourites");
+ } else {
+ Ui.printList(favoriteReflectionsList.getReflectionList(), "Favourites list:");
+ }
+ }
+
+ /**
+ * Prints the help menu for reflection commands.
+ */
+ public void printHelpMenu() {
+ ArrayList helpMenuInstructionsList = new ArrayList<>(Arrays.asList(HELP_MENU_INSTRUCTIONS));
+
+ assert helpMenuInstructionsList.size() == 5 : "Help menu should have 5 instructions";
+
+ Ui.printList(helpMenuInstructionsList, "Commands for reflection feature:");
+ }
+
+}
diff --git a/src/main/java/reflection/ReflectionQuestion.java b/src/main/java/reflection/ReflectionQuestion.java
new file mode 100644
index 0000000000..bec607d309
--- /dev/null
+++ b/src/main/java/reflection/ReflectionQuestion.java
@@ -0,0 +1,27 @@
+package reflection;
+
+/**
+ * Represents a reflection question.
+ */
+public class ReflectionQuestion {
+ private String question;
+
+ /**
+ * Constructs a ReflectionQuestion with the given question text.
+ *
+ * @param question The text of the reflection question.
+ */
+ public ReflectionQuestion(String question) {
+ this.question = question;
+ }
+
+ /**
+ * Retrieves the text of the reflection question.
+ *
+ * @return The text of the reflection question.
+ */
+ @Override
+ public String toString() {
+ return question;
+ }
+}
diff --git a/src/main/java/reflection/ReflectionQuestionBank.java b/src/main/java/reflection/ReflectionQuestionBank.java
new file mode 100644
index 0000000000..5fcfc375ae
--- /dev/null
+++ b/src/main/java/reflection/ReflectionQuestionBank.java
@@ -0,0 +1,112 @@
+package reflection;
+
+import exceptions.ReflectException;
+
+import java.util.Collections;
+import java.util.ArrayList;
+
+/**
+ * Represents a bank of reflection questions.
+ */
+public class ReflectionQuestionBank extends ReflectionList{
+ private static final String[] REFLECTION_QUESTIONS = {
+ "What have been the most significant lessons you've learned about yourself in the past year?",
+ "How have your values evolved over time, and why?",
+ "What habits or behaviors do you want to cultivate or change to become a better version of yourself?",
+ "Reflect on a recent failure or setback. What did you learn from it, and how will you apply those lessons " +
+ "moving forward?",
+ "In what areas do you feel you've made the most progress recently, and what contributed to that progress?",
+ "What are your biggest strengths, and how can you leverage them more effectively in your daily life?",
+ "Describe a moment when you stepped outside of your comfort zone. What did you discover about yourself?",
+ "How do you define success, and how has that definition evolved over time?",
+ "What activities or hobbies bring you the most joy and fulfillment, and how can you incorporate more of them " +
+ "into your life?",
+ "Reflect on a time when you received constructive criticism. How did you react, and what did you learn from " +
+ "the experience?",
+ "Who are the people in your life who bring you the most happiness and support? How do you nurture " +
+ "those relationships?",
+ "Have there been any conflicts or misunderstandings in your relationships recently? How have you worked " +
+ "to resolve them?",
+ "Reflect on a time when you felt truly understood by someone. What made that connection so meaningful?",
+ "How do you prioritize spending time with loved ones amidst your busy schedule?",
+ "What qualities do you admire most in your closest friends or family members, and why?",
+ "Are there any relationships in your life that feel strained or unfulfilling? How can you address " +
+ "those challenges?",
+ "Reflect on your communication style. In what ways do you excel, and where do you see room for improvement?",
+ "Describe a memorable shared experience with someone you care about. What made it special?",
+ "How do you express gratitude toward the people who enrich your life?",
+ "Reflect on a time when you forgave someone or were forgiven. What did you learn from that experience?",
+ "What motivates you to excel in your career, beyond financial rewards?",
+ "Reflect on a recent professional success. What factors contributed to your achievement?",
+ "In what ways do you seek to grow and develop within your current role or industry?",
+ "How do you balance pursuing your career ambitions with maintaining a healthy work-life balance?",
+ "Reflect on a time when you faced a significant challenge at work. How did you overcome it?",
+ "What steps are you taking to advance your skills and knowledge in your field?",
+ "Describe a mentor or role model who has had a profound impact on your career journey. " +
+ "What lessons have you learned from them?",
+ "How do you navigate workplace conflicts or disagreements with colleagues or superiors?",
+ "Reflect on your career goals. Are they still aligned with your passions and values, or have they evolved?",
+ "What do you consider to be your greatest professional achievement so far, and why?",
+ "How do you prioritize self-care and well-being in your daily life?",
+ "Reflect on a recent creative project or activity that brought you joy. What inspired you to pursue it?",
+ "In what ways do you express yourself creatively, whether through art, writing, music, or other mediums?",
+ "Describe a time when you felt in a state of flow, completely absorbed in a creative endeavor.",
+ "How do you overcome creative blocks or periods of stagnation?",
+ "Reflect on a piece of art, literature, or music that has deeply resonated with you. " +
+ "What emotions or insights did it evoke?",
+ "What role does creativity play in your life, and how do you nurture it?",
+ "How do you make time for creative pursuits amidst your other responsibilities and commitments?",
+ "Reflect on a time when you took a creative risk. What did you learn from the experience?",
+ "Describe a recent moment when you felt inspired by something or someone in your environment.",
+ "How do you celebrate your unique talents and creative voice?"
+ };
+
+ /**
+ * Constructs a ReflectionQuestionBank and initializes the list of reflection questions.
+ */
+ public ReflectionQuestionBank() {
+ super();
+ setUpReflectionBank();
+ }
+
+ /**
+ * Initializes the reflection question bank with predefined questions.
+ */
+ private void setUpReflectionBank() {
+ for(String question : REFLECTION_QUESTIONS) {
+ ReflectionQuestion reflectionQuestion = new ReflectionQuestion(question);
+ addReflectionQuestion(reflectionQuestion);
+ }
+ assert !reflectionList.isEmpty() : "Reflection question bank should not be empty";
+ }
+
+ /**
+ * Retrieves five random reflection questions from the bank.
+ *
+ * @return An ArrayList containing five random reflection questions.
+ * @throws ReflectException if the bank is empty.
+ */
+ public ArrayList getFiveRandomQuestions() throws ReflectException {
+ try {
+ ArrayList randomQuestions = new ArrayList<>();
+
+ // Create a copy of the original list
+ ArrayList copyList = new ArrayList<>(reflectionList);
+
+ // Shuffle the copy list
+ Collections.shuffle(copyList);
+
+ // Select the first five questions from the shuffled copy list and
+ // add them to the result list
+ for (int i = 0; i < 5; i++) {
+ randomQuestions.add(copyList.get(i));
+ }
+
+ assert randomQuestions.size() == 5 : "random questions list size should be 5";
+
+ return randomQuestions;
+ } catch (IndexOutOfBoundsException e) {
+ throw new ReflectException("Question bank is empty");
+ }
+ }
+}
diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java
deleted file mode 100644
index 5c74e68d59..0000000000
--- a/src/main/java/seedu/duke/Duke.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package seedu.duke;
-
-import java.util.Scanner;
-
-public class Duke {
- /**
- * Main entry-point for the java.duke.Duke application.
- */
- public static void main(String[] args) {
- String logo = " ____ _ \n"
- + "| _ \\ _ _| | _____ \n"
- + "| | | | | | | |/ / _ \\\n"
- + "| |_| | |_| | < __/\n"
- + "|____/ \\__,_|_|\\_\\___|\n";
- System.out.println("Hello from\n" + logo);
- System.out.println("What is your name?");
-
- Scanner in = new Scanner(System.in);
- System.out.println("Hello " + in.nextLine());
- }
-}
diff --git a/src/main/java/sleep/DeleteMode.java b/src/main/java/sleep/DeleteMode.java
new file mode 100644
index 0000000000..d2bd798581
--- /dev/null
+++ b/src/main/java/sleep/DeleteMode.java
@@ -0,0 +1,5 @@
+package sleep;
+
+public enum DeleteMode {
+ FIND, BEFORE, BETWEEN
+}
diff --git a/src/main/java/sleep/SleepCycle.java b/src/main/java/sleep/SleepCycle.java
new file mode 100644
index 0000000000..e5e33f8045
--- /dev/null
+++ b/src/main/java/sleep/SleepCycle.java
@@ -0,0 +1,43 @@
+package sleep;
+
+import date.DateFormat;
+
+import java.time.LocalDate;
+
+public class SleepCycle implements Comparable {
+ private double hoursSlept;
+ private LocalDate dateOfSleep;
+
+ public SleepCycle(double hours, LocalDate date) {
+ this.hoursSlept = hours;
+ this.dateOfSleep = date;
+ }
+
+ public double getHoursSlept() {
+ return hoursSlept;
+ }
+
+ public LocalDate getDateOfSleep() {
+ return dateOfSleep;
+ }
+
+ public void setHoursOfSleep(double newHours) {
+ hoursSlept = newHours;
+ }
+
+ public int compareTo(SleepCycle sleepCycle) {
+ LocalDate compareDate = sleepCycle.dateOfSleep;
+ if (compareDate.isEqual(this.dateOfSleep)) {
+ return 0;
+ } else if (compareDate.isAfter(this.dateOfSleep)) {
+ return -1;
+ } else {
+ return 1;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return DateFormat.convertDateToString(this.getDateOfSleep()) + ": " + this.hoursSlept;
+ }
+}
diff --git a/src/main/java/sleep/SleepCycleList.java b/src/main/java/sleep/SleepCycleList.java
new file mode 100644
index 0000000000..c4dc45bfa6
--- /dev/null
+++ b/src/main/java/sleep/SleepCycleList.java
@@ -0,0 +1,200 @@
+package sleep;
+
+import date.DateFormat;
+import ui.Ui;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ * Represents the lists of sleep cycles
+ */
+public class SleepCycleList {
+ private ArrayList sleepCycleList;
+ private double totalHrsSlept;
+ private int numberOfCycles;
+
+ /**
+ * Constructs a new SleepCycleList object with an empty list.
+ */
+ public SleepCycleList() {
+ this.sleepCycleList = new ArrayList<>();
+ this.totalHrsSlept = 0;
+ this.numberOfCycles = 0;
+ }
+
+ /**
+ * Adds a new sleep cycle into sleepCycleList.
+ * @param sleepCycle sleep cycle to be added
+ * @param isPrint true if user wants to print out message, false otherwise
+ */
+ public void addSleepCycle(SleepCycle sleepCycle, boolean isPrint) {
+ int cyclesDeleted = 0;
+ String message = "";
+ LocalDate startDate = null;
+ LocalDate endDate = null;
+ if (numberOfCycles > 0) {
+ startDate = sleepCycleList.get(0).getDateOfSleep();
+ endDate = sleepCycleList.get(0).getDateOfSleep();
+ }
+ while (totalHrsSlept >= Double.MAX_VALUE - 24) {
+ cyclesDeleted += 1;
+ SleepCycle currSleepCycle = sleepCycleList.get(0);
+ endDate = currSleepCycle.getDateOfSleep();
+ totalHrsSlept -= currSleepCycle.getHoursSlept();
+ sleepCycleList.remove(0);
+ numberOfCycles--;
+ }
+ if (cyclesDeleted > 0) {
+ message += "Deleted sleep cycles from " + startDate + " to " + endDate;
+ }
+ sleepCycleList.add(sleepCycle);
+ totalHrsSlept += sleepCycle.getHoursSlept();
+ numberOfCycles += 1;
+ if (isPrint) {
+ message += ("--- SleepCycle for "
+ + DateFormat.convertDateToString(sleepCycle.getDateOfSleep())
+ + " has been added ---");
+ Ui.printMessageWithSepNewLine(message);
+ }
+ Collections.sort(sleepCycleList);
+ }
+
+ /**
+ * Deletes a new sleep cycle in sleepCycleList.
+ * @param date date of sleep cycle to be deleted
+ */
+ public void deleteSleepCycle(LocalDate date) {
+ for (int i = 0; i < numberOfCycles; i++) {
+ SleepCycle currSleepCycle = sleepCycleList.get(i);
+ if (date.isEqual(currSleepCycle.getDateOfSleep())) {
+ totalHrsSlept -= currSleepCycle.getHoursSlept();
+ sleepCycleList.remove(i);
+ numberOfCycles--;
+ Ui.printMessageWithSepNewLine("Sleep cycle for " + DateFormat.convertDateToString(date)
+ + " has been removed from list");
+ return;
+ }
+ }
+ Ui.printMessageWithSepNewLine("No entry for sleep cycle on " + DateFormat.convertDateToString(date));
+ }
+
+ /**
+ * Deletes a new sleep cycle in sleepCycleList.
+ * @param date date at which sleep cycles logged before this date are to be deleted
+ */
+ public void deleteSleepCyclesBefore(LocalDate date) {
+ int numberOfDeletion = 0;
+ while (numberOfCycles > 0 && sleepCycleList.get(0).getDateOfSleep().isBefore(date)) {
+ totalHrsSlept -= sleepCycleList.get(0).getHoursSlept();
+ sleepCycleList.remove(0);
+ numberOfCycles--;
+ numberOfDeletion++;
+ }
+ Ui.printMessageWithSepNewLine("A total of " + numberOfDeletion + " sleep cycles have been deleted");
+ }
+
+ /**
+ * Deletes a new sleep cycle in sleepCycleList.
+ * @param startDate lower bound of date of sleep cycles to be deleted
+ * @param endDate upper bound of date of sleep cycles to be deleted
+ */
+ public void deleteSleepCyclesBetween(LocalDate startDate, LocalDate endDate) {
+ int startId = 0;
+ int numberOfDeletion = 0;
+ while (startId < numberOfCycles) {
+ SleepCycle currSleepCycle = sleepCycleList.get(startId);
+ if (startDate.isAfter(currSleepCycle.getDateOfSleep())) {
+ startId++;
+ } else {
+ break;
+ }
+ }
+ while (startId < numberOfCycles) {
+ SleepCycle currSleepCycle = sleepCycleList.get(startId);
+ if (!currSleepCycle.getDateOfSleep().isAfter(endDate)) {
+ totalHrsSlept -= currSleepCycle.getHoursSlept();
+ sleepCycleList.remove(startId);
+ numberOfDeletion++;
+ numberOfCycles--;
+ } else {
+ break;
+ }
+ }
+ Ui.printMessageWithSepNewLine("A total of " + numberOfDeletion + " sleep cycles have been deleted");
+ }
+
+ /**
+ * List out all sleep cycles in sleepCycleList
+ */
+ public void listSleepCycles() {
+ String sleepListMessage = "Total hrs slept: " + totalHrsSlept + System.lineSeparator();
+ for (int i = 0; i < numberOfCycles - 1; i++) {
+ sleepListMessage += (i + 1) + ". " + sleepCycleList.get(i) + System.lineSeparator();
+ }
+ if (numberOfCycles > 0) {
+ sleepListMessage += numberOfCycles + ". " + sleepCycleList.get(numberOfCycles - 1);
+ } else {
+ sleepListMessage += "No sleep cycle has been added";
+ }
+ Ui.printMessageWithSepNewLine(sleepListMessage);
+ }
+
+ /**
+ * Prints out number of hours of a date's sleep cycle in sleepCycleList.
+ * @param date date of sleep cycle to be found
+ * @param isPrint true if user wants to print out message, false otherwise
+ * @return id of sleepCycle of the specific date
+ */
+ public int getSleepCycle(LocalDate date, boolean isPrint) {
+ for (int i = 0; i < numberOfCycles; i++) {
+ SleepCycle currSleep = sleepCycleList.get(i);
+ boolean isSame = currSleep.getDateOfSleep().isEqual(date);
+ if (isSame && isPrint) {
+ Ui.printMessageWithSepNewLine("Hours slept on " + DateFormat.convertDateToString(date)
+ + ": " + currSleep.getHoursSlept());
+ return i;
+ } else if (isSame && !isPrint) {
+ return i;
+ }
+ }
+ if (isPrint) {
+ Ui.printMessageWithSepNewLine("No entry found for the date.");
+ }
+ return -1;
+ }
+
+ /**
+ * Updates a sleep cycle in sleepCycleList.
+ * @param date date of sleep cycle to be updated
+ * @param newHours updated hours of sleep cycle
+ */
+ public void updateSleepCycle(LocalDate date, double newHours) {
+ for (int i = 0; i < numberOfCycles; i++) {
+ SleepCycle currSleep = sleepCycleList.get(i);
+ if (currSleep.getDateOfSleep().isEqual(date)){
+ double oldHours = currSleep.getHoursSlept();
+ currSleep.setHoursOfSleep(newHours);
+ totalHrsSlept = totalHrsSlept - oldHours + newHours;
+ Ui.printMessageWithSepNewLine("Hours of sleep for " + DateFormat.convertDateToString(date) +
+ " has been updated from " + oldHours + " to " + newHours);
+ return;
+ }
+ }
+ Ui.printMessageWithSepNewLine("No entry found for the date.");
+ }
+
+ public int getNumberOfCycles() {
+ return numberOfCycles;
+ }
+
+ public double getTotalHrsSlept() {
+ return totalHrsSlept;
+ }
+
+ public ArrayList getSleepCycleList() {
+ return this.sleepCycleList;
+ }
+
+}
diff --git a/src/main/java/sleep/SleepTracker.java b/src/main/java/sleep/SleepTracker.java
new file mode 100644
index 0000000000..1dd9a3b7d5
--- /dev/null
+++ b/src/main/java/sleep/SleepTracker.java
@@ -0,0 +1,59 @@
+package sleep;
+
+import exceptions.SleepException;
+import storage.sleep.SleepTrackerStorage;
+import ui.Ui;
+
+import java.time.LocalDate;
+
+/**
+ * Represents the interface for SleepTracker
+ * The sleepCycleList contains all the tasks managed by Wellness360.
+ */
+public class SleepTracker {
+ SleepCycleList sleepCycleList;
+
+ public SleepTracker() {
+ try {
+ this.sleepCycleList = SleepTrackerStorage.loadSleepListFromFile();
+ } catch (SleepException e){
+ Ui.printMessageWithSepNewLine(e.getMessage());
+ }
+
+ }
+
+ public void listSleepCycles() {
+ sleepCycleList.listSleepCycles();
+ }
+
+ public void addSleepCycle(SleepCycle sleepCycleToAdd) throws SleepException {
+ if (sleepCycleList.getSleepCycle(sleepCycleToAdd.getDateOfSleep(), false) >= 0) {
+ throw new SleepException("There is already an existing sleep cycle for the date");
+ }
+ sleepCycleList.addSleepCycle(sleepCycleToAdd, true);
+ }
+
+ public void updateSleepCycle(LocalDate date, double newHours) {
+ sleepCycleList.updateSleepCycle(date, newHours);
+ }
+
+ public void getSleepCycle(LocalDate date) {
+ sleepCycleList.getSleepCycle(date, true);
+ }
+
+ public void deleteSleepCycle(LocalDate date) {
+ sleepCycleList.deleteSleepCycle(date);
+ }
+
+ public void deleteSleepCyclesBefore(LocalDate date) {
+ sleepCycleList.deleteSleepCyclesBefore(date);
+ }
+
+ public void deleteSleepCyclesBetween(LocalDate startDate, LocalDate endDate) {
+ sleepCycleList.deleteSleepCyclesBetween(startDate, endDate);
+ }
+
+ public void saveSleepCycles() {
+ SleepTrackerStorage.saveSleepListToFile(sleepCycleList.getSleepCycleList());
+ }
+}
diff --git a/src/main/java/storage/Storage.java b/src/main/java/storage/Storage.java
new file mode 100644
index 0000000000..45c50e7ee1
--- /dev/null
+++ b/src/main/java/storage/Storage.java
@@ -0,0 +1,59 @@
+package storage;
+
+import ui.Ui;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Scanner;
+
+public class Storage {
+
+ public static ArrayList loadDataFromFile(String filePath) {
+ ArrayList data = new ArrayList<>();
+
+ try (Scanner scanner = new Scanner(new File(filePath))) {
+ while (scanner.hasNextLine()) {
+ String nextLineOfData = scanner.nextLine();
+
+ data.add(nextLineOfData);
+ }
+ } catch (FileNotFoundException e) {
+ createFolder(filePath);
+ }
+
+ return data;
+ }
+
+ public static void createFolder(String filePath) {
+ try {
+ File f = new File(filePath);
+ f.getParentFile().mkdirs();
+ f.createNewFile();
+ } catch (IOException e) {
+ Ui.printMessageWithSepNewLine(e.getMessage());
+ }
+ }
+
+ public static void saveTasksToFile(String filePath, ArrayList data) {
+ try {
+ FileWriter fw = new FileWriter(filePath);
+ for (T item : data) {
+ fw.write(item.toString() + System.lineSeparator());
+ }
+ fw.close();
+ } catch (IOException e) {
+ Ui.printMessageWithSepNewLine(e.getMessage());
+ }
+ }
+
+ public static boolean isFileCreated (String filePath) {
+ File f = new File(filePath);
+ return f.exists() && !f.isDirectory();
+ }
+
+}
+
+
+
diff --git a/src/main/java/storage/habit/HabitTrackerStorage.java b/src/main/java/storage/habit/HabitTrackerStorage.java
new file mode 100644
index 0000000000..a3c6bdc4ad
--- /dev/null
+++ b/src/main/java/storage/habit/HabitTrackerStorage.java
@@ -0,0 +1,62 @@
+package storage.habit;
+
+import exceptions.HabitException;
+import habit.Habit;
+import habit.Priority;
+import storage.Storage;
+
+import java.util.ArrayList;
+
+/**
+ * Class to handle storage of data for Habit Tracker.
+ */
+public class HabitTrackerStorage {
+ private static final String HABIT_FILE_PATH = "data/habits.txt";
+ private static final String COMMA_SEPARATION = ", ";
+ private static final int DATA_SIZE = 3;
+
+ /**
+ * Saves list of habits added by user into a text file.
+ *
+ * @param habitList List of habits added by user to be saved.
+ */
+ public static void saveHabitListToFile(ArrayList habitList) {
+ ArrayList data = new ArrayList<>();
+
+ for (Habit habit : habitList) {
+ data.add(habit.getDescription() + COMMA_SEPARATION + habit.getHabitCount()
+ + COMMA_SEPARATION + habit.getPriority());
+ }
+
+ Storage.saveTasksToFile(HABIT_FILE_PATH, data);
+ }
+
+ /**
+ * Load list of habits from a local text file.
+ *
+ * @return ArrayList List of habits added by user.
+ * @throws HabitException If there are any errors in loading the data from the text file.
+ */
+ public static ArrayList loadHabitListFromFile() throws HabitException {
+ ArrayList habitList = new ArrayList<>();
+ ArrayList data = Storage.loadDataFromFile(HABIT_FILE_PATH);
+
+ for (String line : data) {
+ String[] parts = line.split(", ");
+
+ if (parts.length != DATA_SIZE) {
+ throw new HabitException("Error in loading habit tracker data from local storage\n" +
+ "To fix: Add a new habit to override the corrupted habit data file!");
+ }
+
+ String description = parts[0];
+ int habitCount = Integer.parseInt(parts[1]);
+ Priority priority = Priority.valueOf(parts[2]);
+ Habit habit = new Habit(description, habitCount, priority);
+ habitList.add(habit);
+ }
+
+ return habitList;
+ }
+
+}
diff --git a/src/main/java/storage/sleep/SleepTrackerStorage.java b/src/main/java/storage/sleep/SleepTrackerStorage.java
new file mode 100644
index 0000000000..1756441b63
--- /dev/null
+++ b/src/main/java/storage/sleep/SleepTrackerStorage.java
@@ -0,0 +1,73 @@
+package storage.sleep;
+
+import date.DateFormat;
+import exceptions.SleepException;
+import sleep.SleepCycle;
+import sleep.SleepCycleList;
+import storage.Storage;
+import ui.Ui;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeParseException;
+import java.util.ArrayList;
+
+/**
+ * Class to handle storage of data for Sleep Tracker
+ */
+public class SleepTrackerStorage {
+ private static final String SLEEP_FILE_PATH = "data/sleep.txt";
+ private static final String ERROR_MESSAGE = "Error in loading habit tracker data from local storage";
+ private static final int HOURS_POS = 0;
+ private static final int DATE_POS = 1;
+
+ /**
+ * Saves list of sleep cycles added by user into a text file.
+ * @param sleepList list of sleep cycles added by user to be saved
+ */
+ public static void saveSleepListToFile(ArrayList sleepList) {
+ if (!Storage.isFileCreated(SLEEP_FILE_PATH)) {
+ Storage.createFolder(SLEEP_FILE_PATH);
+ }
+
+ ArrayList data = new ArrayList<>();
+
+ for (SleepCycle sleepCycle : sleepList) {
+ data.add(sleepCycle.getHoursSlept() + "," + DateFormat.convertDateToString(sleepCycle.getDateOfSleep()));
+ }
+
+ Storage.saveTasksToFile(SLEEP_FILE_PATH, data);
+ Ui.printMessageWithSepNewLine("Saved list to storage file");
+ }
+
+ /**
+ * Load list of sleep cycles added by user from a text file.
+ * @return SleepCycleList list of sleep cycles added by user
+ */
+ public static SleepCycleList loadSleepListFromFile() throws SleepException {
+ if (!Storage.isFileCreated(SLEEP_FILE_PATH)) {
+ Storage.createFolder(SLEEP_FILE_PATH);
+ }
+
+ ArrayList data = Storage.loadDataFromFile(SLEEP_FILE_PATH);
+ SleepCycleList sleepCycleList = new SleepCycleList();
+
+ for (String line : data) {
+ String[] parts = line.split(",");
+
+ if (parts.length != 2) {
+ throw new SleepException(ERROR_MESSAGE);
+ }
+ double hour;
+ LocalDate date;
+ try {
+ hour = Double.parseDouble(parts[HOURS_POS]);
+ date = DateFormat.convertStringToDate(parts[DATE_POS]);
+ } catch (NumberFormatException | DateTimeParseException e) {
+ throw new SleepException(ERROR_MESSAGE);
+ }
+ SleepCycle sleepCycle = new SleepCycle(hour, date);
+ sleepCycleList.addSleepCycle(sleepCycle, false);
+ }
+ return sleepCycleList;
+ }
+}
diff --git a/src/main/java/ui/Ui.java b/src/main/java/ui/Ui.java
new file mode 100644
index 0000000000..d85eca976c
--- /dev/null
+++ b/src/main/java/ui/Ui.java
@@ -0,0 +1,77 @@
+package ui;
+
+import java.util.ArrayList;
+
+/**
+ * Ui class handles user interface related functionalities
+ * such as displaying messages,prompting user input, and
+ * printing separators.
+ */
+public class Ui {
+ private static final String BOT_NAME = "Wellness360";
+ private static final String SEP = "_______________________________________________________" +
+ "_________________________________________________________";
+ private static final String LOGO =
+ "__ __ _ _ _____ __ ___ \n" +
+ "\\ \\ / /__| | |_ __ ___ ___ ___|___ / / /_ / _ \\ \n" +
+ " \\ \\ /\\ / / _ \\ | | '_ \\ / _ \\/ __/ __| |_ \\| '_ \\| | | |\n" +
+ " \\ V V / __/ | | | | | __/\\__ \\__ \\___) | (_) | |_| |\n" +
+ " \\_/\\_/ \\___|_|_|_| |_|\\___||___/___/____/ \\___/ \\___/\n";
+
+ /**
+ * Greets the user upon starting the application.
+ */
+ public static void greetUser() {
+ System.out.println("Welcome to Wellness360!\n\n" +
+ "Use the following help commands to view the various help menus:\n" +
+ "- reflect help: Reflection Manager\n" +
+ "- habit help: Habit Tracker\n" +
+ "- sleep help: Sleep Tracker\n" +
+ "- focus help: Focus Timer\n" +
+ "- fitness help: Fitness Motivator\n" + SEP);
+ }
+
+ /**
+ * Prints the prompt for user input.
+ */
+ public static void promptUserInput() {
+ System.out.print("You:");
+ }
+
+ /**
+ * Says goodbye to the user upon exiting the application.
+ * Displays a farewell message.
+ */
+ public static void sayGoodbye() {
+ Ui.printMessageWithSepNewLine("Goodbye! See you again!");
+ }
+
+ /**
+ * Prints a message followed by a separator and a new line.
+ *
+ * @param message the message to be printed
+ */
+ public static void printMessageWithSepNewLine(String message) {
+ System.out.println(SEP + "\n" +message + "\n" + SEP);
+ }
+
+ /**
+ * Prints a message without a new line after, and subsequently followed by a separator.
+ *
+ * @param message the message to be printed
+ */
+ public static void printMessageWithoutNewLine(String message) {
+ System.out.println(SEP + "\n" + message + SEP);
+ }
+
+
+ public static void printList(ArrayList list, String message) {
+ System.out.println(SEP);
+ System.out.println(message);
+ for (int i = 0; i < list.size(); i ++) {
+ System.out.println((i + 1) + ". " + list.get(i).toString());
+ }
+ System.out.println(SEP);
+ }
+
+}
diff --git a/src/test/java/commands/reflectcommands/GetReflectionQuestionsCommandTest.java b/src/test/java/commands/reflectcommands/GetReflectionQuestionsCommandTest.java
new file mode 100644
index 0000000000..5c217b7629
--- /dev/null
+++ b/src/test/java/commands/reflectcommands/GetReflectionQuestionsCommandTest.java
@@ -0,0 +1,39 @@
+package commands.reflectcommands;
+
+import exceptions.ReflectException;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import reflection.ReflectionManager;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+
+class GetReflectionQuestionsCommandTest {
+
+ private ReflectionManager reflectionManager;
+ private GetReflectionQuestionsCommand command;
+
+ @BeforeEach
+ void setUp() throws ReflectException {
+ reflectionManager = new ReflectionManager();
+ command = new GetReflectionQuestionsCommand(reflectionManager, "");
+ }
+
+ @Test
+ void constructor_emptyArgs_noExceptionThrown() {
+ assertDoesNotThrow(() -> new GetReflectionQuestionsCommand(reflectionManager, ""));
+ }
+
+ @Test
+ void constructor_nonEmptyArgs_exceptionThrown() {
+ assertThrows(ReflectException.class, () -> new GetReflectionQuestionsCommand(reflectionManager, "args"));
+ }
+
+
+ @Test
+ void isExit_notExitCommand_returnFalse() {
+ assertFalse(command.isExit());
+ }
+}
+
diff --git a/src/test/java/commands/reflectcommands/ListFavouriteReflectionsCommandTest.java b/src/test/java/commands/reflectcommands/ListFavouriteReflectionsCommandTest.java
new file mode 100644
index 0000000000..e4d3df5144
--- /dev/null
+++ b/src/test/java/commands/reflectcommands/ListFavouriteReflectionsCommandTest.java
@@ -0,0 +1,37 @@
+package commands.reflectcommands;
+
+import exceptions.ReflectException;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import reflection.ReflectionManager;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+
+public class ListFavouriteReflectionsCommandTest {
+ private ReflectionManager reflectionManager;
+ private ListFavouriteReflectionsCommand command;
+
+ @BeforeEach
+ void setUp() throws ReflectException {
+ reflectionManager = new ReflectionManager();
+ command = new ListFavouriteReflectionsCommand(reflectionManager, "");
+ }
+
+ @Test
+ void constructor_emptyArgs_noExceptionThrown() {
+ assertDoesNotThrow(() -> new ListFavouriteReflectionsCommand(reflectionManager, ""));
+ }
+
+ @Test
+ void constructor_nonEmptyArgs_exceptionThrown() {
+ assertThrows(ReflectException.class, () -> new ListFavouriteReflectionsCommand(reflectionManager, "args"));
+ }
+
+ @Test
+ void isExit_notExitCommand_returnFalse() {
+ assertFalse(command.isExit());
+ }
+}
+
diff --git a/src/test/java/commands/reflectcommands/ReflectionHelpCommandTest.java b/src/test/java/commands/reflectcommands/ReflectionHelpCommandTest.java
new file mode 100644
index 0000000000..c62ad5f67f
--- /dev/null
+++ b/src/test/java/commands/reflectcommands/ReflectionHelpCommandTest.java
@@ -0,0 +1,37 @@
+package commands.reflectcommands;
+
+import exceptions.ReflectException;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import reflection.ReflectionManager;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+
+public class ReflectionHelpCommandTest {
+
+ private ReflectionManager reflectionManager;
+ private ReflectionHelpCommand command;
+
+ @BeforeEach
+ void setUp() throws ReflectException {
+ reflectionManager = new ReflectionManager();
+ command = new ReflectionHelpCommand(reflectionManager, "");
+ }
+
+ @Test
+ void constructor_emptyArgs_noExceptionThrown() {
+ assertDoesNotThrow(() -> new ReflectionHelpCommand(reflectionManager, ""));
+ }
+
+ @Test
+ void constructor_nonEmptyArgs_exceptionThrown() {
+ assertThrows(ReflectException.class, () -> new ReflectionHelpCommand(reflectionManager, "args"));
+ }
+
+ @Test
+ void isExit_notExitCommand_returnFalse() {
+ assertFalse(command.isExit());
+ }
+}
diff --git a/src/test/java/commands/reflectcommands/SaveToFavouritesCommandTest.java b/src/test/java/commands/reflectcommands/SaveToFavouritesCommandTest.java
new file mode 100644
index 0000000000..86789cae7e
--- /dev/null
+++ b/src/test/java/commands/reflectcommands/SaveToFavouritesCommandTest.java
@@ -0,0 +1,31 @@
+package commands.reflectcommands;
+
+import exceptions.ReflectException;
+import org.junit.jupiter.api.Test;
+import reflection.ReflectionManager;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+
+class SaveToFavouritesCommandTest {
+
+ @Test
+ void constructor_validReflectionId_success() {
+ ReflectionManager reflectionManager = new ReflectionManager();
+ assertDoesNotThrow(() -> new SaveToFavouritesCommand(reflectionManager, "1"));
+ }
+
+ @Test
+ void constructor_invalidReflectionId_throwReflectException() {
+ ReflectionManager reflectionManager = new ReflectionManager();
+ assertThrows(ReflectException.class, () -> new SaveToFavouritesCommand(reflectionManager, "invalidId"));
+ }
+
+ @Test
+ void testIsExit() throws ReflectException {
+ ReflectionManager reflectionManager = new ReflectionManager();
+ SaveToFavouritesCommand saveCommand = new SaveToFavouritesCommand(reflectionManager, "1");
+ assertFalse(saveCommand.isExit());
+ }
+}
diff --git a/src/test/java/commands/reflectcommands/UnsaveFromFavouritesCommandTest.java b/src/test/java/commands/reflectcommands/UnsaveFromFavouritesCommandTest.java
new file mode 100644
index 0000000000..6db8025176
--- /dev/null
+++ b/src/test/java/commands/reflectcommands/UnsaveFromFavouritesCommandTest.java
@@ -0,0 +1,30 @@
+package commands.reflectcommands;
+
+import exceptions.ReflectException;
+import org.junit.jupiter.api.Test;
+import reflection.ReflectionManager;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+
+public class UnsaveFromFavouritesCommandTest {
+ @Test
+ void constructor_validReflectionId_success() {
+ ReflectionManager reflectionManager = new ReflectionManager();
+ assertDoesNotThrow(() -> new UnsaveFromFavouritesCommand(reflectionManager, "1"));
+ }
+
+ @Test
+ void constructor_invalidReflectionId_throwReflectException() {
+ ReflectionManager reflectionManager = new ReflectionManager();
+ assertThrows(ReflectException.class, () -> new UnsaveFromFavouritesCommand(reflectionManager, "invalidId"));
+ }
+
+ @Test
+ void testIsExit() throws ReflectException {
+ ReflectionManager reflectionManager = new ReflectionManager();
+ UnsaveFromFavouritesCommand saveCommand = new UnsaveFromFavouritesCommand(reflectionManager, "1");
+ assertFalse(saveCommand.isExit());
+ }
+}
diff --git a/src/test/java/fitness/FitnessMotivatorTest.java b/src/test/java/fitness/FitnessMotivatorTest.java
new file mode 100644
index 0000000000..b3147d721f
--- /dev/null
+++ b/src/test/java/fitness/FitnessMotivatorTest.java
@@ -0,0 +1,137 @@
+package fitness;
+
+import commands.fitnesscommands.AddExerciseCommand;
+import exceptions.FitnessException;
+import exceptions.Wellness360Exception;
+import fitness.exercise.Exercise;
+import fitness.exercise.ExerciseList;
+import fitness.goals.ExerciseGoal;
+import fitness.goals.ExerciseGoalList;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+
+
+import static commands.fitnesscommands.ErrorMessageConstants.INCORRECT_SETS_REPS_ERROR_MESSAGE;
+import static commands.fitnesscommands.ErrorMessageConstants.INSUFFICIENT_ADD_PARAMS_ERROR_MESSAGE;
+import static commands.fitnesscommands.ErrorMessageConstants.ILLEGAL_TYPE_ERROR_MESSAGE;
+import static fitness.FitnessMotivator.DATA_FILE_PATH;
+import static fitness.FitnessMotivator.GOALS_FILE_PATH;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static storage.Storage.isFileCreated;
+
+public class FitnessMotivatorTest {
+
+ private FitnessMotivator fitnessMotivator;
+ private ExerciseList allExercises;
+ private ExerciseGoalList dailyGoals;
+
+ @BeforeEach
+ public void setUp() {
+ File file1 = new File(DATA_FILE_PATH);
+ File file2 = new File(GOALS_FILE_PATH);
+ if (isFileCreated(DATA_FILE_PATH)) {
+ file1.delete();
+ }
+ if (isFileCreated(GOALS_FILE_PATH)) {
+ file2.delete();
+ }
+ this.fitnessMotivator = new FitnessMotivator();
+ this.allExercises = fitnessMotivator.allExercises;
+ this.dailyGoals = fitnessMotivator.dailyGoals;
+ }
+
+ @Test
+ public void printExercises_getExercises_success() {
+ String output = fitnessMotivator.getExercises();
+ assertTrue(output.contains("Arms"));
+ assertTrue(output.contains("Chest"));
+ assertTrue(output.contains("Abs"));
+ assertTrue(output.contains("Back"));
+ assertTrue(output.contains("Legs"));
+ assertTrue(output.contains("sets"));
+ assertTrue(output.contains("reps"));
+ }
+
+ @Test
+ public void addExerciseIntoList_addExercises_success() {
+ // Test input for adding exercise
+ String[] exerciseDetails = {
+ "Arms", "testing", "3", "10"
+ };
+
+ // Generating results before adding and after adding exercises
+ Exercise exercise = new Exercise(
+ "testing", ExerciseType.ARMS, "3", "10");
+ Exercise searchResultBeforeAdding =
+ allExercises.findExercise(ExerciseType.ARMS, "testing");
+ fitnessMotivator.addExercises(exerciseDetails);
+ Exercise searchResultAfterAdding =
+ allExercises.findExercise(ExerciseType.ARMS, "testing");
+
+ // Assertions
+ assertNull(searchResultBeforeAdding);
+ assertEquals(exercise.getExerciseName(), searchResultAfterAdding.getExerciseName());
+ assertEquals(exercise.getType(), searchResultAfterAdding.getType());
+ assertEquals(exercise.getSets(), searchResultAfterAdding.getSets());
+ assertEquals(exercise.getReps(), searchResultAfterAdding.getReps());
+ }
+
+ @Test
+ public void incorrectParameters_addExercises_success() {
+ // Checks if the validation of input for add Exercise works
+ Wellness360Exception exceptionOne = assertThrows(FitnessException.class, () ->
+ new AddExerciseCommand(fitnessMotivator, "testing"));
+ Wellness360Exception exceptionTwo = assertThrows(FitnessException.class, () ->
+ new AddExerciseCommand(fitnessMotivator, "/type arms /name testing /sets a /reps b"));
+ Wellness360Exception exceptionThree = assertThrows(FitnessException.class, () ->
+ new AddExerciseCommand(fitnessMotivator, "/type testing /name testing /sets 3 /reps 10"));
+
+ assertEquals("ERROR MSG: " + INSUFFICIENT_ADD_PARAMS_ERROR_MESSAGE, exceptionOne.getMessage());
+ assertEquals("ERROR MSG: " + INCORRECT_SETS_REPS_ERROR_MESSAGE, exceptionTwo.getMessage());
+ assertEquals("ERROR MSG: " + ILLEGAL_TYPE_ERROR_MESSAGE, exceptionThree.getMessage());
+ }
+
+ @Test
+ public void deleteExercise_deleteFromList_success() {
+
+ // Add pre-defined exercise
+ Exercise exercise = new Exercise(
+ "testing", ExerciseType.CHEST, "3", "10");
+ allExercises.add(exercise);
+ Exercise testExerciseAdded = allExercises.findExercise(ExerciseType.CHEST, "testing");
+
+ // Check that the exercise created matches the exercise
+ assertEquals(exercise.getExerciseName(), testExerciseAdded.getExerciseName());
+ assertEquals(exercise.getType(), testExerciseAdded.getType());
+ assertEquals(exercise.getSets(), testExerciseAdded.getSets());
+ assertEquals(exercise.getReps(), testExerciseAdded.getReps());
+
+ // Delete pre-defined exercise
+ allExercises.remove(exercise);
+
+ // Check does not exist
+ Exercise testExerciseDeleted = allExercises.findExercise(ExerciseType.CHEST, "testing");
+ assertNull(testExerciseDeleted);
+ }
+
+ @Test
+ public void toggleGoal_markGoalDone_success() {
+ int index = 3;
+
+ // Create goal, get status of goal 3
+ fitnessMotivator.newGoals();
+ ExerciseGoal testExerciseBeforeToggle = dailyGoals.findExercise(index - 1);
+ assertFalse(testExerciseBeforeToggle.getStatus());
+
+ // Mark goal 3 using toggle, then get status of goal 3
+ fitnessMotivator.toggleGoal(index);
+ ExerciseGoal testExerciseAfterToggle = dailyGoals.findExercise(index - 1);
+ assertTrue(testExerciseAfterToggle.getStatus());
+ }
+}
diff --git a/src/test/java/focus/CountdownTimerTest.java b/src/test/java/focus/CountdownTimerTest.java
new file mode 100644
index 0000000000..5293b934f1
--- /dev/null
+++ b/src/test/java/focus/CountdownTimerTest.java
@@ -0,0 +1,44 @@
+package focus;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class CountdownTimerTest {
+ private CountdownTimer countdownTimer;
+
+ @BeforeEach
+ public void setUp() {
+ countdownTimer = new CountdownTimer();
+ }
+
+ @Test
+ public void setStartTimer_startTimer_success() {
+ countdownTimer.setStart();
+ assertEquals(true, countdownTimer.getRunningStatus());
+ }
+
+ @Test
+ public void setStopTimer_stopTimer_success() {
+ countdownTimer.setStart();
+ countdownTimer.setStop();
+ assertEquals(false, countdownTimer.getRunningStatus());
+ }
+
+ @Test
+ public void setPause_pauseTimer_success() {
+ countdownTimer.setStart();
+ countdownTimer.setPause();
+ assertEquals(false, countdownTimer.getPausedStatus());
+ }
+
+ @Test
+ public void setResume_resumeTimer_success() {
+ countdownTimer.setStart();
+ countdownTimer.setPause();
+ countdownTimer.setResume();
+ assertEquals(true, countdownTimer.getPausedStatus());
+ }
+
+}
diff --git a/src/test/java/focus/CountupTimerTest.java b/src/test/java/focus/CountupTimerTest.java
new file mode 100644
index 0000000000..17c3c86c72
--- /dev/null
+++ b/src/test/java/focus/CountupTimerTest.java
@@ -0,0 +1,43 @@
+package focus;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class CountupTimerTest {
+ private CountupTimer countupTimer;
+
+ @BeforeEach
+ public void setUp() {
+ countupTimer = new CountupTimer();
+ }
+
+ @Test
+ public void setStartTimer_startTimer_success() {
+ countupTimer.setStartTiming();
+ assertEquals(true, countupTimer.getStartedStatus());
+ }
+
+ @Test
+ public void setStopTimer_stopTimer_success() {
+ countupTimer.setStartTiming();
+ countupTimer.setStopTiming();
+ assertEquals(false, countupTimer.getStartedStatus());
+ }
+
+ @Test
+ public void setPause_pauseTimer_success() {
+ countupTimer.setStartTiming();
+ countupTimer.setPause();
+ assertEquals(true, countupTimer.getPauseStatus());
+ }
+
+ @Test
+ public void setResume_resumeTimer_success() {
+ countupTimer.setStartTiming();
+ countupTimer.setPause();
+ countupTimer.setResume();
+ assertEquals(false, countupTimer.getPauseStatus());
+ }
+}
diff --git a/src/test/java/habit/HabitTest.java b/src/test/java/habit/HabitTest.java
new file mode 100644
index 0000000000..fa6bf6b07c
--- /dev/null
+++ b/src/test/java/habit/HabitTest.java
@@ -0,0 +1,56 @@
+package habit;
+
+import exceptions.HabitException;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class HabitTest {
+
+ @Test
+ public void updateCount_increaseCount_success() throws HabitException {
+ Habit habit = new Habit("Exercise");
+ int changeInCount = habit.updateCount("2");
+ assertEquals(2, habit.getHabitCount());
+ assertEquals(2, changeInCount);
+ }
+
+ @Test
+ public void updateCount_decreaseCount_success() throws HabitException {
+ Habit habit = new Habit("Exercise", 3, Priority.HIGH);
+ int changeInCount = habit.updateCount("-2");
+ assertEquals(1, habit.getHabitCount());
+ assertEquals(-2, changeInCount);
+ }
+
+ @Test
+ public void updateCount_invalidInput_throwException() {
+ Habit habit = new Habit("Exercise", 3, Priority.HIGH);
+ assertThrows(HabitException.class, () -> {
+ habit.updateCount("invalid");
+ });
+ }
+
+ @Test
+ public void updateCount_negativeCount_throwException() {
+ Habit habit = new Habit("Exercise", 3, Priority.HIGH);
+ assertThrows(HabitException.class, () -> {
+ habit.updateCount("-4");
+ });
+ }
+
+ @Test
+ public void setPriority_validInput_success() {
+ Habit habit = new Habit("Exercise");
+ habit.setPriority("high");
+ assertEquals(Priority.HIGH, habit.getPriority());
+ }
+
+ @Test
+ public void setPriority_invalidInput_noChange() {
+ Habit habit = new Habit("Exercise");
+ habit.setPriority("invalid");
+ assertEquals(Priority.LOW, habit.getPriority());
+ }
+}
diff --git a/src/test/java/habit/HabitTrackerTest.java b/src/test/java/habit/HabitTrackerTest.java
new file mode 100644
index 0000000000..18f43db93d
--- /dev/null
+++ b/src/test/java/habit/HabitTrackerTest.java
@@ -0,0 +1,69 @@
+package habit;
+
+import exceptions.HabitException;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class HabitTrackerTest {
+ private HabitTracker habitTracker;
+ @BeforeEach
+ public void setUp() {
+ habitTracker = new HabitTracker();
+ }
+
+ @AfterEach
+ public void tearDown() {
+ habitTracker.clearHabitList();
+ }
+
+ @Test
+ public void addHabit_addTwoHabits_success() {
+ Habit habitOne = new Habit("Complete my homework");
+ Habit habitTwo = new Habit("Brush my teeth");
+ habitTracker.addHabit(habitOne);
+ habitTracker.addHabit(habitTwo);
+ assertEquals(2, HabitTracker.getNumberOfHabits());
+ }
+
+ @Test
+ public void updateHabitCount_habitCountTwo_success() throws HabitException {
+ Habit habitOne = new Habit("Complete my homework");
+ habitTracker.addHabit(habitOne);
+ habitTracker.updateHabitCount(1, "2");
+ assertEquals(2, habitOne.getHabitCount());
+ }
+
+ @Test
+ public void deleteHabit_deleteOneHabit_success() throws HabitException {
+ Habit habitOne = new Habit("Vacuum the floor");
+ habitTracker.addHabit(habitOne);
+ habitTracker.deleteHabit(1);
+ assertEquals(0, HabitTracker.getNumberOfHabits());
+ }
+
+ @Test
+ public void setPriorityLevel_setPriorityToHigh_success() throws HabitException {
+ Habit habitOne = new Habit("Complete my homework");
+ habitTracker.addHabit(habitOne);
+ habitTracker.setPriorityLevel(1, "high");
+ assertEquals(Priority.HIGH, habitOne.getPriority());
+ }
+
+ @Test
+ public void sortHabits_sortByPriority_success() throws HabitException {
+ Habit habitOne = new Habit("Complete my homework", 2, Priority.LOW);
+ Habit habitTwo = new Habit("Brush my teeth", 1, Priority.MED);
+ habitTracker.addHabit(habitOne);
+ habitTracker.addHabit(habitTwo);
+
+ habitTracker.sortHabits();
+
+ assertEquals(Priority.MED, habitTracker.getHabitList().get(0).getPriority());
+ assertEquals(Priority.LOW, habitTracker.getHabitList().get(1).getPriority());
+ }
+
+
+}
diff --git a/src/test/java/reflect/FavoriteReflectionsListTest.java b/src/test/java/reflect/FavoriteReflectionsListTest.java
new file mode 100644
index 0000000000..40886d67f7
--- /dev/null
+++ b/src/test/java/reflect/FavoriteReflectionsListTest.java
@@ -0,0 +1,60 @@
+package reflect;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import reflection.FavoriteReflectionsList;
+import reflection.ReflectionQuestion;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+
+public class FavoriteReflectionsListTest {
+
+ private FavoriteReflectionsList favoriteReflectionList;
+
+ @BeforeEach
+ public void setUp() {
+ favoriteReflectionList = new FavoriteReflectionsList();
+ }
+
+ @Test
+ public void addReflectionQuestion_addQuestion_success() {
+ ReflectionQuestion question = new ReflectionQuestion("What is reflection?");
+ favoriteReflectionList.addReflectionQuestion(question);
+
+ assertEquals(1, favoriteReflectionList.getReflectionList().size());
+ assertTrue(favoriteReflectionList.getReflectionList().contains(question));
+ }
+
+ @Test
+ public void addReflectionQuestion_addBlankQuestion_skipOverBlankQuestion() {
+ //Attempt to add blank question
+ ReflectionQuestion question = new ReflectionQuestion("");
+ favoriteReflectionList.addReflectionQuestion(question);
+
+ //Empty or blank questions should be ignored
+ assertEquals(0, favoriteReflectionList.getReflectionList().size());
+ assertFalse(favoriteReflectionList.getReflectionList().contains(question));
+ }
+
+ @Test
+ public void removeReflectionQuestion_removeQuestion_success() {
+ ReflectionQuestion question = new ReflectionQuestion("What is reflection?");
+ favoriteReflectionList.addReflectionQuestion(question);
+ favoriteReflectionList.removeReflectionQuestion(question);
+
+ assertEquals(0, favoriteReflectionList.getReflectionList().size());
+ assertFalse(favoriteReflectionList.getReflectionList().contains(question));
+ }
+
+ @Test
+ public void get_getQuestionById_success() {
+ ReflectionQuestion question = new ReflectionQuestion("What is reflection?");
+ favoriteReflectionList.addReflectionQuestion(question);
+ ReflectionQuestion retrievedQuestion = favoriteReflectionList.get(0);
+
+ assertEquals(question, retrievedQuestion);
+ }
+}
+
diff --git a/src/test/java/reflect/ReflectQuestionBankTest.java b/src/test/java/reflect/ReflectQuestionBankTest.java
new file mode 100644
index 0000000000..567aaddf5e
--- /dev/null
+++ b/src/test/java/reflect/ReflectQuestionBankTest.java
@@ -0,0 +1,82 @@
+
+package reflect;
+
+import exceptions.ReflectException;
+import reflection.ReflectionQuestion;
+import reflection.ReflectionQuestionBank;
+import java.util.ArrayList;
+import java.util.Arrays;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.fail;
+
+public class ReflectQuestionBankTest {
+
+ private ReflectionQuestionBank reflectionQuestionBank;
+
+ @BeforeEach
+ public void setUp() {
+ reflectionQuestionBank = new ReflectionQuestionBank();
+ }
+
+ @Test
+ public void addReflectionQuestion_addQuestion_success() {
+ ReflectionQuestion question = new ReflectionQuestion("What is reflection?");
+ reflectionQuestionBank.addReflectionQuestion(question);
+ assertEquals(42, reflectionQuestionBank.getSize());
+ }
+
+ @Test
+ public void addReflectionQuestion_addBlankQuestion_skipOverBlankQuestion() {
+ //Attempt to add blank question
+ ReflectionQuestion question = new ReflectionQuestion("");
+ reflectionQuestionBank.addReflectionQuestion(question);
+
+ //Empty or blank questions should be ignored
+ assertEquals(41, reflectionQuestionBank.getSize());
+ }
+
+ @Test
+ public void getFiveRandomQuestions_getFiveQuestions_success() {
+ ReflectionQuestion[] questions = {
+ new ReflectionQuestion("Question 1"),
+ new ReflectionQuestion("Question 2"),
+ new ReflectionQuestion("Question 3"),
+ new ReflectionQuestion("Question 4"),
+ new ReflectionQuestion("Question 5"),
+ new ReflectionQuestion("Question 6"),
+ new ReflectionQuestion("Question 7"),
+ new ReflectionQuestion("Question 8"),
+ new ReflectionQuestion("Question 9"),
+ new ReflectionQuestion("Question 10")
+ };
+
+ ArrayList expectedQuestions = new ArrayList<>(Arrays.asList(questions));
+
+ for (ReflectionQuestion question : expectedQuestions) {
+ reflectionQuestionBank.addReflectionQuestion(question);
+ }
+
+ try {
+ ArrayList randomQuestions = reflectionQuestionBank.getFiveRandomQuestions();
+
+ // Assert that there are exactly 5 questions returned
+ assertEquals(5, randomQuestions.size());
+
+ // Assert that all questions returned are from the expected set
+ for (ReflectionQuestion question : randomQuestions) {
+ assertNotNull(expectedQuestions.contains(question));
+ }
+ } catch (ReflectException e) {
+ fail("Question list is empty: " + e.getMessage());
+ }
+ }
+
+ @Test
+ public void getSize_getSizeOfQuestionBank_success() {
+ // Question bank size has 41 questions
+ assertEquals(41, reflectionQuestionBank.getSize());
+ }
+}
diff --git a/src/test/java/reflect/ReflectionManagerTest.java b/src/test/java/reflect/ReflectionManagerTest.java
new file mode 100644
index 0000000000..b46c694cf9
--- /dev/null
+++ b/src/test/java/reflect/ReflectionManagerTest.java
@@ -0,0 +1,78 @@
+package reflect;
+
+import exceptions.ReflectException;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import reflection.ReflectionManager;
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class ReflectionManagerTest {
+
+ private ReflectionManager reflectionManager;
+ private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream();
+ private final PrintStream originalOut = System.out;
+
+ @BeforeEach
+ void setUp() {
+ System.setOut(new PrintStream(outputStreamCaptor));
+ reflectionManager = new ReflectionManager();
+ }
+
+ @Test
+ void printFiveRandomQuestions_printAnyFiveQuestions_success() {
+ reflectionManager.printFiveRandomQuestions();
+
+ // Check that exactly 5 questions, 1 header and 2 separation lines
+ // have been printed to console
+ String[] lines = outputStreamCaptor.toString().trim().split("\r\n|\r|\n");
+ assertFalse(outputStreamCaptor.toString().trim().isEmpty());
+ assertEquals(8, lines.length);
+ }
+
+ @Test
+ void unsaveReflectionQuestion_unsaveFromNonEmptyFavouritesList_success() throws ReflectException {
+ //Add question to favourites list and delete it after
+ reflectionManager.printFiveRandomQuestions();
+ reflectionManager.saveReflectionQuestion(1);
+ assertDoesNotThrow(() -> reflectionManager.unsaveReflectionQuestion(1));
+ }
+
+ @Test
+ void saveReflectionQuestion_saveFavouriteAfterGenerateQuestions_success() throws ReflectException {
+ //Save question after questions have been generated
+ reflectionManager.printFiveRandomQuestions();
+ assertDoesNotThrow(() -> reflectionManager.saveReflectionQuestion(1));
+ }
+
+ @Test
+ void saveReflectionQuestion_failToSaveWhenNoQuestionsGenerated_throwReflectException() throws ReflectException {
+ //Unable to save as question has not been generated yet
+ assertThrows(ReflectException.class, () -> reflectionManager.saveReflectionQuestion(1));
+ }
+
+ @Test
+ void printHelpMenu_printHelpMenu_success() {
+ reflectionManager.printHelpMenu();
+
+ // Check that exactly 5 help menu items, 1 header and 2 separation lines
+ // have been printed to console
+ String[] lines = outputStreamCaptor.toString().trim().split("\r\n|\r|\n");
+ assertFalse(outputStreamCaptor.toString().trim().isEmpty());
+ assertEquals(8, lines.length);
+ }
+
+ @Test
+ void saveReflectionQuestion_indexOutOfBounds_throwsReflectException() {
+ // Users are only allowed to input id numbers between 1 and 5
+ // as each list of newly generated questions is size 5
+ assertThrows(ReflectException.class, () -> {
+ reflectionManager.saveReflectionQuestion(6);
+ });
+ }
+}
+
diff --git a/src/test/java/seedu/duke/DukeTest.java b/src/test/java/seedu/duke/DukeTest.java
deleted file mode 100644
index 2dda5fd651..0000000000
--- a/src/test/java/seedu/duke/DukeTest.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package seedu.duke;
-
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-import org.junit.jupiter.api.Test;
-
-class DukeTest {
- @Test
- public void sampleTest() {
- assertTrue(true);
- }
-}
diff --git a/src/test/java/sleep/SleepCycleListTest.java b/src/test/java/sleep/SleepCycleListTest.java
new file mode 100644
index 0000000000..77e1069d04
--- /dev/null
+++ b/src/test/java/sleep/SleepCycleListTest.java
@@ -0,0 +1,79 @@
+package sleep;
+
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class SleepCycleListTest {
+ private SleepCycleList sleepCycleList;
+
+ @BeforeEach
+ public void setUp() {
+ sleepCycleList = new SleepCycleList();
+ }
+
+ @Test
+ public void addSleepCycle_addCycle_success() {
+ LocalDate date;
+ date = LocalDate.parse("25/01/2022", DateTimeFormatter.ofPattern("dd/MM/yyyy") );
+ SleepCycle cycle = new SleepCycle(2, date);
+ sleepCycleList.addSleepCycle(cycle, false);
+ assertEquals(1, sleepCycleList.getNumberOfCycles());
+ }
+
+ @Test
+ public void addTwoSleepCycle_getTotalHoursSlept_success() {
+ LocalDate date1;
+ LocalDate date2;
+ date1 = LocalDate.parse("25/01/2022", DateTimeFormatter.ofPattern("dd/MM/yyyy"));
+ date2 = LocalDate.parse("26/01/2022", DateTimeFormatter.ofPattern("dd/MM/yyyy"));
+ SleepCycle cycle = new SleepCycle(2, date1);
+ sleepCycleList.addSleepCycle(cycle, false);
+ cycle = new SleepCycle(3, date2);
+ sleepCycleList.addSleepCycle(cycle,false);
+ assertEquals(5, sleepCycleList.getTotalHrsSlept());
+ }
+
+ @Test
+ public void deleteSleepCycles_success() {
+ LocalDate date1;
+ LocalDate date2;
+ LocalDate date3;
+ LocalDate date4;
+ date1 = LocalDate.parse("25/01/2022", DateTimeFormatter.ofPattern("dd/MM/yyyy"));
+ date2 = LocalDate.parse("26/01/2022", DateTimeFormatter.ofPattern("dd/MM/yyyy"));
+ date3 = LocalDate.parse("27/01/2022", DateTimeFormatter.ofPattern("dd/MM/yyyy"));
+ date4 = LocalDate.parse("28/01/2022", DateTimeFormatter.ofPattern("dd/MM/yyyy"));
+ SleepCycle cycle = new SleepCycle(2, date1);
+ sleepCycleList.addSleepCycle(cycle, false);
+ cycle = new SleepCycle(3, date2);
+ sleepCycleList.addSleepCycle(cycle,false);
+ cycle = new SleepCycle(4, date3);
+ sleepCycleList.addSleepCycle(cycle,false);
+ cycle = new SleepCycle(5, date4);
+ sleepCycleList.addSleepCycle(cycle,false);
+
+ sleepCycleList.deleteSleepCycle(date1);
+ assertEquals(3, sleepCycleList.getNumberOfCycles());
+ sleepCycleList.deleteSleepCyclesBefore(date3);
+ assertEquals(2, sleepCycleList.getNumberOfCycles());
+ sleepCycleList.deleteSleepCyclesBetween(date3, date4);
+ assertEquals(0, sleepCycleList.getNumberOfCycles());
+ }
+
+ @Test
+ public void updateSleepCycle_success() {
+ LocalDate date;
+ date = LocalDate.parse("25/01/2022", DateTimeFormatter.ofPattern("dd/MM/yyyy") );
+ SleepCycle cycle = new SleepCycle(2, date);
+ sleepCycleList.addSleepCycle(cycle, false);
+ assertEquals(1, sleepCycleList.getNumberOfCycles());
+ sleepCycleList.updateSleepCycle(date, 5);
+ assertEquals(5, sleepCycleList.getTotalHrsSlept());
+ }
+}
diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT
index 892cb6cae7..c006f553b7 100644
--- a/text-ui-test/EXPECTED.TXT
+++ b/text-ui-test/EXPECTED.TXT
@@ -1,9 +1,10 @@
-Hello from
- ____ _
-| _ \ _ _| | _____
-| | | | | | | |/ / _ \
-| |_| | |_| | < __/
-|____/ \__,_|_|\_\___|
+Welcome to Wellness360!
-What is your name?
-Hello James Gosling
+Use the following help commands to view the various help menus:
+- reflect help: Reflection Manager
+- habit help: Habit Tracker
+- sleep help: Sleep Tracker
+- focus help: Focus Timer
+- fitness help: Fitness Motivator
+________________________________________________________________________________________________________________
+You:
\ No newline at end of file
diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt
index f6ec2e9f95..e69de29bb2 100644
--- a/text-ui-test/input.txt
+++ b/text-ui-test/input.txt
@@ -1 +0,0 @@
-James Gosling
\ No newline at end of file