By: CS2103JAN2018-W13-B4      Since: April 2018      Licence: MIT

1. Introduction

PrioriTask is a desktop task management application. It is not just any task management application, but a combination of a to-do list, calendar and reminder features, with the added bonus of an automatic updating and sorting of your tasks based on priority levels. It helps you keep track of your tasks, while providing you with at-a-glance views of upcoming tasks.

PrioriTask is a fairly new application that has much room for improvement. We welcome any form of contributions to PrioriTask project, be it from beginners or seasoned Java developers. Experienced or not, this guide can serve as a helpful reference and source for information for those who hope to contribute to the project.

2. Getting Started

This section contains a list of steps to help you get started with developing for PrioriTask in no time.

2.1. Installing prerequisites

Before working on PrioriTask, ensure that you have the following software installed:

  1. JDK 1.8.0_60 or later

    PrioriTask works best on JDK 1.8.0_60 or later.
    It will not work with earlier versions of Java 8.
  2. IntelliJ IDE

    PrioriTask requires Gradle and JavaFX plugins.
    IntelliJ has both plugins installed by default. If you have disabled them, go to File > Settings > Plugins to re-enable them.

2.2. Setting up the project on your computer

Follow these steps to setup the project on your computer:

The following steps are written for IntelliJ IDEA 2017.3.5 (Ultimate Edition).
There may be slight variations in the steps if using other versions of IntelliJ.
  1. Fork this repo, and clone the fork to your computer.

  2. Open IntelliJ. You should see the welcome screen (refer to Figure 1, “IntelliJ’s welcome screen”).

    If you do not see the welcome screen, select File > Close Project to close the existing project dialog before continuing.
DeveloperGettingStarted1
Figure 1. IntelliJ’s welcome screen
  1. Click on Import Project.

  2. Locate the cloned folder, and select it. Click OK. You will be directed to the Import Project window (refer to Figure 2, “The first page of the Import Project window”).

DeveloperGettingStarted2
Figure 2. The first page of the Import Project window
  1. Check the Create project from existing sources box and click Next. You will be directed to the next window (refer to Figure 3, “The second page of the Import Project window”).

DeveloperGettingStarted3
Figure 3. The second page of the Import Project window
  1. Fill up the necessary information as follows, and click Next. You will be directed to the next window.

  2. Click Next repeatedly, until the window to select the project SDK appears (refer to Figure 4, “Selecting project SDK in the Import Project window”).

DeveloperGettingStarted4
Figure 4. Selecting project SDK in the Import Project window
  1. Click on the JDK 1.8 folder in the left pane and click Next.

    If you do not see a JDK 1.8 folder, click on + > JDK, and find the directory of the JDK.
  2. Click Next until you reach the last window. Click Finish to complete the import of the project. The project will be loaded and you will be brought to the main project window (refer to Figure 5, “Main project window”).

DeveloperGettingStarted5
Figure 5. Main project window
  1. Select File > New > Module from Existing Sources…​.

  2. Locate the build.gradle file and select it. Click OK. You will be directed to the Import Module from Gradle window (refer to Figure 6, “Import Module from Gradle window”).

DeveloperGettingStarted6
Figure 6. Import Module from Gradle window
  1. Click OK to accept the default settings.

  2. Open the Terminal (select Tool Windows > Terminal) and run the command gradlew processResources (run ./gradlew processResources for Mac/Linux users). It should finish with the BUILD SUCCESSFUL message.
    This generates all the resources required by the application and its tests.

2.3. Verifying the setup

Follow these steps to verify that the project has been correctly set up:

  1. Run seedu.organizer.MainApp and try a few commands. You may refer to the UserGuide.adoc for more information about the commands.

  2. Run the tests to ensure they all pass.

2.4. Configuring the project before writing code

Before you begin development, there are a few configurations to be done to ensure that the code you write meets our project standards.

2.4.1. Configuring the coding style

This project follows oss-generic coding standards. IntelliJ’s default style is mostly compliant with the mentioned standard, but uses a different import order.

Follow these steps to ensure that your code adheres to the required coding standards:

  1. Go to File > Settings…​ (Windows/Linux), or IntelliJ IDEA > Preferences…​ (Mac).

  2. Select Editor > Code Style > Java.

  3. Click on the Imports tab to set the import order.

    • Set Class count to use import with '\*' to 999. This prevents the contraction of import statements.

    • Set Names count to use static import with '*' to 999. This prevents the contraction of import statements.

    • Set the following order in Import Layout: import static all other imports, import java.*, import javax.*, import org.*, import com.*, import all other imports. Add a <blank line> between each import.

Optionally, you can follow the UsingCheckstyle.adoc document to configure Intellij to check for style-compliance as you write code.

2.4.2. Setting up CI

This project relies heavily on Travis to perform Continuous Integration (CI). You may also set up Travis for your fork. See UsingTravis.adoc to learn how to set it up.

After setting up Travis, you can optionally set up coverage reporting for your team fork (see UsingCoveralls.adoc).

Coverage reporting could be useful for a team repository that hosts the final version, but is not particularly useful for your personal fork.

Optionally, you can set up AppVeyor as a second CI (see UsingAppVeyor.adoc).

Having both Travis and AppVeyor ensures the application works on both Unix-based platforms and Windows-based platforms (Travis is Unix-based and AppVeyor is Windows-based).

2.5. Getting started with development

When you are ready to start development:

  1. Read Section 3.1, “Architecture” to get an understanding of the overall design.

  2. Read Section 4, “Implementation” to find out more about the major features, and how you can contribute enhancements to those features.

3. Design

PrioriTask consists of multiple components written in Java, with data being written and stored in XML files. This section contains an overview of the architecture systems used to design and implement the application, and an introduction to some of its major components.

3.1. Architecture

The Architecture Diagram given below (Figure 7, “Architecture diagram”) provides an overview of the high-level design of the application.

The .pptx files used to create diagrams in this document can be found in the diagrams folder. To update a diagram, modify the diagram in the pptx file, select the objects of the diagram, and choose Save as picture.
Architecture
Figure 7. Architecture diagram

3.1.1. Primary components

There are two primary components of the application:

  1. Main, which contains only one class called MainApp. It is responsible for:

    1. Initializing all components in the correct sequence, and connecting them with each other at application launch.

    2. Shutting down all components and invoking the cleanup method where necessary during application shut down.

  2. Commons, contains a collection of classes used by multiple other components. Two of those classes play important roles at the architecture level.

    1. EventsCenter : This class (written using Google’s Event Bus library) is used by components to communicate with other components using events (i.e. a form of Event Driven design).

    2. LogsCenter : This class is used by many classes to write log messages to the application’s log file.

3.1.2. Secondary components

The rest of the application comprises of four components:

  1. UI: Represents the UI of the application.

  2. Logic: Executes commands.

  3. Model: Holds the data of the application in-memory.

  4. Storage: Reads data from, and writes data to, the hard disk.

Each of the four components:

  • Defines its API in an interface with the same name as the Component.

  • Exposes its functionality using a {Component Name}Manager class.

