diff --git a/README.md b/README.md index 8715d4d915..c1198fbd27 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,124 @@ -# Duke project template - -This is a project template for a greenfield Java project. It's named after the Java mascot _Duke_. Given below are instructions on how to use it. - -## Setting up in Intellij - -Prerequisites: JDK 11, update Intellij to the most recent version. - -1. Open Intellij (if you are not in the welcome screen, click `File` > `Close Project` to close the existing project first) -1. Open the project into Intellij as follows: - 1. Click `Open`. - 1. Select the project directory, and click `OK`. - 1. If there are any further prompts, accept the defaults. -1. Configure the project to use **JDK 11** (not other versions) as explained in [here](https://www.jetbrains.com/help/idea/sdk.html#set-up-jdk).
- In the same dialog, set the **Project language level** field to the `SDK default` option. -3. After that, locate the `src/main/java/Duke.java` file, right-click it, and choose `Run Duke.main()` (if the code editor is showing compile errors, try restarting the IDE). If the setup is correct, you should see something like the below as the output: - ``` - Hello from - ____ _ - | _ \ _ _| | _____ - | | | | | | | |/ / _ \ - | |_| | |_| | < __/ - |____/ \__,_|_|\_\___| - ``` +
+

BrotherBot

+ + +> "We must live together as brothers or perish together like fools." - Martin Luther King Jr. +> +![](docs/Ui.png) + +
+ +## About The Project + +### Overview +We all need a brother out there who's got our back all the time. Introducing BrotherBot, your one-stop task planner with a "Brother" personality. +BrotherBot is designed to help you manage tasks with a unique 'bro' personality, ensuring that you stay on top of your responsibilities while feeling supported. This application transitions from traditional task management methods to a friendly, interactive approach. + +### Contributor(s) + +- [Daniel Ling](https://github.com/danilingzy) + +### Built With + +This section lists major libraries used. + +* [JavaFX](https://openjfx.io/) +* [JUnit](https://junit.org/junit5/) + + +## Features + +
+ +- [X] Tracks tasks +- [ ] Eat supper with you *(coming soon)* + +
+ + *** +### Adding Tasks +1. #### Add ToDo Tasks: ```todo``` + Add a ToDo task that you need to complete using the ```todo``` keyword. + Example: ```todo run``` will add "run" to your list of tasks. + +2. #### Add Deadline Tasks: ```deadline``` + Add a Deadline Task that you have to complete by a certain deadline using the ```deadline``` keyword. + Deadline will be entered behind a slash, with the format of ***'dd/MM/yyyy HHmm'***. + Example: ```deadline project /by 23/02/2023 1400``` will add "project" to your list of tasks. + +3. #### Add Event Tasks: ```event``` + Add an Event that you have to attend using the ```event``` keyword. + Duration of the event will be entered with behind a slash, with the first slash representing the start of the event and the second slash representing the end of the event. + Example: ```event CS2103T Lecture /from 17/01/2023 1400 /to 17/01/2023 1600``` will add "CS2103T Lecture" to your list of tasks. + +*** + +### Deleting Tasks: ```delete``` + +Delete a task in the task list using the ```delete``` keyword. +A positive integer (i.e., 1, 2, 3...) must be specified behind the keyword, and the task to be deleted should exist. +Example: ```delete 2``` will delete the second item in the list. + +*** + +### Exiting the Application: ```bye``` + +Simply by using the keyword ```bye```, the program will exit on its own. +Example: ```bye``` will exit the program. + +*** + +### Finding Tasks: ```find``` + +Find a task in the list using the ```find``` keyword. +The task you are looking for should be entered behind the keyword. +Example: ```find project``` will list out all the tasks that contain the word 'project'. + +*** + +### Displaying Tasks: ```display``` + +List out all the tasks using the ```display``` keyword. +Example: ```display``` will list out all the tasks that exist. + +*** + +### Marking Tasks: ```mark``` + +Mark a task that has been completed using the ```mark``` keyword. +A positive integer (i.e., 1, 2, 3...) should be specified behind the keyword. +Example: ```mark 3``` will mark the third task on the list. + +*** + +### Finding Freeslot: ```free``` + +Find the next free slot using the ```free``` keyword. +A valid duration of format ***'xHyyM'*** where x is the number of hours, and yy is the number of minutes, should be entered behind the keyword. +Example: ```free 2H30M``` will return your next free 2 hour 30 minute time slot. + +## Getting Started + +Bro! It is: + +- ~~Hard~~ Easy to use +- *Friendly* +- Treats u like a real **bro** :star_struck: + +### Wanna gain a real bro? + +Download it [here](https://github.com/daniellingzy/ip/releases/tag/v0.2) + +Here’s the `Launcher` class: +```java +package brotherbot; + +import javafx.application.Application; + +public class Launcher { + public static void main(String[] args) { + Application.launch(BrotherBot.class, args); + } +} +``` + diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000000..365a1ca212 --- /dev/null +++ b/build.gradle @@ -0,0 +1,62 @@ +plugins { + id 'java' + id 'application' + id 'com.github.johnrengelman.shadow' version '5.1.0' +} + +repositories { + gradlePluginPortal() + mavenCentral() +} + +dependencies { + String javaFxVersion = '11' + + implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'win' + implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'mac' + implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'linux' + implementation group: 'org.openjfx', name: 'javafx-controls', version: javaFxVersion, classifier: 'win' + implementation group: 'org.openjfx', name: 'javafx-controls', version: javaFxVersion, classifier: 'mac' + implementation group: 'org.openjfx', name: 'javafx-controls', version: javaFxVersion, classifier: 'linux' + implementation group: 'org.openjfx', name: 'javafx-fxml', version: javaFxVersion, classifier: 'win' + implementation group: 'org.openjfx', name: 'javafx-fxml', version: javaFxVersion, classifier: 'mac' + implementation group: 'org.openjfx', name: 'javafx-fxml', version: javaFxVersion, classifier: 'linux' + implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'win' + implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'mac' + implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'linux' + + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.5.0' + testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.5.0' +} + +test { + useJUnitPlatform() + + testLogging { + events "passed", "skipped", "failed" + + showExceptions true + exceptionFormat "full" + showCauses true + showStackTraces true + showStandardStreams = false + } +} + +application { + mainClassName = "brotherbot.Launcher" +} + +shadowJar { + archiveBaseName = "Launcher" + archiveClassifier = null +} + +run { + standardInput = System.in + enableAssertions = true +} + +run { + +} diff --git a/data.txt b/data.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index 8077118ebe..0000000000 --- a/docs/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# User Guide - -## Features - -### Feature-ABC - -Description of the feature. - -### Feature-XYZ - -Description of the feature. - -## Usage - -### `Keyword` - Describe action - -Describe the action and its outcome. - -Example of usage: - -`keyword (optional arguments)` - -Expected outcome: - -Description of the outcome. - -``` -expected output -``` diff --git a/docs/README_old.md b/docs/README_old.md new file mode 100644 index 0000000000..9a2a86dfeb --- /dev/null +++ b/docs/README_old.md @@ -0,0 +1,141 @@ +# BrotherBot + +> "We must live together as brothers or perish together like fools." - Martin Luther King Jr. + +We all need a brother out there who's got our back all the time. Introducing BrotherBot, your one-stop task planner with a "Brother" personality. + +Bro! It is: + +- ~~Hard~~ Easy to use +- *Friendly* +- Treats u like a real **bro** :star_struck: + +## Wanna gain a real bro? + +1. Download it [here](https://github.com/daniellingzy/ip/releases/tag/v0.2) + +## Bro features + + +- [X] Tracks tasks +- [ ] Eat supper with you *(coming soon)* + +If you are a Java programmer, you can review the code easily with its well designed code and JavaDocs. Here's the `Launcher` class: +``` +package brotherbot; + +import javafx.application.Application; + +public class Launcher { + public static void main(String[] args) { + Application.launch(BrotherBot.class, args); + } +} +``` + + ``` + Adapted from + ____ _ + | _ \ _ _| | _____ + | | | | | | | |/ / _ \ + | |_| | |_| | < __/ + |____/ \__,_|_|\_\___| + ``` + + + + +# User Guide + + +## **Welcome to _BrotherBot_.** +BrotherBot is an application used to manage your tasks with a unique 'bro' personality! + +## Feature Summary +- [Adding Tasks](https://github.com/daniellingzy/ip/edit/master/docs/README.md#adding-tasks) + - ToDo Tasks: ```todo``` + - Deadline Tasks: ```deadline``` + - Event Tasks: ```event``` +- [Deleting Tasks](https://github.com/daniellingzy/ip/edit/master/docs/README.md#deleting-tasks-delete): ```delete``` +- [Exiting Program](https://github.com/daniellingzy/ip/edit/master/docs/README.md#exiting-the-application-bye): ```bye``` +- [Finding Tasks](https://github.com/daniellingzy/ip/edit/master/docs/README.md#finding-tasks-find): ```find``` +- [Displaying Tasks](https://github.com/daniellingzy/ip/edit/master/docs/README.md#displaying-tasks-display): ```display``` +- [Marking Tasks](https://github.com/daniellingzy/ip/edit/master/docs/README.md#marking-tasks-mark): ```mark``` +- [Finding Freeslot](https://github.com/daniellingzy/ip/edit/master/docs/README.md#finding-freeslot): ```free``` + +## Features + +### Adding Tasks +1. #### Add ToDo Tasks: ```todo``` +Add a ToDo task that you have to complete using the ```todo``` keyword. + +Example: ```todo run``` will add run to your list of tasks + +2. #### Add Deadline Tasks: ```deadline``` +Add a Deadline Task that you have to complete by a certain deadline using the ```deadline``` keyword. +Deadline will be entered behind a slash, with the format of ***'dd/MM/yyyy HHmm'***. + +Example: ```deadline project /by 23/02/2023 1400``` will add project to your list of tasks + +3. #### Add Event Tasks: ```event``` +Add an Event that you have to attend using the ```event``` keyword. +Duration of the event will be entered with behind a slash, with the first slash representing the start of the event and the second slash representing the end of the event. +Duration format for the start and end of the event will follow ***'dd/MM/yyyy HHmm'***. + +Example: ```event CS2103T Lecture /from 17/01/2023 1400 /to 17/01/2023 1600``` will add CS2103T Lecture to your list of tasks + +*** + +### Deleting Tasks: ```delete``` + +Delete a task in the tasklist using the ```delete``` keyword. +A positive integer (ie. 1, 2, 3...) must be specified behind the keyword and the task to be deleted should exist. + +Example: ```delete 2``` will delete the second item in the list + +*** + +### Exiting the Application: ```bye``` + +Simply by using the keyword ```bye```, the program will exit on its own. + +Example: ```bye``` will exit the program + +*** + +### Finding Tasks: ```find``` + +Find a task in the list using the ```find``` keyword. +The task you are looking for should be entered behind the keyword. + +Example: ```find project``` will list out all the tasks that contains the word 'project' + +*** + +### Displaying Tasks: ```display``` + +List out all the tasks using the ```display``` keyword. + +Example: ```display``` will list out all the tasks that exist + +*** + +### Marking Tasks: ```mark``` + +Mark a task that has been completed using the ```mark``` keyword. +A positive integer (ie 1, 2, 3...) should be specified behind the keyword. + +Note: Task to be marked should exist and should not already be marked. + +Example: ```mark 3``` will mark the third task on the list + + +*** + +### Finding Freeslot: ```free``` + +Find the next freeslot using the ```free``` keyword. +A valid duration of format ***'xHyyM'*** where x is the number of hours, and yy is the number of minutes, +should be entered behind the keyword. + +Example: ```free 2H30M``` will return your next free 2 hour 30 minute timeslot. diff --git a/docs/Ui.png b/docs/Ui.png new file mode 100644 index 0000000000..794ff758ae Binary files /dev/null and b/docs/Ui.png differ diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000..f3d88b1c2f Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..84a906615c --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.2.2-all.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000000..2fe81a7d95 --- /dev/null +++ b/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000000..9109989e3c --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,103 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java deleted file mode 100644 index 5d313334cc..0000000000 --- a/src/main/java/Duke.java +++ /dev/null @@ -1,10 +0,0 @@ -public class Duke { - public static void main(String[] args) { - String logo = " ____ _ \n" - + "| _ \\ _ _| | _____ \n" - + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"; - System.out.println("Hello from\n" + logo); - } -} diff --git a/src/main/java/brotherbot/BrotherBot.java b/src/main/java/brotherbot/BrotherBot.java new file mode 100644 index 0000000000..c68090e16e --- /dev/null +++ b/src/main/java/brotherbot/BrotherBot.java @@ -0,0 +1,50 @@ +package brotherbot; + +import brotherbot.commands.Command; +import brotherbot.exceptions.BroException; +import brotherbot.parser.Parser; +import brotherbot.storage.Storage; +import brotherbot.storage.TaskList; +import brotherbot.ui.Gui; +import javafx.application.Application; +import javafx.stage.Stage; + +public class BrotherBot extends Application { + + private final Storage storage; + private final TaskList tasks; + private final Gui gui; + private final Parser parser; + + public BrotherBot() { + this.storage = new Storage("data.txt"); + this.tasks = storage.getTasks(); + this.gui = new Gui(this); + this.parser = new Parser(); + } + + @Override + public void start(Stage stage) { + gui.initialise(stage); + } + + public String getResponse(String input) { + String output; + try { + Command c = this.parser.parse(input, this.tasks); + output = c.execute(this.tasks); + storage.save(c); + return output; + } catch (BroException e) { + output = e.getMessage(); + return output; + } + } + + public String welcome() { + return "Welcome to Brother Bot - your one-stop Personal Task Planner with a very 'bro' personality!\n" + + "Hello my brother, what can I do for you mi amigo...\n" + this.storage.load() + + "\n\nInput 'help' to see available commands!"; + } +} + diff --git a/src/main/java/brotherbot/DialogBox.java b/src/main/java/brotherbot/DialogBox.java new file mode 100644 index 0000000000..43d740854e --- /dev/null +++ b/src/main/java/brotherbot/DialogBox.java @@ -0,0 +1,63 @@ +package brotherbot; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.Node; +import javafx.scene.control.Label; +import javafx.scene.image.ImageView; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; +import javafx.scene.paint.Color; +import javafx.scene.shape.Circle; +import javafx.scene.text.Font; +import javafx.scene.text.FontWeight; + + +public class DialogBox extends HBox { + + public DialogBox(Label l, ImageView iv) { + formatLabel(l); + formatImage(iv); + this.setAlignment(Pos.TOP_RIGHT); + this.getChildren().addAll(l, iv); + } + + /** + * Flips the dialog box such that the ImageView is on the left and text on the right. + */ + private void flip() { + this.setAlignment(Pos.TOP_LEFT); + ObservableList tmp = FXCollections.observableArrayList(this.getChildren()); + FXCollections.reverse(tmp); + this.getChildren().setAll(tmp); + } + + public static DialogBox getUserDialog(Label l, ImageView iv) { + return new DialogBox(l, iv); + } + + public static DialogBox getBrotherDialog(Label l, ImageView iv) { + var db = new DialogBox(l, iv); + db.flip(); + return db; + } + + private void formatLabel(Label l) { + l.setWrapText(true); // enable text wrapping + l.setMinHeight(Label.USE_PREF_SIZE); // set the minimum height of the label + setHgrow(l, Priority.ALWAYS); + l.setTextFill(Color.ORANGE); + Font poppinsFont = Font.font("Poppins", FontWeight.BOLD, 13.5); + l.setFont(poppinsFont); + setMargin(l, new Insets(10, 10, 20, 10)); + } + + private void formatImage(ImageView iv) { + iv.setFitWidth(100.0); + iv.setFitHeight(100.0); + iv.setStyle("-fx-padding: 20; -fx-border-radius: 20"); + iv.setClip(new Circle(50, 50, 50)); + } +} \ No newline at end of file diff --git a/src/main/java/brotherbot/Launcher.java b/src/main/java/brotherbot/Launcher.java new file mode 100644 index 0000000000..af8d309c62 --- /dev/null +++ b/src/main/java/brotherbot/Launcher.java @@ -0,0 +1,9 @@ +package brotherbot; + +import javafx.application.Application; + +public class Launcher { + public static void main(String[] args) { + Application.launch(BrotherBot.class, args); + } +} diff --git a/src/main/java/brotherbot/commands/AddTaskCommand.java b/src/main/java/brotherbot/commands/AddTaskCommand.java new file mode 100644 index 0000000000..306a26c9a0 --- /dev/null +++ b/src/main/java/brotherbot/commands/AddTaskCommand.java @@ -0,0 +1,61 @@ +package brotherbot.commands; + +import brotherbot.storage.*; + +import java.time.DateTimeException; + + + +public class AddTaskCommand extends Command { + + /** + * Constructor to create an AddTaskCommand object. + * + * @param input Input string required for command execution. + */ + public AddTaskCommand(String input) { + super(input); + } + + /** + * Executes command. + * + * @param storage Existing TaskList object required for command execution. + */ + public String execute(TaskList storage) { + String output; + if (super.input.substring(0, 4).equalsIgnoreCase("todo")) { + storage.add(new Todo(input.substring(5))); + } else if (super.input.substring(0, 5).equalsIgnoreCase("event")) { + int startIndex = input.indexOf("/from "); + int x = input.indexOf("/to "); + int endIndex = x + 4; + String description = input.substring(6, startIndex - 1); + String start = input.substring(startIndex + 6, x - 1); + String end = input.substring(endIndex); + try { + storage.add(new Event(description, start, end)); + } catch (DateTimeException e) { + output = "Invalid Date and Time input brother. Here's the correct format:\ndd/MM/yyyy HHmm"; + return output; + } + } else if (super.input.substring(0, 8).equalsIgnoreCase("deadline")) { + int startIndex = input.indexOf("/by "); // exception + String description = input.substring(9, startIndex - 1); + String deadline = input.substring(startIndex + 4); + try { + storage.add(new Deadline(description, deadline)); + } catch (DateTimeException e) { + output = "Invalid Date and Time input brother. Here's the correct format:\ndd/MM/yyyy HHmm"; + return output; + } + } else { + storage.add(new Task(input)); + + } + int x = storage.size(); + output = "added to list my brother: \n" + x + "." + storage.get(x - 1).toString() + "\nNow you have " + x + " tasks!"; + return output; + + } +} \ No newline at end of file diff --git a/src/main/java/brotherbot/commands/Command.java b/src/main/java/brotherbot/commands/Command.java new file mode 100644 index 0000000000..2891231027 --- /dev/null +++ b/src/main/java/brotherbot/commands/Command.java @@ -0,0 +1,28 @@ +package brotherbot.commands; + +import brotherbot.storage.TaskList; + +/** + * Represents a command input by user. + */ +public abstract class Command { + public static boolean isExit; + protected String input; + + /** + * Constructor to create a Command object. + * + * @param input Input string with task information required for command execution. + */ + public Command(String input) { + this.input = input; + isExit = false; + } + + /** + * Executes command. + * + * @param storage Existing TaskList object required for command execution. + */ + public abstract String execute(TaskList storage); +} diff --git a/src/main/java/brotherbot/commands/DeleteCommand.java b/src/main/java/brotherbot/commands/DeleteCommand.java new file mode 100644 index 0000000000..f09d4c22aa --- /dev/null +++ b/src/main/java/brotherbot/commands/DeleteCommand.java @@ -0,0 +1,30 @@ +package brotherbot.commands; + +import brotherbot.storage.Task; +import brotherbot.storage.TaskList; + + +public class DeleteCommand extends Command { + + /** + * Constructor to create an DeleteCommand object. + * + * @param input Input string required for command execution. + */ + public DeleteCommand(String input) { + super(input); + } + + /** + * Executes command. + * + * @param storage Existing TaskList object required for command execution. + */ + public String execute(TaskList storage) { + int i = Integer.parseInt(input.substring(7)) - 1; + Task removed = storage.get(i); + storage.remove(i); + String output = "Deleted this task for you my brother:\n" + removed.toString()+ "\nNow you have " + storage.size() + " tasks left"; + return output; + } +} diff --git a/src/main/java/brotherbot/commands/DisplayCommand.java b/src/main/java/brotherbot/commands/DisplayCommand.java new file mode 100644 index 0000000000..b78ce420c8 --- /dev/null +++ b/src/main/java/brotherbot/commands/DisplayCommand.java @@ -0,0 +1,26 @@ +package brotherbot.commands; + +import brotherbot.storage.TaskList; + +public class DisplayCommand extends Command { + + /** + * Constructor to create an DisplayCommand object. + * + * @param input Input string required for command execution. + */ + public DisplayCommand(String input) { + super(input); + } + + /** + * Executes command. + * + * @param storage Existing TaskList object required for command execution. + */ + public String execute(TaskList storage) { + String output; + output = storage.display() + "\nAnything else I can do for you top G"; + return output; + } +} diff --git a/src/main/java/brotherbot/commands/ExitCommand.java b/src/main/java/brotherbot/commands/ExitCommand.java new file mode 100644 index 0000000000..72440a0eed --- /dev/null +++ b/src/main/java/brotherbot/commands/ExitCommand.java @@ -0,0 +1,26 @@ +package brotherbot.commands; + +import brotherbot.storage.TaskList; + +public class ExitCommand extends Command { + + /** + * Constructor to create an ExitCommand object. + * + * @param input Input string required for command execution. + */ + public ExitCommand(String input) { + super(input); + isExit = true; + } + + /** + * Executes command. + * + * @param storage Existing TaskList object required for command execution. + */ + public String execute(TaskList storage) { + String output = "bye brother hope you had a good one!"; + return output; + } +} diff --git a/src/main/java/brotherbot/commands/FindCommand.java b/src/main/java/brotherbot/commands/FindCommand.java new file mode 100644 index 0000000000..d4ac1de5d4 --- /dev/null +++ b/src/main/java/brotherbot/commands/FindCommand.java @@ -0,0 +1,37 @@ +package brotherbot.commands; + +import brotherbot.storage.TaskList; + +import java.util.ArrayList; + +public class FindCommand extends Command { + + /** + * Constructor to create an FindCommand object. + * + * @param input Input string required for command execution. + */ + public FindCommand(String input) { + super(input); + } + + /** + * Executes command. + * + * @param storage Existing TaskList object required for command execution. + */ + public String execute(TaskList storage) { + String keyword = this.input.substring(5); + String output = "Here are the matching task(s) my brother!"; + ArrayList results = storage.search(keyword); + if (results.isEmpty()) { + output = "No matches found brother, try again!"; + return output; + } + for (Integer i : results) { + output = output + "\n" + (i + 1) + ". " + storage.get(i).toString(); + } + return output; + } + +} diff --git a/src/main/java/brotherbot/commands/FreeTimeCommand.java b/src/main/java/brotherbot/commands/FreeTimeCommand.java new file mode 100644 index 0000000000..d0b9ac9fc7 --- /dev/null +++ b/src/main/java/brotherbot/commands/FreeTimeCommand.java @@ -0,0 +1,55 @@ +package brotherbot.commands; + +import brotherbot.storage.Event; +import brotherbot.storage.Task; +import brotherbot.storage.TaskList; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Objects; + +public class FreeTimeCommand extends Command { + + /** + * Constructor to create a FreeTimeCommand object. + * + * @param input Input string required for command execution. + */ + public FreeTimeCommand(String input) { + super(input); + } + + /** + * Executes command. + * + * @param storage Existing TaskList object required for command execution. + */ + public String execute(TaskList storage) { + ArrayList events = new ArrayList<>(); + for (int i = 0; i < storage.size(); i++) { + Task x = storage.get(i); + if (Objects.equals(x.type, "E")) { + events.add((Event) x); + } + } + events.sort((e1, e2) -> e1.start.compareTo(e2.start)); + + LocalDateTime start = LocalDateTime.now(); + Duration duration = Duration.parse("PT" + this.input.substring(5)); + LocalDateTime end = start.plus(duration); + + for (Event x : events) { + if (start.isBefore(x.start) && end.isBefore(x.start)) { + break; + } + start = x.end; + end = start.plus(duration); + } + + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMM dd yyyy HHmm"); + String output = "My brother, your next freeslot is from " + start.format(formatter) + " to " + end.format(formatter) + ". Create new event now?"; + return output; + } +} diff --git a/src/main/java/brotherbot/commands/HelpCommand.java b/src/main/java/brotherbot/commands/HelpCommand.java new file mode 100644 index 0000000000..8baebf8646 --- /dev/null +++ b/src/main/java/brotherbot/commands/HelpCommand.java @@ -0,0 +1,31 @@ +package brotherbot.commands; + +import brotherbot.storage.TaskList; + +public class HelpCommand extends Command { + + /** + * Constructor to create an HelpCommand object. + * + * @param input Input string required for command execution. + */ + public HelpCommand(String input) { + super(input); + } + + /** + * Executes command. + * + * @param storage Existing TaskList object required for command execution. + */ + public String execute(TaskList storage) { + String output = "Here is a list of commands\n\n" + + "Add To-Do item: todo task_name\nAdd Deadline: deadline task_name /by dd/MM/yyyy HHmm\n" + + "Add Event: event task_name /from dd/MM/yyyy HHmm /to dd/MM/yyyy HHmm\nDisplay Tasks: display\n" + + "Mark Task as Done: mark task_number\nDelete Task: delete task_number\nFind Tasks: find keyword\n" + + "Find Next Freeslot: free xHyyM 'note: where x is no. of hrs, yy is no. of min'\nExit: bye "; + return output; + } +} + + diff --git a/src/main/java/brotherbot/commands/MarkTaskCommand.java b/src/main/java/brotherbot/commands/MarkTaskCommand.java new file mode 100644 index 0000000000..d47af94b49 --- /dev/null +++ b/src/main/java/brotherbot/commands/MarkTaskCommand.java @@ -0,0 +1,26 @@ +package brotherbot.commands; + +import brotherbot.storage.TaskList; + +public class MarkTaskCommand extends Command { + + /** + * Constructor to create an AddTaskCommand object. + * + * @param input Input string required for command execution. + */ + public MarkTaskCommand(String input) { + super(input); + } + + /** + * Executes command. + * + * @param storage Existing TaskList object required for command execution. + */ + public String execute(TaskList storage) { + int i = Integer.parseInt(input.substring(5)) - 1; + storage.mark(i); + return "Marked as you wish my brother:\n" + ((i + 1) + ". " + storage.getPrintFormat(i)); + } +} diff --git a/src/main/java/brotherbot/exceptions/BroException.java b/src/main/java/brotherbot/exceptions/BroException.java new file mode 100644 index 0000000000..e3c65a46c7 --- /dev/null +++ b/src/main/java/brotherbot/exceptions/BroException.java @@ -0,0 +1,18 @@ +package brotherbot.exceptions; + + +/** + * Represents an exception specific to features in BrotherBot. + */ +public class BroException extends Exception { + + + /** + * Constructor for BroException. + * + * @param message Error message for BroException object. + */ + public BroException(String message) { + super(message); + } +} diff --git a/src/main/java/brotherbot/parser/Parser.java b/src/main/java/brotherbot/parser/Parser.java new file mode 100644 index 0000000000..b1886a20fb --- /dev/null +++ b/src/main/java/brotherbot/parser/Parser.java @@ -0,0 +1,104 @@ +package brotherbot.parser; + +import brotherbot.commands.*; + +import brotherbot.exceptions.BroException; +import brotherbot.storage.TaskList; + +import java.time.Duration; +import java.time.format.DateTimeParseException; + +public class Parser { + + /** + * Constructor to create a Parser object. + */ + public Parser() { + } + + /** + * Parse valid user input into corresponding Command objects. + * + * @return Command object to be executed. + * @throws BroException if invalid inputs are parsed. + */ + public Command parse(String input, TaskList storage) throws BroException { + validateInput(input, storage); + Command command; + if (input.equalsIgnoreCase("bye")) { + command = new ExitCommand(input); + } else if (input.equalsIgnoreCase("display")) { + command = new DisplayCommand(input); + } else if (input.length() >= 6 && input.substring(0, 4).equalsIgnoreCase("mark")) { + command = new MarkTaskCommand(input); + } else if (input.length() >= 8 && input.substring(0, 6).equalsIgnoreCase("delete")) { + command = new DeleteCommand(input); + } else if (input.length() >= 6 && input.substring(0, 4).equalsIgnoreCase("find")) { + command = new FindCommand(input); + } else if (input.length() > 4 && input.substring(0, 4).equalsIgnoreCase("todo")) { + command = new AddTaskCommand(input); + } else if (input.length() > 5 && input.substring(0, 5).equalsIgnoreCase("event")) { + command = new AddTaskCommand(input); + } else if (input.length() > 8 && input.substring(0, 8).equalsIgnoreCase("deadline")) { + command = new AddTaskCommand(input); + } else if (input.length() > 4 && input.substring(0, 4).equalsIgnoreCase("free")) { + command = new FreeTimeCommand(input); + } else if (input.length() >= 4 && input.substring(0, 4).equalsIgnoreCase("help")) { + command = new HelpCommand(input); + } + else { + throw new BroException("OOPS! wrong command la bro, Input 'help' to see available commands!"); + } + return command; + } + + /** + * Checks if inputs are valid. + */ + private static void validateInput(String input, TaskList storage) throws BroException { + + if (input.length() > 4 && input.substring(0, 4).equalsIgnoreCase("todo") && input.length() <= 5) { + throw new BroException("OOPS wrong format my brother! consider this format: \ntodo xxx"); + } + + if (input.length() > 5 && input.substring(0, 5).equalsIgnoreCase("event") && (!input.contains("/from") || input.indexOf("/from") == 6 || !input.contains("/to") || input.indexOf("/from") > input.indexOf("/to"))) { + throw new BroException("OOPS wrong format my brother! consider this format: \nevent xxxx /from xxx /to xxx"); + } + + if (input.length() > 6 && input.substring(0, 6).equalsIgnoreCase("delete")) { + try { + Integer.parseInt(input.substring(7)); + } catch(NumberFormatException e) { + throw new BroException("OOPS wrong format my brother! consider this format: \ndelete INSERT_NUMBER"); + } + if (Integer.parseInt(input.substring(7)) > storage.size()) { + throw new BroException("OOPS inserted number is invalid"); + } + } + + if (input.length() > 4 && input.substring(0, 4).equalsIgnoreCase("mark")) { + try { + Integer.parseInt(input.substring(5)); + } catch(NumberFormatException e) { + throw new BroException("OOPS wrong format my brother! consider this format: \ndelete INSERT_NUMBER"); + } + if (Integer.parseInt(input.substring(5)) > storage.size()) { + throw new BroException("OOPS inserted number is invalid"); + } + } + + if (input.length() > 8 && input.substring(0, 8).equalsIgnoreCase("deadline") && (!input.contains("/by") || input.indexOf("/by") == 9)) { + throw new BroException("OOPS wrong format my brother! consider this format: \nevent xxx /from xxx /to xxx"); + } + + if (input.length() > 4 && input.substring(0, 4).equalsIgnoreCase("free")) { + try { + Duration.parse("PT" + input.substring(5)); + } catch (DateTimeParseException e) { + throw new BroException("Brother, input your free duration following this example: 'xHyyM', replace x " + + "with number of hours and yy with number of minutes! "); + } + } + + } +} diff --git a/src/main/java/brotherbot/storage/Deadline.java b/src/main/java/brotherbot/storage/Deadline.java new file mode 100644 index 0000000000..34eafb906e --- /dev/null +++ b/src/main/java/brotherbot/storage/Deadline.java @@ -0,0 +1,34 @@ +package brotherbot.storage; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +public class Deadline extends Task { + + protected LocalDateTime deadline; + + /** + * Constructor to create a Deadline object. + * + * @param description Description of the task. + * @param deadline Deadline of the task. + */ + public Deadline(String description, String deadline) { + super(description); + super.type = "D"; + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HHmm"); + this.deadline = LocalDateTime.parse(deadline, formatter); + } + + /** + * Prints type, status, description and deadline of Task. + * + * @return String representation of Deadline object. + */ + @Override + public String toString() { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMM dd yyyy HHmm"); + return "[" + super.type + "] " + "[" + this.getStatusIcon() + "] " + this.description + " By: " + this.deadline.format(formatter); + } + +} diff --git a/src/main/java/brotherbot/storage/Event.java b/src/main/java/brotherbot/storage/Event.java new file mode 100644 index 0000000000..6d6caf3abe --- /dev/null +++ b/src/main/java/brotherbot/storage/Event.java @@ -0,0 +1,36 @@ +package brotherbot.storage; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +public class Event extends Task { + public LocalDateTime start; + public LocalDateTime end; + + /** + * Constructor to create an Event object. + * + * @param description Description of the task. + * @param start Start date and time of the task. + * @param end End date and time of the task. + */ + public Event(String description, String start, String end) { + super(description); + super.type = "E"; + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HHmm"); + this.start = LocalDateTime.parse(start, formatter); + this.end = LocalDateTime.parse(end, formatter); + } + + /** + * Prints type, status, description, start and end of Task. + * + * @return String representation of Event object. + */ + @Override + public String toString() { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMM dd yyyy HHmm"); + return "[" + super.type + "] " + "[" + this.getStatusIcon() + "] " + this.description + " From: " + this.start.format(formatter) + " To: " + this.end.format(formatter); + } + +} diff --git a/src/main/java/brotherbot/storage/Storage.java b/src/main/java/brotherbot/storage/Storage.java new file mode 100644 index 0000000000..841aef1274 --- /dev/null +++ b/src/main/java/brotherbot/storage/Storage.java @@ -0,0 +1,111 @@ +package brotherbot.storage; + +import brotherbot.commands.Command; +import brotherbot.commands.DisplayCommand; +import brotherbot.commands.ExitCommand; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Scanner; +import java.nio.file.Path; +import java.nio.file.Paths; + +public class Storage { + private final File hardDisk; + private final TaskList taskStorage; + + /** + * Constructor for creating a Storage object. + * + * @param filePathString The string representation of the path where the Tasks are stored. + */ + public Storage(String filePathString) { + Path filePath = Paths.get(filePathString); + assert filePath.toFile().exists(); + + this.hardDisk = new File(filePathString); + this.taskStorage = new TaskList(); + } + + /** + * Load existing data into taskList if existing data file exists. + * Creates new data file if no existing data file exist. + */ + public String load() { + try { + assert hardDisk.exists(); + String output; + boolean created = hardDisk.createNewFile(); + assert hardDisk.exists(); + // load existing data.txt file + if (!created) { + Scanner scanner = new Scanner(hardDisk); + while (scanner.hasNextLine()) { + String input = scanner.nextLine(); + if (input == "") { + continue; + } + boolean isDone = Character.isLetter(input.charAt(8)); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMM dd yyyy HHmm"); + DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("dd/MM/yyyy HHmm"); + Task x; + if (input.contains("From:")) { + int endDescription = input.indexOf("From:") - 1; + int endStart = input.indexOf("To:") - 1; + String start = LocalDateTime.parse(input.substring(endDescription + 7, endStart), formatter).format(formatter2); + String end = LocalDateTime.parse(input.substring(endStart + 5), formatter).format(formatter2); + x = new Event(input.substring(11, endDescription), start, end); + } else if (input.contains("By:")) { + int endDescription = input.indexOf("By:") - 1; + String deadline = LocalDateTime.parse(input.substring(endDescription + 5), formatter).format(formatter2); + x = new Deadline(input.substring(11, endDescription), deadline); + } else { + x = new Todo(input.substring(11)); + } + if (isDone) { + x.markAsDone(); + } + this.taskStorage.add(x); + } + output = this.taskStorage.display(); + scanner.close(); + } else { + output = "No Existing Tasks, add one now my brother!"; + } + return output; + } catch (IOException e) { + String output = "An error occurred while creating the new file: data.txt"; + e.printStackTrace(); + return output; + } + } + + public TaskList getTasks() { + return this.taskStorage; + } + + /** + * Saves existing tasks in taskList into a data file. + * + * @param command Executed command to determine if save action to update data file is required. + */ + public void save(Command command) { + if (!(command instanceof ExitCommand || command instanceof DisplayCommand)) { + try { + FileWriter writer = new FileWriter(hardDisk, false); + PrintWriter printWriter = new PrintWriter(writer); + this.taskStorage.write(printWriter); + printWriter.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + +} + diff --git a/src/main/java/brotherbot/storage/Task.java b/src/main/java/brotherbot/storage/Task.java new file mode 100644 index 0000000000..e5ea4faaaa --- /dev/null +++ b/src/main/java/brotherbot/storage/Task.java @@ -0,0 +1,40 @@ +package brotherbot.storage; + +public class Task { + protected String description; + protected boolean isDone; + public String type; + + /** + * Constructor to create a Task object. + * + * @param description Description of the task. + */ + public Task(String description) { + this.description = description; + this.isDone = false; + } + + protected String getStatusIcon() { + return (isDone ? "X" : " "); // mark done task with X + } + + /** + * Marks task as done. + */ + public void markAsDone() { + this.isDone = true; + } + + /** + * Prints type, status and description of Task. + * + * @return String representation of Task object. + */ + @Override + public String toString() { + return "[" + this.getStatusIcon() + "] " + this.description; + } + +} + diff --git a/src/main/java/brotherbot/storage/TaskList.java b/src/main/java/brotherbot/storage/TaskList.java new file mode 100644 index 0000000000..263f2e5905 --- /dev/null +++ b/src/main/java/brotherbot/storage/TaskList.java @@ -0,0 +1,104 @@ +package brotherbot.storage; + +import java.io.PrintWriter; +import java.util.ArrayList; + +public class TaskList { + private final ArrayList taskStorage; + + /** + * Constructor for creating a TaskList object containing Tasks. + */ + public TaskList() { + this.taskStorage = new ArrayList<>(); + } + + public Task get(int i) { + return this.taskStorage.get(i); + } + + /** + * Adds Task into the list of Tasks. + * + * @param task Task to be added. + */ + public void add(Task task) { + this.taskStorage.add(task); + } + + /** + * Removes Task from the list of Tasks. + * + * @param i Index of Task to be removed. + */ + public void remove(int i) { + this.taskStorage.remove(i); + } + + /** + * Marks Task as done from the list of Tasks. + * + * @param i Index of Task to be marked as done. + */ + public void mark(int i) { + taskStorage.get(i).markAsDone(); + } + + /** + * Returns number of tasks in list of tasks. + * + * @return Size of taskList. + */ + public int size() { + return this.taskStorage.size(); + } + + /** + * Displays the list of Tasks. + */ + public String display() { + String output = "\nHere are your existing tasks!"; + int i = 0; + for(Task task: this.taskStorage) { + output = output + "\n" + (i + 1) + ". " + task.toString(); + i++; + } + if (this.taskStorage.size() == 0) { + output = "You have 0 Existing Tasks... cannot la brother, start adding now! Faster!"; + } + return output; + + } + + /** + * Writes the list of Tasks into existing data file. + * + * @param x PrintWriter object to write the list of Tasks object. + */ + public void write(PrintWriter x) { + for (int i = 0; i < this.taskStorage.size(); i++) { + x.println((i + 1) + ". " + this.taskStorage.get(i)); + i++; + } + } + + /** + * Search for tasks with relevant keyword. + * + * @param keyword Keyword used for search. + * @return An array of indexes of relevant tasks. + */ + public ArrayList search(String keyword) { + ArrayList relevantTaskIndexes = new ArrayList<>(); + for (int i = 0; i < this.taskStorage.size(); i++) { + if (this.taskStorage.get(i).description.contains(keyword)) { + relevantTaskIndexes.add(i); + } + } + return relevantTaskIndexes; + } + + public String getPrintFormat(int index) { + return this.taskStorage.get(index).toString(); + } +} diff --git a/src/main/java/brotherbot/storage/Todo.java b/src/main/java/brotherbot/storage/Todo.java new file mode 100644 index 0000000000..ba2332272c --- /dev/null +++ b/src/main/java/brotherbot/storage/Todo.java @@ -0,0 +1,25 @@ +package brotherbot.storage; + +public class Todo extends Task { + + /** + * Constructor to create a Todo object. + * + * @param description Description of the task. + */ + public Todo(String description) { + super(description); + super.type = "T"; + } + + /** + * Prints type, status and description of Task. + * + * @return String representation of Todo object. + */ + @Override + public String toString() { + return "[" + super.type + "] " + "[" + this.getStatusIcon() + "] " + this.description; + } + +} diff --git a/src/main/java/brotherbot/ui/Gui.java b/src/main/java/brotherbot/ui/Gui.java new file mode 100644 index 0000000000..8b79f28bac --- /dev/null +++ b/src/main/java/brotherbot/ui/Gui.java @@ -0,0 +1,147 @@ +package brotherbot.ui; + +import brotherbot.BrotherBot; +import brotherbot.DialogBox; +import brotherbot.commands.Command; +import javafx.animation.PauseTransition; +import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.ScrollPane; +import javafx.scene.control.TextField; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.*; +import javafx.stage.Stage; +import javafx.util.Duration; + +import static java.util.Objects.requireNonNull; + +public class Gui { + + private final BrotherBot brotherBot; + private Stage stage; + private VBox dialogContainer; + private TextField userInput; + private final Image user = new Image(requireNonNull(this.getClass().getResourceAsStream("/images/user.png"))); + private final Image brother = new Image(requireNonNull(this.getClass().getResourceAsStream("/images/brother.png"))); + private final Image background = new Image(requireNonNull(this.getClass().getResourceAsStream("/images/background.png"))); + + + public Gui(BrotherBot brotherBot) { + this.brotherBot = brotherBot; + } + + public void initialise(Stage stage) { + this.stage = stage; + stageSetup(); + stage.show(); + loadWelcome(); + } + + + private void stageSetup() { + ScrollPane scrollPane = new ScrollPane(); + dialogContainer = new VBox(); + scrollPane.setContent(dialogContainer); + + userInput = new TextField(); + Button sendButton = new Button("SEND"); + AnchorPane mainLayout = new AnchorPane(); + mainLayout.getChildren().addAll(scrollPane, userInput, sendButton); + + formatStage(stage); + formatScrollPane(scrollPane); + formatAnchorPane(mainLayout); + formatDialogContainer(dialogContainer); + formatSendButton(sendButton); + formatUserInput(userInput); + anchor(scrollPane, sendButton); + + // User Interaction Setup + dialogContainer.heightProperty().addListener((observable) -> scrollPane.setVvalue(1.0)); + sendButton.setOnMouseClicked((event) -> { + handleUserInput(); + }); + + userInput.setOnAction((event) -> { + handleUserInput(); + }); + + Scene scene = new Scene(mainLayout); + stage.setScene(scene); + } + + + private void loadWelcome() { + Label load = new Label(this.brotherBot.welcome()); + dialogContainer.getChildren().addAll( + DialogBox.getBrotherDialog(load, new ImageView(brother)) + ); + } + + + private void handleUserInput() { + + Label userText = new Label(userInput.getText()); + String output = brotherBot.getResponse(userInput.getText()); + Label broText = new Label(output); + dialogContainer.getChildren().addAll( + DialogBox.getUserDialog(userText, new ImageView(user)), + DialogBox.getBrotherDialog(broText, new ImageView(brother)) + ); + userInput.clear(); + if (Command.isExit) { + PauseTransition delay = new PauseTransition(Duration.seconds(3)); + delay.setOnFinished(event -> stage.close()); + delay.play(); + } + } + + private void formatStage(Stage stage) { + stage.setTitle("BrotherBot"); + stage.setResizable(false); + stage.setMinHeight(600.0); + stage.setMinWidth(750.0); + } + + private void formatScrollPane(ScrollPane scrollPane) { + scrollPane.setPrefSize(750, 565); + scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); + scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.ALWAYS); + scrollPane.setVvalue(1.0); + scrollPane.setFitToWidth(true); + scrollPane.setStyle("-fx-background: #121c2b"); + } + + private void formatAnchorPane(AnchorPane mainLayout) { + mainLayout.setPrefSize(750.0, 600.0); + } + + private void formatDialogContainer(VBox dialogContainer) { + BackgroundImage backgroundImage = new BackgroundImage(background, + BackgroundRepeat.REPEAT, BackgroundRepeat.REPEAT, + BackgroundPosition.DEFAULT, BackgroundSize.DEFAULT); + Background background = new Background(backgroundImage); + dialogContainer.setBackground(background); + dialogContainer.setPrefHeight(565); + } + + + private void anchor(ScrollPane scrollPane, Button sendButton) { + AnchorPane.setTopAnchor(scrollPane, 1.0); + AnchorPane.setBottomAnchor(sendButton, 1.0); + AnchorPane.setRightAnchor(sendButton, 1.0); + AnchorPane.setLeftAnchor(userInput , 1.0); + AnchorPane.setBottomAnchor(userInput, 1.0); + } + + private void formatSendButton(Button sendButton) { + sendButton.setPrefWidth(55.0); + } + + private void formatUserInput(TextField userInput) { + userInput.setPrefWidth(690.0); + } + +} diff --git a/src/main/resources/images/background.png b/src/main/resources/images/background.png new file mode 100644 index 0000000000..840a7cffc1 Binary files /dev/null and b/src/main/resources/images/background.png differ diff --git a/src/main/resources/images/brother.png b/src/main/resources/images/brother.png new file mode 100644 index 0000000000..d44736e724 Binary files /dev/null and b/src/main/resources/images/brother.png differ diff --git a/src/main/resources/images/user.png b/src/main/resources/images/user.png new file mode 100644 index 0000000000..fc2e929904 Binary files /dev/null and b/src/main/resources/images/user.png differ diff --git a/src/test/java/brotherbot/parser/ParserTest.java b/src/test/java/brotherbot/parser/ParserTest.java new file mode 100644 index 0000000000..f6e88bc70b --- /dev/null +++ b/src/test/java/brotherbot/parser/ParserTest.java @@ -0,0 +1,48 @@ +package brotherbot.parser; + +import brotherbot.exceptions.BroException; + +import brotherbot.storage.TaskList; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class ParserTest { + @Test + public void parseInvalidInputsTest() throws BroException { + /** + // valid inputs test + dummy.add(new Todo("eat")); + // AddTaskCommand Testcases + String validTest1 = "todo eat"; + String validTest2 = "deadline eat /by 11/11/2023 1111"; + String validTest3 = "event eat /from 11/11/2023 1111 /to 11/11/2023 1112"; + String validTest4 = "display"; + String validTest5 = "bye"; + String validTest6 = "mark 1"; + String validTest7 = "delete 1"; + Command x = new AddTaskCommand(validTest1); + + assertEquals(new AddTaskCommand(validTest1), Parser.parse(validTest1,dummy)); + assertEquals(new AddTaskCommand(validTest2), Parser.parse(validTest2,dummy)); + assertEquals(new AddTaskCommand(validTest3), Parser.parse(validTest3,dummy)); + assertEquals(new DisplayCommand(validTest4), Parser.parse(validTest4,dummy)); + assertEquals(new ExitCommand(validTest5), Parser.parse(validTest5, dummy)); + assertEquals(new MarkTaskCommand(validTest6), Parser.parse(validTest6, dummy)); + assertEquals(new DeleteCommand(validTest7), Parser.parse(validTest7, dummy)); + **/ + TaskList dummy = new TaskList(); + Parser parser = new Parser(); + + String invalidInput = "deadline eat"; // wrong format + String typoInput = "tod eat"; + String invalidDeleteInput = "delete 1"; // invalid delete + String invalidMarkInput = "mark 1"; // invalid mark + + assertThrows(BroException.class, () -> {parser.parse(invalidInput, dummy);}); + assertThrows(BroException.class, () -> {parser.parse(typoInput, dummy);}); + assertThrows(BroException.class, () -> {parser.parse(invalidDeleteInput, dummy);}); + assertThrows(BroException.class, () -> {parser.parse(invalidMarkInput, dummy);}); + } + +} \ No newline at end of file diff --git a/src/test/java/brotherbot/storage/StorageTest.java b/src/test/java/brotherbot/storage/StorageTest.java new file mode 100644 index 0000000000..3dbe0ce88b --- /dev/null +++ b/src/test/java/brotherbot/storage/StorageTest.java @@ -0,0 +1,32 @@ +package brotherbot.storage; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class StorageTest { + @Test + public void taskToStringTest(){ + Task test1 = new Todo("eat"); + Task test2 = new Deadline("eat", "11/11/2022 1111"); + Task test3 = new Event("eat", "11/11/2022 1111", "11/11/2022 2222"); + + assertEquals("[T] [ ] eat",test1.toString()); + assertEquals("[D] [ ] eat By: Nov 11 2022 1111", test2.toString()); + assertEquals("[E] [ ] eat From: Nov 11 2022 1111 To: Nov 11 2022 2222", test3.toString()); + } + + @Test + public void markTaskAsDoneTest(){ + Task test1 = new Todo("eat"); + Task test2 = new Deadline("eat", "11/11/2022 1111"); + Task test3 = new Event("eat", "11/11/2022 1111", "11/11/2022 2222"); + + test1.markAsDone(); + test2.markAsDone(); + test3.markAsDone(); + + assertEquals("[T] [X] eat",test1.toString()); + assertEquals("[D] [X] eat By: Nov 11 2022 1111", test2.toString()); + assertEquals("[E] [X] eat From: Nov 11 2022 1111 To: Nov 11 2022 2222", test3.toString()); + } +} \ No newline at end of file diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 657e74f6e7..090824530b 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,7 +1,20 @@ -Hello from - ____ _ -| _ \ _ _| | _____ -| | | | | | | |/ / _ \ -| |_| | |_| | < __/ -|____/ \__,_|_|\_\___| - +Hello Brother +Welcome to Brother Bot +Whats up what can I do for you mi amigo +added to list my brother: + [D] [ ] deadline bball /by today By: today + Now you have 1 tasks! +added to list my brother: + [E] [ ] /from today /to tmr From: today To: tmr + Now you have 2 tasks! +Here you go my brother! +1. [D] [ ] deadline bball /by today By: today +2. [E] [ ] /from today /to tmr From: today To: tmr +Anything else I can do for you top G +Marked as you wish my brother: +1. [D] [X] deadline bball /by today By: today + Whats next? +Here you go my brother! +1. [D] [X] deadline bball /by today By: today +2. [E] [ ] /from today /to tmr From: today To: tmr +Anything else I can do for you top G diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index e69de29bb2..5708d1c88b 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -0,0 +1,5 @@ +deadline bball /by today +event bball /from today /to tmr +display +mark 1 +display \ No newline at end of file diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh old mode 100644 new mode 100755 index c9ec870033..1153ab1faf --- a/text-ui-test/runtest.sh +++ b/text-ui-test/runtest.sh @@ -13,7 +13,7 @@ then fi # compile the code into the bin folder, terminates if error occurred -if ! javac -cp ../src/main/java -Xlint:none -d ../bin ../src/main/java/*.java +if ! javac -cp /Users/danielling/Desktop/CS2103T/ip/src/main/java -Xlint:none -d ../bin /Users/danielling/Desktop/CS2103T/ip/src/main/java/*.java then echo "********** BUILD FAILURE **********" exit 1 @@ -28,6 +28,7 @@ dos2unix ACTUAL.TXT EXPECTED-UNIX.TXT # compare the output to the expected output diff ACTUAL.TXT EXPECTED-UNIX.TXT +# shellcheck disable=SC2181 if [ $? -eq 0 ] then echo "Test result: PASSED"