Developer Guide
- Introduction
- Setting up, getting started
- Design
- Implementation
- Documentation, logging, testing, configuration, dev-ops
-
Appendix: Requirements
- Product scope
- User stories
-
Use cases
- Use case: View introduction
- Use case: Add a contact
- Use case: Delete a contact
- Use case: Delete contacts by tag
- Use case: Sort through contacts
- Use case: Edit contact information
- Use case: Search for contacts
- Use case: Add an event
- Use case: Delete an event
- Use case: Edit an event
- Use case: Search for events
- Use case: Sort through events
- Use case: View details of an event
- Use case: List all saved events
- Use case: Add a tag
- Use case: List all tags
- Use case: Add a reminder
- Use case: Delete a reminder
- Use case: List all reminders
- Use case: Find a tag
- Non-Functional Requirements
- Glossary
- Appendix: Instructions for manual testing
- Appendix: Effort
Introduction
This document shows program architecture and implementation decisions for the contact-and-event management app, Athena.
Purpose
The purpose of this document is to provide technical details and explanations for the high-level design of Athena and each subcomponent that Athena uses. The intended audience for this document is software engineers maintaining or expanding on Athena, and software testers.
Setting up, getting started
Refer to the guide Setting up and getting started.
Design
Architecture
The Architecture Diagram given above explains the high-level design of the App. Given below is a quick overview of each component.
.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.
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.
Each of the four components,
- defines its API in an
interface
with the same name as the Component. - exposes its functionality using a concrete
{Component Name}Manager
class (which implements the corresponding APIinterface
mentioned in the previous point.
For example, the Logic
component (see the class diagram given below) defines its API in the Logic.java
interface and exposes its functionality using the LogicManager.java
class which implements the Logic
interface.
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 -c 1
.
The sections below give more details of each component.
UI component
API :
Ui.java
The UI consists of a MainWindow
that is made up of parts e.g.CommandBox
, ResultPanel
, PersonListPanel
, StatusBarFooter
etc. All these, including the MainWindow
, inherit from the abstract UiPart
class.
The UI
component uses 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.
Logic component
API :
Logic.java
-
Logic
uses theParser
class to parse the user command. - This results in a
Command
object which is executed by theLogicManager
. - The command execution can affect the
Model
(e.g. adding a contact). - The result of the command execution is encapsulated as a
CommandResult
object which is passed back to theUi
. - In addition, the
CommandResult
object can also instruct theUi
to perform certain actions, such as displaying help to the user.
Given below is the Sequence Diagram for interactions within the Logic
component for the execute("delete -c 1")
API call.
DeleteContactCommandParser
should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
Model component
API : Model.java
The Model
encapsulates all data required for Athena to run. In particular, it stores all contacts (as person
objects), tag
s and event
s.
It is meant to fulfill the Facade pattern as the Facade class by hiding the individual classes that store the individual entities and forcing higher level components like the Command
s and Logic
to interact only with Model
.
It is made up of four major components:
-
AddressBook
which manages the contacts in the form ofPerson
objects. -
Calendar
which manages the events in the form ofEvent
objects. -
TagTree
which managestag
totag
relations. -
RemindersImpl
which managesreminder
toevent
relations.
Additionally, Model
also has the following characteristics:
- stores a
UserPref
object that represents the user’s preferences. - exposes an unmodifiable
ObservableList<Person>
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. - does not depend on any of the other three high-level components.
Storage component
API : Storage.java
The Storage
component,
- can save
UserPref
objects in json format and read it back. - can save the address book data in json format and read it back.
Common classes
Classes used by multiple components are in the seedu.addressbook.commons
package.
Implementation
This section describes some noteworthy details on how certain features are implemented.
Displaying introduction window
General design
An introduction window is displayed by Athena for users unfamiliar with how the application works. This is done by
checking if either AddressBook.json
or Calendar.json
exist in the designated save-path. If neither are found, Athena
assumes that the user has not opened the application before, and will display the introduction window. The introduction
comprises a tutorial on basic command use that prompts users to make changes to the default save, and thus will create
a new save file.
The sequence diagram below illustrates the interaction between UiManager
, MainWindow
, LogicManager
and
IntroCommand
when the application is launched.
This implementation deviates from the sequence regular commands obey (see below)
by implementing executeIntro()
, whereas regular commands use execute(String)
. This is to prevent users from being
able to display the introduction window through the invocation of a command.
Design choice
As the introduction window should be displayed when the application is opened, any method calls has to be done while the system is aware that the application is in the phase of being set up. As a result, determining whether the introduction window should be shown is done when the Ui is being created and filled in.
An important factor to consider when implementing this command is to prevent users from being to invoke it. This means
that, unlike regular commands, no keyword parsed should call the method, and thus it can be entirely absent from
AddressBookParser
. Since it should not be parseable, regular methods that are used to handle command inputs are
inapplicable, thus necessitating creation of custom methods such as handleIntro()
and executeIntroCommand()
.
Design improvements
As the presence of save files are used to check if the introduction window should be shown, Athena will falsely flag users who have no save file as first time users and show the introduction window nonetheless. A solution for this problem would be to have a file dedicated to storing user preferences. As Athena grows in features and customisability, it is likely that such a file would be necessary in the future; however, it is currently difficult to justify this implementation.
Command implementation
This section details the implementation of Athena’s Command system.
General design
In Athena, actions done by the system are determined by user inputs. There are 3 components that enable this:
-
AddressBookParser
- manages the parsing of user inputs as Strings, -
XYZCommand
- contains the logic used in the execution of the Command, -
XYZCommandParser
- parses any additional inputs that the Command may need; depending on the actual Command, this class may not exist.
AddressBookParser
component:
User input is parsed by AddressBookParser
’s parseCommand(String)
method, which first ensures the input conforms to
the correct input pattern. It then splits the input into 3 parts:
-
commandWord
- describes the command that is to be invoked e.g.add
,list
, etc., -
commandType
- describes the type of functionality that the command targets e.g.-c
for contacts,-e
for events, etc., -
arguments
- describes additional arguments that the command may need; this field may be empty depending on the commmand.
The commandWord
and commandType
fields are both instances of CommandWord
and CommandType
enumerations
respectively. These enumerations each contain a HashMap
that stores Strings of the commands as keys and the actual
enumerations as their values. Based on the enumerations that each input generate, either a ParseException
is thrown
for an invalid input, or an XYZCommand
or XYZCommandParser
object is created, depending on the command.
XYZCommand
component:
All XYZCommand
classes extend from the Command
abstract class, which implements the execute(Model)
method. This
method updates the model with appropriate changes and returns a CommandResult
object with a message to display to be
displayed. The changes are handled by the façade class Model
: changes to data are reflected in PersonListPanel
and
EventListPanel
, whereas the message is displayed on the ResultPanel
.
XYZCommandParser
component:
Certain commands may necessitate a way to parse additional inputs - this is done through the implementation of an
XYZCommandParser
class that implements the Parser
interface, which implements the parse(String)
method. This
method uses ArgumentTokenizer.tokenize(String)
to map each prefix to the corresponding input(s), stored in an
ArgumentMultimap
object. For instance, an input of n/John t/tag1 t/tag2
will generate the following
key-value pairs in ArgumentMultimap
:
n/
-> John
t/
-> tag1
, tag2
Any invalid inputs will throw a ParseException
. If the necessary inputs exist and are valid, then the corresponding
XYZCommand
object will be created.
Design choice
As Athena supports multiple functionalities and sub-functionalities, there are many similar commands that have
overlapping command names. Initially, these command inputs followed closely to AB-3’s - adding a contact was done with
add
. As the event feature was added, the commands were appended with the appropriate type, so adding an event was
done with addEvent
. This led to inconsistency in the command naming structure, proving to be unsustainable should
more methods and functionalities are added.
This command implementation structure thus makes it easier for users to invoke commands (since there is no need to input
an uppercase character) and developers to add more commands while keeping the AddressBookParser
class relatively clean
and understandable.
Design improvements
Ideally, the AddressBookParser
should not know whether an XYZCommand
or an XYZCommandParser
object is created. A
possible improvement would be to implement an intermediary static method in the XYZCommandParser
class that creates
an XYZCommand
or XYZCommandParser
object depending on the type of the command.
Sort contacts feature
The sort contacts feature is facilitated by Addressbook
that stores reminder entries and their details in Athena.
Additionally, it executes the sorting operation based on 3 sorting patterns:
-
sort -c 1
- Sorts the contacts based on the name of the contacts in alphabetical order. -
sort -c 2
- Sorts the contacts based on the address of the contacts in alphabetical order. -
sort -c 3
- Sorts the contacts based on the email of the contacts in alphabetical order.
SortContactCommand#execute() : Does validity check of input and then sorts the contacts according to user input (index))
The sorting function is designed with the aim of temporary modification of the contact-related entries in RemindersImpl
.
How sort contact executes
The following activity diagram summarizes what happens when a user executes sort -e
command:
Contact and tag management
General design
Contact (Person
) component:
In Athena, contacts are represented by Person
objects. Person
objects have several properties such as email, address, etc. A Person
can also be tagged with multiple Tag
s.
-
AddressBook
handles all direct matters concerningPerson
objects. It has aTagManager
andUniquePersonList
. -
UniquePersonList
keeps track of allPerson
objects. It usesPerson
class’isSamePerson(Person)
method to ensure that there are no duplicate contacts in Athena. -
TagManager
keeps track of which contacts contain which tags. It uses a hash map, mapping eachTag
to the set ofPerson
s that contain theTag
.
All manipulation of Person
objects have to be done through AddressBook
. AddressBook
provides methods that can be used by higher-level components to query or edit details regarding contacts.
Tag
component:
Tag
s are represented by a single alphanumeric string with no spaces. There is support for child-tagging. This allows directional relations to be established between Tag
s. Certain commands will group a Tag
together with its sub-tags to perform an action.
-
TagTree
handles tag-to-tag relations. -
TagTreeImpl
extends from the abstract classTagTree
. It uses a tree data structure to store directional tag-to-tag relations. The implementation of the tree is done with a hash map, mapping eachTag
to its set of child-tags. For convenience, the reverse mapping (i.e. map from eachTag
to its set of parent-tags) is also provided.
Any new links established between tags have to go through the TagTree
. TagTree
provides methods to query or edit tag-to-tag relationships.
Integration
component:
The ContactTagIntegrationManager
class provides a few predefined methods for functions that will affect both Person
s and Tag
s at the same time.
This class is meant to address the difficulty in preserving consistency within the system.
For example, two different deleteTag
methods are implemented in both TagTree
and ContactTagIntegrationManager
.
However, the method in TagTree
only deletes the specific Tag
in TagTree
while AddressBook
would still maintain a reference to the same Tag
.
The method in ContactTagIntegrationManager
uses TagTree
’s deleteTag(Tag)
method, then removes the Tag
from all Person
objects that has the Tag
.
The sequence diagram below illustrates the interactions between the ContactTagIntegrationManager
, AddressBook
and TagTree
when execute("delete -t t/cs2103")
.
As such, the ContactTagIntegrationManager
’s job is to preserve consistency in the Model
when a change is made in either AddressBook
or TagTree
that will indirectly affect the other component.
Thus, higher-level modules should use the methods in ContactTagIntegrationManager
if available.
Design choice
In the context of tag management, this design choice is aimed at properly encapsulating two separate functionalities.
-
AddressBook
- usesTagManager
to track whichPerson
objects falls under whichTag
s.-
TagManager
is necessary to avoid cyclic dependency betweenPerson
andTag
. -
TagManager
is embedded inAddressBook
, so whenever there is a change to the list ofPerson
s, the tag-to-person map can be updated immediately.
-
-
TagTree
tracks the tag-to-tag relationships only. In particular, it keeps track of which set ofTag
s are child-tags of whichTag
.
Using these two mutable constructs, it allows for accurate realtime queries by higher-level components even though the internal mapping changes frequently between commands.
To ensure that the right commands are called at the right time, Model
only implements a limited set of methods that can change the internal mapping.
To support a greater variety of Command
s, ensure that the correct methods from either AddressBook
, TagTree
or ContactTagIntegrationManager
are chosen.
A rule of thumb is to search for the method in ContactTagIntegrationManager
first before looking for a similar method in the other two classes.
To implement a new functionality that is not currently supported in any of the three classes, consider whether the method should affect both AddressBook
and TagTree
.
If it has the potential to affect both, the method should be implemented in ContactTagIntegrationManager
. Otherwise, implement them in the relevant class.
To view the full list of methods and documentation for the three major classes, you can view them at AddressBook
,
TagTree
and ContactTagIntegrationManager
.
Design improvement
A possible way to improve the current design is to remove dependency from ModelManager
to TagTree
, AddressBook
and ContactTagIntegrationManager
by creating another Facade class containing these three classes.
As such, only the relevant methods required for specific Command
s to work will be exposed to Model
. For example, right now, both ContactTagIntegrationManager
’s deleteTag(Tag)
method and TagTree
’s deleteTag(Tag)
method are exposed to ModelManager
when only one of them is actually used.
This makes it easier for others working at a similar level of abstraction to avoid using the wrong methods.
Testing
Testing of ContactTagIntegrationManager
methods are inherently difficult as unit testing is in this context achieves very little. To help with integration testing of methods in ContactTagIntegrationManager
, a test contactTagIntegrationManager
object can be created from ContactTagIntegrationManagerTest.buildTestContactTagIntegrationManager()
.
The test object structure is presented in the diagram below.
This provides support for testing of new methods, making it easy to write new test cases and check the expected behaviour. It is also easier for others to understand what the test cases are accomplishing.
[Proposed] Visualisation
The current implementation of parent-child tagging is difficult to visualise. Currently, there exists only the list -t
method that states a brief summary, and view -t
that lists out full details for singular tag.
This proposed feature is a new command viewtree -t
that displays a pop-up visual of a tag tree display together with a summary of contacts tagged under each tag.
Required modules:
- Graphical node: Given a tag and a set of contacts, the node will contain the tag name and a summary of contacts.
- Graphical edge: An arrow directing from parent-tag to child-tag.
- Graph: A class that holds all graphical nodes and graphical edges
- Graph layout algorithm: Given a graph, this algorithm decides how to layout the various nodes in the graph
A partial implementation can be found here. The image used in testing was created by this partial implementation.
This implementation supports the viewtree -t
command that shows the current tag tree for all contacts in Athena.
Way forward:
The current implementation always displays every single parent-child tag relation. As such, it can get convoluted really quickly. We can implement a way to distill only the requested information such as displaying the nodes and edges of a tag and all its child-tags. Additionally, there is a need to scope the tag tree viewing feature towards the target user, with a focus towards keyboard commands. As such, there are two general alternatives:
- Add a textbox in the graph display that allows input commands. The supported commands could include
-
{tag name}
: Displays only relevant information for{tag name}
and all its sub-tags. -
-all
: Shows all parent-child tag relations. -
-exit
: Exits the graphical display.
-
- Replace the current
view -t
command. Instead, theview -t
command will support only one tag argument and display a visual representation of the tag and all its sub-tags. Using the modules listed above, a possible activity diagram for this implementation is shown below.
Issues:
As of now, the ability for commands to affect GUI components in Athena is limited. Thus, properly implementing this would likely require additional backend support for allowing commands to produce GUI effects.
Additional notes:
Definitions:
- For a
Tag
to exist in Athena, theTag
must have either at least onePerson
with theTag
or at least one child-tag. - A child-tag of a tag signifies a directional relation from a tag to its child-tag. It allows for some commands that affect a tag to also affect its child-tag. The reverse cannot be done. The other tag in the relationship is the parent-tag.
- Parent-tag: see child-tag.
- A sub-tag of a tag signifies a multi-step directional relation from a tag to the sub-tag (i.e. a sub-tag of a tag is a child-tag, or a child-tag of a child-tag, etc.). A child-tag of a tag is also a sub-tag.
Delete single tag behavior:
Suppose we delete a single Tag
, the parent-tags of Tag
will be reconnected to the child-tags of Tag
as illustrated by the image below.
We have intentionally chosen this design in order to preserve the effectiveness of top-down queries of all sub-tags.
Event and attendees management
General design
Event
component:
In Athena, events are represented by Event
objects. Event
objects have a description and a time. An Event
may or may not have attendees.
-
Calendar
handles all direct matters concerningEvent
objects. It has aUniqueEventList
. -
UniqueEventList
keeps track of allEvent
objects. It uses Event class’isSameEvent(Event)
method to ensure that there are no duplicate events in Athena. - All manipulation of
Event
objects have to be done through Calendar. Calendar provides simple methods that can be used by higher-level components such as theModelManager
class.
Attendees (FauxPerson
) component:
-
Event
objects can have attendees in the form of a set ofFauxPerson
. -
FauxPersons
are created fromPerson
objects taken from theAddressBook
.
How events gain/lose attendees
During add event:
- No change if there are no attendees to add.
- If user indicates to add attendees:
- The
Person
s representing the contacts are retrieved using the indices provided by user input. - A
FauxPerson
is created for each of them -
FauxPerson
s are grouped into a set and set as theEvent
’s attendees.
- The
This diagram shows the overview of the operation:
During edit event:
- No change if there are no attendees to add or remove.
- If user indicates to remove attendees:
- Removal of attendees is done first before addition of attendees.
- Based on the indices provided, the
FauxPerson
s at the given indices is removed.
- If user indicates to add attendees:
- The
Person
s representing the contacts are retrieved using the indices provided by user input. - A
FauxPerson
is created for each of them -
FauxPerson
s are grouped into a set and set as theEvent
’s attendees.
- The
This diagram shows the overview of the operation:
Sort events feature
The sort events feature is facilitated by Calendar
that stores event entries and their details in Athena.
Additionally, it executes the sorting operation based on 2 sorting patterns:
-
sort -e 1
- Sorts the events based on the description of the events in alphabetical order. -
sort -e 2
- Sorts the events based on the time of the events in chronological order.
SortEventCommand#execute() : Does validity check of input and then sorts the event according to user input (index))
The sorting function is designed with the aim of temporary modification of the event-related entries in the Calendar
.
This is to create a default sorting pattern (based on the description of the events in alphabetical order) for easier reference.
The following sequence diagram shows how the sort -e
operation works:
Design consideration:
Aspect: How sort executes
-
Alternative 1 (current choice): Temporarily sorts the event entries.
- Pros: Will use less memory.
- Cons: May cause inconvenience to the users.
-
Alternative 2: Automatically sorts the event entries based on one sorting order as they are being added/ deleted/ edited.
- Pros: Will be more convenient for the users. (As they do not need to manually sort)
- Cons: May restrict the usage and benefit of the app. (As only one sorting pattern is available)
The following activity diagram summarizes what happens when a user executes sort -e
command:
List events feature
Display feature
The display feature for the events is facilitated by Calendar
that stores event entries and their details in Athena,
and has a specific command of list -e
where the command makes use of ListEventCommand
.
ListEventCommand#execute() : Does validity check of current list and displayed all event-related entries.
Aspect: How list executes
The feature is designed to provide the users with the entire list of event-related entries, especially after when user executes certain commands that display partial list of event list (e.g. SearchEvent Command).
Reminders Management
General design
Reminders
component:
In Athena, reminders are represented by Reminder
objects.
Reminder objects have two fields, an Event eventForReminder and a Time dateForReminder, which indicates when the reminder will be activated.
-
RemindersImpl
handles all direct matters concerningReminder
objects. It has aUniqueRemindersList
. -
UniqueReminderList
keeps track of allReminder
objects. It usesReminder
class’isSameReminder(Reminder)
method to ensure that there are no duplicate reminders in Athena.
All manipulation of Reminder
objects have to be done through RemindersImpl
. RemindersImpl
provides simple methods that can be used by higher-level components such as
boolean hasReminder(Reminder)
void addReminder(Reminder)
void deletePerson(Reminder)
- and more
When reminders are active (the current date is after or equals to the dateForReminder of the reminder), a pop up window be displayed whenever Athena is opened. The pop up window will alert the user of this reminder.
Design choice
Reminder implements a very similar storage and manipulating system to events and contacts. This is to maintain homogeneity and ensure that developers can add-on or edit its features easily as long as they understand the other systems. The pop up window is designed as such so that reminders are less passive and can actively remind the users without them pulling up the list of reminders themselves.
Way Forward: Design improvement
More reminder functions should be implemented for user convenience. As the RemindersImpl and other components of reminders have already been set up. Developers aiming to add more reminder functions should work on these relevant classes.
For instance, if the developer wishes to add an EditReminderCommand
, they should make use of the setReminder function that
already exists in the ModelManager and the RemindersImpl classes. If the pre-existing helper functions do not exist, do create
them within those classes as well so as to follow the encapsulation principle.
Add Reminder Feature
RemindEventCommand#execute() : Creates the reminder and adds it to the uniqueRemindersList in RemindersImpl. It also handles validation of the reminder before it’s created. Making sure there is no duplicate reminder and the reminder is not set for a date that has already passed.
When creating a reminder through int daysInAdvance, the Reminder constructor calculates the dateForReminder by subtracting daysInAdvance from the eventForReminder’s scheduled date.
The below sequence diagram shows how Athena handles the construction and addition of a reminder.
Documentation, logging, testing, configuration, dev-ops
Appendix: Requirements
Product scope
Target user profile:
- has a need to manage a significant number of contacts and events
- requires seamless integration between contact and event tracking (vastly differing participants for different events, for instance)
- prefer desktop apps over other types
- can type fast
- prefers typing to mouse interactions
- is reasonably comfortable using CLI apps
Value proposition: manage contacts and events faster than a typical mouse/GUI driven app
User stories
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 | understand how to get started by adding new contacts, removing sample ones and learning advanced commands |
* * * |
user | add a new contact | keep track of my contacts |
* * * |
user | delete a contact | remove contacts that I no longer need, keeping my contact storage neat and uncluttered |
* * * |
user | delete all contacts of a specific tag | remove contacts that I no longer need, keeping my contact storage neat and uncluttered |
* * * |
user | view my contacts in an ordered manner | view the details of my contacts |
* * * |
user | add a new event | keep track of my events |
* * * |
user | delete an event | remove events that I no longer need, keeping my event storage neat and uncluttered |
* * * |
user | view my events in an ordered manner | view the details of my events |
* * |
user | search for a contact | locate details of contacts without having to go through the entire list |
* * |
user | edit a contact’s details | change outdated information without having to delete and re-add contacts |
* * |
user with many contacts in the address book | sort contacts by contact details | so that I can view my contacts in a more consistent manner and find the contacts I want quickly |
* * |
user | search for an event | locate details of events without having to go through the entire list |
* * |
user | edit an event’s details | change outdated information without having to delete and re-add events |
* * |
user with many events in the calendar | sort events by event details | so that I can view my events in a more consistent manner and find the events I want quickly |
* * |
user with important events | set reminders for events | so that I can get reminder alerts for my important events |
* * |
experienced user | assign tags to a super-tag | so that I can manage my tags in a more orderly fashion |
Use cases
(For all use cases below, the System is the Athena
and the Actor is the user
, unless specified otherwise)
Use case: View introduction
-
User opens Athena for the first time.
-
Athena displays an introduction message, with a guide on how to use basic commands.
Use case ends.
Use case: Add a contact
MSS
-
User requests to add a contact.
Use case ends.
Extensions
-
1a. User input missing one or multiple fields.
- 1a1. Athena shows an error message.
Use case ends.
Use case: Delete a contact
MSS
-
User requests to list contacts.
-
Athena shows a list of contacts.
-
User requests to delete a specific contact in the list.
-
Athena deletes the contact.
Use case ends.
Extensions
-
2a. The list is empty.
Use case ends.
-
3a. The given index is invalid.
-
3a1. Athena shows an error message.
Use case resumes at step 2.
-
Use case: Delete contacts by tag
MSS
-
User requests to delete contacts tagged under a specific tag.
-
User specifies a tag.
-
Athena deletes all contacts under the specified tag.
Use case ends.
Extensions
-
2a. The tag does not exist.
-
3a1. Athena shows an error message.
Use case resumes at step 2.
-
Use case: Sort through contacts
MSS
-
User requests to list contacts.
-
Athena shows a list of contacts in default order.
-
User requests to sort the contacts in the list.
-
Athena displays the contacts in the requested order.
Use case ends.
Extensions
-
2a. The list is empty.
Use case ends.
-
3a. The given index is invalid.
-
3a1. Athena shows an error message.
Use case resumes at step 2.
-
Use case: Edit contact information
Preconditions: The contact the user wishes to edit is displayed on the UI.
MSS
-
User uses the edit command to inform the system of which fields of the contact they wish to change.
-
Athena updates the contact information.
-
Athena sends an acknowledgement message to the user to notify of the edits made.
Use case ends.
Extensions
-
1a. Tag to be removed does not exist.
-
1a1. Athena notifies the user of the error.
-
1a2. Athena aborts the command without making changes.
Use case ends.
-
Use case: Search for contacts
MSS
-
User uses the find command and informs the systems of keywords and additional fields the contact has to match.
-
Athena displays a list of contacts that matches the keywords and contains the additional fields.
Use case ends.
Extensions
-
2a. The list is empty.
-
2a1. Athena informs the user that their search did not come up with any valid matches.
Use case ends.
-
Use case: Add an event
MSS
-
User requests to add an event
Use case ends.
Extensions
-
1a. The description and/or date time is missing.
-
1a1. Athena shows an error message.
Use case ends.
-
-
1b. The date time format is not accepted.
-
1b1. Athena shows an error message.
Use case ends.
-
-
1c. There is a duplicate event.
-
1c1. Athena shows an error message.
Use case ends.
-
-
1d. There is an event that is occurring at the same time.
-
1d1. Athena shows an error message.
Use case ends.
-
-
1e. The person(s) to be added is/are not found.
-
1e1. Athena shows an error message.
Use case ends.
-
Use case: Delete an event
MSS
-
User requests to view events.
-
Athena shows a list of events.
-
User requests to delete a specific event in the list.
-
Athena deletes the event.
Use case ends.
Extensions
-
2a. The list is empty.
Use case ends.
-
3a. The given index is invalid.
-
3a1. Athena shows an error message.
Use case resumes at step 2.
-
Use case: Edit an event
MSS
-
User requests to view events.
-
Athena shows a list of events.
-
User requests to edit a specific event in the list.
-
Athena edits the event.
Use case ends.
Extensions
-
2a. The list is empty.
Use case ends.
-
3a. The given index is invalid.
-
3a1. Athena shows an error message.
Use case resumes at step 2.
-
-
3b. The date time format is not accepted.
-
3b1. Athena shows an error message.
Use case resumes at step 2.
-
-
3c. There is a duplicate event.
-
3c1. Athena shows an error message.
Use case resumes at step 2.
-
-
3d. There is an event that is occurring at the same time.
-
3d1. Athena shows an error message.
Use case resumes at step 2.
-
-
3e. The person(s) to be added or removed is/are not found.
-
3e1. Athena shows an error message.
Use case resumes at step 2.
-
Use case: Search for events
MSS
-
User requests to search for a particular event.
-
Athena shows a list of events that match the criteria (e.g. contain a certain keyword).
Use case ends.
Extensions
-
1a. The given keyword is invalid.
-
1a1. Athena shows an error message.
Use case ends.
-
-
2a. The list is empty.
Use case ends.
Use case: Sort through events
MSS
-
User requests to list events.
-
Athena shows a list of events in default order.
-
User requests to sort the events in the list.
-
Athena displays the events in the requested order.
Use case ends.
Extensions
-
2a. The list is empty.
Use case ends.
-
3a. The given index is invalid.
-
3a1. Athena shows an error message.
Use case resumes at step 2.
-
Use case: View details of an event
MSS
-
User requests to view events.
-
Athena shows a list of events.
-
User requests to view details of an event.
-
Athena displays the event details in the result panel.
Use case ends.
Extensions
-
2a. The list is empty.
Use case ends.
-
3a. The given index is invalid.
-
3a1. Athena shows an error message.
Use case resumes at step 2.
-
Use case: List all saved events
MSS
-
User requests a list of all events saved.
-
Athena shows a list of events.
Use case ends.
Extensions
-
2a. The list is empty.
Use case ends.
Use case: Add a tag
MSS
-
User requests to add a reminder.
-
User specifies contacts to be added to this tag.
-
User specifies other tags to be added as child-tags.
-
Athena adds the new tag and its relations to specified contacts and child-tags.
Use case ends.
Extensions
-
1a. The tag already exists in Athena.
-
1a1. Athena shows an error message.
Use case ends.
-
-
2a. Contact specified is invalid.
-
2a1. Athena shows an error message.
Use case ends.
-
-
3a. Child-tag specified does not exist.
-
3a1. Athena shows an error message.
Use case ends.
-
Use case: List all tags
MSS
-
User requests to view all tags in Athena.
-
Athena shows a list of tags.
Use case ends.
Use case: Add a reminder
MSS
-
User requests to add a reminder.
-
User specifies which event this reminder is for.
-
User specifies when he wants to activate this reminder.
-
Athena adds the new reminder.
Use case ends.
Extensions
-
2a. There is already a reminder for the event.
-
2a1. Athena shows an error message.
Use case ends.
-
-
2a. The target event does not exist. Event index entered is negative or greater than the size of the events list.
-
2a1. Athena shows an error message.
Use case ends.
-
-
3a. The reminder is set to activate before the current date.
-
3a1. Athena shows an error message.
Use case ends.
-
Use case: Delete a reminder
MSS
-
User requests to delete a reminder.
-
User specifies which reminder to delete.
-
Athena deletes the reminder.
Use case ends.
Extensions
-
2a. The reminder does not exist. Reminder index entered is negative or greater than the size of the reminders list.
-
2a1. Athena shows an error message.
Use case ends.
-
Use case: List all reminders
MSS
-
User requests to view all reminders in Athena.
-
Athena shows a list of reminders.
Use case ends.
Use case: Find a tag
MSS
-
User requests to find a tag.
-
User specifies keyword to use in the search.
-
User specifies whether any tag filters should be used.
-
Athena displays all tags that match the keyword and filter.
Use case ends.
Extensions
-
3a. Tag filter input is invalid.
- 3a1. Athena displays an error message.
Use case resumes at step 3.
-
4a. List is empty.
- 4a1. Athena displays a message stating there are no matches.
Use case ends.
Non-Functional Requirements
- Should work on any mainstream OS as long as it has Java
11
or above installed. - Should be able to hold up to 1000 contacts and events without a noticeable sluggishness in performance for typical usage.
- Should be sufficiently intuitive for new or inexperienced users to understand and navigate.
- 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.
- Should not require an internet connection (bar downloading the app).
Glossary
- Contact: A contact to be tracked by Athena; comprises a name, phone number, email and address, as well as an arbitrary quantity of tags
- Event: An event to be tracked by Athena; comprises a name and a date and time, as well as an arbitrary quantity of tags
- Mainstream OS: Windows, Linux, Unix, OS-X
Appendix: Instructions for manual testing
Given below are instructions to test the app manually.
Launch and shutdown
-
Initial launch
-
Download the jar file and copy into an empty folder.
-
Double-click the jar file Expected: Shows the GUI with a set of sample contacts. 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.
-
Editing a contact
-
Editing a contact while all contacts are being shown
-
Test case:
edit -c 1 n/test name p/912345 t/edittest
Expected: Contact at index1
in the list has its name changed totest name
, phone number to912345
and the tagedittest
added. -
Test case:
edit -c 1 t/cs2103 t/cs2101 rt/edittest
(done after step 1)
Expected: Contact at index1
has tagedittest
removed and tagscs2103
andcs2101
added. -
Test case:
edit -c 1 rt/*
Expected: Contact at index1
has all its tags removed. -
Test case:
edit -c 1 n/$%^&a
Expected: Error message shown as the name input fails the field constraints.
-
Finding a contact
-
Finding a contact
-
Test case:
find -c n/alex betsy
Expected: For default contact list, showsAlex Yeoh
andBetsy Crower
(and possibly other contacts containing eitheralex
orbetsy
). -
Test case:
find -c e/@example
Expected: Lists all contacts with@example
in their emails. -
Test case:
find -c t/friends
Expected: Lists all contacts with the tagcs2030
.
-
Deleting a contact
-
Deleting a contact while all contacts are being shown
-
Prerequisites: List all contacts using the
list -c
command. Multiple contacts in the list. -
Test case:
delete -c 1
Expected: First contact is deleted from the list. Details of the deleted contact shown in the status message. Timestamp in the status bar is updated. -
Test case:
delete -c 1,2
Expected: First 2 contacts are deleted from the list. Details of the contacts events shown in the status message. -
Test case:
delete -c 0
Expected: No contact is deleted. Error details shown in the status message. Status bar remains the same. -
Other incorrect delete commands to try:
delete -c
,delete -c x
,...
(where x is larger than the list size)
Expected: Similar to previous.
-
Editing an event
-
Editing an event while all events are being shown
-
Test case:
edit -e 1 d/test at/10-10-21 14/00
Expected: Event at index1
in the list has its description changed totest
and time changed toSun 10th Oct 2021 2:00pm
. -
Test case:
edit -e 1 ap/*
(assuming there are contacts displayed, and done after step 1)
Expected: All persons show in the contact list is added to event at index1
as attendees. -
Test case:
edit -e 1 rp/*
(done after step 1 and 2)
Expected: Event at index1
has all attendees removed. -
Test case:
edit -e 1 d/
Expected: Error message shown as the description input is empty.
-
Finding an event
-
Finding an event
-
Test case:
find -e meeting
Expected: For default event list, showsCS2103 Meeting
(and possibly other contacts containingmeeting
). -
Test case:
find -e Run
Expected: For default event list, showsNight run
(and possibly other contacts containingrun
).
-
Deleting an event
-
Deleting a contact while all events are being shown
-
Prerequisites: List all events using the
list -e
command. Multiple events in the list. -
Test case:
delete -e 1
Expected: First event is deleted from the list. Details of the deleted event shown in the status message. -
Test case:
delete -e 1,2
Expected: First 2 events are deleted from the list. Details of the deleted events shown in the status message. -
Test case:
delete -e 0
Expected: No contact is deleted. Error details shown in the status message. Status bar remains the same. -
Other incorrect delete commands to try:
delete -e
,delete -e x
,...
(where x is larger than the list size)
Expected: Similar to previous.
-
Adding a tag
-
Adding a new tag to various contacts
-
Prerequisites: List all contacts using the
list -c
command. Multiple contacts in the list. -
Test case:
add -t n/testtag1 i/1 i/2
Expected: Contacts at indices1
and2
now have the tagtesttag1
. -
Test case:
add -t n/testtag2 i/3 t/testtag1
Expected: Contact at index3
has the tagtesttag2
. Whenlist -t
command is used,testtag2
will be labelled as asupertag
. -
Test case:
add -t n/testtag1 i/4
(has to be done after step 2)
Expected: Error message shown, saying thattesttag1
already exists.
-
Deleting a tag
-
Deleting a tag
-
Prerequisites: List all contacts using the
list -c
command. Multiple contacts in the list. -
Perform steps 2 & 3 of Adding a tag if it has not been done. Then perform
add -t n/testtag3 t/testtag2
. -
Test case:
delete -t t/testtag2
Expected: Contact at index 3 no longer has the tagtesttag2
. Whenlist -t
is used,testtag2
can no longer be found. When usingview -t t/testtag3
,testtag1
is listed as a child-tag oftesttag3
. -
Test case:
delete -t t/testtag3 r/1
Expected: Contacts at indices 1 and 2 no longer have the tagtesttag1
.list -t
now has neithertesttag1
nortesttag3
.
-
Editing a tag
-
Editing a tag
-
Prerequisites: List all contacts using the
list -c
command. Multiple contacts in the list. -
Perform steps 2 & 3 of Adding a tag if it has not been done.
-
Test case:
edit -t n/testtag2 i/2 ri/3
Expected: Contact at index 3 no longer has the tagtesttag2
. Contact at index 1 has the tagtesttag1
. -
Test case:
edit -t n/testtag2 rt/testtag1
Expected: Whenview -t t/testtag2
,testtag1
is no longer listed as a child-tag oftesttag2
. -
Test case:
edit -t n/testtag2 t/testtag1
followed byedit -t n/testtag1 t/testtag2
Expected: Error message shown, indicating an attempt in making a cyclic relationship.
-
Adding a reminder
-
Adding a new reminder to various events
-
Prerequisites: Create an event at index 1 that is scheduled to happen one day from now.
-
Test case:
add -r 1 in/1
Expected: Reminder has been added for the Event at index1
starting today. -
Test case: Exit and re-open the application Expected: Reminder window pops up and displays the event at index 1.
-
Test case:
add -r 0 in/1
(has to be done after step 2)
Expected: Error message shown, saying that the event index is invalid.
-
Finding a tag
-
Finding a tag
-
Prerequisites: List all persons using the
list -c
command. Multiple persons in the list. -
Perform steps 2 & 3 of Adding a tag if it has not been done.
-
Test case:
find -t t/testtag1
Expected: Onlytesttag1
is displayed, with its corresponding contacts. -
Test case:
find -t t/testtag
Expected: Bothtesttag1
andtesttag2
are displayed, with their corresponding contacts. -
Test case:
find -t t/testtag st/1
Expected: Onlytesttag2
is displayed, with its corresponding contacts. -
Test case:
find -t t/
Expected: Error message shown, stating that search specifiers cannot be empty.
-
Saving data
-
Dealing with missing/corrupted data files
-
In the directory with Athena, open the folder
data
and edit the json save files. -
Open Athena. Expected: The individual lists should be blank as a corrupted save file has been detected.
-
Add some contacts and events to ensure that Athena has a proper save file.
-
Delete one of either
Calendar.json
orAddressBook.json
. Expected: When opening Athena again, a default set of entries will fill up the component that was deleted.
-
Appendix: Effort
-
Contact and Tag management: It was difficult to come up with a good OOP solution in order to keep track of tag-contact and tag-tag relations while avoiding cyclic dependency. Such functionality was not present within AB3 prior so it had to be implemented from scratch. Additionally, a major challenge was that we intended to implement commands that could affect tags and contacts in a way where other contacts and tags that were not specified could also be affected (e.g. deleting a tag requires removal of the tag from contacts with it). There were also issues that were more difficult to spot, such as the ability to create cyclic relations between tags. As such, multiple solutions and designs were considered, outlining exact behavior that could be supported and allowed within this tracking system. Extensive testing was also necessary for every single method as many higher-level components and commands rely on accurate queries of tags and contacts.
-
Event and Attendees management: When contacts were integrated into events as attendees, there was challenge to simplify a Person object so that the calendar save file did not need contain multiple repeated copies of Persons. Otherwise, the file size can get unnecessarily big given our aim is to have large number of contacts and events. Hence, FauxPerson class was created. When creating the structure for adding and removing attendees, it was also necessary to think ahead and design code that allows commands related to contacts to trigger changes to attendees. Namely, when deleting and editing contacts, the GUI should update the event list as well. It was also important to design it in such a way that these commands did not need to be modified such that they gained too much coupling from interacting with too many other classes. Hence, Calendar gained 2 methods. One for deleting a Person from being associated, and the other for setting a Person with its edited self.