For example, Logic (refer to Figure 8, “Class diagram of Logic) defines it’s API in the Logic.java interface and exposes its functionality using the LogicManager.java class.

LogicClassDiagram
Figure 8. Class diagram of Logic

More Information

Events-Driven nature of the design

The Sequence Diagram (refer to Figure 9, “Component interactions for delete 1 command (part 1)”) shows how the components interact in a scenario where the user issues the command delete 1.

SDforDeleteTask
Figure 9. Component interactions for delete 1 command (part 1)
Note how the Model simply raises a OrganizerChangedEvent when the Organizer data are changed, instead of asking the Storage to save the updates to the hard disk.

The following diagram (refer to Figure 10, “Component interactions for delete 1 command (part 2)”) below shows how the EventsCenter reacts to that event. This eventually results in the updates being saved to the hard disk and the status bar of the UI being updated to reflect the 'Last Updated' time.

SDforDeleteTaskEventHandling
Figure 10. Component interactions for delete 1 command (part 2)
Note how the event is propagated through the EventsCenter to the Storage and UI without Model having to be coupled to either of them. This is an example of how this Event Driven approach helps us reduce direct coupling between components.

The following sections will provide more details on each of the 4 secondary components.

3.2. UI Component

UI consists of a MainWindow that is made up of numerous parts (e.g.CommandBox, ResultDisplay, TaskListPanel, StatusBarFooter, CalendarPanel, etc.). These parts, including the MainWindow, inherit from the abstract UiPart class.

The class diagram below (refer to Figure 11, “Structure of UI) describes the structure of UI.

UiClassDiagram
Figure 11. Structure of UI

API : Ui.java

The JavaFX UI framework is used to build this component. The layout of these UI parts are defined in matching .fxml files that are in the src/main/resources/view folder. For example, the layout of the MainWindow is specified in MainWindow.fxml.

UI:

  • Executes user commands using Logic.

  • Binds itself to some data in Model so that the UI can auto-update when data in the Model changes.

  • Responds to events raised from various parts of the application and updates the UI accordingly.

3.3. Logic Component

Logic consists of the commands and their respective parsers (if necessary). Parser classes are defined in .java files suffixed with 'Parser', and are found in the src/main/java/logic/parser folder. For example, the parser for AddCommand is defined in AddCommandParser.java.

The class diagram below (refer to Figure 12, “Structure of Logic) gives an overview of the structure of Logic.

LogicClassDiagram
Figure 12. Structure of Logic

API : Logic.java

There are two types of commands. Commands that modify the state of the organizer inherit from the UndoableCommand class, and are used by the undo/redo mechanism (see [Undo/Redo feature] for more information). Commands that do not modify the state of the organizer, and cannot be undone, inherit from the Command class instead.

The class diagram below (refer to Figure 13, “Structure of commands in Logic) gives a more detailed overview of how the command system in Logic works. It shows the finer details concerning XYZCommand and Command in Figure 12, “Structure of Logic.

LogicCommandClassDiagram
Figure 13. Structure of commands in Logic

Given below (refer to Figure 14, “Interactions inside Logic for the delete 1 command”) is the sequence diagram for interactions within Logic for the execute("delete 1") API call.

DeleteTaskSdForLogic
Figure 14. Interactions inside Logic for the delete 1 command

The sequence flows as such:

  1. Logic uses the OrganizerParser class to parse the user command.

  2. Command object is created.

  3. Command object is executed by the LogicManager.

    The command execution can affect the Model (e.g. adding a task) and/or raise events.
  4. CommandResult object is created as an encapsulation of the result of the command execution.

  5. CommandResult object is passed to UI.

3.4. Model Component

Model consists of a UniqueTaskList, UniqueTagList, and UniqueUserList that contains and manipulates the data within the application using an ObservableList. The component also includes Task, and its respective parameters (e.g. Name, Priority, Date Added, etc.).

The class diagram below (refers to Figure 15, “Structure of Model) gives an overview of the structure of model.

ModelClassDiagram
Figure 15. Structure of Model

API : Model.java

The Model:

  • stores a UserPref object that represents the user’s preferences.

  • stores the Organizer data.

  • updates Tasks' priority levels within the Organizer via the UniqueTaskList automatically.

  • sorts tasks within the Organizer.

  • exposes an unmodifiable ObservableList<Task> that can be 'observed' (e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list changes).

  • does not depend on any of the other three components.

3.5. Storage Component

Storage consists of the XmlOrganizerStorage, which reads data from, and writes data to, the hard disk. Data is stored in XML format, and can be retrieved from data > organizer.xml. This component also contains the UserPrefsStorage, which stores user preferences (e.g. the application window’s preferred height and width) in a JSON file. The file is titled preferences.json, and can be retrieved from the main folder.

The class diagram below (refer to Figure 16, “Structure of Storage) gives an overview of the structure of Storage.

StorageClassDiagram
Figure 16. Structure of Storage

API : Storage.java

Storage:

  • saves UserPref objects in JSON format and reads it back.

  • saves the Organizer data in XML format and reads it back.

4. Implementation

PrioriTask consists of many different features. This section describes some noteworthy details on how certain features are implemented.

4.1. Undo/Redo Feature

The undo feature restores PrioriTask to the state before the previous undoable command was executed. Undoable commands are defined as commands that modify PrioriTask’s content (e.g. add, delete, edit and clear).

The redo feature does the opposite - it reverses the most recent undo command.

4.1.1. Current Implementation

The undo/redo mechanism is facilitated by an UndoRedoStack, which resides inside LogicManager. It supports undoing and redoing of commands that modifies the state of the organizer (e.g. add, edit). Such commands inherit from UndoableCommand.

UndoRedoStack only deals with UndoableCommands. Commands that cannot be undone inherit from Command instead. The following diagram (refer to Figure 17, “Structure of commands in the Logic) shows the inheritance diagram for commands:

LogicCommandClassDiagram
Figure 17. Structure of commands in the Logic

UndoableCommand adds an extra layer between the abstract Command class and concrete commands that can be undone, such as the DeleteCommand. Note that extra tasks need to be done when executing a command in an undoable way, such as saving the state of the organizer before execution. UndoableCommand contains the high-level algorithm for those extra tasks while child classes implement the details of how to execute the class-specific command. Note that this technique of putting the high-level algorithm in the parent class and lower-level steps of the algorithm in child classes is also known as the template pattern.

Commands that are not undoable are implemented this way:

public class ListCommand extends Command {
    @Override
    public CommandResult execute() {
        // ... list logic ...
    }
}

With the extra layer, the commands that are undoable are implemented this way:

public abstract class UndoableCommand extends Command {
    @Override
    public CommandResult execute() {
        // ... undo logic ...

        executeUndoableCommand();
    }
}

public class DeleteCommand extends UndoableCommand {
    @Override
    public CommandResult executeUndoableCommand() {
        // ... delete logic ...
    }
}

4.1.2. Scenario

Suppose that the user has just launched the application. The UndoRedoStack will be empty at the beginning.

The user executes a new UndoableCommand, delete 5, to delete the 5th task in the organizer. The current state of the organizer is saved before the delete 5 command executes. The delete 5 command will then be pushed onto the undoStack (the current state is saved together with the command).

