Developer Guide
- Setting up, getting started
- Design
- Implementation
- Documentation, logging, testing, configuration, dev-ops
- Appendix: Requirements
- Appendix: Instructions for manual testing
Setting up, getting started
Refer to the guide Setting up and getting started.
Design
.puml
files used to create diagrams in this document can be found in the diagrams folder. Refer to the PlantUML Tutorial at se-edu/guides to learn how to create and edit diagrams.
Architecture
The Architecture Diagram given above explains the high-level design of the App.
Given below is a quick overview of main components and how they interact with each other.
Main components of the architecture
Main
has two classes called Main
and MainApp
. It is responsible for,
- At app launch: Initializes the components in the correct sequence, and connects them up with each other.
- At shut down: Shuts down the components and invokes cleanup methods where necessary.
Commons
represents a collection of classes used by multiple other components.
The rest of the App consists of four components.
-
UI
: The UI of the App. -
Logic
: The command executor. -
Model
: Holds the data of the App in memory. -
Storage
: Reads data from, and writes data to, the hard disk.
How the architecture components interact with each other
The Sequence Diagram below shows how the components interact with each other for the scenario where the user issues the command delete 1
.
Each of the four main components (also shown in the diagram above),
- defines its API in an
interface
with the same name as the Component. - implements its functionality using a concrete
{Component Name}Manager
class (which follows the corresponding APIinterface
mentioned in the previous point.
For example, the Logic
component defines its API in the Logic.java
interface and implements its functionality using the LogicManager.java
class which follows the Logic
interface. Other components interact with a given component through its interface rather than the concrete class (reason: to prevent outside component’s being coupled to the implementation of a component), as illustrated in the (partial) class diagram below.
The sections below give more details of each component.
UI component
The API of this component is specified in Ui.java
The UI consists of a MainWindow
that is made up of parts e.g.CommandBox
, ResultDisplay
, TaskListPanel
, StatusBarFooter
etc. All these, including the MainWindow
, inherit from the abstract UiPart
class which captures the commonalities between classes that represent parts of the visible GUI.
The UI
component uses the JavaFx UI framework. The layout of these UI parts are defined in matching .fxml
files that are in the src/main/resources/view
folder. For example, the layout of the MainWindow
is specified in MainWindow.fxml
The UI
component,
- executes user commands using the
Logic
component. - listens for changes to
Model
data so that the UI can be updated with the modified data. - keeps a reference to the
Logic
component, because theUI
relies on theLogic
to execute commands. - depends on some classes in the
Model
component, as it displaysTask
object residing in theModel
.
Logic component
API : Logic.java
Here’s a (partial) class diagram of the Logic
component:
How the Logic
component works:
- When
Logic
is called upon to execute a command, it uses theHarmoniaParser
class to parse the user command. - This results in a
Command
object (more precisely, an object of one of its subclasses e.g.,AddCommand
) which is executed by theLogicManager
. - The command can communicate with the
Model
when it is executed (e.g. to add a person). - The result of the command execution is encapsulated as a
CommandResult
object which is returned back fromLogic
.
The Sequence Diagram below illustrates the interactions within the Logic
component for the execute("delete 1")
API call.
DeleteCommandParser
should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
Here are the other classes in Logic
(omitted from the class diagram above) that are used for parsing a user command:
How the parsing works:
- When called upon to parse a user command, the
HarmoniaParser
class creates anXYZCommandParser
(XYZ
is a placeholder for the specific command name e.g.,AddCommandParser
) which uses the other classes shown above to parse the user command and create aXYZCommand
object (e.g.,AddCommand
) which theHarmoniaParser
returns back as aCommand
object. - All
XYZCommandParser
classes (e.g.,AddCommandParser
,DeleteCommandParser
, …) inherit from theParser
interface so that they can be treated similarly where possible e.g, during testing.
Model component
API : Model.java
The Model
component,
- stores the task list data i.e., all
Task
objects (which are contained in aUniqueTaskList
object). - stores the currently ‘selected’
Task
objects (e.g., results of a search query) as a separate filtered list which is exposed to outsiders as an unmodifiableObservableList<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 change. - stores a
UserPref
object that represents the user’s preferences. This is exposed to the outside as aReadOnlyUserPref
objects. - does not depend on any of the other three components (as the
Model
represents data entities of the domain, they should make sense on their own without depending on other components)
Tag
list in the TaskList
, which Task
references. This allows TaskList
to only require one Tag
object per unique tag, instead of each Task
needing their own Tag
objects.Storage component
API : Storage.java
The Storage
component,
- can save both task list data and user preference data in json format, and read them back into corresponding objects.
- inherits from both
TaskListStorage
andUserPrefStorage
, which means it can be treated as either one (if only the functionality of only one is needed). - depends on some classes in the
Model
component (because theStorage
component’s job is to save/retrieve objects that belong to theModel
)
Common classes
Classes used by multiple components are in the seedu.address.commons
package.
Implementation
This section describes some noteworthy details on how certain features are implemented.
Mass Operations
What is the feature about
This feature provides a way for users to mark
, unmark
and delete
multiple Tasks
at a time. For the previous implementation of mark
, unmark
and delete
, users had to type in a command for each task that they wished to mark
, unmark
or delete
one at a time.
How the feature is implemented
These features are implemented with the addition of a MassOpsParser
class which parses through user inputs, consisting of multiple indexes. MassOpsParser
processes the indexes to return an ArrayList
of Indexes
for MarkCommand
, UnmarkCommand
or DeleteCommand
to execute.
Given below is an example usage scenario of how the MassOps mechanism behaves at each step to mark
tasks in the task list.
Step 1. User inputs mark 1 2 3
to mark the tasks at the first, second and third index of the task list as completed.
Step 2. Upon receiving the user’s input, LogicManager
calls HarmoniaParser#parseCommand()
to parse the user input.
Step 3. The first word of the user input is mark
, which matches the command for MarkCommand
.
Step 4. MarkCommandParser#parse()
is called and MassOpsParser#massOpsProcessor
is invoked to process the user input into a list of Indexes
.
Step 5. A MarkCommand
is initialised using the list of indexes
and is returned to LogicManager
for execution.
Step 6. After MarkCommand#execute()
is executed, Harmonia looks through lastShownList
to check the validity of marking the tasks at each inputted index.
Step 7. The valid indexes are stored in the ArrayList
, markTaskIndexes
, which is passed into result()
.
Step 8. In result()
, createMarkedTask
is called to mark the tasks at each index stored in markTaskIndexes
respectively.
Step 9. The result is converted into a string markedTasksToString
which is used to initialise a CommandResult
returned and displayed to the user.
The unmark
feature follows a similar implementation involving UnmarkCommandParser
and UnmarkCommand
instead.
The delete
feature follows a similar implementation as well, involving DeleteCommandParser
and DeleteCommand
instead.
Design considerations:
Aspect: How the user input is processed:
-
Alternative 1 (current choice): Create a separate
MassOpsParser
class to parse the user input- Pros:
- Reduces similar looking code
- Easier to introduce mass operations into other features in future
- Abstracts the processing of user input from the respective commands, reducing chances of introducing errors into the different commands.
- Cons: -
- Pros:
-
Alternative 2: Process the user input using the respective command parser functions themselves
- Pros:
- More control over the way the indexes are processed (can order the indexes in a specific format for execution according to the command)
- Cons:
- Repetitive code
- Makes it more difficult and time-consuming to apply mass operations to other features in future
- Pros:
Priority
What is the feature about
Provides a way to attach priorities to Tasks
. The current implementation allows it to be set to 3 values: low
, medium
and high
.
How the feature is implemented
The feature was implemented using an enum
class. To maintain consistency with other properties among the Task
, the class was also implemented with isValidPriority()
that functions as a validity checker and valueOfLabel()
to retrieve the enum constant corresponding to a given String
input.
Why it is implemented that way
The design of the priority feature was built using an enum as a Priority
is only designed to support one out of a small set of possible values – in this case, low
, medium
and high
. Appropriately, the enum
type enables us to define a finite set of values, providing a higher level of type-safety as compared to literal values such as String
or Integer
.
In this case, the enum
type also increases the extensibility of the feature. If we wanted to add more fields to the priority (i.e. lower
and higher
), all we have to do is to add those fields as enum
constants.
Design considerations:
Aspect: How the priority is saved:
-
Alternative 1 (current choice): Use an enum.
- Pros:
- Allows for code that is clearer and more readable
- More extensible
- Compile-time type safety
- Reduction in memory-use. When we create a new object, we only refer to the static
enum
constant, instead of creating a newPriority
object.
- Cons: -
- Pros:
-
Alternative 2: Use
int
orString
itself.- Pros:
- Easier to write (in the beginning)
- Cons:
- Lacks compile-time type safety
- Using an integer to represent a priority level could be less descriptive as to what it exactly represents
- More tedious to extend. To implement this, we might have to use conditionals to check if the
String
orint
input corresponds with the accepted values in ourPriority
design. This can pose a problem when we try to extend the number of properties aPriority
field can take. In this case, we might have to increase the number of conditionals, which could reduce readability and make the code more prone to errors. - Possibly increases memory use. If we use
String
orint
types, we might have to instantiate newPriority
classes every time we create a newTask
object.
- Pros:
Mark/unmark
What is the feature about
Provides a way to mark Task
objects as either completed or uncompleted.
How the feature is implemented
The first stage of the implementation mark
feature involves parsing the user input. MarkCommandParser
is used to parse and check whether the user input is valid. After which a MarkCommand
object is created with the respective task index. The second stage requires MarkCommand#execute()
to be called. The execution would update TaskList
by replacing task to be marked by the copy of it with the CompletionStatus
set to true
.
The unmark
feature follows a similar implementation involving UnmarkCommandParser
, UnmarkCommand
.
The following activity diagram summarizes what happens when a user inputs mark
command:
Why it is implemented that way
It is designed to preserve the Command Design Pattern. Through the implementation of the MarkCommmandParser
and UnmarkCommandParser
, we can enforce the input format of mark command. Furthermore, isolating MarkCommand
and UnmarkCommand
into separate classes, we narrow down functionality of each class. This gives the application more control by limiting the outcome in successful execution. For example, successful execution of MarkCommand will only lead to the task being marked as complete.Whereas an alternative design combining mark and unmark functionality together will lead vague outcome (application unaware whether the task is marked as complete or incomplete after execution).
Design considerations:
Aspect: How the functionality of mark/unmark is broken down:
-
Alternative 1 (current choice): Use two separate Command classes:
MarkCommand
andUnmarkCommand
.- Pros:
- More control over the final outcome of the Command execution (Knowledge whether task is completed or uncompleted after execution)
- Ability to check whether a task is either
MarkCommand
orUnmarkCommand
during runtime - Ability to extend either mark or unmark functionality isolated from each other
- Cons:
- Makes the code more bloated with similar looking code (for each class)
- Pros:
-
Alternative 2: Use a single
Command
to toggleTask
as either complete or incomplete.- Pros:
- Less redundant code
- Easier to extend if both mark and unmark are required to change synchronously
- Cons:
- The final state of task’s
CompletionStatus
after executing toggle command cannot be precisely known. (i.e. We only know the final outcome of the task’sCompletionStatus
during runtime. Whereas, having with two different commands,MarkCommand
andUnmarkCommand
we know for certain the final state of theCompletionStatus
can be predicted.)
- The final state of task’s
- Pros:
Sorting
What is the feature about
Sorts the tasks presented by specified attribute and order.
How the feature is implemented
The sort feature uses the sort
command, prefix by/
before the specified SORT_KEY
and prefix in/
before the specified SORT_ORDER
.
Given below is an example usage scenario of how the sort mechanism behaves at each step to sort the task:
Step 1. User inputs sort by/deadline in/asc
to sort the task list by the Deadline
in the ascending order.
Step 2. Upon receiving the user’s input, LogicManager
calls HarmoniaParser#parseCommand()
to parse the user input.
Step 3. The first word of the user input is sort
, which matches the command for SortCommand
. This initialises SortCommandParser
.
Step 4. SortCommandParser#parse()
is called. SORT_KEY
with prefix by/
and SORT_ORDER
with prefix in/
are extracted out as a SortKey
and SortOrder
objects.
Step 5. A SortCommand
is initialised using the SortKey
and SortOrder
. The SortCommand
constructor creates a Comparator
used for sorting the list. SortCommand
is returned to LogicManager
for execution.
Step 6. After SortCommand#execute()
is called, model#updateSortedTaskList(Comparator)
is invoked to sort the task list using the created Comparator
. The command result is returned and displayed to the user.
The following activity diagram summarizes what happens when a user inputs sort
command:
Why it is implemented that way
The underlying UniqueTaskList
uses an ObservableList
and therefore it allows the application listen to changes and render according specified requirements. One such abstraction used in the initial implementation is the FilteredList
. With the FilteredList
the application grants the ability to filter the list using a Predicate
. Sorting was designed to be an extension of this concept using SortedList
abstraction where the application can sort the list by a given Comparator
and render its output instantaneously.
Design considerations:
Aspect: How the Comparator
is created:
-
Alternative 1 (current choice): Use Factory design pattern.
- Pros:
- Easier to extend
- Abstracts out the complexity of creating a
Comparator
- Promotes reuse of code as there is no need to repeat necessary conditions needed to create a
Comparator
- Easier to conduct Unit-testing as we only need to test the Factory to check whether the
Comparator
returned meets the correct type and the conditions. - Cons:
- Makes the code verbose
- Pros:
-
Alternative 2: Create
Comparator
at the point of parsing.- Pros:
- Less code needed to implement
- Cons:
- Leads to poor code quality as
Comparator
creation becomes more complex. (For example: Poor quality may arise due to violations of KISS principle)
- Leads to poor code quality as
- Pros:
Aspect: How the sorted list is stored/rendered:
-
Alternative 1 (current choice): Use
javafx.collections.transformation.SortedList
.- Pros:
- Abstracts out the details of rendering
- Abstracts out the details behind sorting (i.e. only need to pass in the
Comparator
)
- Cons:
- Difficult to preserve the ordering if tasks are added or edited. Since
TaskList
is implemented usingObservableList
, any changes to it would cause them to be propagated to theSortedList
(which wraps theObservableList
). Therefore, adding/editing aTask
would cause the contents to be automatically sorted. Hence, this might lead to tasks being reordered.
- Difficult to preserve the ordering if tasks are added or edited. Since
- Pros:
-
Alternative 2: Sorts the tasks using a stream and repopulate the
TaskList
.- Pros:
- Straightforward implementation
- Cons:
- Mutability may introduce latent bugs (For examples: Storage may be updated with
incorrect
tasks that may have been introduced when mutating theTaskList
since saving to storage is dependent onTaskList
) - Redundant computation to delete and reinsert the same tasks after each operation
- Mutability may introduce latent bugs (For examples: Storage may be updated with
- Pros:
Search by various attributes
What is the feature about
This feature allows the user to search for tasks that meet the different criteria as specified in the input.
Currently, Harmonia allows user to search for tasks by
-
The deadline of a task, by specifying a range of date that deadline should fall into
-
The name of a task, by specifying keywords that should be contained by the name
-
The description of a task, by specifying keywords that should be contained by the description
-
The tag of a task
-
The priority of a task
-
The completion status of a task
Additionally, the fields above could be combined in a single query using find
, to simplify the process of querying by multiple criteria.
How the feature is implemented
Overall, the workflow of finding a task can be generalized into 3 steps:
-
The
FindCommand
class accepts the parsed inputs as various predicates from theFindCommandParser
.-
The
FindCommandParser
makes use of various utility methods inParserUtil
, to parse the user input into several tokens that correspond to a type of input, e.g.Keyword
,CompletionStatus
, etc. -
After parsing the user input into several tokens, the predicates are constructed to represent the criteria of filtering.
-
User inputs are seperated into several subgroups, since some fields allow multiple inputs. There are 6 subgroups of fields: tag(s), description keyword(s), name keyword(s), priority(s), date range, completion status.
-
Within each subgroup, if a task has a match, e.g. the name of the task contains only one of the name keywords specified, it is considered as a match, and the name predicate evaluates to true for this task.
-
Since all the fields in
find
are optional, all the predicates are able to handle null input. In the case of null input, the predicates always evaluate to true.
-
-
Then, the
FindCommand
chains the predicates up using AND, i.e. all the predicates must be evaluated to true, for a task to be displayed in the result. -
Finally, the chained predicate is used to filter the tasks, and the tasks with the criteria met are displayed on the screen.
Given below is an example usage scenario of how the find mechanism behaves at each step to search for tasks:
Step 1. User inputs find t/CS2103T t/CS2105 p/low
to find tasks that have a ‘CS2103T’ or ‘CS2105’ tag, and with a low priority.
Step 2. Upon receiving the user’s input, LogicManager
calls HarmoniaParser#parseCommand()
to parse the user input.
Step 3. The first word of the user input is find
, which matches the command for FindCommand
. This initializes FindCommandParser
.
Step 4. FindCommandParser#parse()![img.png](img.png)
is called.
Step 5. For the fields that could only appear once, FindCommandParser
checks whether they are present. If they are present, the value is extracted out and stored. Otherwise, null is assigned to them.
Step 6. For the fields that accept multiple occurrences, they are stored as a set. In this case, Tags with prefix t/
are extracted out as a list of keywords, and the priority low
with prefix p/
is also extracted out and stored as a list, as multiple priorities are allowed in general.
Step 7. For each subgroup, the corresponding predicate is initialized, and used to initialize a FindCommand
.
Step 8. FindCommand
then proceeds to chain the predicates up using AND.
Step 9. After FindCommand#execute()
is called using the predicate created at step 8, model#updateFilteredTaskList()
is invoked to filter the task list using the given predicate. The command result is returned and displayed to the user.
The following is the sequence diagram summarizing the above steps:
Note: the creation process of some predicates is omitted, to simplify the diagram. In the diagram, only the creation process of TagContainsKeywordPredicate
and PriorityMatchedPredicate
are included.
Design considerations:
Aspect: Command to be used for searching various fields:
-
Alternative 1 (current choice): Use single
find
that supports multiple fields.- Pros:
- Does not increase the size of the command set.
- More intuitive, as the user does not have to remember other similar commands.
- Allows combination of search fields, to make it more powerful.
- Cons:
- The parsing of an
AddCommand
becomes more complicated.
- The parsing of an
- Pros:
-
Alternative 2: Create a new command for each field itself.
- Pros:
- Simpler implementation, less complex logic.
- Cons:
- Increase the size of command sets.
- Could cause confusion with other similar commands, which compromises user experience.
- Does not allow searching by multiple fields.
- Pros:
Aspect: How keywords are matched:
-
Alternative 1 (current choice): Ignore case and require a full word match, with words separated by punctuations or spaces.
- Pros:
- Allows more accurate search.
- A word that is preceded by punctuations, or with trailing punctuations, e.g.
emails,
, will not be affected by the punctuation in search.
- Cons:
- May not find any match if users only enter a part of the word (e.g.
cs2103
will not match withcs2103t
).
- May not find any match if users only enter a part of the word (e.g.
- Pros:
-
Alternative 2: Ignore case and allow partial match.
- Pros:
- Gives a larger result list, when the user enters a very short keyword like
a
.
- Gives a larger result list, when the user enters a very short keyword like
- Cons:
- The result list might not be specific enough, and too large.
- May give additional tasks that the user is not searching for (e.g. user searches for tasks with name
ma
but result list shows unexpected tasks likeRead e**ma**ils
andRead infor**ma**tion
).
- Pros:
List Tags
What is the feature about
Provides a way to view a list of all tags currently in use in the task list. This feature complements the other features where users can view the tags that are in use before executing the other commands such as add
, edit
and find
.
How the feature is implemented
This feature enhances the list
command, with an additional parameter t/
. There is a TagList
that stores all tags currently in use in the task list. This TagList
is initialised upon start-up of Harmonia and is updated accordingly after every add
, edit
and clear
command. When the user inputs the list t/
command, the TagList
is retrieved and displayed to the user.
Given below is an example usage scenario of how the list tags feature behaves at each step:
Step 1. User inputs list t/
to view all tags that are used in the task list.
Step 2. Upon receiving the user input, LogicManager
calls HarmoniaParser#parseCommand()
to parse the user input.
Step 3. As the first word of the user input is list
, ListCommandParser
is initialised.
Step 4. ListCommandParser#parse()
is called. Since t/
is specified in the user input, a ListCommand
is initialised, with isListTag
set to true
. isListTag
is a boolean which specifies whether the user’s input is a command to list tags (isListTag = true
) or to list tasks (isListTag = false
). The ListCommand
is then returned to LogicManager
for execution.
Step 5. ListCommand#execute()
is called. Since isListTag
is set to true
, model#getTagList()
is invoked to get the list of tags currently in use in the task list. This list of tags is used to initialise a CommandResult
, which is returned and displayed to the user.
The following is the sequence diagram summarising the above steps:
Design considerations:
Aspect: How the tag list is initialised:
-
Alternative 1 (current choice): Initialise the tag list upon start-up of Harmonia based on the stored task list.
- Pros:
- Easier to implement.
- Ensures that the tag list will be consistent with the data in task list.
- Cons:
- Upon each start-up of Harmonia, all tasks in the task list have to be iterated through to retrieve their tags so as to initialise the tag list. This may increase the start-up duration of the application.
- Pros:
-
Alternative 2: Store the data of the tag list as a separate JSON file and load the data upon start-up of Harmonia.
- Pros:
- Reduces the application start-up duration as the data of the tag list can directly be loaded from the JSON file.
- Cons:
- May result in inconsistencies between tags stored in the tag list and tags used in the task list. For example, if the user were to manually make changes to one of the JSON files without making the corresponding changes to the other JSON file. As such, additional checks may need to be put in place to ensure that the data in the tag list is consistent with that of the task list.
- Pros:
[Proposed] Undo/redo feature
Proposed Implementation
The proposed undo/redo mechanism is facilitated by VersionedTaskList
. It extends TaskList
with an undo/redo history, stored internally as a taskListStateList
and currentStatePointer
. Additionally, it implements the following operations:
-
VersionedTaskList#commit()
— Saves the current task list state in its history. -
VersionedTaskList#undo()
— Restores the previous task list state from its history. -
VersionedTaskList#redo()
— Restores a previously undone task list state from its history.
These operations are exposed in the Model
interface as Model#commitTaskList()
, Model#undoTaskList()
and Model#redoTaskList()
respectively.
Given below is an example usage scenario and how the undo/redo mechanism behaves at each step:
Step 1. The user launches the application for the first time. The VersionedTaskList
will be initialized with the initial task list state, and the currentStatePointer
pointing to that single task list state.
Step 2. The user executes delete 5
command to delete the 5th task in the task list. The delete
command calls Model#commitTaskList()
, causing the modified state of the task list after the delete 5
command executes to be saved in the taskListStateList
, and the currentStatePointer
is shifted to the newly inserted task list state.
Step 3. The user executes add n/tutorial …
to add a new task. The add
command also calls Model#commitTaskList()
, causing another modified task list state to be saved into the taskListStateList
.
Model#commitTaskList()
, so the task list state will not be saved into the taskListStateList
.
Step 4. The user now decides that adding the task was a mistake, and decides to undo that action by executing the undo
command. The undo
command will call Model#undoTaskList()
, which will shift the currentStatePointer
once to the left, pointing it to the previous task list state, and restores the task list to that state.
currentStatePointer
is at index 0, pointing to the initial TaskList state, then there are no previous TaskList states to restore. The undo
command uses Model#canUndoTaskList()
to check if this is the case. If so, it will return an error to the user rather
than attempting to perform the undo.
The following sequence diagram shows how the undo operation works:
UndoCommand
should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
The redo
command does the opposite — it calls Model#redoTaskList()
, which shifts the currentStatePointer
once to the right, pointing to the previously undone state, and restores the task list to that state.
currentStatePointer
is at index taskListStateList.size() - 1
, pointing to the latest task list state, then there are no undone TaskList states to restore. The redo
command uses Model#canRedoTaskList()
to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo.
Step 5. The user then decides to execute the command list
. Commands that do not modify the task list, such as list
, will usually not call Model#commitTaskList()
, Model#undoTaskList()
or Model#redoTaskList()
. Thus, the taskListStateList
remains unchanged.
Step 6. The user executes clear
, which calls Model#commitTaskList()
. Since the currentStatePointer
is not pointing at the end of the taskListStateList
, all task list states after the currentStatePointer
will be purged. Reason: It no longer makes sense to redo the add n/tutorial …
command. This is the behavior that most modern desktop applications follow.
The following activity diagram summarizes what happens when a user executes a new command:
Design considerations:
Aspect: How undo & redo executes:
-
Alternative 1 (current choice): Saves the entire task list.
- Pros: Easy to implement.
- Cons: May have performance issues in terms of memory usage.
-
Alternative 2: Individual command knows how to undo/redo by
itself.
- Pros: Will use less memory (e.g. for
delete
, just save the task being deleted). - Cons: We must ensure that the implementation of each individual command are correct.
- Pros: Will use less memory (e.g. for
Documentation, logging, testing, configuration, dev-ops
Appendix: Requirements
Product scope
Target user profile:
- students who have a need to manage a significant number of tasks of different types
- prefer desktop apps over other types
- can type fast
- prefers typing to mouse interactions
- is reasonably comfortable using CLI apps
Value proposition: Harmonia is the easiest way for students to manage the complexity associated with their schooling years – from the plethora of assignments, ad-hoc consultations and events, to deadlines. This app will only help to manage tasks, and does not act as a calendar notifying the user of any upcoming event/deadline.
User stories
Priorities: High (must have) - ****
, Medium (nice to have) - ***
, Medium-low (nice to have but difficult) - **
, Low (unlikely to have) - *
Priority | As a … | I want to … | So that I can… |
---|---|---|---|
**** |
user | add a new task | |
**** |
user | delete a task | remove tasks that I no longer need |
**** |
user | mark a task as complete | |
**** |
user | mark a task as incomplete | |
**** |
user | see all my tasks when I start up the application | view my tasks more conveniently without having to perform any extra operations |
**** |
user | tag a task | categorise my tasks according to my preferred system |
**** |
user | modify a task | amend a mistake or update a task |
**** |
user | search by tags | find tasks related to a category |
**** |
user | search by keywords | find a specific task more easily |
**** |
new user | have straightforward commands I can use | use the application more intuitively |
**** |
expert user | modify the tasks in the data file directly | modify the tasks in a way that is not supported by the application |
*** |
user | tag a task with multiple tags | categorise tasks under multiple categories at a time |
*** |
user | search for tasks that fall within a specific time range | find the tasks that lie between a specific period |
*** |
user | view upcoming deadlines | prioritise what tasks I need to do first |
*** |
user | view tags I have already added when creating a new task | know how to tag new tasks and avoid creating similar tags |
*** |
user | view all of my upcoming–deadlines and longer-term deadlines | have a more holistic view of all events in the short-term and long-term future |
*** |
user | access the user guide through the interface | access the documentation without having to search online for it |
*** |
user | sort my tasks by certain filters | organise my view the way I prefer |
*** |
user | label my tasks with priorities | keep track of which tasks are more important |
*** |
new user | view suggestions if I type in the wrong command | recover from mistakes and use the correct command more easily |
*** |
potential user | see the app populated with sample data | easily see how the application would look like when in use |
** |
user | set repeated occurrence of a task | avoid having to add a task multiple times |
** |
new user | revert changes made | undo changes that were made by mistake |
** |
expert user | use shortcuts | perform operations more efficiently |
* |
user | bulk mark different tasks as complete | marking everything I finished a day with just one command |
* |
expert user | delete multiple tasks at once | do not have to delete tasks one by one |
Use cases
(For all use cases below, the System is Harmonia
and the Actor is the user
, unless specified otherwise)
Use case: UC01 - Add a task
MSS
- User chooses to add a task.
- User enters the task description.
- Harmonia adds the task.
-
Harmonia informs the user that the task has been successfully added.
Use case ends.
Extensions
- 2a. Harmonia detects an error in the entered request.
- 2a1. Harmonia outputs an error message.
- 2a2. User enters a new request.
Steps 2a1-2a2 are repeated until valid request is inputted. Use case resumes from step 3.
Use case: UC02 - Delete a task
MSS
- User chooses to remove a task from the task list.
- User enters the request to remove the task.
- Harmonia deletes the task.
-
Harmonia informs the user that the task has been successfully deleted.
Use case ends.
Extensions
- 2a. Harmonia detects an error in the entered request.
- 2a1. Harmonia outputs an error message.
- 2a2. User enters a new request.
Steps 2a1-2a2 are repeated until valid request is inputted. Use case resumes from step 3.
Use case: UC03 - Mark a task as complete
MSS
- User chooses to mark a task as complete from the task list.
- User enters the request to mark a task based on its index in the task list.
- Harmonia marks the task as complete.
-
Harmonia informs the user that the task has been successfully marked.
Use case ends.
Extensions
- 2a. Harmonia detects an error in the entered request.
- 2a1. Harmonia outputs an error message.
- 2a2. User enters a new request.
Steps 2a1-2a2 are repeated until valid request is inputted. Use case resumes from step 3.
Use case: UC04 - Unmark a task from completion
MSS
- User chooses to unmark a task from completion from the task list.
- User enters the request to unmark a task based on its index in the task list.
- Harmonia marks the task as incomplete.
-
Harmonia informs the user that the task has been successfully unmarked.
Use case ends.
Extensions
- 2a. Harmonia detects an error in the entered request.
- 2a1. Harmonia outputs an error message.
- 2a2. User enters a new request.
Steps 2a1-2a2 are repeated until valid request is inputted. Use case resumes from step 3.
Use case: UC05 - Add a tag to the task
MSS
- User chooses to add a tag to an existing task.
- User enters the request to add the tag to the task.
- Harmonia adds the tag to the task.
-
Harmonia informs the user that the tag has been successfully added to the task.
Use case ends.
Extensions
- 2a. Harmonia detects an error in the entered request.
- 2a1. Harmonia outputs an error message.
- 2a2. User enters a new request.
Steps 2a1-2a2 are repeated until valid request is inputted. Use case resumes from step 3.
Use case: UC06 - Edit a task
MSS
- User chooses to edit a task from the task list.
- User enters the request to edit the task with the updated details.
- Harmonia updates the task.
-
Harmonia informs the user that the task has been successfully updated.
Use case ends.
Extensions
- 2a. Harmonia detects an error in the entered request.
- 2a1. Harmonia outputs an error message.
- 2a2. User enters a new request.
Steps 2a1-2a2 are repeated until valid request is inputted. Use case resumes from step 3.
Use case: UC07 - List all tasks
MSS
- User chooses to list out all tasks.
- User enters the request to list all tasks.
-
Harmonia lists out all tasks.
Use case ends.
Extensions
- 2a. Harmonia detects an error in the entered request.
- 2a1. Harmonia outputs an error message.
- 2a2. User enters a new request.
Steps 2a1-2a2 are repeated until valid request is inputted. Use case resumes from step 3.
Use case: UC08 - Search for tasks by keyword
MSS
- User chooses to search for tasks which contain a specific keyword.
- User enters the request to search for tasks by the specified keyword.
- Harmonia shows a list of tasks that match the specified keyword.
-
Harmonia informs the user that the search result has been displayed successfully.
Use case ends.
Extensions
- 2a. Harmonia detects an error in the entered request.
- 2a1. Harmonia outputs an error message.
- 2a2. User enters a new request.
Steps 2a1-2a2 are repeated until valid request is inputted. Use case resumes from step 3.
- 2b. Harmonia detects that there are no tasks which contain the specified keyword.
- 2b1. Harmonia shows an empty list.
Use case resumes from step 4.
- 2b1. Harmonia shows an empty list.
Use case: UC09 - Undo the previous change
Preconditions: Changes were made by the user.
MSS
- User chooses to undo the previous change.
- User enters the request to undo the previous change.
-
Harmonia restores the state before the previous change.
Use case ends.
Extensions
- 2a. Harmonia is unable to recognize the request entered by the user.
- 2a1. Harmonia outputs an error message.
- 2a2. User enters a new request.
Steps 2a1-2a2 are repeated until valid request is inputted. Use case resumes from step 3.
Use case: UC10 - Search for tasks within date range
MSS
- User chooses to search for tasks that fall within a specific date range.
- User enters the request to search for the tasks by date range.
- Harmonia displays the tasks that match the criteria.
-
Harmonia informs the user that the result has been displayed successfully.
Use case ends.
Extensions
- 2a. Harmonia detects an error in the entered request.
- 2a1. Harmonia outputs an error message.
- 2a2. User enters a new request.
Steps 2a1-2a2 are repeated until valid request is inputted. Use case resumes from step 3.
Use case: UC11 - Sort tasks
MSS
- User chooses to sort the task by a certain property.
- User enters the request to sort the task based on a property.
- Harmonia displays the tasks based on the property.
-
Harmonia informs the user that the sort has been performed successfully.
Use case ends.
Extensions
- 2a. Harmonia detects an error in the entered request.
- 2a1. Harmonia outputs an error message.
- 2a2. User enters a new request.
Steps 2a1-2a2 are repeated until valid request is inputted. Use case resumes from step 3.
Use case: UC12 - Set repeated occurrences of a task
MSS
- User chooses to set repeated occurrences of a task.
- User enters the request to set repeated occurrences of a task.
- Harmonia sets the task as a repeated occurrence.
-
Harmonia informs the user that the task has been added as a repeated occurrence.
Use case ends.
Extensions
- 2a. Harmonia detects an error in the entered request.
- 2a1. Harmonia outputs an error message.
- 2a2. User enters a new request.
Steps 2a1-2a2 are repeated until valid request is inputted. Use case resumes from step 3.
Use case: UC13 - Categorise task according to priority
MSS
- User chooses to assign a priority to a task.
- User enters the request to assign priority to the task.
- Harmonia assigns priority to the task.
-
Harmonia informs the user that the priority of the task has been updated.
Use case ends.
Extensions
- 2a. Harmonia detects an error in the entered request.
- 2a1. Harmonia outputs an error message.
- 2a2. User enters a new request.
Steps 2a1-2a2 are repeated until valid request is inputted. Use case resumes from step 3.
Use case: UC14 - View user guide
MSS
- User chooses to view the user guide.
- User requests to view the user guide.
- Harmonia outputs a message with a URL to the user guide.
-
User copies the URL to the user guide.
Use case ends.
Extensions
- 2a. Harmonia detects an error in the entered request.
- 2a1. Harmonia outputs an error message.
- 2a2. User enters a new request.
Steps 2a1-2a2 are repeated until valid request is inputted. Use case resumes from step 3.
Use case: UC15 - Add multiple tags to a task
MSS
- User chooses to add multiple tags to an existing task.
- User requests to add multiple tags to a task.
- Harmonia adds the different tags to the task.
-
Harmonia informs the user that the tags have been successfully added to the task.
Use case ends.
Extensions
- 2a. Harmonia detects an error in the entered request.
- 2a1. Harmonia outputs an error message.
- 2a2. User enters a new request.
Steps 2a1-2a2 are repeated until valid request is inputted. Use case resumes from step 3.
Non-Functional Requirements
- Should work on most Mainstream OS as long as it has Java
11
or above installed. - Should be able to accomplish most of the tasks faster using commands than using the mouse by users with above average typing speed for regular English text (i.e. not code, not system admin commands)
- Should respond within two seconds.
- Should be usable by a novice who has never used Harmonia before.
- Is not required to remind users about upcoming deadlines or events.
Glossary
- Mainstream OS: Windows, Linux, Unix, OS-X
- Task: A piece of work that the user needs to complete
- Tag: A label or category attached to a task to give additional information to it
- Keyword: A word that the user queries to search for a task
Appendix: Instructions for manual testing
Given below are instructions to test the app manually.
- Initial launch
-
Download harmonia.jar and move it into an empty folder.
-
Run the application.
- For Windows user:Double-click harmonia.jar.
-
For Mac/Linux user: Open the terminal, navigate to the directory where harmonia.jar is located, then run
java -jar harmonia.jar
in the terminal. - Expected: Shows the GUI with a set of sample tasks. The window size may not be optimum.
-
-
Saving window preferences
-
Resize the window to an optimum size. Move the window to a different location. Close the window.
-
Re-launch the app by double-clicking the jar file.
Expected: The most recent window size and location is retained.
-
Adding a task into Harmonia
-
Test case:
add n/Review PRs d/To review PRs assigned to me dl/2022-04-10 p/high t/CS2103T t/review
Expected: The new task is added to the task list. Details of the newly added task is also shown in the status message. -
Test case:
add n/Review PRs d/To review PRs assigned to me dl/2022-04-10 p/high t/CS2103T t/review
(adding a task that already exists in Harmonia)
Expected: No task is added. Error details shown in the status message. -
Test case:
add n/CS2103T PE dl/2022-04-16 p/high t/CS2103T
(with one of the compulsory fields:n/
,d/
,dl/
,p/
removed)
Expected: No task is added. Error details shown in the status message. -
Other incorrect add commands to try:
add
-
add n/CS2103T d/This coming sat PE dl/2022-04-16 p/low t/#CS2103T
(t/
only takes alphanumeric characters) -
add n/CS2103T d/This coming sat PE dl/2022-04-16 p/none t/CS2103T
(invalid priority, which only acceptslow
,medium
orhigh
) -
add n/CS2103T d/This coming sat PE dl/2022 p/high t/CS2103T
(invalid date format for thedl
field) Expected: No task is added. Error details are shown in the status message.
Editing an existing task in Harmonia
- Editing a task while all tasks are being shown
-
Prerequisites: List all tasks using the
list
command. Multiple tasks are shown in the list. -
Test case:
edit 2 d/Practice with group dl/2022-04-01
Expected: The description of the second task in the task list is edited. Details of the newly edited task is also shown in the status message. -
Test case:
edit 2 d/Practi/ce
(including/
inn/
ord/
)
Expected: No task is edited. Error details shown in the status message. -
Test case:
edit 2 dl/2022-13-13
(invalid date)
Expected: No task is edited. Error details shown in the status message. -
Test case:
edit 0 n/Testing
(invalid index)
Expected: No task is edited. Error details shown in the status message. -
Other incorrect add commands to try:
edit
-
edit x t/tag
(where x is larger than the list size)
-
edit 2 t/#CS2103T
(t/
only takes alphanumeric characters) -
edit 2 p/none
(invalid priority, which only acceptslow
,medium
orhigh
) Expected: No task is edited. Error details are shown in the status message.
-
- Editing a task while filtered tasks are being shown
-
Prerequisites: List all tasks using the
list
command. Multiple tasks are shown in the list. -
Test case:
edit 2 d/Practice with group
Expected: The description of the second task in the filtered task list is edited. Details of the newly edited task is also shown in the status message. -
Test case:
edit 2 d/Practi/ce
(including/
inn/
ord/
)
Expected: No task is edited. Error details shown in the status message. -
Test case:
edit 2 dl/2022-13-13
(invalid date)
Expected: No task is edited. Error details shown in the status message. -
Test case:
edit 0 n/Testing
(invalid index)
Expected: No task is edited. Error details shown in the status message. -
Other incorrect add commands to try:
edit
-
edit x t/tag
(where x is larger than the filtered list size)
-
edit 2 t/#CS2103T
(t/
only takes alphanumeric characters) -
edit 2 p/none
(invalid priority, which only acceptslow
,medium
orhigh
) Expected: No task is edited. Error details are shown in the status message.
-
Listing all tasks in Harmonia
- Test case:
list
Expected: All tasks in Harmonia are listed. Multiple tasks are shown in the list. Success message displayed.
Listing all tags in Harmonia
- Test case:
list t/
Expected: All tags are listed in the results display. - Test case:
list t/test
Expected: No tags are listed. Error details are shown in the status message.
Marking/Unmarking a task
-
Marking a task while all tasks are being shown
-
Prerequisites: List all tasks using the
list
command. Multiple tasks are shown in the list. -
Test case:
mark 1
Expected: Completion status of the first task in the list is changed toCompleted
. Details of the completed task are shown in the status message. -
Test case:
mark 0
Expected: No task is marked. Error details are shown in the status message. -
Other incorrect mark commands to try:
mark
,mark x
(where x is larger than the list size)
Expected: No task is marked. Error details are shown in the status message.
-
-
Marking a task while filtered tasks are shown
-
Prerequisites: Filter the incomplete tasks using the
find c/false
command. Multiple tasks are shown in the list. -
Test case:
mark 1
Expected: Completion status of the first task in the filtered list is changed toCompleted
. Details of the marked task are shown in the status message. -
Test case:
mark 0
Expected: No task is marked. Error details are shown in the status message. -
Other incorrect mark commands to try:
mark
,mark x
(where x is larger than the filtered list size)
Expected: No task is marked. Error details are shown in the status message.
-
-
Mark multiple tasks while all tasks are being shown
-
Prerequisites: List all tasks using the
list
command. Multiple tasks are shown in the list. -
Test case:
mark 2 1 3
Expected: Completion status of the first, second and third tasks in the list are changed toCompleted
. Details of the marked tasks are shown in the status message. -
Test case:
mark 0 2 3
Expected: No task is marked. Error details are shown in the status message. -
Other incorrect mark commands to try:
mark
,mark x y z
(where x, y, and/or z is larger than the list size)
Expected: No task is marked. Error details are shown in the status message.
-
-
Marking multiple tasks while filtered tasks are shown
-
Prerequisites: Filter the incomplete tasks using the
find c/false
command. Multiple tasks are shown in the list. -
Test case:
mark 1 2
Expected: Completion status of the first and second tasks in the list are changed toCompleted
. Details of the marked tasks are shown in the status message. -
Test case:
mark 0 1
Expected: No task is marked. Error details are shown in the status message. -
Other incorrect mark commands to try:
mark
,mark x y z
,...
(where x, y, and/or z is larger than the filtered list size)
Expected: No task is marked. Error details are shown in the status message.
-
unmark
command is similar to the mark
command, you can refer to the steps here to test for the unmarking
of tasks by just changing mark
to unmark
. When filtering the tasks, remember to use find c/true
instead to see a list of all the completed tasks that you can unmark.Deleting a task
-
Deleting a task while all tasks are being shown
-
Prerequisites: List all tasks using the
list
command. Multiple tasks are shown in the list. -
Test case:
delete 1
Expected: First task is deleted from the list. Details of the deleted task are shown in the status message. -
Test case:
delete 0
Expected: No task is deleted. Error details are shown in the status message. -
Other incorrect delete commands to try:
delete
,delete x
,...
(where x is larger than the list size)
Expected: No task is deleted. Error details are shown in the status message.
-
-
Deleting a task while filtered tasks are shown
-
Prerequisites: Filter the not-yet-completed tasks using the
find c/false
command. Multiple tasks are shown in the list. -
Test case:
delete 1
Expected: The first task shown in the filtered list is deleted. Details of the deleted task are shown in the status message. The rest of the filtered tasks remain in the list. -
Test case:
delete 0
Expected: No task is deleted. Error details are shown in the status message. The filtered tasks remain in the list. -
Other incorrect delete commands to try:
delete
,delete x
,...
(where x is larger than the filtered list size)
Expected: No task is deleted. Error details are shown in the status message. The filtered tasks remain in the list.
-
-
Delete multiple tasks while all tasks are being shown
-
Prerequisites: List all tasks using the
list
command. Multiple tasks are shown in the list. -
Test case:
delete 2 1 3
Expected: The first, second, and third tasks are deleted from the list. Details of the deleted tasks are shown in the status message. -
Test case:
delete 0 2 3
Expected: No task is deleted. Error details are shown in the status message. -
Other incorrect delete commands to try:
delete
,delete x y z
,...
(where x, y, and/or z is larger than the list size)
Expected: No task is deleted. Error details are shown in the status message.
-
-
Deleting multiple tasks while filtered tasks are shown
-
Prerequisites: Filter the not-yet-completed tasks using the
find c/false
command. Multiple tasks are shown in the list. -
Test case:
delete 1 2
Expected: The first and second tasks shown in the filtered list are deleted. Details of the deleted tasks are shown in the status message. The rest of the filtered tasks remain in the list. -
Test case:
delete 0 1
Expected: No task is deleted. Error details are shown in the status message. The filtered tasks remain in the list. -
Other incorrect delete commands to try:
delete
,delete x y z
,...
(where x, y, and/or z is larger than the filtered list size)
Expected: No task is deleted. Error details are shown in the status message. The filtered tasks remain in the list.
-
Help
- Opening the help window for Harmonia’s user guide.
- Test case:
help
Expected: The help window pops up with the URL to Harmonia’s User Guide. Status message explains that command was a success.
- Test case:
Saving data
-
Dealing with missing data files
- Expected: Default tasks are loaded instead. Upon an operation that attempts to interact with the tasks, e.g. add/delete/edit/mark/unmark tasks, the data will then be saved as data/harmonia.json.
-
Dealing with corrupted data files
- Expected: An empty list of tasks is loaded instead.
- Upon an operation that attempts to interact with the tasks, e.g. add/delete/edit/mark/unmark tasks, the corrupted data will then be overwritten.
- If Harmonia is closed before any attempt to interact the tasks, the data file will not be overwritten.
- Expected: An empty list of tasks is loaded instead.
-
Dealing with data files with incorrect format
- Example: The deadline for a task is inputted as
2022-09-32
. - Expected: An empty list of tasks is loaded instead.
- Upon an operation that attempts to interact with the tasks, e.g. add/delete/edit/mark/unmark tasks, the data file will then be overwritten.
- If Harmonia is closed before any attempt to interact the tasks, the data file will not be overwritten.
- Example: The deadline for a task is inputted as