Duke, the Java Mascot
[credit: Wikipedia]
Project Duke is a educational software project designed to take you through the steps of building a small software incrementally, while applying as many Java and SE techniques as possible along the way.
The project aims to build a product named Duke, a Personal Assistant Chatbot that helps a person to keep track of various things. The name Duke was chosen as a placeholder name, in honor of Duke, the Java Mascot. You may give it any other name and personality you wish.
Here is a sample interaction with Duke:
____________________________________________________________
____ _
| _ \ _ _| | _____
| | | | | | | |/ / _ \
| |_| | |_| | < __/
|____/ \__,_|_|\_\___|
Hello! I'm Duke
What can I do for you?
____________________________________________________________
list
____________________________________________________________
Here are the tasks in your list:
1.[T][✓] read book
2.[D][✗] return book (by: June 6th)
3.[E][✗] project meeting (at: Aug 6th 2-4pm)
4.[T][✓] join sports club
____________________________________________________________
todo borrow book
____________________________________________________________
Got it. I've added this task:
[T][✗] borrow book
Now you have 5 tasks in the list.
____________________________________________________________
deadline return book /by Sunday
____________________________________________________________
Got it. I've added this task:
[D][✗] return book (by: Sunday)
Now you have 6 tasks in the list.
____________________________________________________________
done 2
____________________________________________________________
Nice! I've marked this task as done:
[D][✓] return book (by: June 6th)
____________________________________________________________
blah
____________________________________________________________
☹ OOPS!!! I'm sorry, but I don't know what that means :-(
____________________________________________________________
bye
____________________________________________________________
Bye. Hope to see you again soon!
____________________________________________________________
The project consists of the following increments:
Level 1
to Level 10
to indicate how the features make the product progressively "level up".
In this initial skeletal version of Duke, it starts by greeting the user, simply echos commands entered by the user, and exits when the user types bye
.
Example:
____________________________________________________________
Hello! I'm Duke
What can I do for you?
____________________________________________________________
list
____________________________________________________________
list
____________________________________________________________
blah
____________________________________________________________
blah
____________________________________________________________
bye
____________________________________________________________
Bye. Hope to see you again soon!
____________________________________________________________
You are strongly encouraged to customize the chatbot name, command/display formats, and even the personality of the chatbot to make your chatbot unique.
Add the ability to store whatever text entered by the user and display them back to the user when requested.
Example:
____________________________________________________________
Hello! I'm Duke
What can I do for you?
____________________________________________________________
read book
____________________________________________________________
added: read book
____________________________________________________________
return book
____________________________________________________________
added: return book
____________________________________________________________
list
____________________________________________________________
1. read book
2. return book
____________________________________________________________
bye
____________________________________________________________
Bye. Hope to see you again soon!
____________________________________________________________
String[100]
) to store the items.
Add the ability to mark tasks as done.
list
____________________________________________________________
Here are the tasks in your list:
1.[✓] read book
2.[✗] return book
3.[✗] buy bread
____________________________________________________________
done 2
____________________________________________________________
Nice! I've marked this task as done:
[✓] return book
____________________________________________________________
When implementing this feature, you are also recommended to implement the following extension:
Extension: A-Classes
While it is possible to represent a task list as a multi-dimensional array containing String
, int
, boolean
etc.primitive values, the more natural approach is to use a Task
class to represent tasks.
Partial solution
public class Task {
protected String description;
protected boolean isDone;
public Task(String description) {
this.description = description;
this.isDone = false;
}
public String getStatusIcon() {
return (isDone ? "\u2713" : "\u2718"); //return tick or X symbols
}
//...
}
Task t = new Taks("read book");
t.markAsDone()
Add support for tracking three types of tasks:
Example:
todo borrow book
____________________________________________________________
Got it. I've added this task:
[T][✗] borrow book
Now you have 5 tasks in the list.
____________________________________________________________
list
____________________________________________________________
Here are the tasks in your list:
1.[T][✓] read book
2.[D][✗] return book (by: June 6th)
3.[E][✗] project meeting (at: Aug 6th 2-4pm)
4.[T][✓] join sports club
5.[T][✗] borrow book
____________________________________________________________
deadline return book /by Sunday
____________________________________________________________
Got it. I've added this task:
[D][✗] return book (by: Sunday)
Now you have 6 tasks in the list.
____________________________________________________________
event project meeting /at Mon 2-4pm
____________________________________________________________
Got it. I've added this task:
[E][✗] project meeting (at: Mon 2-4pm)
Now you have 7 tasks in the list.
____________________________________________________________
At this point, dates/times can be treated as strings; there is no need to convert them to actual dates/times.
Example:
deadline do homework /by no idea :-p
____________________________________________________________
Got it. I've added this task:
[D][✗] do homework (by: no idea :-p)
Now you have 6 tasks in the list.
____________________________________________________________
When implementing this feature, you are also recommended to implement the following extension:
Extension: A-Inheritance
As there are multiple types of tasks that have some similarity between them, you can implement classes Todo
, Deadline
and Event
classes to inherit from a Task
class.
Furthermore, use polymorphism to store all tasks in a data structure containing Task
objects e.g., Task[100]
.
Partial solution
public class Deadline extends Task {
protected String by;
public Deadline(String description, String by) {
super(description);
this.by = by;
}
@Override
public String toString() {
return "[D]" + super.toString() + " (by: " + by + ")";
}
}
Task[] tasks = new Task[100];
task[0] = new Deadline("return book", "Monday");
Teach Duke to deal with errors such as incorrect inputs entered by the user.
Example:
todo
____________________________________________________________
☹ OOPS!!! The description of a todo cannot be empty.
____________________________________________________________
blah
____________________________________________________________
☹ OOPS!!! I'm sorry, but I don't know what that means :-(
____________________________________________________________
When implementing this feature, you are also recommended to implement the following extension:
Add support for deleting tasks from the list.
Example:
list
____________________________________________________________
Here are the tasks in your list:
1.[T][✓] read book
2.[D][✓] return book (by: June 6th)
3.[E][✗] project meeting (at: Aug 6th 2-4pm)
4.[T][✓] join sports club
5.[T][✗] borrow book
____________________________________________________________
delete 3
____________________________________________________________
Noted. I've removed this task:
[E][✗] project meeting (at: Aug 6th 2-4pm)
Now you have 4 tasks in the list.
____________________________________________________________
Save the tasks in the hard disk automatically whenever the task list changes. Load the data from the hard disk when Duke starts up. You may hard-code the file name and location e.g., [project_root]/data/duke.txt
The format of the file is up to you. Example:
T | 1 | read book
D | 0 | return book | June 6th
E | 0 | project meeting | Aug 6th 2-4pm
T | 1 | join sports club
If you use file paths in your code,
C:\data
. If not, your app can cause unpredictable results when used in another computer.
Teach Duke to understand dates and times. For example, if the command is deadline return book /by 2/12/2019 1800
, Duke understands 2/12/2019 1800
as 2nd of December 2019, 6pm, instead of storing it simply as a String.
java.time.LocalDate
in your task objects. Accept dates in a format such as yyyy-mm-dd
format (e.g., 2019-10-15
) and print in a different format such as MMM d yyyy
e.g., (Oct 15 2019
).Using dates/times in Java
A code snippet using the LocalDate
class:
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
public class Main {
public static void main(String[] args) {
//create dates from strings
LocalDate d1 = LocalDate.parse("2019-12-01");
LocalDate d2 = LocalDate.parse("2019-12-02");
LocalDate d3 = LocalDate.parse("2019-12-02");
//compare dates
System.out.println(d1.isBefore(d2)); // -> true
System.out.println(d1.isAfter(d2)); // -> false
System.out.println(d2.equals(d3)); // -> true
//work with dates
System.out.println(d1.getDayOfWeek()); // -> SUNDAY
System.out.println(d1.getMonth()); // -> DECEMBER
System.out.println(d1.plus(1, ChronoUnit.YEARS)); // -> 2020-12-01
// get today's date and print it in a specific format
LocalDate d4 = LocalDate.now();
System.out.println(d4); // -> 2019-10-15
System.out.println(d4.format(DateTimeFormatter.ofPattern("MMM d yyyy"))); // -> Oct 15 2019
}
}
Give users a way to find a task by searching for a keyword.
Example:
find book
____________________________________________________________
Here are the matching tasks in your list:
1.[T][✓] read book
2.[D][✓] return book (by: June 6th)
____________________________________________________________
While it is possible to represent a task list as a multi-dimensional array containing String
, int
, boolean
etc.primitive values, the more natural approach is to use a Task
class to represent tasks.
Partial solution
public class Task {
protected String description;
protected boolean isDone;
public Task(String description) {
this.description = description;
this.isDone = false;
}
public String getStatusIcon() {
return (isDone ? "\u2713" : "\u2718"); //return tick or X symbols
}
//...
}
Task t = new Taks("read book");
t.markAsDone()
As there are multiple types of tasks that have some similarity between them, you can implement classes Todo
, Deadline
and Event
classes to inherit from a Task
class.
Furthermore, use polymorphism to store all tasks in a data structure containing Task
objects e.g., Task[100]
.
Partial solution
public class Deadline extends Task {
protected String by;
public Deadline(String description, String by) {
super(description);
this.by = by;
}
@Override
public String toString() {
return "[D]" + super.toString() + " (by: " + by + ")";
}
}
Task[] tasks = new Task[100];
task[0] = new Deadline("return book", "Monday");
Use exceptions to handle errors. For example, define a class DukeException
to represent exceptions specific to Duke.
Use the input/output redirection technique to semi-automate the testing of Duke.
A tutorial is provided in the duke repository.
Use Java Collections classes for storing data. For example, you can use an ArrayList<Task>
to store the tasks.
Refactor the code to extract out closely related code as classes.
Ui
: deals with interactions with the userStorage
: deals with loading tasks from the file and saving tasks in the fileParser
: deals with making sense of the user commandTaskList
: contains the task list e.g., it has operations to add/delete tasks in the listFor example, the code of the main class could look like this:
public class Duke {
private Storage storage;
private TaskList tasks;
private Ui ui;
public Duke(String filePath) {
ui = new Ui();
storage = new Storage(filePath);
try {
tasks = new TaskList(storage.load());
} catch (DukeException e) {
ui.showLoadingError();
tasks = new TaskList();
}
}
public void run() {
//...
}
public static void main(String[] args) {
new Duke("data/tasks.txt").run();
}
}
*Command
classes (i.e., AddCommand
, DeleteCommand
, ExitCommand
etc.) that inherits from an abstract Command
class, so that you can write the main logic of the App as follows:
public void run() {
ui.showWelcome();
boolean isExit = false;
while (!isExit) {
try {
String fullCommand = ui.readCommand();
ui.showLine(); // show the divider line ("_______")
Command c = Parser.parse(fullCommand);
c.execute(tasks, ui, storage);
isExit = c.isExit();
} catch (DukeException e) {
ui.showError(e.getMessage());
} finally {
ui.showLine();
}
}
}
You can get some inspiration from how the code of the addressbook-level2 is organized.Add JUnit tests to test the behavior of the code.
Conventions to follow:
[project root]\src\test\java\
folder (reason: to follow the convention followed by the project structure so far).Todo.java
can be tested by TodoTest.java
), and put it in a package to match. For example,
seedu.duke.Todo
: src\main\java\seedu\duke\Todo.java
seedu.duke.TodoTest
: src\test\java\seedu\duke\TodoTest.java
Requirements:
Adding JUnit support to your project: As JUnit is a third-party library, you need to add support to it specifically in your project.
[project root]\src\test\java\
(you may have to do this outside of Intellij)File
→ New
→ Module From Existing Sources ...
[project root]\src\test\
(not the java
) folder.Create module from existing sources
Next
until the process is completeProject
panel of Intellij, expand the newly-created test
module, right-click on the java
folder inside it, and choose Mark Directory as
→ Test Source Root
(that will make the folder turn to green color).java
folder and type @Test
inside it. A code example given below.duke.Duke
class, create a duke.DukeTest
i.e., in src\test\java\duke\DukeTest.java
).
public class DukeTest {
@Test
}
@Test
turn to red because Intellij (not having JUnit support yet) does not understand it. But it will pop up a hint, asking if you want to add support for JUnit. Select Add JUnit 5.* to classpath
.Sources
, JavaDocs
and Annotations
boxes. After that, click OK
to add the JUnit 5 to the project dependencies.import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class DukeTest {
@Test
public void dummyTest(){
assertEquals(2, 2);
}
}
Run DukeTest
.Duke
from DukeTest
class, you need to add main
module as a dependency of the test
module you just created.
Duke
inside the DukeTest
, Intellij will flag it as an error and will give you an option (i.e., in the bulb icon that pops up) to add the main
module as a dependency.Refer to the Gradle tutorial at the Duke repo (i.e., the repo you forked from) to find how to use JUnit via Gradle.
Extension A-Gradle
Use Gradle to automate some of the build tasks of the project. Refer to the Gradle tutorial at the Duke repo (i.e., the repo you forked from) to find how to set up Gradle for your project.
Organize the classes into suitable java packages.
duke
duke.task
, duke.command
Add JavaDoc comments to the code.
Tweak the code to comply with a chosen coding standard. From this point onward, ensure any new code added are compliant with the coding standard.
Use checkStyle to detect coding style violations.
If you are using Gradle for your project, refer to the Gradle tutorial at the Duke repo (i.e., the repo you forked from) to find how to use CheckStyle via Gradle. Alternatively, you can try the CheckStyle plugin for Intellij.
Extension A-Gradle
Use Gradle to automate some of the build tasks of the project. Refer to the Gradle tutorial at the Duke repo (i.e., the repo you forked from) to find how to set up Gradle for your project.
Critically examines the code and refactor to improve the code quality where necessary.
Use assert
feature (not JUnit assertions) to document important assumptions that should hold at various points in the code.
Package the app as an executable JAR file so that it can be distributed easily.
Do not commit the JAR file created. Instead, you can make the JAR file available in the following manner.
v0.1
Attach binaries by dropping them ...
.If you are using Gradle for your project, refer to the Gradle tutorial at the Duke repo (i.e., the repo you forked from) to find how to create a jar file using Gradle.
Extension A-Gradle
Use Gradle to automate some of the build tasks of the project. Refer to the Gradle tutorial at the Duke repo (i.e., the repo you forked from) to find how to set up Gradle for your project.
Use Gradle to automate some of the build tasks of the project. Refer to the Gradle tutorial at the Duke repo (i.e., the repo you forked from) to find how to set up Gradle for your project.
Use third-party libraries in your project. For example, you can use the Natty library to parse strings into meaningful dates.
Use JavaFX to create a GUI. Refer to the JavaFX tutorials at the Duke repo (i.e., the repo you forked from) to learn how to get started.
Add a User Guide to the project. Here is one simple way to do it.
docs\README.md
. See this guide to GitHub flavored Markdown (GFMD).docs
folder (you can select a theme too).http://{your username}.github.io/duke/
to view the user guide of your product.Add a Developer Guide to the project, explaining the design and implementation to future developers.
Release the product to be used by potential users. e.g., you can make it available on GitHub
Provide a way for an event to be tentatively scheduled in multiple slots, and later to be confirmed to one the slots.
Provide support for managing recurring tasks e.g., a weekly project meeting.
Support the managing of tasks that need to be done after a specific time/task e.g., return book after the exam is over.
Provide support for managing tasks that need to be done within a certain period e.g., collect certificate between Jan 15 and 25th.
Provide support for managing tasks that takes a fixed amount of time but does not have a fixed start/end time e.g., reading the sales report (needs 2 hours).
Provide a way to get reminders about tasks e.g., remind the user about upcoming deadlines.
Provide a way for the user to find free times e.g., when is the nearest day in which I have a 4 hour free slot?.
Provide a way to view tasks in the form of a schedule e.g., view the schedule for a specific date.
Deal with schedule anomalies e.g., detect if a task being added clashes with another task in the list.
Add the ability to recognize and deal with duplicate items. e.g., the same task added multiple times.
Provide more flexibility with the data source e.g., the ability for the user to specify which file to use as the data source.
The ability to sort items e.g., sort deadlines chronologically.
Support more natural date formats e.g., Mon
in a user command can be interpreted as the date of the next Monday in the calendar.
All more flexibility in search e.g., find items even if the keyword matches the item only partially.
Support a way to easily edit details of items e.g., change the end time of an event without changing anything else.
Minimal: the ability to update an existing item without having to delete it first
Other ideas:
Provide a way to attach priorities to items e.g., mark an item as a high
priority (or priority level 1
).
Provide a way to archive items so that the user can remove items from the app but still keep a record of them somewhere e.g., archive all tasks in the list into a file so that the user can start over with a clean slate.
Provide a way to perform tasks on multiple items e.g., delete some specific items in one go.
Provide a way to leverage statistics about the items managed by the App e.g., show the number of tasks that have been completed in the past week.
Provide a way to undo a command.
Minimal: the ability to undo the most recent command.
Provide in-App guidance to users.
Minimal: add a command to access a help page.
Other ideas:
Make the command syntax more flexible.
Minimal: provide shorter aliases for keywords e.g., t
can be shorter alias for todo
.
Other ideas:
Support managing info about contacts e.g., details of friends
Support managing info about small snippets of textual information the user wants to record e.g., one's own waist size, a name of a movie that the user wants to remember
Support managing info about expenses e.g., the amounts spent on food, books, transport, etc.
Support keeping records of loans given/taken e.g., money lent/owed to colleagues/friends
Support recording info about places e.g., info about restaurants visited, for future reference
Provide the ability to learn/memorize thingse.g., learn vocabulary, answers to questions
Support managing info about clients e.g., for an insurance agent to keep track of clients