The diagram below illustrates how the execution of the delete 5 command affects the UndoRedoStack.

UndoRedoStartingStackDiagram
Figure 18. UndoRedoStack delete 5 command execution

As the user continues to use the program, more commands are added into the undoStack. For example, the user may execute add n/Study …​ to add a new task.

The diagram below (refers to Figure 19, “UndoRedoStack add n/Study …​ command execution”) illustrates how the execution of the add n/Study command affects the UndoRedoStack.

UndoRedoNewCommand1StackDiagram
Figure 19. UndoRedoStack add n/Study …​ command execution
If a command fails its execution, it will not be pushed to the UndoRedoStack at all.

The user now decides that adding the task was a mistake, and decides to undo that action using undo.

We will pop the most recent command out of the undoStack and push it back to the redoStack. We will restore the organizer to the state before the add command executed.

The diagram below (refer to Figure 20, “UndoRedoStack undo command execution”) illustrates the process of the execution of the undo command on the UndoRedoStack.

UndoRedoExecuteUndoStackDiagram
Figure 20. UndoRedoStack undo command execution
If the undoStack is empty, then there are no other commands left to be undone, and an Exception will be thrown when popping the undoStack.

The following sequence diagram (refer to Figure 21, “UndeRedoStack sequence diagram”) shows how the undo operation works:

UndoRedoSequenceDiagram
Figure 21. UndeRedoStack sequence diagram

Redo does the exact opposite (pops from redoStack, pushes to undoStack, and restores the organizer to the state after the command is executed).

If the redoStack is empty, then there are no other commands left to be redone, and an Exception will be thrown when popping the redoStack.

The user now decides to execute a new command, clear. As before, clear will be pushed into the undoStack. This time the redoStack is no longer empty. It will be purged as it no longer make sense to redo the add n/Study command (this is the behavior that most modern desktop applications follow).

The diagram below (refer to Figure 22, “UndoRedoStack clear command execution”) illustrates how the execution of the clear command affects the UndoRedoStack.

UndoRedoNewCommand2StackDiagram
Figure 22. UndoRedoStack clear command execution

Commands that are not undoable are not added into the undoStack. For example, list, which inherits from Command rather than UndoableCommand, will not be added after execution (refer to Figure 23, “UndoRedoStack list command execution”).

UndoRedoNewCommand3StackDiagram
Figure 23. UndoRedoStack list command execution

The following activity diagram (refer to Figure 24, “UndoRedoStack activity diagram”) summarizes what happens inside the UndoRedoStack when a user executes a new command:

UndoRedoActivityDiagram
Figure 24. UndoRedoStack activity diagram

4.1.3. Design Considerations

4.1.3.1. Aspect: Implementation of UndoableCommand
  • Alternative 1 (current choice): Add a new abstract method executeUndoableCommand()

    • Pros: Any undone/redone functionality is not lost as it is now part of the default behaviour. Classes that deal with Command do not have to know that executeUndoableCommand() exist.

    • Cons: It is hard for new developers to understand the template pattern.

  • Alternative 2: Just override execute()

    • Pros: It does not involve the template pattern, easier for new developers to understand.

    • Cons: Classes that inherit from UndoableCommand must remember to call super.execute(), or lose the ability to undo/redo.

4.1.3.2. Aspect: How undo & redo executes
  • Alternative 1 (current choice): Saves the entire organizer

    • Pros: It is easy to implement.

    • Cons: There may be performance issues in terms of memory usage.

  • Alternative 2: Individual command knows how to undo/redo by itself

    • Pros: It will use less memory (e.g. for delete, just save the task being deleted).

    • Cons: There is a need to ensure that the implementation of the counter to each command is correct.

4.1.3.3. Aspect: Type of commands that can be undone/redone
  • Alternative 1 (current choice): Only include commands that modifies the organizer (add, clear, edit).

    • Pros: It only reverts changes that are hard to change back (the view can easily be re-modified as no data are * lost).

    • Cons: A user might think that undo also applies when the list is modified (undoing filtering for example), * only to realize that it does not do that, after executing undo.

  • Alternative 2: Include all commands.

    • Pros: It might be more intuitive for the user.

    • Cons: A user has no way of skipping such commands if he or she just want to reset the state of the organizer and not the view. Additional Info: See our discussion here.

4.1.3.4. Aspect: Data structure to support the undo/redo commands
  • Alternative 1 (current choice): Use separate stack for undo and redo

    • Pros: Easy to understand for new Computer Science student undergraduates, who are likely to be * the new incoming developers of our project.

    • Cons: Logic is duplicated twice. For each new command executed, both HistoryManager and UndoRedoStack must be updated.

  • Alternative 2: Use HistoryManager for undo/redo

    • Pros: Do not need to maintain a separate stack, and just reuse what is already in the codebase.

    • Cons: Requires dealing with commands that have already been undone: Must remember to skip these commands. Violates Single Responsibility Principle and Separation of Concerns as HistoryManager now needs to do two * different things.

4.2. Task’s Priority Auto-updating and Auto-sorting feature

4.2.1. Current Implementation

The autoupdating and autosorting mechanism resides inside UniqueTaskList. The UniqueTaskList is responsible for updating each Task’s priority level and sorting the ObservableList<Task> whenever a new Task is added. In addition, whenever a Task is edited, the ObservableList<Task> is sorted again.

4.2.1.1. Description of UniqueTaskList 's add method

The add method of the UniqueTaskList class is implemented as such:

public void add(Task toAdd) throws DuplicateTaskException {
    //check non null and no duplicate task
    toAdd = updatePriority(toAdd);
    internalList.add(toAdd);
    sortTasks();
}
4.2.1.2. Priority Auto-updating feature

The updatePriority method returns a new Task with a new Priority based on the current date, AddedDate, and Deadline.

The new Priority level will remain within the range of 0 - 9. The new Priority is calculated as such :

  • If the current date is equal to the AddedDate, and the current date is equals or after the Deadline.

    • Priority remains the same.

  • If the current date is past the Deadline.

    • Priority is set to the HIGHEST_SETTABLE_PRIORITY_LEVEL : 9

  • If the current date is before the Deadline and not equal to the AddedDate.

    • Priority is calculated via the following formula :

priorityDifferenceFromMax = HIGHEST_SETTABLE_PRIORITY_LEVEL - currentPriority

dayDifferenceCurrentToDeadline = Duration.between(currentDate, deadline)

dayDifferenceAddedToDeadline = Duration.between(dateAdded, deadline)

priorityToIncrease = priorityDifferenceFromMax * ((dayDifferenceAddedToDeadline - dayDifferenceCurrentToDeadline) / dayDifferenceAddedToDeadline)

newPriority = currentPriority + priorityToIncrease

The new Task with its updated Priority will then be added to UniqueTaskList’s internalList. Following that, the internalList is sorted via the sortTasks method. The sortTasks method is implemented as such:

private void sortTasks() {
    internalList.sort(Task.priorityComparator());
}
4.2.1.3. Task Auto-sorting feature

