Developer Guide
- Setting up, getting started
- Introduction
- Acknowledgements
- System Overview
-
Implementation
- Commands with UI effects
- Import, Merge and Export Features
- Profile Feature
- Profile feature - Reset profile
- Mark Contacts as Done Feature
- Delete Contacts Feature
- Tag feature
- Filter feature
- History tracking features
- Undo/Redo feature
- History feature
- Use of UP and DOWN to get previous or next commands
- Auto completion feature
- Documentation, Logging, Testing, Configuration, Dev-ops
-
Appendix: Requirements
- Product scope
- User stories
-
Use cases
- UC01: Supervisor adds a contact
- UC02: Supervisor deletes a contact
- UC03: Supervisor tags a contact
- UC04: Telemarketer sets a person as done
- UC05: Telemarketer adds a remark to a person
- UC06: Telemarketer imports a customer list from supervisor
- UC07: Telemarketer exports a customer contact file
- UC08: Telemarketer undoes Teletubbies
- UC09: Telemarketer redoes Teletubbies
- UC10: Telemarketer checks Command History
- UC11: User views the help page
- Non-Functional Requirements
- Appendix: Instructions for manual testing
- Appendix: Effort
- References
Setting up, getting started
Refer to the guide Setting up and getting started.
Introduction
Purpose
This document describes the architecture and system design of the Teletubbies desktop application for Telemarketers and Telemarketer Supervisors.
Scope
The document aims to comprehensively cover the software architecture and software design decisions for the implementation of Teletubbies. It is intended to serve as a guide for the developers, the designers, and the testers for the application.
The definitions for the emboldened terms can be found in the Glossary Section.
Design Goals
The developers work closely with telemarketers and telemarketer supervisors to better understand their needs, so that the requirements of the product can be as clear-cut as possible.
User-Centricity
Teletubbies can be used by both telemarketers and supervisors, and chooses the appropriate features according to the current user’s role, so that the user can work effectively and efficiently to complete their tasks.
Improved Workflow Efficiency
Teletubbies is optimised for Command-Line Interface (CLI) usage, which streamlines workflow within the application by centralising the entering of commands in a single text input window.
Seamless Data Integration
Teletubbies allows telemarketers, and their supervisors to share and merge data seamlessly. Synchronization of customer data between telemarketers and their supervisors is often a necessary aspect of their job, and Teletubbies provides avenues to make this process easy and hassle-free.
Data Safety and Recoverability
With many contacts stored in a contact list, it is vital that the user’s current progress is saved frequently to assist in data recovery in the event of unexpected system failure. Hence, the contact list is saved after each command issued by the user.
Additionally, in the event of human error, an undo command is available for users to revert to previous states within the session itself.
Scalability & Maintainability
Within a team of developers, it is important that the developers reduce dependencies within the project to increase testability and extensibility.
In addition, with requirements that constantly change, it is important to be able to easily modify features and
functionalities that have already been written without disturbing other related components. Therefore, the main
components of the application such as the Logic, UI, Model, and Storage are separated into decoupled modules.
Along with the strict adherence to software design principles, such as Single Responsibility and Separation of Concerns, the modularity of the software design allows future developers to add features to Teletubbies without having to deal with tedious side effects.
Glossary
Command-line Interface (CLI): A user interface that allows users to interact with a system through text commands.
Graphical User Interface (GUI): A user interface that allows users to interact with a system through graphical icons.
User stories: Simple descriptions of features told from the perspective of the user.
Completion Status: A contact can be marked as either “complete”, “ongoing” or “incomplete”, indicating if the contact has been contacted.
Role: Users are assigned either the role of telemarketer or supervisor.
Single Responsibility Principle: A software engineering principle that states that every module, class or function in a computer program should be responsible for and encapsulate only a single part of the program’s functionality.
Separation of Concerns Principle: A software engineering principle that states that programs should be separated into distinct sections which address concerns, or sets of information that affects the code of a computer program.
Acknowledgements
System Overview
.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. The Main component,
- (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 outlines how the components interact with each other, in a scenario where the user issues the command delete -p 87654321.

Each of the four main components (also shown in the diagram above),
- defines its API in an
interfacewith the same name as the Component. - implements its functionality using a concrete
{Component Name}Managerclass (which follows the corresponding APIinterfacementioned 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
API : Ui.java
Here’s a (partial) class diagram of the UI component:

The UI consists of a MainWindow that is made up of parts such as CommandBox, ResultDisplay, PersonListPanel, StatusBarFooter, etc. All these parts, including the MainWindow itself, inherit from the abstract UiPart class which captures the commonalities between the 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
Logiccomponent. - listens for changes to
Modeldata so that the UI can be updated with the modified data. - keeps a reference to the
Logiccomponent, because theUIrelies on theLogicto execute commands. - depends on some classes in the
Modelcomponent, as it displays eachPersonobject residing in theModel.
Logic component
API : Logic.java
Here’s a (partial) class diagram of the Logic component:

How the Logic component works:
- When
Logicis called upon to execute a command, it uses theInputParserclass to parse the user command. - This results in a
Commandobject which is executed by theLogicManager. - The command can communicate with the
Modelwhen it is executed (e.g. to add a person). - The result of the command execution is encapsulated as a
CommandResultobject which is returned fromLogicback to theUI. - The
CommandResultobject also contains aUiConsumerobject, which consumesMainWindowfromUI, and encapsulates instructions for the Command’s UI effects (e.g. opening help menu, opening file select window).
The Sequence Diagram below illustrates the interactions within the Logic component for the execute("delete -i 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.
In the diagram above, the creation of the UiConsumer object is represented. Since DeleteCommand does not have a UI effect, the consumer simply does nothing. For more details on how a UI effect is
executed, please see Commands with UI effects. Subsequent sequence diagrams for commands with no UI effect might omit the UiConsumer portion of Command Result.
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
InputParserclass creates anXYZCommandParser. Here,XYZis a placeholder for the specific command name, e.g.AddCommandParser. - The newly created
XYZCommandParserclass utilizes the other classes shown above, such asParserUtil,CliSyntax, etc. to parse the user command and create aXYZCommandobject, e.g.AddCommand. - The
InputParserwill then return the newly createdXYZCommandobject as aCommandobject.
All XYZCommandParser classes like AddCommandParser and DeleteCommandParser inherit from the Parser interface so that they can be treated similarly where possible, such as during testing.
Similarly, all XYZCommand classes inherit from the Command class.
Model component
API : Model.java
Here’s a (partial) class diagram of the Model component:

The Model component,
- stores the list of contacts and all previous lists (or address books) in
VersionedAddressBook, which inherits fromAddressBook. - stores a history of all commands that were input by the user using
CommandInputHistory. - stores the currently ‘selected’
Personobjects (e.g. results of a search query) as a separate filtered list, exposed to outsiders as an unmodifiableObservableList<Person>. - stores a
UserPrefobject that represents the user’s preferences. - does not depend on any of the other three components (as the
Modelrepresents data entities of the domain, they should make sense on their own without depending on other components).
Storage component
API : Storage.java

The Storage component,
- can save both address book data and user preference data in json format, and read them back into corresponding objects.
- inherits from both
AddressBookStorageandUserPrefStorage, which means it can be treated as either one (if only the functionality of only one is needed). - depends on some classes in the
Modelcomponent (because theStoragecomponent’s job is to save/retrieve objects that belong to theModel)
Common classes
Classes used by multiple components are in the teletubbies.commons package.
Implementation
This section describes some noteworthy details on how certain features are implemented.
Commands with UI effects
Commands that require a UI effect will need to set up the UiConsumer functional interface that allows the commands to access the functionality
of MainWindow. The following is an example of a constructor for CommandResult that includes this set-up:
new CommandResult(MESSAGE, UiEffect.SHOW_HELP, new HelpUiConsumer());
The third argument of this method call (new HelpUiConsumer()), is a UiConsumer that uses the handleHelp method in MainWindow. More complicated effects
can be constructed with the exposed functions in MainWindow (See Import, Merge and Export Feature). The UiConsumer can be implemented
either as a concrete class (like HelpUiConsumer) or as a lambda function.
If the UiEffect type (the second constructor argument) does not exist for a new command that you want to add, this corresponding UiEffect
type should be added into the UiEffect enum in CommandResult.
On the other hand, if a command has no special UI response, the UiEffect type should be NONE.
Implementation (help command)
This section describes the implementation of the help command. The implementation of other commands that make use of UI effects/responses (e.g.
file choosers, pop-up windows) will be similar.

ImportCommand should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
Step 1. The user enters the help command in the command box.
Step 2. A CommandResult object containing a UiConsumer is created and returned by the HelpCommand. The UiConsumer contains instructions for the UI effect of the help command.
Step 3. MainWindow calls CommandResult#executeUiEffect of the returned CommandResult object, which in turn call the UiConsumer.
Step 4. The UiConsumer (which contains the implementation for the help command’s) UI effect, calls MainWindow#handleHelp which invokes the help window pop-up.
Design Considerations
-
Alternative 1 (current implementation): Use
UiConsumerto encapsulate UI effects- Pros
- Allows UI effects to be open for extension and closed for modification. Adding additional UI effects incur minimal cost.
- UI effects can contain a combination of features exposed by
MainWindowto create arbitrarily complex UI effects.
- Cons
- Creates significant coupling between
MainWindow(UI) andUiConsumer(Logic).
- Creates significant coupling between
- Pros
-
Alternative 2 (previous implementation): Use boolean flags in
CommandResultto signify instructions for UiEffects- Pros
- Ease of implementation
- Cons
- If a new UI effect needs to be allowed, both
MainWindowandCommandResultwill need to be modified with further boolean flags and checks for whether a UI effect has been enabled. This would be in violation of the open-closed principle, since this is not closed to modification.
- If a new UI effect needs to be allowed, both
- Pros
Import, Merge and Export Features
Implementation
The import, merge and export mechanisms are supported by all the main components, specifically in the following ways:
-
The
Uicomponent is accessed inCommandResultthrough theUiConsumer. This allows the user to interact with the JavaFX FileChooser to select files to be imported, merged or exported to. -
The execution of the
ImportCommand,MergeCommandandExportCommandis distinct from other commands executed byLogicbecause it is passed to the UI consumer in theCommandResultdue to their reliance on the UI file chooser. -
For import, the
Modelcomponent is accessed to set the new AddressBook of contacts. For merge, contacts are merged with the current AddressBook. On the other hand, export filters the AddressBook of theModelusing the tags specified in the user command to retrieve contacts to be exported. -
Functions in
Storagewere used to write AddressBooks to JSON files as well as read and convert JSON files to AddressBook objects.
Import Implementation
The ImportCommand allows users to import contact files to the Teletubbies app. The following sequence diagram shows how the import operation works:

ImportCommand should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
- After an
ImportCommandis created, it will be executed by theLogicManager. During the execution, a newCommandResultis returned with theImportUiConsumer. -
CommandResult#executeUiEffectis called by theMainWindow. TheImportUiConsumerthen callsMainWindow#handleImport, which opens the file chooser for the user to choose a file to import. - When the file is selected, the
ImportUiConsumerconverts the JSON file to an AddressBook and callsModel#setAddressBook. The updated contact list is then displayed in the GUI.
Merge Implementation
Merge functions in a similar way to import. However, instead of replacing the previous AddressBook, the incoming AddressBook is merged with it.
Teletubbies provides commands for users to modify contacts by editing their particulars or tagging them. Since a Person can be entirely changed, each Person is issued a Universally Unique Identifier (UUID) to facilitate the merging process.
- If there is a Person with a matching UUID in the AddressBook, the incoming Person would replace it.
- Else, the Person is new and would be added to the AddressBook.
The following sequence diagram shows how the merge operation works:

MergeCommand should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
Export Implementation
Export is distinct from other features as it comprises of 2 commands, as illustrated in this activity diagram:

As seen in the activity diagram above,
- The first command gives users a preview of the contacts to be exported
- Second command is carried out by the user to confirm and execute the export
- If the user types in another command instead of confirming the export, the pending export is cancelled and the new command is executed.
The following sequence diagram shows how the export operation works:

The first export command processes the AddressBook to filter contacts that contain the tags specified by the user. This is then stored in the Model until the export is confirmed and is displayed to the user too.
The interaction between ExportCommand and Model is illustrated in the sequence diagram below:

The ExportCommand calls Model#updateExportList, invoking ModelManager to do the following:
- Set the boolean
isAwaitingExportConfirmationto true to manage the subsequent confirmation command. - Update the Model’s
versionedAddressBookwith thefilteredPersonsList. This displays the filtered contact list for users to view before confirming export. - Create a copy of the filtered address book and compares it with the previous address book. If they are different,
ModelManagercallsVersionedAddressBook#commitCurrentStateAndSave, which commits and pushes the state on theHistoryManager. This enablesModelManagerto revert to the original address book after the next command is executed.
The following sequence diagram shows how the ConfirmExport operation works:

The execution of the ConfirmExportCommmand is similar to the import and merge commands in the use of UiConsumer. If isAwaitingExportConfirmation in the Model is true, the ExportUiConsumer retrieves the AddressBook to be exported from the Model as shown in the sequence diagram below:

The ExportUiConsumer calls Model#getExportAddressBook, invoking ModelManager to do the following:
- Creates a copy of the AddressBook called
toExportcontaining contacts to be exported. This is subsequently returned to theExportUiConsumerto be converted and exported as a JSON file. - If
isExportListModifiedwas set to true in theExportCommand,-
versionedAddressBookis undone. -
VersionedAddressBook#commitWithoutSavingCurrentState()is called, which clears the history after thehistoryStackpointer and resets theHistoryManagerof theversionedAddressBook.
-
- Resets export related booleans in
ModelManager.
During the execution of other Teletubbies commands, Model#cancelPendingExport() is called to undo the VersionedAddresBook if there is a pending export, before the actual execution of the command. The operation of Model#cancelPendingExport() is shown in the sequence diagram below:

The VersionedAdressBook and HistoryManager are reset in the same way as Model#getExportAddressBook, which was elaborated above.
Design Considerations
Aspect: How to allow commands to specify UI effects
-
Alternative 1 (current choice): Import/Export/Merge command can be executed by CLI command or menu bar button.
- Pros: Users are given the flexibility of choosing either method to enter the command according to their preference.
- Cons: Contacts to be exported are unable to be filtered by tags in the menu bar button.
-
Alternative 2: Import, export and merge are buttons in the menu bar only.
- Pros: Similar to the layout of menu bars in Microsoft Office Applications, which might be familiar to users.
- Cons: Target users can type fast and might prefer typing in commands. Contacts to be exported are unable to be filtered by tags.
Profile Feature
Implementation
Setting the user’s profile is facilitated through the ProfileCommand and the ProfileCommandParser class, which
parses the user’s input to create a new UserProfile object to be set in the current Model component.
The following activity diagram summarizes what happens when a user executes a profile command:

The following sequence diagram further illustrates how the profile command operation works:

ProfileCommandParser and ProfileCommand should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
Design Considerations
Aspect: Parameters of the profile command
-
Alternative 1 (current choice): Profile is set with both name and role as mandatory fields.
- Pros: Ensures that both name and role are accounted for when changing the profile.
- Cons: Both fields must be entered for the user to set the profile, which can be tedious if it has to be done multiple times.
-
Alternative 2: Profile can be set with only the name, or both name and role.
- Pros: Allows users to change their name alone without having to enter role as well.
- Cons: The cost of implementation is not heavily outweighed by the explicit need for the feature, as there should be infrequent changes in the user’s name.
Aspect: Number of times the user’s profile can be set
-
Alternative 1 (current choice): Profile can only be set once per user, excluding any undo.
- Pros: Ensures that users are unable to switch between telemarketer and supervisor functionalities. In the case where the user has misspelt their name, they can still use the undo command to revert back and re-enter the name for their profile.
- Cons: The user is unable to set their profile again in the case where their name or role has changed.
-
Alternative 2: No limits are placed on the number of times the user is able to set their profile.
- Pros: Allows users to change their name or role as needed, hassle-free.
- Cons: There is no explicit need for the feature, as there should be infrequent changes in the user’s name and role.
Profile feature - Reset profile
Implementation
As stated above, users are only able to set their profile once using the profile command.
However, there may be times when there is a need for the users to change their name and/or role. In such circumstances,
it is possible for the users to reset their profile by manually deleting preferences.json.
The following activity diagram summarizes what happens when a user deletes preferences.json:

Mark Contacts as Done Feature
Implementation
Marking contacts as done, or contacted, is assisted by CompletionStatus. It has a composition association with Person
and holds a boolean value indicating that the person has been contacted by the telemarketer.
The following sequence diagram shows how the done operation works:

Delete Contacts Feature
Implementation
Deletion of a contact is supported by the DeleteCommand and DeleteCommandParser classes. The DeleteCommandParser
parses the user’s input and deletes the contact at the given index or with the given phone number.
The following sequence diagram shows how the delete operation works:

DeleteCommandParser should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
The following activity diagram summarizes what happens when a user executes a delete command:

Design Considerations
Aspect: Parameters of the delete command
Since the telemarketer is responsible for talking to customers on the phone to sell products, it will be useful for them to interact with their contact lists through the customer’s phone number.
-
Alternative 1 (current choice): Delete using either index or phone number.
- Pros: Flexibility in method of deletion, as users can specify whether they would like to reference a contact by
index or phone number by using the
-ior-pprefixes. - Cons: Slightly more complicated for user to delete a contact, as a longer command has to be typed.
- Pros: Flexibility in method of deletion, as users can specify whether they would like to reference a contact by
index or phone number by using the
-
Alternative 2: Delete via phone number only.
- Pros: Implementation is more straightforward, as there is only one type of input to be expected.
- Cons: Removes the convenience of deleting using a contact’s index.
Tag feature
The following section will describe the implementation of the tag feature. The implementation for remove tag feature is similar and hence won’t be repeated in this section.
The TagCommand allows users to tag contacts. A tag has a mandatory name and an optional value (both are case-sensitive).
It also requires a specification of indices of the contacts to tag. To allow batch tagging, this is done with a Range
(found in commons/core/index). A Range encapsulates a set of indices. The TagCommandParser supports ranges of the
form 1,2,3 (Comma-separated) or 1-5 (Hyphen-separated). The parsing of range is handled by ParserUtil#parseRange.
The sequence diagram below represents the creation of a TagCommand object by the TagCommandParser. Details related to
obtaining the tag’s name, value and supervisor flag have been omitted from the diagram below.

ProfileCommandParser and ProfileCommand should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
After the creation of the TagCommand, it will be executed by LogicManager. Below is the sequence diagram for the execution
of the TagCommand.

ProfileCommandParser and ProfileCommand should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
Step 1. LogicManager executes the TagCommand.
Step 2. TagCommand obtains the list of Persons corresponding to its range.
Step 3. For each person in the obtained person list a new tag is generated by generateNewTag() and added to the person’s list of tags (or used to replace the existing
tag if the person already has the tag). The new set of tags is used to instantiate the editedPerson. Details of adding the tag to the editedPerson has been
omitted for brevity.
Step 4. The updated editedPerson is used to replace the original person with setPerson.
Design Considerations
Tags are used extensively in Teletubbies with a variety of purposes. Tags in Teletubbies are much more versatile than their implementation in AB-3. Tags now have both names and values, and can also be set to be only editable by supervisors. This allows for supervisors to create assignee tags for example, with name ‘assignee’ and value as the name of the assignee. The tag can also be set to be modifiable only by supervisors, so that employees won’t be able to tamper with the manpower assignment.
The completion status of a contact is also internally represented as a tag, with name ‘CompletionStatus’ and value
either ‘INCOMPLETE’, ‘ONGOING’ or ‘COMPLETE’. Since the filter and export commands make use of tags, this allows users to filter and
export contacts by ‘CompletionStatus’, since CompletionStatus is also a tag after all. Supervisors can also export contacts corresponding
to a particular assignee for contact dissemination, which is a critical feature for supervisors.
Aspect: How general (in terms of functionality) should tags be?
-
Alternative 1 (current choice): Users are free to set tag name, value and accessibility
- Pros:
- Users have more configurability options for tags, which opens up use-cases and ultimately makes tags more useful.
- Implementation for
filterandexportis simplified since only tags will be used to specify contacts for these commands.
- Cons:
- Commands pertaining to adding and removing tags now get more complex and risk being counter-intuitive since there are more configuration options.
- Pros:
-
Alternative 2: Tags only have a name attribute.
- Pros:
- Ease of implementation (virtually identical to AB-3).
- Lesser configurability of tags and hence simpler and easier to use tag commands.
- Cons:
- Separate functionality will need to be created for assignee and completion status, which will function very similarly to tags, but are not tags.
-
filterandexportcommands will potentially be a lot more complex, since users can export by tags, completion status or assignee, and they can’t be handled similarly.
- Pros:
Filter feature
The following section will describe the implementation of the filter feature. The use of a Predicate<Person> here to
filter the person list is similar to the implementation of the find feature.
The sequence diagram below represents the creation of a FilterCommand object by the FilterCommandParser.

ProfileCommandParser and ProfileCommand should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
PersonHasTagsPredicate is a subclass of Predicate

ProfileCommandParser and ProfileCommand should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
The existing functionality of Java’s FilteredList is leveraged for the implementation of the command. The execution of the
FindCommand is near identical except to FilterCommand for the use of NameContainsKeywordsPredicate instead of
PersonHasTagsPredicate.
History tracking features
We saw a need for Teletubbies to track the history of the application as a core feature while we were devising our user stories. This would allow users to traverse back and regain access to previous states, as well as access previous commands.
The features that required the tracking of history were the
-
undo/redocommands, -
historycommand, and - Unix-style way of obtaining previous and next commands, using the UP and DOWN arrow keys
The HistoryManager class was created to serve as an abstraction for devices that can store the history of any object. It’s sole responsibility is to
manage the history of the type of object we wanted to track, including the current state and a list of previous states or previously undone states. It is a generic class, which
allows for efficient code re-usability.
HistoryManager uses a stack-like data structure, with a Last-In, First-out (LIFO) method of storing states.
HistoryManager contains an ArrayList called historyStack which serves as the stack-like data structure to store the states of the object in question, and a stackPointer which stores the
index of the current state in question.
When HistoryManager is first initialised, historyStack is empty while stackPointer does not point to any state, since there are none stored.

Then, new states of the object with type parameter T are added and stored in the HistoryManager, using HistoryManager#commitAndPush(T item). This method causes the new item to be added to
historyStack and the stackPointer to be pointed to the new item.

HistoryManager#commitAndPush(T item) returns a new HistoryManager object which has the new state pushed to the top of the
historyStack. This ensures the immutability of historyStack and guarantees that if anything is added, stackPointer will point to the latest version.
The HistoryManager#undo() and HistoryManager#redo() methods allow the pointer to be pushed up or down to previous states or previously undone states, by simply pointing to the object below the current state or above it.
For instance, this is how HistoryManager looks like after HistoryManager#undo() is called twice:

And after HistoryManager#redo() is called once, it looks like this:

HistoryManager#undo() and HistoryManager#redo() do not return new HistoryManager objects as the historyStack is not manipulated.
Calling HistoryManager#commitAndPush(T item) here causes the states above the stackPointer to be removed and replaced by the new state, as indicated by item.
Once again, a new HistoryManager is created, this time with the new state added and the state v2 to be removed.

HistoryManager also has a HistoryManager#peek() method which returns the current state that is pointed at by the stackPointer, as well as the
HistoryManager#resetFullHistory() method which simply resets the stackPointer to point to the top of the historyStack.
Design Considerations
Aspect: How can the previous states be stored
-
Alternative 1 (current choice): Use a single stack to record the history
- Pros: Easy to visualise
- Cons: Need to be careful while committing as both previous states and previously undone states exist on the same stack. Does not adhere closely to Single Responsibility Principle.
-
Alternative 2 (proposed): Use a 2-stack method to record history, one which stores the states which can be undone and the other to store the states which can be redone.
- The
HistoryManagerwould originally have 2 stacks: an undo stack and a redo stack. Before actions are undone, the redo stack would be empty while the new states would be added directly to the top of redo stack.
- The

- Then, as
HistoryManager#undo()orHistoryManager#redo()is called states would be popped from one stack and pushed to the other.

- If we want to commit to
HistoryManager, all we have to do is clearredoStackand push the new state toundoStack.

- Pros: Single responsibility principle,
undoStackonly manages states to be undone whileredoStackonly manages states to be redone. - Cons: Difficult to visualise.
Undo/Redo feature
The undo command allows the telemarketer or the supervisor to revert to previous states. This mechanism is supported by the VersionedAddressBook. VersionedAddressBook extends
AddressBook, but internally implements the HistoryManager class to store and track ReadOnlyAddressBook, the states of the address book.
It also implements the following methods:
-
VersionedAddressBook#commitCurrentStateAndSave()- This action adds a copy of the current state toHistoryManager -
VersionedAddressBook#commitWithoutSavingCurrentState()- This action clears previously undone states stored byHistoryManager. This method will only be called -
VersionedAddressBook#undo()- This action restores a previously stored state as stored inHistoryManager -
VersionedAddressBook#redo()- This action restores a previously undone state as stored inHistoryManager
The Model interface also exposes methods such as Model#undoAddressBook(), Model#redoAddressBook() and Model#commitAddressBook() that allows the states to be changed.
An example of the usage scenario of undo is given below:
Step 1. Teletubbies is launched for the first time by the user. VersionedAddressBook is first initialised with an initial address book state.

Step 2. The user changes the current state of Teletubbies by executing a done 1 command. The done command calls Model#commitAddressBook() which in turn causes
VersionedAddressBook#commitCurrentStateAndSave() to be called. This causes the newest state of Teletubbies (which is different from it’s previous state) to be
saved.

Step 3. The user changes the current state of Teletubbies by executing a delete -i 1 command. The delete command calls Model#commitAddressBook() as before, causing
the state of Teletubbies to be changed.

Step 4. The user decides to undo by executing undo. This causes Model#undoAddressBook() to be called, which causes HistoryManager to revert back to state tb1.

Model#canUndoAddressBook() to confirm if this is the case.
The sequence diagram below illustrates the undo operation:

However, the redo command simply does the opposite and calls Model#redoAddressBook(), which causes the undone state to be reinstated.
Model#canRedoAddressBook() to confirm if this is the case.
Step 5. The user decides to execute the command find Alex. However, find does not modify VersionedAddressBook. Commands that do not modify VersionedAddressBook do not call
Model#commitAddressBook() which allows the history of states to remain unchanged.

Step 6. The clear command is then executed by the user. Model#commitAddressBook() is called by the command, which causes the states after the current state to no longer be stored.

Design Considerations
Aspect: How undo and redo would be executed
-
Alternative 1 (current choice): Just save the entire AddressBook state.
- Pros: Easier to implement, debug and trace together
- Cons: Very memory-intensive as multiple states and copies are stored.
-
Alternative 2: Only store the difference in the AddressBook states, much like GitHub’s approach to version control. This means a separate data structure which stores the difference between states like a tree is needed.
- Pros: Much less memory-intensive since only differences between each version are saved
- Cons: Difficult to implement a tree-like data structure that can effectively track the changes within the given time period. Undo would simply be a traversal up the tree while redo would be traversing down. Cannot reuse
HistoryManager
History feature
The history command allows the user to view the previous commands that were given to Teletubbies. This mechanism is supported by CommandInputHistory, which internally implements HistoryManager
to store and track user inputs which are Strings.
CommandInputHistory also implements the following methods:
-
CommandInputHistory#getChronologicallyAscendingHistory()- This method returns a List of Strings which are the commands keyed in by the user in chronological order, i.e. the first element is the earliest command given by the user. -
CommandInputHistory#getChronologicallyDescendingHistory()- This method returns a List of Strings which are the commands keyed in by the user in chronologically descending order, i.e. the first element is the latest command given by the user. -
CommandInputHistory#addCommandInput(String input)- This method callsHistoryManager#resetFullHistory()before callingHistoryManager#commitAndPush()to add a new String to theHistoryManager. This is necessary asCommandInputHistoryshould keep track of all inputs without overwriting them.
The Model interface also exposes methods such as Model#addCommandInput(), Model#getNextCommand() and Model#getPreviousCommand() that allows access to the previous and next commands
as stored by the HistoryManager in CommandInputHistory.
The sequence diagram below shows the mechanism in storing the input given by the user.

This allows the command to be stored regardless of the outcome of the command, even if it is an invalid command.
The sequence diagram below shows the execution path which is taken by the history command.

Use of UP and DOWN to get previous or next commands
As we were inspired heavily by the elegance of Unix-like systems and their ability to cycle through previously input commands,
we decided to implement this as well. This feature is facilitated by the Model and CommandHistoryInput.
The UI component uses EventHandlers that detects if the UP or DOWN buttons are pressed by the user. Then, UI calls upon Model#getPreviousCommand()
or Model#getNextCommand() to get the previous or next command as stored by CommandInputHistory.
The activity diagram below show how the UP button key press is handled.

The activity diagram below show how the DOWN button key press is handled.

Design Considerations
Aspect: The interaction between UI and CommandInputHistory
-
Alternative 1 (current choice): Let
ModelhouseCommandInputHistory- Pros: Minimal dependency between
Logicand the components ofModel - Cons: Additional dependency between
UIandModelcreated asUIneeds to callModelmethods
- Pros: Minimal dependency between
-
Alternative 2: Let
LogichouseCommandInputHistory- Pros: Since
Logicalready interacts withUI, no additional coupling created between them. - Cons: Requires passing of
CommandInputHistorytoInputParserin order to handlehistorycommands. The parser should not have to know aboutCommandInputHistory
- Pros: Since
Auto completion feature
Since Teletubbies is designed to be mainly used through its CLI, we prioritised the convenience of our users in typing out commands by implementing an auto completion feature.
The autocompletion mechanism is facilitated by the CommandMap class. The CommandMap contains a a HashMap
called classMap which stores the individual command words as keys and the XYZCommand classes as values. Within
each XYZCommand class, the recommended command fields for the command are stored in a List called RECOMMENDED_FLAGS.
This list is not stored if the command does not require command fields. Here, XYZ is a placeholder for the specific
command name, e.g. AddCommand.
The UI component uses EventHandlers that detects if the TAB button is pressed by the user. Then, UI calls
upon CommandMap#getClass() to retrieve the class that corresponds to the input command, which then prints out
the command fields specified within the RECOMMENDED_FLAGS list (or does not print out anything, in the case that no command fields
are required for the input command).
Documentation, Logging, Testing, Configuration, Dev-ops
Appendix: Requirements
Product scope
Target User Profiles
David Wong
- works as a telemarketer from home and makes internet calls on his computer
- likes to import/export data quickly
- wants to mark the contacts that he has already called and/or successfully marketed to
- wants to keep track of his progress
- has a need to manage a significant number of contacts
- prefer desktop apps over other types
- can type fast
- prefers typing to mouse interactions
- is reasonably comfortable using CLI apps
Anne Goh
- works as a telemarketing supervisor from home
- needs to pass lists of contacts to her subordinates to contact
- wants to monitor the performance of her subordinates
- has a need to manage a significant number of contacts
- prefer desktop apps over other types
- can type fast
- prefers typing to mouse interactions
- is reasonably comfortable using CLI apps
Value Proposition
- Able to import / export data into an easy-to-view format, useful for those who are not experienced at working with data files such as JSON / CSV / etc.
- Easy to add/annotate details on users to keep track of user defined metrics
- Ability to get analytics about metrics for progress-tracking
User stories
Priorities:
- High (must have) -
* * * - Medium (nice to have) -
* * - Low (unlikely to have) -
*
| Priority | As a … | I want to … | So that I can… |
|---|---|---|---|
* * * |
Telemarketer | import the list of customers | easily view all the contacts I need to call |
* * * |
Telemarketer | export the list of customers | send the list to my supervisor |
* * * |
Telemarketing Supervisor | merge my employees’ customer lists | easily view the contacts completion status of my subordinates |
* * * |
Telemarketing Supervisor | export the list of customers | send the list to my subordinates for them to complete |
* * * |
Telemarketer / Telemarketing Supervisor | save data from current session | save my current progress to continue during the next session |
* * * |
Telemarketer / Telemarketing Supervisor | load data from previous session | pick up where I left off from my previous session |
* * * |
Telemarketer | indicate my name under ‘profile’ | identify myself in progress reports for my supervisor |
* * * |
Telemarketer / Telemarketing Supervisor | indicate my role under ‘profile’ | get access to the functionalities that cater to my specific job |
* * * |
Telemarketer | mark a contact as completed | see that I have already called a contact successfully |
* * * |
Telemarketer | click to copy the contact’s phone number to my clipboard | easily transfer it to my calling app |
* * * |
Telemarketer | add remarks to my contacts | store additional long-form information about specific contacts |
* * * |
Telemarketing Supervisor | add a contact | add contacts that need to be called by my subordinates |
* * * |
Telemarketing Supervisor | delete a contact | remove contacts that no longer need to be tracked or have been added wrongly |
* * * |
Telemarketer / Telemarketing Supervisor | view progress on the contacts in a simple chart | so that I can easily get help whenever I need it without losing focus |
* * * |
Telemarketer / Telemarketing Supervisor | undo or redo previously made changes | revert any possible mistakes made |
* * * |
Telemarketer / Telemarketing Supervisor | tag contacts with additional information | get useful analytics on specific contacts and include useful information like assignee |
* * * |
Telemarketer / Telemarketing Supervisor | filter entries by tag | focus my view of contacts |
* * |
Telemarketer / Telemarketing Supervisor | view the user guide from the app | easily get help whenever I need it without losing focus |
* * |
Telemarketer / Telemarketing Supervisor | use up-down arrows to access command history | repeat previous commands without needed to type it out again |
* * |
Telemarketer / Telemarketing Supervisor | preview and confirm the contacts I’m exporting | reduce export mistakes |
* |
Telemarketer / Telemarketing Supervisor | press tab to autofill recommended tags | quickly fill out flags without needing to check the user guide |
* |
Telemarketer / Telemarketing Supervisor | view my command history | trace previously entered commands at a glance |
{ More to be added as new features are introduced }
Use cases
| Use Case ID | Description |
|---|---|
| UC01 | Supervisor adds a contact |
| UC02 | Supervisor deletes a contact |
| UC03 | Supervisor tags a contact |
| UC04 | Telemarketer sets a person as done |
| UC05 | Telemarketer adds a remark to a person |
| UC06 | Telemarketer imports a customer list from supervisor |
| UC07 | Telemarketer exports a customer contact file |
| UC08 | Telemarketer undoes Teletubbies |
| UC09 | Telemarketer redoes Teletubbies |
| UC10 | Telemarketer checks Command History |
| UC11 | User views the help page |
For all use cases below, the System is the Teletubbies application, and the Actor is the user, unless specified otherwise.
UC01: Supervisor adds a contact
MSS
- User adds a contact.
-
Teletubbies adds the contact to the contact list.
Use case ends.
Extensions
-
2a. Flags are missing for the command / Fields are invalid / Compulsory fields are empty.
-
2a1. Teletubbies shows an error message.
Use case ends.
-
-
2b. There is an existing contact with the same phone number being added.
-
2b1. Teletubbies shows an error message.
Use case ends.
-
*a. At any time, User chooses to undo UC08 or redo UC09.
UC02: Supervisor deletes a contact
MSS
- User requests to list contact.
- Teletubbies shows a list of contacts.
- User requests to delete a specific contact in the list.
-
Teletubbies deletes the contact.
Use case ends.
Extensions
-
2a. The list is empty.
Use case ends.
-
3a. The given index is invalid or the given phone number does not exist.
-
3a1. Teletubbies shows an error message.
Use case resumes at step 2.
-
*a. At any time, User chooses to undo UC08 or redo UC09.
UC03: Supervisor tags a contact
MSS
- User requests to list contact.
- Teletubbies shows a list of contacts.
- User requests to tag a specific contact with a name and value.
-
Teletubbies tags the contact appropriately.
Use case ends.
Extensions
-
3a. Teletubbies detects an invalid user input in the tag command.
-
3a1. Teletubbies displays an error message.
Use case ends.
-
*a. At any time, User chooses to undo UC08 or redo UC09.
UC04: Telemarketer sets a person as done
MSS
- User requests to mark a contact as done.
-
Teletubbies sets the completed status of the contact as
COMPLETE.Use case ends.
Extensions
-
1a. The specified contact index is invalid.
-
1a1. Teletubbies shows an error message.
Use case resumes at step 1.
-
*a. At any time, User chooses to undo UC08 or redo UC09.
UC05: Telemarketer adds a remark to a person
MSS
- User requests to add a remark to a contact.
-
Teletubbies adds the remark to the contact.
Use case ends.
Extensions
-
1a. The specified contact index is invalid.
-
1a1. Teletubbies shows an error message.
Use case ends.
-
-
1b. The remark is over the 40 character limit.
-
1b1. Teletubbies shows an error message.
Use case ends.
-
*a. At any time, User chooses the view help (UC11). *b. At any time, User chooses to undo UC08 or redo UC09.
UC06: Telemarketer imports a customer list from supervisor
MSS
- User starts up the Teletubbies desktop application.
- User selects and imports the JSON file containing the list of contacts for the shift from a file location.
- Teletubbies displays the imported contacts.
- User copies a customer’s contact number and makes the phone call.
- User marks the customer as completed.
-
Teletubbies saves the file and displays that the call for the contact is completed.
Use case ends.
Extensions
-
3a. Teletubbies does not detect a JSON file in the specified file path.
- 3a1. Teletubbies displays a message prompting the Telemarketer to try importing the JSON file again.
-
3a2. Telemarketer imports a JSON file containing the customers’ details.
Steps 3a1 to 3a2 are repeated until a valid JSON file is selected.
Use case resumes from step 4.
-
3b. Teletubbies detects that the JSON file selected is not in the correct format.
- 3b1. Teletubbies displays a message informing the Telemarketer that the file is not in the correct format.
-
3b2. Telemarketer informs their Supervisor about the error.
Use case ends.
-
5a. Teletubbies detects that the index used in the command is invalid.
- 5a1. Teletubbies displays a message to inform Telemarketer about the input error.
-
5a2. Telemarketer re-enters the command with a correct index number for the contact.
Steps 5a1 to 5a2 are repeated until a valid customer phone number is input.
Use case resumes from step 6.
*a. At any time, User chooses to undo UC08 or redo UC09.
UC07: Telemarketer exports a customer contact file
MSS
- User enters the
exportcommand. - Teletubbies requests for confirmation for export.
- User confirms request to export.
- Teletubbies opens the file chooser window.
- User selects a directory to export to.
-
Teletubbies exports the customer contact list.
Use case ends.
Extensions
-
1a. User chooses to export contacts with a specific tag.
-
1a1. Teletubbies displays the contact list with the specified tags.
Use case resumes at step 2.
-
-
3a. User enters an invalid command.
-
3a1. Teletubbies cancels the request to export.
Use case ends.
-
*a. At any time, User chooses to undo UC08 or redo UC09.
UC08: Telemarketer undoes Teletubbies
MSS
- User enters the
undocommand. -
Teletubbies undoes to the previous state.
Use case ends.
Extensions
-
1a. Teletubbies is in its earliest possible state.
- 1a1. Teletubbies shows an error message: “Teletubbies is currently at its earliest version and cannot be reverted.”.
*a. At any time, User chooses to undo UC08 or redo UC09.
UC09: Telemarketer redoes Teletubbies
MSS
- User enters the
redocommand. -
Teletubbies redoes to the previously undone state.
Use case ends.
Extensions
-
1a. Teletubbies is in its latest possible state.
- 1a1. Teletubbies shows an error message: “Teletubbies is currently at its latest version and cannot be redone.”.
*a. At any time, User chooses to undo UC08 or redo UC09.
UC10: Telemarketer checks Command History
MSS
- User enters the
historycommand. -
Teletubbies shows the recently keyed-in commands in chronologically-reverse order, with the latest command at the top after history.
Use case ends.
Extensions
*a. At any time, User chooses to undo UC08 or redo UC09.
UC11: User views the help page
MSS
- User enters the help command.
-
Teletubbies displays the user guide in a web view.
Use case ends.
Extensions
*a. At any time, User chooses to undo UC08 or redo UC09.
Non-Functional Requirements
- New telemarketers should be able to easily use the application. (Quality requirement)
- The application should save and load data fast, and startup should take a few seconds at the most. (Performance requirement / Response time)
- Data should be saved frequently and automatically to allow for easy recovery on possible crashes. (Disaster recovery)
- The product should work on all OS, and both 32-bit and 64-bit environments. (Technical / Environment requirement)
- The application should be able to efficiently (without a noticeable slowdown) handle standard operations (add, delete, search, etc) for up to 10,000 users. (Data requirement, ie. volatility)
- The product project is expected to adhere to a schedule that delivers a feature set every two weeks. (Process requirements)
- The product is not required to handle in-app sharing of data across users on different devices. (Project scope)
- The product should be available for potential testers to test the capabilities of the product / find any bugs or issues. (Testability)
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.
Managing contacts
Deleting a contact
- Deleting a contact with the correct command format, by referencing their index while all persons are being shown
- Prerequisites: List all contacts using the
listcommand. Multiple contacts in the list. - Test case:
delete -i 1
- Expected: First contact is deleted from the list. Details of the deleted contact shown in the status message. Pie chart will be updated to reflect the progress of the updated contact list.
- Test case:
delete -i 0
- Expected: No contact is deleted. Error details shown in the status message. Pie chart remains the same
- Other incorrect delete commands to try:
delete,delete -i x,...(wherexis larger than the list size)
- Expected: Similar to previous.
- Prerequisites: List all contacts using the
- Deleting a contact with the correct command format, by referencing their phone number while all contacts are being shown
- Prerequisites: List all contacts using the list command, Multiple contacts in the list.
- Test case:
delete -p 87654321- Expected: Assuming a contact with phone number 87654321 exists, that contact is deleted from the list. Details of the deleted contact shown in the status message. Pie chart will be updated to reflect the progress of the updated contact list.
- Test case:
delete -p 11111111- Expected: Assuming a contact with phone number 11111111 does not exist, no contact is deleted. Pie chart remains the same.
Adding a contact
- Adding a contact with the correct command format
- Prerequisites: None.
- Test case:
add -n John Doe -p 87654321- Expected: A contact named “John Doe” with phone number 87654321 will be added into the contact list. The pie chart will be updated to reflect the progress of the updated contact list.
- Test case:
add -n Albert Saw -p 98765432 -e albertsaw@gmail.com -a Tampines Street 23, Block 777, #05-12- Expected: A contact named “Albert Saw” with phone number 98765432, email address “albertsaw@gmail.com” and address “Tampines Street 23, Block 777, #05-12 “ will be added into the contact list.
- Adding a contact with an incorrect command format
- Prerequisites: None.
- Test case:
add -n John Doe -e johndoe@gmail.com- Expected: No contact is added. Error details are shown in the status message.
Editing a contact
- Editing a contact with the correct command format
- Prerequisites: List all contacts using the list command, Multiple contacts in the list
- Test case:
edit 1 -n Regina Phalange -p 999- Expected: The first contact in the list has name set to “Regina Phalange” and phone number 999
- Editing a contact with an incorrect command format (wrong email format)
- Prerequisites: List all contacts using the list command, Multiple contacts in the list
- Test case:
edit 1 -e help@- Expected: Error message detailing correct expected email format
- Editing a contact with an incorrect command format (wrong phone number format)
- Prerequisites: List all contacts using the list command, Multiple contacts in the list
- Test case:
edit 1 -p 99- Expected: Error message explaining phone number should be at least 3 digits long
Adding a remark to a contact
- Adding a remark to a contact with the correct command format (with
-rflag)- Prerequisites: List all contacts using the list command, Multiple contacts in the list
- Test case:
remark 1 -r Need to call back- Expected: A remark “Need to call back” is added to the first contact in the list.
- Test case:
remark 1 -r- Expected: Assuming that the contact has an existing remark, the existing remark is removed from the contact.
- Adding a remark to a contact with the correct command format (with no
-rflag)- Prerequisites: List all contacts using the list command, Multiple contacts in the list, contact at index 1 with remark: ‘Call back at 2pm’
- Test case: remark 1
- Expected: A message is shown that the remark has been removed from the first person on the contact list.
- Test case:
remark 1 remark- Expected: Error message about command format
- Other incorrect remark commands to try:
remark,remark -r x,...(wherexis larger than the list size or a negative number)
Tagging and removing tag from contact
- Adding a tag to a range of contacts with the correct command format
- Prerequisites: List all contacts using the list command, At least 6 contacts in the list
- Test case:
tag 1-4 -n Assignee -v Ken- Expected: A success message with the first 4 contacts in the list with the
Assignee: Kentag
- Expected: A success message with the first 4 contacts in the list with the
- Other correct inputs to try:
- Comma separated indices (e.g.
tag 1,2,3,4 -n Assignee -v Ken) - Range with single value (e.g.
tag 1 -n Assignee -v Ken) - Name and no value (e.g. tag 1-4 -n IMPORTANT)
- Comma separated indices (e.g.
- Adding a tag with incorrect command format (with value and no name)
- Prerequisites: List all contacts using the list command, Non-empty contact list
- Test case:
tag 1 -v TEST- Expected: Error message about command format
- Removing a tag from a range of contacts with correct command format (with name and value)
- Prerequisites: List all contacts using the list command, Contact list must be of length at least 6, and contacts at index 1, 3, and 5 with the tag
priority:IMPORTANT - Test case:
tagrm 1-6 -n priority -v IMPORTANT- Expected: No contact from index 1 to 6 (inclusive) should have the
priority: IMPORTANTtag
- Expected: No contact from index 1 to 6 (inclusive) should have the
- Prerequisites: List all contacts using the list command, Contact list must be of length at least 6, and contacts at index 1, 3, and 5 with the tag
- Removing a tag from a range of contacts with correct command format (with name and no value)
- Prerequisites: List all contacts using the list command, Contact list must be of length at least 6, with the contacts at index 1, 3, and 5 with the tag
priority:IMPORTANT, and contact at index 2 with the tagpriority: LOW - Test case:
tagrm 1-6 -n priority- Expected: No contact from index 1 to 6 (inclusive) should have a tag with the name
important, includingpriority: IMPORTANTandpriority: LOW.
- Expected: No contact from index 1 to 6 (inclusive) should have a tag with the name
- Prerequisites: List all contacts using the list command, Contact list must be of length at least 6, with the contacts at index 1, 3, and 5 with the tag
Setting Profile
- Setting the user profile with the correct command format
- Prerequisites: None.
- Note: Between test cases for the
profilecommand, set the fieldisProfileSetto false in thepreferences.jsonfile, which can be found in the same folder asteletubbies.jar. - Test case:
profile -n John Doe -role Telemarketer- Expected: A success message showing the user’s newly set name and role
- Test case:
profile -n John Boe -role Supervisor- Expected: A success message showing the user’s newly set name and role
- Test case:
profile -n -role Telemarketer- Expected: A success message showing the user’s newly set name (empty) and role
Importing, Exporting and Merging contacts
- If you have been using the app prior to this, exit the app and delete the contacts file
[JAR file location]/data/teletubbies.json. - Launch the Teletubbies app by double-clicking the jar file.
- Export contacts with a specified tag
- Prerequisites: Contacts list contains the sample data set.
- Test case:
export -t ProductB- Expected: The contact list would contain contacts that have the tag “ProductB”. There would also be a message requesting user confirmation for export.
- Test case:
y(continued)- Expected: File chooser window opens.
- Test case: Choose a file location and name file
- Expected: A success message showing that the export was successful. Original contact list is displayed
- Import contacts from a file
- Prerequisite: Completed the Export test case above or have a correctly formatted contact data file
- Test case:
import- Expected: File chooser window opens.
- Test case: Choose the file to import
- Expected: A success message showing that the import was successful. Contact list is updated with contacts from the imported file.
- Merge contacts from a file
- Prerequisite: Contact list has a few contacts. Export a subset of the contacts and make changes to the JSON file (E.g. change their particulars) ensuring that the JSON formatting remains correct
- Test case:
merge- Expected: File chooser window opens.
- Test case: Choose the file to merge
- Expected: A success message showing that the merge was successful. Contacts that were originally in the list are updated with the changes made in the merged list. Contacts that were not in the merged list remain unchanged.
- Other incorrect commands to try: Closing the file selector window
undo / redo
- If you have been using the application before this, exit the app
- Launch the application by double-clicking on the jar file.
- Prerequisites: None.
- Test case:
undo- Expected: An error message which says “Teletubbies is currently at its earliest version and cannot be reverted.”
- Test case: Clear the Teletubbies application by pressing
clearand typeundo- Expected: The application reverts back to the state before the
clearcommand was input.
- Expected: The application reverts back to the state before the
- Test case:
undo- Expected: An error message which says “Teletubbies is currently at its earliest version and cannot be reverted.”
- Test case:
redo- Expected: The empty teletubbies application which was present after
clear
- Expected: The empty teletubbies application which was present after
- Test case:
redo- Expected: An error message which says “Teletubbies is currently at its latest version and cannot be redone.”
- Test case: Enter 3 unique commands of either
add,delete,tag,done,remarkand take note of the order. These are some of the Contact Commands that you can type to modify contacts Key inundo3 times, and after eachundois keyed in, verify that the state corresponds with the state from before.- Expected: All states are verified to be accurate
- Test case: Enter one command of either
add,delete,tag,doneorremarkand ensure that a previous command was not keyed in. Key inundotwice.- Expected: The first
undocommand would return a Teletubbies state which corresponds to the state it was in when it originally started. The secondundocommand should return an error message which says “Teletubbies is currently at it’s earliest version and cannot be reverted.”
- Expected: The first
- Test case: Enter
redotwice- Expected: The first
redocommand returns the Teletubbies state which was present after 7a was executed. The secondredocommand should return an error message which says “Teletubbies is currently at it’s latest version and cannot be redone.”
- Expected: The first
history
- Prerequisite: Enter at least 5 commands after starting up the application and remember the order in which the commands were keyed in.
- Test case: Key in
history- Expected: a list of commands keyed in chronologically descending order, with history at the top.
Key bindings ([UP], [DOWN] and [TAB])
- [UP], and [DOWN] arrows keys to navigate command history
- Prerequisite: Enter at least 5 unique commands after starting up the application
- Test case: Pressing the [UP] and [DOWN] keys in some order, making sure to press [UP] more than 5 times, and [DOWN] more than 5 times in a row
- Expected: The command box text should correspond with the appropriate commands in the command history (viewable with the
historycommand). [UP] should show a previous command and [DOWN] should show a later command. If the first command is showing [UP] should not change the command box text. If the last command is showing, [DOWN] should not change the command box text.
- Expected: The command box text should correspond with the appropriate commands in the command history (viewable with the
- [TAB] key to autocomplete command flags
- Test case: Type the command
add(or another command) and press [TAB]- Expected: The command box text should show the command word, with the recommended flags. For add, it should show:
add -n -p -e -a
- Expected: The command box text should show the command word, with the recommended flags. For add, it should show:
- Test case: Type the command
Appendix: Effort
Teletubbies started out as a brownfield project that extended from AB3, and aimed to evolve it into an optimized contact manager application for a specific target audience (i.e. Telemarketers and Telemarketing supervisors).
Our team has put in a significant amount of effort during the development of Teletubbies. We currently have a total of over 10,000 lines of code contributed, and over 300 automated tests.
Through the development period, we have made significant progress in terms of new features and enhancements. Listed below are the achievements of our brownfield project, and the challenges that we faced during development.
Project Achievements & Challenges
- Enhancement of the CLI
As Teletubbies is a CLI-based application, we implemented various enhancements to the CLI, including:
- Ability to view command history
- Ability to access previous / next commands in the command history through up and down arrows
- Ability to auto-complete commands
- Ability to
undoandredocommands
The functionality of these commands were highly influenced by UNIX CLI. While the implementation of the command history and auto-complete features were not as complex, they nevertheless posed a moderate amount of challenge as we had to implement them from scratch.
Moreover, the implementations of undo and redo commands were more complex than we initially thought, as we wanted the undo and redo features to revert back any changes made to the application as a whole. Hence, we had to develop a way to store the state for the entire application and manage the different stored states over a single session, which took some time to even conceptualise (with discussions on various edge cases), and posed a significant challenge to implement.
- Enhancement of the GUI
While Teletubbies is a CLI-based application, it nevertheless uses a GUI for the users to interact with. Hence, we implemented various enhancements to the GUI as well:
- Overhaul of the GUI
- Ability to view progress of each contact as a progress bar
- Ability to view the aggregate progress of the entire contact list as a pie chart
- Ability to copy the phone number from a contact using a clipboard button
- Ability to view the User Guide in a web view directly from the application
The first GUI change that was made was the help feature. The original AB3 application simply had the help button show a copyable link to the User Guide, which was not very user-friendly. Hence, we decided to implement a web view of the User Guide within the application itself, as it seemed to be rather straightforward. However, this posed challenges not in terms of the actual implementation, but rather in terms of understanding the JavaFX mechanics, and as a result we spent some time on trying to resolve some JavaFX-related issues.
As an app that tracks progress of each contact within a list of contacts, we also decided that the ability to view individual contact progress and the total progress over the current contact list as part of the GUI would be crucial. Hence, we implemented a progress bar and a pie chart respectively, which again posed some level of challenge in terms of learning the JavaFX architecture and the syntax, but was not overly complex to implement. Finally, in order to seamlessly incorporate these features into our application, we had to overhaul the GUI layout in order to fit these features in.
- Implementation of Teletubbies-specific Features
- Ability to
import/export/mergecontact lists in JSON format - Ability to set user profile
- Reworked tagging system separate from the
addandeditcommands - Ability to add remarks to contacts
- Ability to
filterthe list based on tags
The main challenge we faced when transforming AB3 into Teletubbies was the interaction of the contact list data with the application itself. Hence, we had to spend a considerable amount of time discussing the architecture and the implementation of the import / export features, and eventually, we decided to expand the import feature into two separate features: import and merge.
Apart from the interaction of the application with the data files, we also reworked the existing systems and added in features that were Teletubbies-specific, such as the newly reworked tagging system that uses the tag and tagrm commands instead of the add and edit commands, the ability to add remarks to contacts via the remark command, filtering based on tags, and the ability for the user to set a profile that would determine the functionalities that the user has access to. The implementation for all these features also took some considerable amount of discussion and collaboration in order to ensure that all the features worked together seamlessly.
References
The Commands with UI effects section was in part adapted from the Developer’s Guide of CoLAB from AY20/21 since their product had a feature involving very similar implementation considerations and diagrams.