The sortTasks method makes use of the ObservableList class’s sort method and Task’s priorityComparator method. The priorityComparator method returns a Comparator that sorts Task s based on descending Priority levels. The internalList is sorted with respect to the comparator.

4.2.2. Scenario

Consider an addTask method call by the Organizer. The current date is 2018-03-19.

The following is a sequence diagram (refer to Figure 25, “addTask(Study) method execution sequence diagram”) which represents the Organizer adding a Study Task.

OrganizerAddTaskSequenceDiagram
Figure 25. addTask(Study) method execution sequence diagram

Suppose the tasks currently contains the following two Task s as shown in Figure 26, “tasks 's initial Task s”.

OrganizerAddTaskDiagram1
Figure 26. tasks 's initial Task s

Figure 27, “"Study" Task represents the "Study" Task.

OrganizerAddTaskDiagram2
Figure 27. "Study" Task

When tasks calls updatePriority(Study), the new priority level is calculated as such :

priorityDifferenceFromMax = 9 - 0 = 9

dayDifferenceCurrentToDeadline = 14

dayDifferenceAddedToDeadline = 31

priorityToIncrease = 9 * ((31 - 14) / 31) = 4

newPriority = 0 + 4 = 4

A new "Study" Task is created and added to the internalList as shown in Figure 28, “internalList after "Study" Task is added”.

OrganizerAddTaskDiagram3
Figure 28. internalList after "Study" Task is added

The sortTasks method is then called, which calls the priorityCompartor method, and uses the Comparator returned to sort the Task s. This results in the following UniqueTaskList as show in Figure 29, “internalList after sortTasks is called”.

OrganizerAddTaskDiagram4
Figure 29. internalList after sortTasks is called

4.2.3. Design Considerations

4.2.3.1. Aspect : Implementation of updatePriority
  • Alternative 1 (current choice): Add a new method updatePriority in UniqueTaskList’s add

    • Pros : It is convenient to update the priority during this method call; since during initialization of Organizer, add is called for every Task to initialize the UniqueTaskList.

    • Cons : There is a possible violation of Single Responsibility Principle and Separation of Concerns as UniqueTaskList now updates Task priorities and stores Task s.

  • Alternative 2 : Add a new class UpdatedUniqueTaskList which extends UniqueTaskList

    • Pros : This prevents the violation of Single Responsibility Principle and Separation of Concerns in Alternative 1.

    • Cons : This repeats the logic in UniqueTaskList by adding one method.

4.2.3.2. Aspect : How updatePriority executes
  • Alternative 1 (current choice) : Return a new Task with new Priority

    • Pros : It is easy to implement.

    • Cons : A new object is created twice during every addition, even if Priority is not updated; Hence slightly inefficient.

  • Alternative 2 : Edit the Priority parameter of the Task

    • Pros : No new objects are created.

    • Cons : It requires a major overhaul of Task and it’s parameters to be mutable.

4.2.3.3. Aspect : Scope of updatePriority and sortTasks
  • Alternative 1 (current choice) : Implement within Model

    • Pros : There is no need to deal with commands, and fits with the idea of automation.

    • Cons : The concern of updating priorities and sorting tasks may not lie with Model but Logic.

  • Alternative 2 : Implement within Logic

    • Pros : It fits with the concern of updating priorities and sorting tasks.

    • Cons : It requires development of commands and extra command calls to be automatically called upon start of application; unnecessary trouble for same feature.

4.3. Calendar Feature

The Calendar resides mainly in the UI component, constantly interacting with the Logic and Model components to complete its tasks. It allows users to have a chronological overview of the deadlines of all their tasks. Tasks on the calendar changes according to the last task listing. By default, users will view the current month when they first open PrioriTask.

The calendar feature is currently in its early stages of implementation. At present, it only supports the switching between months, and is best viewed fully-maximised on a 13” computer screen.

4.3.1. Current Implementation

The calendar feature is facilitated by the MonthView class, which supports the CalendarPanel. The MonthView is responsible for displaying a monthly calendar view to the users through the CalendarPanel. The following diagram (refer to Figure 30, “Structure of the Calendar Feature”) illustrates the structure of the calendar feature system:

CalendarComponentClassDiagram
Figure 30. Structure of the Calendar Feature

The following sequence diagram (refer to Figure 31, “Calendar Sequence Diagram”) illustrates how the different classes work together to display the calendar when users first run the application.

CalendarSequenceDiagram
Figure 31. Calendar Sequence Diagram

Below describes the the implementation of the different classes supporting the calendar feature.

4.3.1.1. Implementation of EntryCard

EntryCard is responsible for displaying the name of a Task on the calendar. It is called by MonthView when mapping each Task to an EntryCard. The method is implemented as such:

private ObservableList<EntryCard> getEntryCardsList(int year, int month) {
    FilteredList<Task> filteredList = getFilteredTaskList(year, month);
    SortedList<Task> taskSortedList = getSortedTaskList(filteredList);

    return EasyBind.map(taskSortedList, (task) -> new EntryCard(task));
}

An EntryCard will be added to a specific date which reflects the deadline of the Task. It is displayed on the calendar with the help of the JavaFX ListView object. More details of how this is implemented is further discussed in Section 4.3.1.2, “Implementation of MonthView.

4.3.1.2. Implementation of MonthView

MonthView is in charge of executing several tasks. It is responsible for,

  • Drawing the month view of the calendar, and populating the dates.

  • Adding the tasks’ entries onto the calendar, according to the last task listing. The calendar is also updated every time an undoable command is executed (refer to [Undo/Redo feature] for more details about an undoable command).

  • Aiding the Logic component by switching the month view accordingly when users execute the respective calendar feature commands (e.g. pmonth, nmonth, etc.). A calendar feature command is defined as a command that changes the display of the calendar. For the full list of calendar feature commands, head to User Guide: Calendar Features.

MonthView interacts very closely with its corresponding FXML file, MonthView.fxml to execute its tasks. The FXML file reflects the basic layout of the calendar feature, which is illustrated by Figure 32, “Basic Calendar Layout”.

MonthViewLayoutDiagram
Figure 32. Basic Calendar Layout

MonthView` uses the JavaFX layout objects extensively when executing its tasks. The following are some instances when JavaFX layout objects are called:

  • Populating the dates in the calendar: Adding a Text object, which contains the numerical value of a particular date, to the exact column and row in the taskCalendar. The method is implemented as such:

private void addMonthDate(Text dateToPrint, int column, int row) {
    // To update the JavaFX component from a non-JavaFX thread
    Platform.runLater(new Runnable() {
        @Override
        public void run() {
            taskCalendar.add(dateToPrint, column, row);
        }
    });

    taskCalendar.setHalignment(dateToPrint, HPos.LEFT);
    taskCalendar.setValignment(dateToPrint, VPos.TOP)
    dateToPrint.setId("date" + String.valueOf(dateCount));
}
  • Adding the tasks’ entries onto the calendar: Adding a ListView object, which contains a list of EntryCard`s, to the exact column and row in the `taskCalendar. An EntryCard represents a task entry, which will be added to the date which reflects the deadline of the task. The method is implemented as such:

private void addEntryListView(ObservableList<EntryCard> toAddObservableList, int row, int column) {
    ListView<EntryCard> entries = new ListView<>();
    entries.setId("entry" + String.valueOf(row) + String.valueOf(column));
    entries.setItems(toAddObservableList);
    entries.setCellFactory(listView -> new EntryListViewCell());
    entries.setMaxHeight(60);

    // To update the JavaFX component from a non-JavaFX thread
    Platform.runLater(new Runnable() {
        @Override
        public void run() {
            taskCalendar.add(entries, column, row);
        }
    });

    taskCalendar.setValignment(entries, VPos.BOTTOM);
}

MonthView also works very closely with the Logic component to accomplish,

  • Adding of the tasks’ entries onto the calendar, by retrieving an unmodifiable ObservableList containing the list of Task`s. A listener to added to the `ObservableList so that the calendar is updated with the latest tasks’ entries when a change in the list is detected. The method is implemented as such:

private void addListenerToTaskList() {
    taskList.addListener(new ListChangeListener<Task>() {
        @Override
        public void onChanged(Change change) {
            while (change.next()) {
                clearCalendar();
                setMonthCalendarDatesAndEntries(viewYearMonth.getYear(), viewYearMonth.getMonthValue());
            }
        }
    });
}
  • Switching the month view when users execute the respective calendar feature commands, by retrieving an ObservableList containing a list of executed commands by the user. A listener is added to the ObservableList so that the month view is changed accordingly when a calendar feature command in the list is detected. The method is implemented as such:

private void addListenerToExecutedCommandsList() {
    executedCommandsList.addListener(new ListChangeListener<String>() {
        @Override
        public void onChanged(Change change) {
            while (change.next()) {
                int size = executedCommandsList.size();
                String executedCommand = executedCommandsList.get(size - 1);

                if ((executedCommand.equals(CurrentMonthCommand.COMMAND_WORD)) || (executedCommand.equals(CurrentMonthCommand.COMMAND_ALIAS))) {
                    goToCurrentMonth();
                }

                // … other similiar if statements for the remaining calendar feature commands ...
            }
        }
    });
}
4.3.1.3. Implementation of CalendarPanel

CalendarPanel simply displays the calendar, by using the JavaFX StackPane object as a placeholder for MonthView, which contains the basic layout of the calendar. After loading its own FXML file, it will fetch the display for MonthView as such:

private void createMainView() {
    monthView.getMonthView(currentYearMonth);
    calendarPane.getChildren().add(monthView.getRoot());
}

4.3.2. Scenario

Suppose the user executes a calendar feature command. Figure 33, “Calendar Sequence Diagram” shows the interactions within the Logic and UI components for the execute(“nmonth”) API call.

CalendarCommandExecutionSequenceDiagram
Figure 33. Calendar Sequence Diagram

The sequence flow is slightly similar to that of the execution of a normal command (refer to Figure 14, “Interactions inside Logic for the delete 1 command”), and is as follows:

  1. Logic uses the OrganizerParser class to parse the calendar feature command.

  2. This results in a Command object which is executed by the LogicManager.

  3. The command execution triggers the MonthView class in the UI, which will display the new month view.

  4. The result of the command execution is encapsulated as a CommandResult object which is passed to the UI. It will be shown in the Result Display Box.

4.3.3. Design Considerations

4.3.3.1. Aspect: Implementation of calendar feature
  • Alternative 1 (current choice): Draw the calendar manually using the JavaFX library

    • Pros: The feature is customised for PrioriTask.

    • Cons: There is a need to invest a lot of time into creating a feature that has been already been developed and freely available.

  • Alternative 2: Use a third-party framework / library

    • Pros: This speeds up development, especially if it has already been thoroughly tested and does not have many bugs.

    • Cons: It is not customised for the application, and may require much time to tweak or work around the framework / library to suit the application.

4.3.3.2. Aspect: Updating of tasks’ entries on the calendar
  • Alternative 1 (current choice): Clears the calendar, and draws everything (i.e. the dates and entries) again

    • Pros: It is easy to implement.

    • Cons: There may be performance issues (there may be lag during execution of undoable commands).

  • Alternative 2: Track the Task that is being added / modified, and update the calendar accordingly

    • Pros: The update of the calendar will be almost instantaneous. The chances of lag is rare.

    • Cons: It is difficult to implement. There is a need to ensure that the tracking of Task s added / modified is accurate.

4.4. Subtask

4.4.1. Current Implementation

Subtask feature allows users to split their tasks into multiple subtasks. This feature allows users to better manage their task. It is implemented using Subtask class and UniqueSubtaskList class as shown by Figure 34, “Subtask UML Class Diagram” below:

SubtaskComponentDiagram
Figure 34. Subtask UML Class Diagram

UniqueSubtaskList is created to ensure that no task has a duplicated subtask.

Subtask management

To manage the subtasks (Add, edit, etc), a copy of the Task object is created, since Task is immutable.

4.4.2. Design Considerations

4.4.2.1. Aspect: Implementation of Subtask
  • Alternative 1 (current implementation): Add a new class Subtask

    • Pros: There is better isolation, and allows for easier modification to Subtask if needed.

    • Cons: It restricts the possibility of having a subtask that has subtasks.

  • Alternative 2: Use current Task class

    • Pros: It is easier to implement and subtask will share the same properties with Task.

    • Cons: There is a ossibility of having a cyclic subtask (a task that is also a subtask of it self) if not handled carefully.

4.4.2.2. Aspect: Implementation of UniqueSubtaskList
  • Alternative 1 (current implementation): Use List to store subtasks.

    • Pros: It is easier to implement.

    • Cons: There will be performance issues when data is large, since most operations in UniqueSubtaskList is O(n).

  • Alternative 2 (Planned for implementation): Use a combination of BBST and LinkedList to store subtasks.

    • Pros: If is faster than the first alternative since each operation is O(log n).

    • Cons: It is harder to implement and more robust testing is required since it is more bug prone.

4.5. Recurring Tasks

Recurring Tasks feature allows users to recur tasks weekly using the RecurWeeklyCommand. It also allows users to delete a task and all its recurred versions using the DeleteRecurredTasksCommand.

4.5.1. Current Implementation

The recurring task feature is implemented through a Recurrence component, which is a parameter for Task.

The first recurred version of the task has a deadline set to be a week after the original task’s deadline. The second recurred version of the task (if asked for by the user) has a deadline set to be 2 weeks after the original task’s deadline.

The pattern continues for the rest of the recurred versions.

The recurred versions of the tasks and all their subtasks are set to be Not Completed by default.

Additionally, the priorities of the recurred versions of the tasks are set to be the priority of the orginal task, as last set in the AddCommand or EditCommand. This is done instead of taking the priority of the original task as it is, which could have been changed by PrioriTask through its auto-updating feature.

Both RecurWeeklyCommand and DeleteRecurredTasksCommand are UndoableCommands.

The Recurrence component is implemented using a Recurrence class. The following diagram (refer to Figure 35, “Recurrence UML Class Diagram”) shows the class diagram for Recurrence.

RecurrenceComponentDiagram
Figure 35. Recurrence UML Class Diagram

4.5.2. Scenario

Suppose that the user wants to recur a task weekly.

The user executes a new RecurWeeklyCommand, recurw 1 x/2, to recur the 1st task in the organizer weekly for 3 times.

Assuming that the task has not been recurred before, the following sequence diagram (refer to Figure 36, “RecurWeekly sequence diagram”) shows how the recur weekly operation works:

RecurWeeklySequenceDiagram
Figure 36. RecurWeekly sequence diagram

The object diagram below (refer to Figure 37, “Recurred tasks object diagram”) shows the original task and the 2 Tasks created after the execution of the command.

RecurredTasksObjectDiagram
Figure 37. Recurred tasks object diagram

Suppose the user now wants to delete the 1st task in the PrioriTask and all its recurred versions.

The user executes a new DeleteRecurredTasksCommand, deleter 1, to delete the task that was just recurred and all its recurred versions.

4.5.3. Design Considerations

4.5.3.1. Aspect: Implementation of recurrenceGroup in Recurrence
  • Alternative 1 (current choice): Set recurrenceGroup as hash code of the task`

    • Pros: It is guaranteed to be unique, which is needed to identify a group of recurred tasks. It is also easy to implement.

    • Cons: It is not as intuitive to identify the group of recurred tasks using long hash codes.

  • Alternative 2: Set recurrenceGroup using a counter.

    • Pros: It is a more intuitive way of identifying recurrence groups.

    • Cons: There is a need to implement a counter for recurrence groups in Model.

4.5.3.2. Aspect: Implementation of RecurWeeklyCommand
  • Alternative 1 (current choice): Add a new Task for each recurred version of the original task

    • Pros: It is easy to implement.

    • Cons: It takes up more memory for Objects that have very similar parameters..

  • Alternative 2: Maintain a list for each of the parameters that change when recurred within the original Task, such as a list of DateCompleted and a list of Status.

    • Pros: It will use less memory.

    • Cons: There is a need to change how certain parameters in Task are implemented.

4.5.3.3. Aspect: Implementation of DeleteRecurredTasksCommand
  • Alternative 1 (current choice): Add tasks that are not part of the recurrence group into a new task list and set the new task list to replace the old one.

    • Pros: It only reverts changes that are hard to change back (the view can easily be re-modified as no data are * lost).

    • Cons: A user might think that undo also applies when the list is modified (undoing filtering for example), * only to realize that it does not do that, after executing undo.

  • Alternative 2: Remove tasks that are part of the recurrence group while iterating through the task list.

    • Pros: This is a more intuitive way of removing tasks from the list.

    • Cons: There is a need to implement an Iterator to remove tasks, as we cannot use a for loop which can cause undefined behaviour.

4.6. Logging

We are using java.util.logging package for logging. The LogsCenter class is used to manage the logging levels and logging destinations.

  • The logging level can be controlled using the logLevel setting in the configuration file (See Section 4.7, “Configuration”).

  • The Logger for a class can be obtained using LogsCenter.getLogger(Class) which will log messages according to the specified logging level.

  • Currently log messages are output through the Console and to a .log file.

Logging Levels

The following are brief descriptions of different logging levels:

  • SEVERE : A message level indicating a serious failure (e.g. failure resulting in application termination).

  • WARNING : A message level indicating a potential problem (e.g. application can still continue, but do so with caution).

  • INFO : A message level for informational messages (e.g. noteworthy actions by the application).

  • FINE : A message level providing tracing information (e.g. print the actual list of an array instead of its size).

4.7. Configuration

Certain properties of the application (e.g App name, logging level) can be controlled through the configuration file (default: config.json).

5. Documentation

Asciidoc is used for writing documentation.

Asciidoc was chosen over Markdown as asciidoc provides more flexibility in formatting.

5.1. Editing Documentation

See UsingGradle.adoc to learn how to render .adoc files locally to preview the end result of edits.

Alternatively, the AsciiDoc plugin for IntelliJ provides a real-time view of .adoc file edits .

5.2. Publishing Documentation

See UsingTravis.adoc to learn how to deploy GitHub Pages using Travis.

5.3. Converting Documentation to PDF format

Google Chrome can be used for converting documentation to PDF format, as Chrome’s PDF engine preserves hyperlinks used in webpages.

The following are the steps to convert the project documentation files to PDF format:

  1. Follow the instructions in UsingGradle.adoc to convert the AsciiDoc files in the docs/ directory to HTML format.

  2. Access the generated HTML files in the build/docs folder, right click on them and select Open withGoogle Chrome.

  3. Within Chrome, click on the Print option in Chrome’s menu.

  4. Set the destination to Save as PDF, then click Save to save a copy of the file in PDF format. For the best results, use the settings indicated in Figure 38, “Saving documentation as PDF files in Chrome”.

chrome save as pdf
Figure 38. Saving documentation as PDF files in Chrome

6. Testing

6.1. Running Tests

There are three ways to run tests.

The most reliable way to run tests is the third method. The first two methods might fail some GUI tests due to platform/resolution-specific idiosyncrasies.

Method 1: Using IntelliJ JUnit test runner

  • To run all tests, right-click on the src/test/java folder and choose Run 'All Tests'.

  • To run a subset of tests, right-click on a test package, test class, or a test and choose Run 'ABC'.

Method 2: Using Gradle

Open a console and run the command gradlew clean allTests (Mac/Linux: ./gradlew clean allTests).

See UsingGradle.adoc for more info on how to run tests using Gradle.

Method 3: Using Gradle (headless)

Open a console and run the command gradlew clean headless allTests (Mac/Linux: ./gradlew clean headless allTests)

Thanks to the TestFX library, GUI tests can be run in the headless mode. In the headless mode, GUI tests do not show up on the screen. This means that the developer is free to do something else while the tests are running.

6.2. Types of Tests

There are two types of tests:

  1. GUI Tests - Tests involving the GUI. They include:

    1. System Tests that tests the entire application by simulating user actions on the GUI. These are found in the systemtests package.

    2. Unit tests that test individual components. These are found in seedu.organizer.ui package.

  2. Non-GUI Tests - Tests not involving the GUI. They include:

    1. Unit tests targeting the lowest level methods/classes.
      e.g. seedu.organizer.commons.StringUtilTest

    2. Integration tests that check the integration of multiple code units (these code units are assumed to be working).
      e.g. seedu.organizer.storage.StorageManagerTest

    3. Hybrids of unit and integration tests. These tests check multiple code units as well as how they are connected together.
      e.g. seedu.organizer.logic.LogicManagerTest

6.3. Troubleshooting Testing

Problem: HelpWindowTest fails with a NullPointerException.

  • Reason: UserGuide.html, a dependency which should be found in src/main/resources/docs, is missing.

  • Solution: Execute Gradle task processResources.

7. Dev Ops

7.1. Build Automation

See UsingGradle.adoc to learn how to use Gradle for build automation.

7.2. Continuous Integration

Travis CI and AppVeyor are used to perform Continuous Integration on the project. See UsingTravis.adoc and UsingAppVeyor.adoc for more details.

7.3. Coverage Reporting

Coveralls is used to track the code coverage of the project. See UsingCoveralls.adoc for more details.

7.4. Documentation Previews

When a pull request contains changes to asciidoc files, Netlify can be used to preview a HTML version of the edited asciidoc files. See UsingNetlify.adoc for more details.

7.5. Making a Release

The following are steps to create a new release:

  1. Update the version number in MainApp.java.

  2. Generate a JAR file using Gradle.

  3. Tag the repo with the version number. e.g. v0.1.

  4. Create a new release using GitHub and upload the JAR file you created.

7.6. Managing Dependencies

A project often depends on third-party libraries. For example, PrioriTask depends on the Jackson library for XML parsing. Managing these dependencies can be automated using Gradle. For example, Gradle can download the dependencies automatically, which is better than the following alternatives:
a. Including those libraries in the repo (this bloats the repo size)
b. Requiring developers to download those libraries manually (this creates extra work for developers)

Appendix A: Product Scope

Target user profile:

  • Busy university students who have many tasks to keep track of.

  • Takes many modules, is part of various student organizations, and has many activities.

  • Prefers a digital organizer over a physical organizer.

  • Requires help in monitoring numerous tasks and their levels of importance.

  • Would like assistance in task planning.

  • Is forgetful, and requires constant reminders.

  • Prefers desktop apps over other types.

  • Is reasonably comfortable with CLI apps.

  • Can type fast.

  • Prefers typing over mouse input.

Value proposition:

Help busy university students keep track of their tasks and priorities.

Feature contribution:

  • Agus Sentosa Hermawan

    • Major : Subtasks parameter

      • Allows tasks to be split into subtasks for better management.

    • Minor : Toggle command

      • Allows tasks to be marked as completed.

  • Dominic Kenn Lim

    • Major : User login

      • Allows multiple users to access their private tasks within the same machine.

    • Minor : Priority autosorting and autoupdate

      • Allows application to automatically manage task priority levels so that the user does not have to.

  • Natania Djohaari

    • Major : Recurring Tasks

      • Helps users automatically create tasks that are recurring.

    • Minor : Remove tags command

      • Allows users to delete multiple tags from all tasks.

  • Yeo Guek Ling

    • Major : Calendar

      • Allows users to be able to have a chronological overview of the deadlines of all their tasks.

    • Minor : Find deadline / description commands

      • Allows users to be search for tasks using keywords in the deadline and / or description parameters.

Appendix B: User Stories

The following is a list of user stories for PrioriTask.

Priorities: High (must have) - * * *, Medium (nice to have) - * *, Low (unlikely to have) - *

Priority As a …​ I want to …​ So that I can…​

* * *

new user

see usage instructions

refer to instructions when I forget how to use the application

* * *

user

add a new task

* * *

user

add a priority level to a task

keep track of the priority levels of my tasks and allow the application to order tasks by priority levels

* * *

user

add a deadline to a task

keep track of the deadlines of my tasks and allow the application to help me manage my priorities according to my deadlines

* *

user

add a subtask to a task

split tasks into smaller subtasks for easier management

* *

user

add tags to a task

organize the tasks based on groups such as modules or projects

* * *

user

add a description to a task

give a task a description that cannot be sufficiently represented by name or subtasks

* * *

user

delete a task

remove a task

* * *

user

delete a deadline from a task

remove a task’s deadline when necessary

* *

user

delete a subtask from a task

remove a subtask I no longer need

* *

user

delete a tag from a task

remove a tag from a task that no longer belongs to the group

* *

user

delete a description from a task

remove a description that is no longer relevant

* *

user

delete all tasks

remove all tasks quickly

* * *

user

edit a task’s name

update the task’s name when required

* * *

user

edit a task’s priority level

change a task’s priority level when I need to re-organize my priorities

* * *

user

edit a task’s deadline

change a task’s deadline

* *

user

edit a subtask of a task

change a subtask’s name

* * *

user with many uncompleted tasks

have all tasks constantly ordered according to their priority levels

know the order I should complete my tasks

* *

user

edit a task’s description

change a task’s description

* * *

user

mark a task as complete

complete the task

* *

user

mark a subtask as complete

keep track of subtasks already done

* * *

user with many uncompleted tasks

view all uncompleted tasks

see all tasks that need to be done, along with the deadlines and priorities

* * *

user

view all details of a task

view all details of a specified task, after seeing a list of all tasks

* * *

user with many tasks

view all completed tasks

see all tasks that have been completed, for past reference

* *

user with many tasks

view all tasks with a specific tag

locate all tasks with a specific tag, without the need for a manual search

* *

user with many tasks

view all tasks within a certain deadline

locate all tasks within a certain deadline, without the need of a manual search

* *

user

view a calender showing task’s deadlines

see a chronic overview of all tasks and their deadlines

* *

user with many tasks

find a task by name

locate a task without the need to search through an entire list

* *

user with many tasks

find a task by description

locate a task when I have forgotten it’s name

* *

user who is forgetful

have tasks' priority levels be automatically updated as deadline approaches

be reminded to complete tasks

* *

user who is forgetful

have tasks' priority levels be automatically increased to maximum level any are still uncompleted after the deadline has passed

be reminded to complete tasks which are past the deadline

* *

user

have a reminder of tasks with high priority levels

be reminded to complete tasks with high priority levels

* *

user

undo an operation

revert an operation

* *

user

redo an undo operation

redo an undo operation

* *

user

have a task with no priority level to have it’s priority level be automatically set to the lowest priority level

have priority levels for all tasks even if I had forgotten to set the priority levels

*

user

change the view of the calendar

choose to view the calendar by year, month, week, or day

*

user

reorder a task’s subtasks

rearrange the order of a task’s subtasks when necessary

*

user

recover my past data

rewrite existing data by recovering past data when necessary

* *

user using the same machine as others

have my tasks privatized

avoid privacy issues

* *

user using the same machine as others

create a user account

have an account with only my tasks accessible to me

* *

user using the same machine as others

login to PrioriTask with my own account

manage my own tasks

* *

user using the same machine as others

logout of PrioriTask

ensure privacy of my tasks

* *

user who has a user account

retrieve my password

login to my account if I forget my password

* *

user who has similar tasks that happen weekly

recur tasks weekly

easily create versions of the same task but with different deadlines.

Appendix C: Use Cases

(For all use cases below, the System is the Organizer and the Actor is the user, unless specified otherwise)

Use case: Add task

MSS

  1. User creates an account.

  2. User logs into PrioriTask.

  3. User requests to add a task.

  4. Organizer shows the newly added task as part of a list of all tasks.

    Use case ends.

Extensions

  • 1a. User already exists.

    • 1a1. Organizer shows an error message.

      Use case resumes at step 1.

  • 2a. User does not exist.

    • 2a1. Organizer shows an error message.

      Use case resumes at step 2.

  • 3a. Invalid parameters given.

    • 3a1. Organizer shows an error message.

      Use case resumes at step 3.

Use case: Delete task

MSS

  1. User logs into PrioriTask.

  2. User requests to delete a task.

  3. Organizer shows a list of tasks without the deleted task.

    Use case ends.

Extensions

  • 1a. User does not exist.

    • 1a1. Organizer shows an error message.

      Use case resumes at step 1.

  • 2a. Invalid index is given.

    • 2a1. Organizer shows an error message.

      Use case resumes at step 2.

Appendix D: Non Functional Requirements

  1. Should work on any mainstream OS as long as it has Java 1.8.0_60 or higher installed.

  2. Should be able to hold up to 1000 tasks without a noticeable sluggishness in performance for typical usage.

  3. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse.

  4. Should come with a help feature and various helper messages so that it is usable by a novice who had only have minimal CLI experience.

  5. Users are expected to know how to set up the project without an installer.

  6. The end product is geared towards power users, and may not be suitable for everyone.

Appendix E: Glossary

Mainstream OS

Windows, Linux, Unix, OS-X

Appendix F: Instructions for Manual Testing

Given below are instructions to test the app manually:

These instructions only provide a starting point for testers to work on; testers are expected to do more exploratory testing.

F.1. Launch and Shutdown

  1. Initial launch

    1. Download the jar file and copy into an empty folder.

    2. Double-click the jar file.
      Expected: Shows the GUI with an empty task list. The window size may not be optimum.

  2. Saving window preferences

    1. Resize the window to an optimum size. Move the window to a different location. Close the window.

    2. Re-launch the app by double-clicking the jar file.
      Expected: The most recent window size and location is retained.

If the window size is smaller than the default height and width, the window size will be reset to default upon app re-launch.

F.2. User Management

  1. Signing up

    1. Prerequisites: Application is not currently logged in.

    2. Test case: signup u/dave p/dave
      Expected: No user account is created. Error details shown in the status message. Status bar remains the same.

    3. Test case: signup u/admin p/admin
      Expected: A user account is created. Username of the created user account is shown in the status message. Timestamp in the status bar is updated.

  2. Logging in

    1. Prerequisites: User account with username 'admin' and password 'admin' has been created.

    2. Test case: login u/admin123 p/admin123
      Expected: Login attempt fails. Error details shown in the status message. Status bar remains the same.

    3. Test case: login u/dave p/dave
      Expected: Login attempt fails. Error details shown in the status message. Status bar remains the same.

    4. Test case: login u/admin p/admin
      Expected: Login attempt is successful. Username of currently logged in user is shown in the status message. Timestamp in the status bar is updated. Currently logged in user in status bar is updated.

  3. Adding a question answer set

    1. Prerequisites: Application is currently logged in.

    2. Test case: addqa q/ a/
      Expected: No question answer set is added. Error details shown in the status message. Status bar remains the same.

    3. Test case: addqa q/are cats cool? a/yes
      Expected: Question answer set is added. Username of the currently logged in user and the command success is shown in the status message. Timestamp in the status bar is updated.

  4. Retrieving a question for password retrieval

    1. Prerequisites: Application is not currently logged in. User account with username 'admin' and password 'admin' has been created. UserQuestion answer set has been added to user : 'admin'.

    2. Test case: forgotpassword u/admin123
      Expected: Question retrieval fails. Error details shown in the status message. Status bar remains the same.

    3. Test case: forgotpassword u/admin
      Expected: Question retrieval successful. Question of 'admin` shown in the status message. Status bar reminas the same.

  5. Answering a question for password retrieval

    1. Prerequisites: Application is not currently logged in. User account with username 'admin' and password 'admin' has been created. UserQuestion answer set with answer 'yes' has been added to user : 'admin'.

    2. Test case: answer u/admin123 a/yes
      Expected: Password retrieval fails. Error details shown in the status message. Status bar remains the same.

    3. Test case: answer u/admin a/no
      Expected: Password retrieval fails. Error details shown in the status message. Status bar remains the same.

    4. Test case: answer u/admin a/yes
      Expected: Password retrieval successful. Password of 'admin' shown in the status message. Status bar remains the same.

F.3. Task Management

  1. Recurring a task weekly

    1. Prerequisites: Application is logged in, and at least a task is added that has not been recurred yet.

    2. Test case: recurw 1 x/2
      Expected: Task recurred successfully. A success message will be shown in the results display box. 2 tasks will have been added to the task list panel and the calendar.

  2. Deleting a task and its recurred versions

    1. Prerequisites: Application is logged in, and at least a task is added that has been recurred.

    2. Test case: deleter 1
      Expected: Task and its recurred versions deleted successfully. A success message will be shown in the results display box. The task and its recurred versions will not be shown in the task list panel or the calendar.

  3. Adding a subtask

    1. Prerequisites: Application is logged in, and at least a task is added with no subtask.

    2. Test case: adds 1 n/Prepare Project Report
      Expected: Subtask added successfully. A success message will be shown in the results display box, and subtask with name Prepare Project Report appears on the first subtask.

  4. Editing a subtask

    1. Prerequisites: Application is logged in, and a task is added with a subtask.

    2. Test case: edits 1 1 n/Prepare Project Report
      Expected: Subtask edited successfully. A success message will be shown in the results display box, and subtask with name Prepare Project Report appears on the first subtask.

  5. Deleting a subtask

    1. Prerequisites: Application is logged in, and a task is added with a subtask.

    2. Test case: delete-subtask 1 1 n/Prepare Project Report
      Expected: Subtask deleted successfully. A success message will be shown in the results display box, and the task will have no subtask.

  6. Toggling a subtask

    1. Prerequisites: Application is logged in, and a task is added with a subtask.

    2. Test case: toggle-subtask 1 1
      Expected: Subtask toggled successfully. A success message will be shown in the results display box, and the subtask status will be changed.

F.4. Calendar

  1. Adding a task

    1. Prerequisites: Application is logged in.

    2. Test case: add n/CS2101 OP1 d/2018-03-23
      Expected: Task added successfully. A success message will be shown in the results display box. Task is reflected on the calendar on the 2018-03-23 date.

  2. Editing a task

    1. Prerequisites: Application is logged in. At least one task is present in the organizer.

    2. Test case: edit 1 n/Prepare for OP1
      Expected: Task updated successfully. A success message will be shown in the results display box. Calendar will be updated with the task’s new name.

    3. Test case: edit 1 p/3
      Expected: Task updated successfully. A success message will be shown in the results display box. No changes will be made on the calendar because the task’s name and deadline was not changed.

  3. Deleting a task

    1. Prerequisites: Application is logged in. At least one task is present in the organizer.

    2. Test case: delete 1
      Expected: Task deleted successfully. A success message will be shown in the results display box. Calendar will remove the deleted task.

  4. Switching between months

    1. Prerequisites: Application is logged in.

    2. Test case: pmonth
      Expected: Calendar view changed successfully. A success message will be shown in the results display box. If the currently viewed month on the application is April 2018, calendar will show March 2018 after execution of command.

    3. Test case: nmonth
      Expected: Calendar view changed successfully. A success message will be shown in the results display box. If the currently viewed month on the application is April 2018, calendar will show May 2018 after execution of command.

    4. Test case: cmonth
      Expected: Calendar view changed successfully. A success message will be shown in the results display box. If the currently viewed month on the application is December 2018 and current month is April 2018, calendar will show April 2018 after execution of command.