Developer Guide
CohortConnect v1.4
Type. Explore. Connect.CohortConnect is an advanced desktop address book which facilitates networking among Computer Science (CS) students. It is optimized for use via a Command Line Interface (CLI) while still having the benefits of a Graphical User Interface (GUI).
With advanced features for managing large groups of contacts, CohortConnect is intended for use in a university setting. At the start of the semester, professors will distribute csv
or json
files containing a list of students. Instantly load them into CohortConnect with a single Import command. With data collected from students before the semester, our Find A Buddy feature helps you find potential groupmates by leveraging GitHub’s metadata using a proprietary algorithm. In the Events tab, you can identify events and hackathons that your peers will be attending.
Table of Contents
- Table of Contents
- Purpose
- Prerequisites
- Acknowledgements
- Getting Started
- Design
-
Implementation
- Telegram
- Optional Fields
- GitHub
- Favorite command
- Unfavorite command
- Export command
- Import command
- Edit Profile command
- Find command
- Tag command
- Welcome Window
- Profile SetUp Window
- User Profile Window
- Help Window
- User Profile in Menu Bar
- Show command
- Command History (Keyboard Shortcut)
- Switching Between Tabs (Keyboard Shortcut)
- GitHub and Telegram Commands
- Find A Buddy Feature
- Documentation, logging, testing, configuration, dev-ops
- Appendix: Requirements
- Appendix: Instructions for manual testing
Purpose
This guide is intended for current or future developers working on features for CohortConnect, or developers who want to learn more about how CohortConnect is built. It explains the requirements of CohortConnect, the implementation of each individual component, as well as how they are pieced together to execute user commands.
Prerequisites
- General knowledge of Java.
- Object-Oriented Programming.
- UML Notation.
- Basic knowledge of
csv
andjson
file formats. - Basic knowledge of technical terms like
CLI
,UI
, andJAR
. -
Java 11
installed on the system.
Acknowledgements
- This project is based on the AddressBook-Level3 project created by the SE-EDU initiative.
- This project uses jackson-dataformat-csv for reading from and writing to CSV files.
Getting Started
Refer to our Quick Start in our User Guide.
Design
Architecture
The Architecture Diagram given above explains the high-level design of the App.
Given below is a quick overview of main components and how they interact with each other.
Main components of the architecture
Main
has two classes called Main
and MainApp
. It is responsible for,
- At app launch: Initializes the components in the correct sequence, and connects them up with each other.
- At shut down: Shuts down the components and invokes cleanup methods where necessary.
Commons
represents a collection of classes used by multiple other components.
The rest of the App consists of four components.
-
UI
: The UI of the App. -
Logic
: The command executor. -
Model
: Holds the data of the App in memory. -
Storage
: Reads data from, and writes data to, the hard disk.
How the architecture components interact with each other
The Sequence Diagram below shows how the components interact with each other for the scenario where the user issues the command delete 1
.
Each of the four main components (also shown in the diagram above),
- defines its API in an
interface
with the same name as the Component. - implements its functionality using a concrete
{Component Name}Manager
class (which follows the corresponding APIinterface
mentioned in the previous point.)
For example, the Logic
component defines its API in the Logic.java
interface and implements its functionality using the LogicManager.java
class which follows the Logic
interface. Other components interact with a given component through its interface rather than the concrete class (reason: to prevent outside component’s being coupled to the implementation of a component), as illustrated in the (partial) class diagram below.
The sections below give more details of each component.
UI component
The API of this component is specified in Ui.java
The UI consists of a MainWindow
that is made up of parts e.g.CommandBox
, ResultDisplay
, PersonListPanel
, StatusBarFooter
etc. All these, including the MainWindow
and WelcomeWindow
, inherit from the abstract UiPart
class which captures the commonalities between classes that represent parts of the visible GUI.
The UI
component uses the JavaFx UI framework. The layout of these UI parts are defined in matching .fxml
files that are in the src/main/resources/view
folder. For example, the layout of the MainWindow
is specified in MainWindow.fxml
The UI
component,
- executes user commands using the
Logic
component. - listens for changes to
Model
data so that the UI can be updated with the modified data. - keeps a reference to the
Logic
component, because theUI
relies on theLogic
to execute commands. - depends on some classes in the
Model
component, as it displaysPerson
object residing in theModel
.
Logic component
API : Logic.java
Here’s a (partial) class diagram of the Logic
component:
How the Logic
component works:
- When
Logic
is called upon to execute a command, it uses theAddressBookParser
class to parse the user command. - This results in a
Command
object (more precisely, an object of one of its subclasses e.g.,AddCommand
) which is executed by theLogicManager
. - The command can communicate with the
Model
when it is executed (e.g. to add a person). - The result of the command execution is encapsulated as a
CommandResult
object which is returned fromLogic
.
The Sequence Diagram below illustrates the interactions within the Logic
component for the execute("delete 1")
API call.

DeleteCommandParser
should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
Here are the other classes in Logic
(omitted from the class diagram above) that are used for parsing a user command:
How the parsing works:
- When called upon to parse a user command, the
AddressBookParser
class creates anXYZCommandParser
(XYZ
is a placeholder for the specific command name e.g.,AddCommandParser
) which uses the other classes shown above to parse the user command and create aXYZCommand
object (e.g.,AddCommand
) which theAddressBookParser
returns back as aCommand
object. - All
XYZCommandParser
classes (e.g.,AddCommandParser
,DeleteCommandParser
, …) inherit from theParser
interface so that they can be treated similarly where possible e.g, during testing.
Model component
API : Model.java
The Model
component,
- stores the address book data i.e., all
Person
objects (which are contained in aUniquePersonList
object). - stores the currently ‘selected’
Person
objects (e.g., results of a search query) as a separate filtered list which is exposed to outsiders as an unmodifiableObservableList<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. - stores a
UserPref
object that represents the user’s preferences. This is exposed to the outside as aReadOnlyUserPref
objects. - does not depend on any of the other three components (as the
Model
represents data entities of the domain, they should make sense on their own without depending on other components)

Tag
list in the AddressBook
, which Person
references. This allows AddressBook
to only require one Tag
object per unique tag, instead of each Person
needing their own Tag
objects.
Storage component
API : Storage.java
The Storage
component,
- saves both address book data and user preference data in json format, and read them back into corresponding objects.
- exports address book data JSON or CSV files.
- imports address book data from JSON or CSV files.
- inherits from both
AddressBookStorage
andUserPrefStorage
, which means it can be treated as either one (if only the functionality of only one is needed). - depends on some classes in the
Model
component (because theStorage
component’s job is to save/retrieve objects that belong to theModel
)
Common classes
Classes used by multiple components are in the seedu.addressbook.commons
package.
Implementation
This section describes noteworthy details on how certain features are implemented.
Telegram
Implementation
The telegram handle field is facilitated by the Telegram
class.
It is stored internally as a String
in the data file addressbook.json
and is then initialized as a Telegram
object.
The Telegram
class implements the following operation:
-
Telegram#isValidTelegram(String test)
— Returns true if a given string is a valid telegram handle with the help of a validation regex.
Regex used in verifying the validity of telegram handle:
public static final String VALIDATION_REGEX = "\\w{5,32}";
-
\w
— Word. Any word character (alphanumeric & underscore) -
{5,32}
— Quantifier. Match between 5 and 32 of the preceding token.
Design considerations
The Telegram
class is first integrated into the Person
class and added as
a new field to the Person
class. This is illustrated by the class diagram below,
where every field, including the Telegram
field, is compulsory except the Tag
field.
Optional Fields
Implementation
The Phone
, Email
and Address
fields are modified such that these fields are
no longer compulsory. The class diagram below illustrates the Person
class after
the addition of the Telegram
field. The Name
and Telegram
fields are compulsory
while the rest are optional.
In order to accomodate to the above mentioned new optional fields, the respective constructors were modified such that the following examples are considered valid inputs.
Example 1: Adding new contact without email and address.
add n/John Doe te/@johndoe123 p/98765432
Example 2: Adding new contact without phone, email and address.
add n/John Doe te/@johndoe123
A more interesting input would be as such.
Example 3: Adding new contact without email and address but with empty phone input.
add n/John Doe te/@johndoe123 p/
In such a case, the constructors are modified such that the above input is also deemed
as valid. The rationale behind this is that there is nothing for the VALIDATION_REGEX
to verify, unlike in the following example.
Example 4: Adding new contact without email and address but with invalid phone input.
add n/John Doe te/@johndoe123 p/invalidPhoneNumber
For the case above, the respective constructors will carry out validation on the given input.
Design considerations
In order to allow for optional fields, the AddCommandParser
also has to be modified.
In particular, the following methods are modified
-
AddCommandParser#arePrefixesPresent(argumentMultimap, prefixes)
— Returns true if none of the prefixes contains emptyOptional
values in the givenArgumentMultimap
. -
AddCommandParser#parse(args)
— Parses the givenString
of arguments in the context of theAddCommand
and returns anAddCommand
object for execution. ThrowsParseException
if the user input does not conform the expected format.
For the arePrefixesPresent
method, the prefixes provided were changed to only
include the following mandatory fields:
PREFIX_NAME
PREFIX_TELEGRAM
PREFIX_GITHUB
AddCommandParser#arePrefixesPresent(argumentMultimap, prefixes)
uses the static
parsing methods in ParserUtil
to parse the different fields in Person
.
The individual fields are first initialised with an empty string, which is now
a valid input. The method then calls the arePrefixesPresent
method to check if
the provided prefix is present. If present, the method will then call the respective
static parsing methods in ParserUtil
.
GitHub
Implementation
The GitHub field is facilitated by the Github
class. It is stored internally
as a String
in the data file addressbook.json
and is then initialized as a
Github
object.
The Github
class implements the following operation:
-
Github#isValidGithub(String test)
— Returns true if a given string is a valid GitHub username with the help of a validation regex.
Regex used in verifying the validity of GitHub username:
public static final String VALIDATION_REGEX = "[a-zA-Z\d](?:[a-zA-Z\d]|-(?=[a-zA-Z\d])){0,38}";
-
[a-zA-Z\d]
— Character set. Match any character in the set. -
a-z
— Range. Matches a character in the range “a” to “z” (char code 97 to 122). Case sensitive. -
A-Z
— Range. Matches a character in the range “A” to “Z” (char code 65 to 90). Case sensitive. -
\d
— Digit. Matches any digit character (0-9). -
(?:[a-zA-Z\d]|-(?=[a-zA-Z\d]))
— Non-capturing group. Groups multiple tokens together without creating a capture group. -
|
— Alternation. Acts like a boolean OR. Matches the expression before or after the sign. -
-
— Character. Matches a “-“ character (char code 45). -
{0,38}
— Quantifier. Match between 0 and 38 of the preceding token.
Design considerations
The Github
class is first integrated into the Person
class and added as
a new field to the Person
class. This is illustrated by the class diagram below,
where only the Name
, Telegram
and Github
fields are compulsory.
Favorite command
Implementation
The Favorite command favorites a non-favorited contact from the current list of contacts.
In order to distinguish whether a contact has been favorited or unfavorited,
a boolean flag, an isFavorite
field is added into the Person
class and
is initialised as false
when adding new contacts to the address book.
The following methods are added to Person
to help identify and toggle the isFavorite
field within a Person
object:
-
Person#isFavorite()
— Returns a boolean flag to tell whether thePerson
object is Favorited or not. -
Person#SetIsFavorite()
— Sets thePerson
objects’isFavorite
field totrue
, which basically favorites the contact of thisPerson
object. -
Person#SetNotIsFavorite()
— Sets thePerson
objects’isFavorite
field tofalse
, which basically unfavorites the contact of thisPerson
object.
Favoriting contacts is facilitated by the IsFavoritePredicate
.
-
IsFavoritePredicate
stores a boolean flag for comparison with aPerson
object. - It also has a
IsFavoritePredicate#test(Person person)
to determine if thePerson
input into the test is currently favorited or unfavorited.
Given below is an example usage scenario and how the FavoriteCommand
is executed:
-
Step 1: The user enters favorite command keyword followed by the index
of the contact to be favorited
fav 1
. -
Step 2: The
LogicManager
executes the input command text calling the methodLogicManager#execute(String commandText)
. -
Step 3: The
LogicManager#execute(String commandText)
method calls the following method fromAddressBookParser
,AddressBookParser#parseCommand(String userInput)
. -
Step 4: Since the command input is a command with description, the command
word
fav
and argument1
is passed intoAddressBookParser#parseCommandWithDescription(String commandWord, String arguments)
which creates a newFavoriteCommandParser
. -
Step 5:
FavoriteCommandParser#parse(String args)
is then called. Since the argument provided here,1
, is a valid argument for the favorite command, aFavoriteCommand(1)
is returned. -
Step 6: The
FavoriteCommand#execute(Model model)
method is then finally called, favoriting the contact at index1
through themodel
provided as input argument by callingModelManager#favoritePerson(Person target)
.
The Sequence Diagrams below illustrates how the components interact with each other
for the scenario where the user issues the command fav 1
.
Design considerations
Aspect: How to keep track of isFavorite
-
Alternative 1 (Current Implementation): Create a boolean flag within
Person
class which signifies that aPerson
is favorite iftrue
and vice versa.- Pros: Can be easily toggled since the execute method takes in model as arguments.
Model#getFilteredPersonList()
retrieves the list ofPerson
. The status of the boolean flag can be easily identified byPerson#isFavorite()
and easily toggled byPerson#SetIsFavorite()
orPerson#SetNotIsFavorite()
. - Cons: Increased coupling. The toggling of
isFavorite
should ideally be handled by another class, similar to the other fields inPerson
such asGithub
andTelegram
.
- Pros: Can be easily toggled since the execute method takes in model as arguments.
-
Alternative 2: Create a
Favorite
class which stores a boolean flag which signifies that aPerson
is favorite iftrue
and vice versa.- Pros: Reduced coupling. The toggling of
isFavorite
will no longer have to be handled by thePerson
class. This is similar to that of other fields such asGithub
andTelegram
where the contents of the respective fields are handled by their respective classes. - Cons: Does not make sense to create a new class to store a single boolean value.
Unlike other fields which will require validation with the help of a validation regex,
isFavorite
can only hold either atrue
orfalse
value. There is absolutely no need to validate the validity of the boolean values, much less create a validation regex.
- Pros: Reduced coupling. The toggling of
Unfavorite command
Implementation
The Unfavorite command unfavorites a favorited contact from the current list of contacts.
Similar to Favorite command, in order to distinguish whether a contact has
been favorited or unfavorited, a boolean flag, an isFavorite
field is added
into the Person
class and is initialised as false
when adding new contacts
to the address book.
The following methods are added to Person
to help identify and toggle the isFavorite
field within a Person
object:
-
Person#isFavorite()
— Returns a boolean flag to tell whether thePerson
object is Favorited or not. -
Person#SetIsFavorite()
— Sets thePerson
objects’isFavorite
field totrue
, which basically favorites the contact of thisPerson
object. -
Person#SetNotIsFavorite()
— Sets thePerson
objects’isFavorite
field tofalse
, which basically unfavorites the contact of thisPerson
object.
Similar to Favoriting contacts, Unfavoriting contacts is also facilitated by the
IsFavoritePredicate
.
-
IsFavoritePredicate
stores a boolean flag for comparison with aPerson
object. - It also has a
IsFavoritePredicate#test(Person person)
to determine if thePerson
input into the test is currently favorited or unfavorited.
Given below is an example usage scenario and how the UnfavoriteCommand
is executed:
-
Step 1: The user enters unfavorite command keyword followed by the index
of the contact to be unfavorited
unfav 1
. -
Step 2: The
LogicManager
executes the input command text calling the methodLogicManager#execute(String commandText)
. -
Step 3: The
LogicManager#execute(String commandText)
method calls the following method fromAddressBookParser
,AddressBookParser#parseCommand(String userInput)
. -
Step 4: Since the command input is a command with description, the command
word
unfav
and argument1
is passed intoAddressBookParser#parseCommandWithDescription(String commandWord, String arguments)
which creates a newUnfavoriteCommandParser
. -
Step 5:
UnfavoriteCommandParser#parse(String args)
is then called. Since the argument provided here,1
, is a valid argument for the unfavorite command, aUnfavoriteCommand(1)
is returned. -
Step 6: The
UnfavoriteCommand#execute(Model model)
method is then finally called, unfavoriting the contact at index1
through themodel
provided as input argument by callingModelManager#unfavoritePerson(Person target)
.
The Sequence Diagrams below illustrates how the components interact with each other
for the scenario where the user issues the command unfav 1
.
Design considerations
Aspect: How to keep track of isFavorite
-
Alternative 1 (Current Implementation): Create a boolean flag within
Person
class which signifies that aPerson
is favorite iftrue
and vice versa.- Pros: Can be easily toggled since the execute method takes in model as arguments.
Model#getFilteredPersonList()
retrieves the list ofPerson
. The status of the boolean flag can be easily identified byPerson#isFavorite()
and easily toggled byPerson#SetIsFavorite()
orPerson#SetNotIsFavorite()
. - Cons: Increased coupling. The toggling of
isFavorite
should ideally be handled by another class, similar to the other fields inPerson
such asGithub
andTelegram
.
- Pros: Can be easily toggled since the execute method takes in model as arguments.
-
Alternative 2: Create a
Favorite
class which stores a boolean flag which signifies that aPerson
is favorite iftrue
and vice versa.- Pros: Reduced coupling. The toggling of
isFavorite
will no longer have to be handled by thePerson
class. This is similar to that of other fields such asGithub
andTelegram
where the contents of the respective fields are handled by their respective classes. - Cons: Does not make sense to create a new class to store a single boolean value.
Unlike other fields which will require validation with the help of a validation regex,
isFavorite
can only hold either atrue
orfalse
value. There is absolutely no need to validate the validity of the boolean values, much less create a validation regex.
- Pros: Reduced coupling. The toggling of
Export command
Implementation
The Export command exports the current list of contacts to the specified JSON or CSV file, with help from Jackson, an external library. It does not overwrite existing files.
Executing an Export Command can be summarized with the following steps:
- User command is handed over to
ExportCommandParser
. After verification of arguments, an ExportCommand is generated. - Upon executing the
ExportCommand
, one of the 2 methods are called, depending on the file type specified.ImportCommand#exportAddressBookToJson(ReadOnlyAddressBook)
ImportCommand#exportAddressBookToCsv(ReadOnlyAddressBook)
Firstly, the user command is handed over to ExportCommandParser
. ExportCommandParser
implements Parser<ExportCommand>
. It implements the parse()
method, which parses the user’s command and returns an ExportCommand
with the filename. In parse()
, it verifies 3 requirements:
- Arguments are not empty
- Not more than 1 argument
- Filename provided is a .csv or .json file
The Sequence Diagram below illustrates the interactions within the Logic
component during the first step of executing execute("export cs2103t.json")
.
Next, The ExportCommand
is then executed in LogicManager
. The ExportCommand
class extends Command
. It stores the filename as a class variable and its implementation of Command#execute()
. Here, the filename is checked again to verify that it is a .csv or .json file. The current address book is obtained by Model#getAddressBook()
. Then, depending on the file type, one of the following methods are called:
-
exportAddressBookToJson(ReadOnlyAddressBook)
A temporary instance of
JsonAddressBookStorage
is created by passing in the filename, andsaveAddressBook(ReadOnlyAddressBook)
saves the current address book to the specified file.temporaryStorage.saveAddressBook(currentAddressBook, filePath, true);
Note: The third argument
true
indicates thatsaveAddressBook(ReadOnlyAddressBook)
is triggered by an export command. This distinction is necessary as default saving of contact data to the address book is performed with the same method, but requires overwriting of the storage file, whereas the export command does not overwrite files.In the Storage component, utility classes are used to check, create and write to the JSON file.
-
FileUtil#isFileExists(Path)
checks if there exists a file with the specified name. If there is, aFileAlreadyExistsException
is thrown. -
FileUtil#createIfMissing(Path)
creates the JSON file. - The current
ReadOnlyAddressBook
is used to instantiate a newJsonSerializableAddressBook
, a JSON-friendly equivalent ofReadOnlyAddressBook
containing Jackson annotations. It stores a list ofJsonAdaptedPerson
instead ofPerson
, which is also JSON-friendly. -
JsonUtil#saveJsonFile(T, Path)
serializes the address book and writes to the file, usingObjectMapper
, a Jackson class.
-
-
exportAddressBookToCsv(ReadOnlyAddressBook)
A temporary instance of
CsvAddressBookStorage
is created by passing in the filename, andsaveAddressBook(ReadOnlyAddressBook)
saves the current address book to the specified file.temporaryStorage.saveAddressBook(currentAddressBook, filePath);
In the Storage component, utility classes are used to check, create and write to the JSON file.
-
FileUtil#isFileExists(Path)
checks if there exists a file with the specified name. If there is, aFileAlreadyExistsException
is thrown. -
FileUtil#createIfMissing(Path)
creates the CSV file. - The current
ReadOnlyAddressBook
is used to instantiate a newCsvSerializableAddressBook
, a CSV-friendly equivalent ofReadOnlyAddressBook
containing Jackson annotations. It stores a list ofCsvAdaptedPerson
instead ofPerson
, which is also CSV-friendly. -
CsvUtil#saveCsvFile(T, Path)
serializes the address book and writes to the file, usingCsvMapper
, a Jackson class.
-
The Sequence Diagram below illustrates the interactions during the second step of executing execute("export cs2103t.json")
.
Import command
The Import command imports contacts from a JSON or CSV file and adds them to the current contact list, with help from Jackson, an external library. Import does not remove existing contacts, and ignores duplicate contacts.
Executing an Import command can be summarized with the following steps:
- Similar to Export, the first step is parsing the user command, which is done in
ImportCommandParser
. After verification, anImportCommand
is generated. - Upon executing the
ImportCommand
, one of the 2 methods are called to obtain aList<Person>
from the specified file.ImportCommand#getImportedPersonsFromJson()
ImportCommand#getImportedPersonsFromCsv()
- By iterating through the list, each person is added to the current contact list in
Model
, skipping duplicates. - Command Result with success message is returned.
The sequence diagram for the first step is similar to the Export Command. The following sequence diagram illustrates the execution of an import command from part 2 onwards.
Reading JSON / CSV file
In both scenarios, a new JsonAddressBookStorage
or CsvAddressBookStorage
is created. The AddressBookStorage
method readAddressBook()
then reads the respective file using JsonUtil#readJsonFile()
or CsvUtil#readCsvFile()
. In both cases, the files are read using Jackson’s ObjectMapper
or CsvMapper
classes respectively.
Edit Profile command
Implementation
Edits the user’s profile linked to the Address Book.
The user’s profile contains details such as their name, Telegram Handle and GitHub username, which need to be kept up to date. This is especially important as the user’s GitHub username is crucial for the Find a Buddy feature which matches them with a potential teammate using the GitHub metadata.
The edit profile feature allows edit to change their name, Telegram handle and GitHub username.
The implementation for editing the user profile is similar to that of editing a student contact.
It is facilitated by the EditCommandParser
class, which implements Parser<EditCommand>
.
It implements the parse()
method, which determines whether what’s being edited is a contact or the user profile, checks for
the validity of user input (through the checkEditProfileInputFormat()
method) and returns an EditCommand
, to be executed in
LogicManager
.
The EditCommand
class extends Command
. Its instance is created by providing an index
(since a contact isn’t being edited here,
the index passed is 1 by default and will not affect the process), and an editPersonDescriptor
(which represents the updated user profile).
Its implementation of Command#execute()
calls the executeEditProfile()
method which edits the user profile as necessary.
The Sequence Diagram below illustrates the interactions within the Logic
and Model
components for
the execute("edit profile te/john_doe g/john-codes")
API call.
Find command
Implementation
The Find command accepts names, tags, Telegram handles or GitHub usernames as parameters with the following prefixes :
- no prefix : for names
-
t/
: for tags -
te/
: for Telegram handles -
g/
: for GitHub usernames
and allows users to search for contacts based on the specified criterion.
It is facilitated by the FindCommandParser
class, which implements Parser<FindCommand>
.
It implements the parse()
method, which parses the find parameter (eg: name, tag, etc.) and returns a FindCommand
, to be executed in
LogicManager
.
The FindCommand
class extends Command
. Its instance is created by providing a predicate (condition to be fulfilled by the elements of
the FilteredPersonList
that contains the contact(s) matching the find parameters). Its implementation of
Command#execute()
updates the FilteredPersonList
to reflect the search performed on the contacts in the address book.
The Sequence Diagram below illustrates the interactions within the Logic
and Model
components for
the execute("find Bob Joe")
API call.
The Sequence Diagram below illustrates the interactions within the Logic
and Model
components for
the execute("find t/friends teammates")
API call.
The Sequence Diagram below illustrates the interactions within the Logic
and Model
components for
the execute("find te/alex_1")
API call.
The Sequence Diagram below illustrates the interactions within the Logic
and Model
components for
the execute("find g/alex-coder")
API call.
Tag command
Implementation
The Tag command allows users to directly add or remove tags from a specific contact. This command was introduced to overcome the following limitations:
- Editing a contact’s tag field using
edit <INDEX> t/<TAG>
will replace the existing tag with the specified one instead of adding on to it. - No way to remove tags from a contact directly.
It is facilitated by the TagCommandParser
class, which implements Parser<TagCommand>
.
It implements the parse()
method, which parses the index of the contact to which tags are to be added or from which
tags are to be removed. Moreover, checking the validity of the user input (i.e. ensuring the presence of arguments for the Tag
command like tags to add where the prefix a/
is present and the
presence of tags to remove where the prefix r/
is present) is handled by the checkInputFormat
method. Once the input is confirmed to be valid, parse()
returns a TagCommand
, to be executed in
LogicManager
.
The TagCommand
class extends Command
. Its instance is created by providing the targetIndex
of the contact to which
tags are to be added or from which tags are to be removed (of type Index
), an ArrayList containing the tags to be
added (toAdd
) and an ArrayList containing the tags to be removed (toRemove
). Its implementation of Command#execute()
is where the updation of the FilteredPersonList
to reflect the search performed on the contacts in the address book.
The Sequence Diagram below illustrates the interactions within the Logic
and Model
components for
the execute("tag 1 a/friends")
API call.
The Sequence Diagram below illustrates the interactions within the Logic
and Model
components for
the execute("tag 1 a/friends r/family")
API call.
Welcome Window
Implementation
The class WelcomeWindow
is responsible for displaying the welcome window at
the start when the application is launched. It is facilitated by the
WelcomeWindow.fxml
and WelcomeWindow.css
. The .fxml
file is responsible
for the layout of the various components in this window, and the .css
file
adds a style and enhances the overall UI.
The WelcomeWindow
class extends UiPart<Stage>
.
When the app is launched, an instance of this class is created, and the
WelcomeWindow#start()
is invoked to display the window. Various methods, including
fadeTransition()
and displayAnimatedText(String textToDisplay, double delayTime)
, are used within this class to achieve the fading image and character typing
effect, respectively.
Profile SetUp Window
Implementation
The class ProfileSetUpWindow
is responsible for displaying the Profile SetUp Window.
It is only shown once when the User launches the app for the first time, and their
credentials are not present. They have to input their valid credentials here.
It is facilitated by ProfileSetUpWindow.fxml
and
ProfileSetUpWindow.css
. The .fxml
file is responsible for the layout of the
various components in this window, and the .css
file adds a style and enhances
the overall UI.
The ProfileSetUpWindow
class extends UiPart<Stage>
.
This window would only be visible after the Welcome Window Splash Screen.
WelcomeWindow
hands over the control to MainWindow
after execution, which
then sets up the ProfileSetUpWindow
. On Initializing and calling start()
method of the ProfileSetUpWindow
, the ProfileSetUpWindow
with the help of
a Logic
object (which is obtained during initialization) checks, if a
User Profile is present. If it is present, it
again hands over the control to the MainWindow
by calling the start()
method
of the MainWindow
. Else, It displays the Profile SetUp Window and
waits for a response from the User.
After the User has input their credentials in the text fields, they are expected to
click the Submit
button. When that button is clicked upon, the submit()
method is
invoked. This method calls the areUserCredentialsValid()
method to verify if the
entered credentials are valid or not. If they are valid, the User Profile is deemed
complete and is set up with the help of the Logic
object obtained during
the initialization. If the credentials are not valid, an error message
is shown in the Window, highlighting which credential is invalid. The User cannot proceed
forward without entering all valid Credentials.
The areUserCredentialsValid()
class checks if the entered Name, Telegram Handle,
and the GitHub Username are valid.
- Class level method
isValidName()
, ofName
class verifies the Name entered. - Class level method
isValidTelegram()
, ofTelegram
class verifies the Telegram Handle entered. - Class level method
isValidGithub()
, ofGithub
class verifies the GitHub Username entered.
If either of the credentials is not valid, the setErrorMessageText(MESSAGE)
is
called with the appropriate error message. This method is responsible for displaying
this error message in the Window.
A Typical Scenario Would be like:
If The User Enters an Invalid Telegram Credential:
The Big Picture (High Level Diagram):
User Profile Window
Implementation
The class UserProfileWindow
is responsible for displaying the User Profile
Window. It is shown when the user either uses the keyboard shortcut,
Command/Control + P
, or clicks on the User Profile located in the top right in
the Menu Bar. It is facilitated by UserProfileWindow.fxml
and UserProfileWindow.css
. The .fxml
file is responsible for the layout of the
various components in this window, and the .css
file adds a style and enhances the
overall UI.
The UserProfileWindow
class extends UiPart<Stage>
.
This window can only be viewed when the app has successfully started up and has valid User Credentials.
This window is initialized when the MainWindow
is initialized. It is
initialized in the MainWindow
constructor. This window, to be seen, has to be
triggered as an event by the user. The MainWindow
class has a method
handleUserProfileWindow()
, which is responsible for displaying this window.
The handleUserProfileWindow()
when called, calls the method isShowing()
via
the object initialized earlier. If it is not showing, the show()
method of the
UserProfileWindow
class is called upon. Else, if it is already showing, the
focus()
method is called upon. Along with that, in case the window had been
minimized by the User, UserProfileWindow#getRoot()#toFront()
is called to
bring the window to the maximized state.
The UserProfileWindow#show()
, first calls the UserProfileWindow#initializeFields()
,
to initialize all the fields with the latest User Profile Credentials. This would, in turn
also call UserProfileWindow#setFields()
, to set them up in the UI. After all the setting
up is done, the User Profile Window is shown.
The UserProfileWindow#focus()
, calls the getRoot()
method and on that requestFocus()
is called upon to request focus.
Help Window
Implementation
The class HelpWindow
is responsible for displaying the Help
Window. It is shown when the user either uses the keyboard shortcut,
F1
, or clicks on Help
located on the top left in the Menu Bar, or types
in help
in the command box. It is facilitated by HelpWindow.fxml
and HelpWindow.css
. The .fxml
file is responsible for the layout of the
various components in this window, and the .css
file adds a style and enhances the
overall UI.
The HelpWindow
class extends UiPart<Stage>
.
This window can only be viewed when the app has successfully started up and has valid User Credentials.
This window is initialized when the MainWindow
is initialized. It is
initialized in the MainWindow
constructor. This window, to be seen, has to be
triggered as an event by the user. The MainWindow
class has a method
handleHelpWindow()
, which is responsible for displaying this window.
On initializing the HelpWindow
class, HelpWindow#setUpCommandDetails()
and
HelpWindow#setUpHelpTableView()
are called.
setUpCommandDetails()
creates multiple objects of CommandDetails
, all of them
representing a unique command that the app supports. Those are then added to an
ObservableList<CommandDetails>
, which is linked to the TableView
in the UI.
setUpHelpTableView()
sets and places various restrictions on the table.
It restricts any events or scrolling on the table. Also, It adjusts the height
of the table according to the number of CommandDetails
present in it.
The handleHelpWindow()
when called, calls the method isShowing()
via
the object initialized earlier. If it is not showing, the show()
method of the
HelpWindow
class is called upon. Else, if it is already showing, the
focus()
method is called upon. Along with that, in case the window had been
minimized by the User, HelpWindow#getRoot()#toFront()
is called to
bring the window to the maximized state.
There is also a button present in the window, Visit URL
, which upon
clicking, takes the User to the UserGuide. It opens the UserGuide in the
Users systems default browser.
User Profile in Menu Bar
Implementation
The class UserProfileInMenuBar
is responsible for displaying the User
Profile in the Menu Bar. It is shown in the Main Window, in the top right
of the Menu Bar beside the Bell icon. It is facilitated by UserProfileInMenuBar.fxml
.
The .fxml
file is responsible for the layout of the various components inside
this Region. On clicking this, the UserProfileWindow
is shown.
The UserProfileInMenuBar
class extends UiPart<Region>
and implements
UserProfileWatcher
.
This Region can only be viewed when the app has successfully started up and has valid User Credentials.
This Region is initialized when the MainWindow#start()
is called. On
initializing the UserProfileInMenuBar
class, UserProfileInMenuBar#setUserProfileOnMenuBar()
and UserProfileInMenuBar#addToUserProfileWatcherList()
are called.
setUserProfileOnMenuBar()
is responsible for retrieving the User Credentials with
the help of the Logic
object obtained during initialization and setting up the
ImageView
and Label
with the User Credentials retrieved.
addToUserProfileWatcherList()
is responsible for adding this
(UserProfileWatcher)
to a watchers list, such that, in the scenario, the User updates their profile
credentials, the changes are reflected immediately. As the User is able to
edit their Credentials via the EditCommand
, the profile watchers list is present there.
A static method in EditCommand
, addUserProfileWatcher(this)
is called upon, by
passing this
(UserProfileWatcher) as an argument, to add it to the profile watchers list.
Whenever there is a change in the User Credentials, the updateUserProfile()
is
called upon in the UserProfileInMenuBar
, which in turn calls the
setUserProfileOnMenuBar()
. This method then retrieves the new User Credentials
and sets them up.
Show command
Implementation
The Show command accepts the index in list or name of a contact as a parameter, and shows all the details of the contact to the
Person Details Pane on the right-hand side of the screen. It is facilitated by the ShowCommandParser
class, which implements Parser<ShowCommand>
.
It implements the parse()
method, which parses the index/name and returns a ShowCommand
, to be executed in
LogicManager
.
The ShowCommand
class extends Command
. It stores the index/name as a class variable and its implementation of
Command#execute()
calls Command#executeWithIndex()
or Command#executeWithName()
based on whether parameter is index or name.
Implementation of Command#executeWithIndex()
This is called if the parameter is a parsable Integer.
The method calls model.setSelectedIndex(index)
which displays the details of the contact on the Details Pane.
The Sequence Diagram below illustrates the interactions within the Logic
, Model
and UI
components for
the execute("show 5")
API call.
Implementation of Command#executeWithName()
This is called if the parameter is NOT a parsable Integer.
The method calls model.setSelectedIndex(index)
with index gotten from the filteredList
which displays the details of the contact on the Details Pane.
If there are multiple contacts with names that contain the parameter keyword then a list of such persons are shown.
If there is no contact with name that contain the parameter keyword in the filteredList
, then the entire address book is searched.
The Sequence Diagram below illustrates the interactions within the Logic
, Model
and UI
components for
the execute("show alex")
API call.
Command History (Keyboard Shortcut)
In the Command Box, users can use up and down arrow keys to navigate through previous commands, similar to the Command Line. This is achieved by interactions between CommandHistory
and CommandBox
.
The CommandHistory
class contains these key variables and methods:
- An
ArrayList<String>
which stores previous user commands - An
int
which indicates the current index - Methods
add(String)
,getPrevious()
,getNext()
,resetIndex()
When CohortConnect UI is initialized, the CommandBox
class representing the Command Box is created, containing a single instance of CommandHistory
. It interacts with CommandHistory
in 3 ways:
-
If
KeyCode.UP
is detected in theTextField
,CommandHistory#getPrevious()
is called, and the returned String is set to the text field.Note: If the history is empty, UP will return an empty string. If the current index is at the earliest command, it will continue to return the same command.
The activity diagram below illustrates what happens when a user presses the UP arrow key.
-
If
KeyCode.DOWN
is detected in theTextField
,CommandHistory#getNext()
is called, and the returned String is set to the text field.Note: If the history is empty or the current index is at the latest command, DOWN will return an empty string.
The activity diagram below illustrates what happens when a user presses the DOWN arrow key.
-
When the user presses enter to execute a command, the command is saved using
CommandHistory#add(String)
, and the current index is reset usingCommandHistory#resetIndex()
.
Switching Between Tabs (Keyboard Shortcut)
Accelerators are shortcuts to Menu Items, which can be associated with KeyCombinations
. To switch between the 4 tabs, a new Menu Window was added in the MenuBar, containing 4 Menu Items which can be accessed using Cmd/Ctrl + 1/2/3/4. Each of the Menu Items have an onAction
method declared in their FXML files, and defined in MainWindow.java
.
-
Accelerators for the 4 Menu Items are set during initialization of
MainWindow.java
.private void setAccelerators() { setAccelerator(contacts, KeyCombination.valueOf("Shortcut+1")); setAccelerator(favorites, KeyCombination.valueOf("Shortcut+2")); setAccelerator(events, KeyCombination.valueOf("Shortcut+3")); setAccelerator(findABuddy, KeyCombination.valueOf("Shortcut+4")); ... }
Note: When detecting KeyCombination, Javafx automatically switches its interpretation of “Shortcut” as “Ctrl” for Windows and “Cmd” for macOS. The symbols in the MenuItem (shown above) change as well.
-
When their respective key combinations are detected, the MenuItem’s onAction methods are called. For example, the following method is called when Cmd/Ctrl + 4 is detected.
@FXML public void handleFindABuddy() { tabPaneHeader.activateFindABuddy(logic); }
-
TabPaneHeader
then switches to the Find A Buddy tab usingtabPane.getSelectionModel().select(3)
.
GitHub and Telegram Commands
In the Command Box, users can enter g
or te
to open the currently selected user’s GitHub or Telegram in a browser, using their respective username.
The following links are used
- GitHub:
https://github.com/{username}
- Telegram:
https://t.me/{username}
Similar to other commands, the commands g
and te
are parsed in AddressBookParser
, where it is checked that there are no other arguments.
During their execution, it is checked that there is a current user selected, using Model#getSelectedIndex()
. If the returned value is -1
, then a CommandException
is thrown.
The CommandResult returned indicates whether it is triggered by a GitHub or Telegram command using booleans variables.
In MainWindow
, if the command result isGithub()
or isTelegram()
, the GitHub and Telegram links in PersonDetails
will be triggered using PersonDetails#openTelegram()
and PersonDetails#openGithub()
.
Find A Buddy Feature
By switching to the Find A Buddy tab, the user can retrieve the top 5 matches to the user based on the GitHub data gathered.
Implementation
For the feature, the data gathered includes
- Number of Repositories
- Percentage of Contributions by Language
A similarity score is calculated based on the following three metrics:
- Euclidean Distance
- Manhattan Distance
- Cosine Distance
The similarity is calculated using the following formula:
Similarity Score = 1 - Normalize((Euclidean Distance + Manhattan Distance + Cosine Distance) / 3.0)
Documentation, logging, testing, configuration, dev-ops
Appendix: Requirements
Product scope
Target user profile:
- Computer Science students
- needs to manage a significant number of contacts
- needs help finding teammates
- prefer desktop apps over other types
- can type fast
- prefers typing to mouse interactions
- is reasonably comfortable using CLI apps
Value proposition: Facilitates networking among Computer Science students.
User stories
Priorities: High (must have) - * * *
, Medium (nice to have) - * *
, Low (unlikely to have) - *
Priority | As a … | I want to … | So that I can… |
---|---|---|---|
* * * |
user | delete contacts | make sure that the address book remains relevant and up to date |
* * * |
user | edit individual contacts | update any of the fields when necessary |
* * * |
user | add individual contacts | reach out to them later |
* * |
user | search for contact(s) by tag(s) | contact them based on their grouping |
* * |
user | search for contact(s) by Telegram handles(s) | contact them conveniently |
* * |
user | search for contact(s) by GitHub username(s) | contact them conveniently |
* * * |
professor | be able to export a set of contacts | let other professors, TAs and students get a set of contacts quickly |
* * * |
new user | be able to import a set of contacts | have some to begin with |
* * * |
new user | have an introduction splash screen | utilise the app and its feature well |
* * * |
CLI user | avoid using my mouse as much as possible | - |
* * * |
user | group/tag people into teammates, classmates, TA’s and Profs | easily manage my contacts |
* * * |
user | connect with seniors who have taken the same module | have more guidance |
* * * |
Teaching assistant | be able to get in touch with the students in my class through Telegram handles | - |
* * * |
new user | know all the different commands | fully utilise the tools available |
* * * |
new user | learn the command formats | perform tasks quickly and efficiently |
* * * |
user | be able to store my contact omitting certain fields | save contact without having to include email or address |
* * |
user | have a clean and uncluttered GUI | navigate easily between different functions in the application |
* |
user | retrieve previous and next commands with up and down arrow key | browse my command history to retype misspelled commands |
* * * |
user | sync my data with GitHub account | identify my colleagues by their profiles and connect with other users |
* * * |
user | view the profiles of my contacts | learn more about them and connect with them |
* * * |
user | navigate to a contact’s Telegram or GitHub in a single click | easily contact and interact with them |
* * * |
professor | import and export contacts using CSV files | easily create list of contacts using Excel, for my students to import |
* * * |
user | favorite and unfavorite my contacts | easily identify important people |
* * * |
user | enter my details | make use of the Find A Buddy feature |
* * * |
user | find a buddy | work with them for group projects or as a study buddy |
* * |
user | switch between tabs using keyboard shortcuts | navigate between tabs quickly |
* * |
user | edit my profile | modify my details is they are misspelled during set up |
* * |
user | see my profile | verify that my details are correct |
* * * |
user | open a contact’s Github or Telegram with a single command | easily browse their Github page or reach them on Telegram |
Use cases
(For all use cases below, the System is the CohortConnect
and the Actor is the user
, unless specified otherwise)
Use Case 1: Setting Up User Profile
MSS
- User enters their Name.
- User enters their Telegram Handle.
- User enters their GitHub Username.
- User clicks on the submit button.
-
CohortConnect shows the Main Window, signifying that the User Profile was set up.
Use case ends.
Extensions
- 4a. CohortConnect detects an error in the entered Name (Invalid Name).
- 4a1. CohortConnect shows an error message.
- 4a2. CohortConnect requests for a valid Name.
- 4a3. User enters new Name.
- 4a1-4a3 are repeated until the Name entered is valid.
- Use case resumes from step 5.
- 4b. CohortConnect detects an error in the entered Telegram Handle (Invalid Telegram Handle).
- 4b1. CohortConnect shows an error message.
- 4b2. CohortConnect requests for a valid Telegram Handle.
- 4b3. User enters new Telegram Handle.
- 4b1-4b3 are repeated until the Telegram Handle entered is valid.
- Use case resumes from step 5.
- 4c. CohortConnect detects an error in the entered GitHub Username (Invalid GitHub Username).
- 4c1. CohortConnect shows an error message.
- 4c2. CohortConnect requests for a valid GitHub Username.
- 4c3. User enters new GitHub Username.
- 4c1-4c3 are repeated until the GitHub Username entered is valid.
- Use case resumes from step 5.
- *a. At any time, the User chooses to close the app.
- *a1. CohortConnect closes.
Use case ends.
Use Case 2: Add user
MSS
- User enters command to add a contact.
- CohortConnect shows a successfully added message.
-
CohortConnect shows the updated list of contacts.
Use case ends.
Extensions
- 1a. The input command is invalid.
- 1a1. CohortConnect shows an error message.
- 1a2. CohortConnect requests for correct format.
- 1a3. User enters new data.
- 1a1-1a3 are repeated until the data entered are valid.
- Use case resumes from step 2.
- 1b. User enters an existing Telegram handle.
- 1b1. CohortConnect prompts that person already exists.
- 1b2. CohortConnect requests for correct format.
- 1b3. User enters new data.
- Steps 1b1-1b3 are repeated until the data entered are valid.
- Use case resumes from step 2.
- 1c. User enters an existing GitHub username.
- 1c1. CohortConnect prompts that person already exists.
- 1c2. CohortConnect requests for correct format.
- 1c3. User enters new data.
- Steps 1c1-1c3 are repeated until the data entered are valid.
- Use case resumes from step 2.
- 1d. The input command is missing compulsory fields.
- 1d1. CohortConnect shows an error message.
- 1d2. CohortConnect requests for compulsory fields.
- 1d3. User enters new data.
- 1d1-1d3 are repeated until the data entered are valid.
- Use case resumes from step 2.
Use Case 3: Edit user
MSS
- User enters command to edit a contact.
- CohortConnect shows a successfully edited message.
-
CohortConnect shows the updated list of contacts.
Use case ends.
Extensions
- 1a. The given index is not present.
- 1a1. CohortConnect shows an error message.
- 1a2. CohortConnect requests for correct format.
- 1a3. User enters new data.
- Steps 1a1-1a3 are repeated until the data entered are valid.
- Use case resumes from step 2.
- 1b. The given arguments are incorrectly formatted.
- 1b1. CohortConnect shows an error message.
- 1b2. CohortConnect requests for correct format.
- 1b3. User enters new data.
- Steps 1b1-1b3 are repeated until the data entered are valid.
- Use case resumes from step 2.
Use Case 4: Edit profile
MSS
- User enters command to edit profile.
- CohortConnect shows a successfully edited message.
-
CohortConnect updates user’s profile with the new details.
Use case ends.
Extensions
- 1a. The input command is invalid.
- 1a1. CohortConnect shows an error message.
- 1a2. CohortConnect requests for correct format.
- 1a3. User enters new data.
- 1a1-1a3 are repeated until the data entered are valid.
- Use case resumes from step 2.
Use Case 5: Add tags
MSS
- User enters command to add tags to a specific contact.
- CohortConnect shows a successfully added message.
-
CohortConnect updates tags of specific contact to contain new tags.
Use case ends.
Extensions
- 1a. The input command is invalid.
- 1a1. CohortConnect shows an error message.
- 1a2. CohortConnect requests for correct format.
- 1a3. User enters new data.
- 1a1-1a3 are repeated until the data entered are valid.
- Use case resumes from step 2.
- 1b. User tries to add existing tag.
- 1b1. CohortConnect prompts that the tag already exists.
- 1b2. CohortConnect requests for correct format.
- 1b3. User enters new data.
- Steps 1b1-1b3 are repeated until the data entered are valid.
- Use case resumes from step 2.
Use Case 6: Remove tags
MSS
- User enters command to remove tags from a specific contact.
- CohortConnect shows a successfully removed message.
-
CohortConnect updates tags of specific contact.
Use case ends.
Extensions
- 1a. The input command is invalid.
- 1a1. CohortConnect shows an error message.
- 1a2. CohortConnect requests for correct format.
- 1a3. User enters new data.
- 1a1-1a3 are repeated until the data entered are valid.
- Use case resumes from step 2.
- 1b. User tries to remove non-existent tag.
- 1b1. CohortConnect prompts that the tag to be removed does not exist.
- 1b2. CohortConnect requests for correct format.
- 1b3. User enters new data.
- Steps 1b1-1b3 are repeated until the data entered are valid.
- Use case resumes from step 2.
Use Case 7: Delete user
MSS
- User enters command to delete a contact.
- CohortConnect shows a successfully deleted message.
-
CohortConnect shows the updated list of contacts.
Use case ends.
Extensions
- 1a. The given index is not present.
- 1a1. CohortConnect shows an error message.
- 1a2. CohortConnect requests for correct format.
- 1a3. User enters new data.
- Steps 1a1-1a3 are repeated until the data entered are valid.
- Use case resumes from step 2.
- 1b. The given arguments are incorrectly formatted.
- 1b1. CohortConnect shows an error message.
- 1b2. CohortConnect requests for correct format.
- 1b3. User enters new data.
- Steps 1b1-1b3 are repeated until the data entered are valid.
- Use case resumes from step 2.
Use Case 8: Find a contact by name
MSS
- User enters command to find a contact by name.
-
CohortConnect shows list of contacts with matching name.
Use case ends.
Extensions
- 1a. The input command is invalid.
- 1a1. CohortConnect shows an error message.
- 1a2. CohortConnect requests for correct format.
- 1a3. User enters new data.
- 1a1-1a3 are repeated until the data entered are valid.
- Use case resumes from step 2.
- 1b. Contact with this name does not exist.
- 1b1. CohortConnect shows an error message.
- Use case ends.
- 2a. The list is empty.
- Use case ends.
Use Case 9: Find a contact by GitHub username
MSS
- User enters command to find a contact by GitHub username.
-
CohortConnect shows list of contacts with matching GitHub username.
Use case ends.
Extensions
- 1a. The input command is invalid.
- 1a1. CohortConnect shows an error message.
- 1a2. CohortConnect requests for correct format.
- 1a3. User enters new data.
- 1a1-1a3 are repeated until the data entered are valid.
- Use case resumes from step 2.
- 1b. Contact with this GitHub username does not exist.
- 1b1. CohortConnect shows an error message.
- Use case ends.
- 2a. The list is empty.
- Use case ends.
Use Case 10: Find a contact by Telegram handle
MSS
- User enters command to find a contact by Telegram handle.
-
CohortConnect shows list of contacts with matching Telegram handle.
Use case ends.
Extensions
- 1a. The input command is invalid.
- 1a1. CohortConnect shows an error message.
- 1a2. CohortConnect requests for correct format.
- 1a3. User enters new data.
- 1a1-1a3 are repeated until the data entered are valid.
- Use case resumes from step 2.
- 1b. Contact with this Telegram handle does not exist.
- 1b1. CohortConnect shows an error message.
- Use case ends.
- 2a. The list is empty.
- Use case ends.
Use Case 11: Find a contact using tag
MSS
- User enters command to find a contact by tag.
-
CohortConnect shows list of contacts labelled with matching tag.
Use case ends.
Extensions
- 1a. The input command is invalid.
- 1a1. CohortConnect shows an error message.
- 1a2. CohortConnect requests for correct format.
- 1a3. User enters new data.
- 1a1-1a3 are repeated until the data entered are valid.
- Use case resumes from step 2.
- 1b. Contact with this tag does not exist.
- 1b1. CohortConnect shows an error message.
- Use case ends.
- 2a. The list is empty.
- Use case ends.
Use Case 12: Show a person’s details using Index
MSS
- User requests to list persons.
- CohortConnect shows a list of persons.
- User requests to show details of person at a specific index in the list.
-
CohortConnect shows a pop-up with the person’s details.
Use case ends.
Extensions
- 1a. The given arguments are incorrectly formatted.
- 1a1. CohortConnect shows an error message.
- 1a2. CohortConnect requests for correct format.
- 1a3. User enters new data.
- Steps 1a1-1a3 are repeated until the data entered are valid.
- Use case resumes from step 2.
- 2a. The list is empty.
- Use case ends.
- 3a. The given index is invalid.
- 3a1. CohortConnect shows an error message.
- Use case resumes at step 3.
Use Case 13: Show a person’s details using Name
MSS
- User requests to list persons.
- CohortConnect shows a list of persons.
- User requests to show details of a specific person in the list using the name.
-
CohortConnect shows the person’s details in the detail pane.
Use case ends.
Extensions
- 1a. The given arguments are incorrectly formatted.
- 1a1. CohortConnect shows an error message.
- 1a2. CohortConnect requests for correct format.
- 1a3. User enters new data.
- Steps 1a1-1a3 are repeated until the data entered are valid.
- Use case resumes from step 2.
- 2a. The list is empty.
- Use case ends.
- 3a. The given name is not present.
- 3a1. CohortConnect shows an error message.
- Use case resumes at step 3.
- 3b. Multiple matching names.
- 3b1. CohortConnect shows an error message.
- 3b2. Displays list of users with names containing the keyword.
- Use case resumes at step 3.
Use Case 14: Show a person’s details using GitHub Username
MSS
- User requests to list persons.
- CohortConnect shows a list of persons.
- User requests to show details of a specific person in the list using GitHub username.
-
CohortConnect shows the person’s details in the detail pane.
Use case ends.
Extensions
- 1a. The given arguments are incorrectly formatted.
- 1a1. CohortConnect shows an error message.
- 1a2. CohortConnect requests for correct format.
- 1a3. User enters new data.
- Steps 1a1-1a3 are repeated until the data entered are valid.
- Use case resumes from step 2.
- 2a. The list is empty.
- Use case ends.
- 3a. The given GitHub username is not present.
- 3a1. CohortConnect shows an error message.
- Use case resumes at step 3.
- 3b. Multiple matching GitHub usernames.
- 3b1. CohortConnect shows an error message.
- 3b2. Displays list of users with GitHub usernames containing the keyword.
- Use case resumes at step 3.
Use Case 15: Show a person’s details using Telegram Username
MSS
- User requests to list persons.
- CohortConnect shows a list of persons.
- User requests to show details of a specific person in the list using Telegram Username.
-
CohortConnect shows the person’s details in the detail pane.
Use case ends.
Extensions
- 1a. The given arguments are incorrectly formatted.
- 1a1. CohortConnect shows an error message.
- 1a2. CohortConnect requests for correct format.
- 1a3. User enters new data.
- Steps 1a1-1a3 are repeated until the data entered are valid.
- Use case resumes from step 2.
- 2a. The list is empty.
- Use case ends.
- 3a. The given telegram id is not present.
- 3a1. CohortConnect shows an error message.
- Use case resumes at step 3.
- 3b. Multiple matching telegram ids.
- 3b1. CohortConnect shows an error message.
- 3b2. Displays list of users with telegram ids containing the keyword.
- Use case resumes at step 3.
Use Case 16: Favorite an existing contact
MSS
- User enters command to favorite a contact.
- CohortConnect shows a successfully favorited message.
-
CohortConnect shows the updated list of contacts.
Use case ends.
Extensions
- 1a. The given index is not present.
- 1a1. CohortConnect shows an error message.
- 1a2. CohortConnect requests for correct format.
- 1a3. User enters new data.
- Steps 1a1-1a3 are repeated until the data entered are valid.
- Use case resumes from step 2.
- 1b. The given arguments are incorrectly formatted.
- 1b1. CohortConnect shows an error message.
- 1b2. CohortConnect requests for correct format.
- 1b3. User enters new data.
- Steps 1b1-1b3 are repeated until the data entered are valid.
- Use case resumes from step 2.
- 1c. The contact at given index is already favorited.
- 1c1. CohortConnect shows an error message.
- 1c2. User enters new data.
- Steps 1c1-1c2 are repeated until the data entered are valid.
- Use case resumes from step 2.
Use Case 17: Unfavorite an existing contact
MSS
- User enters command to unfavorite a contact.
- CohortConnect shows a successfully unfavorited message.
-
CohortConnect shows the updated list of contacts.
Use case ends.
Extensions
- 1a. The given index is not present.
- 1a1. CohortConnect shows an error message.
- 1a2. CohortConnect requests for correct format.
- 1a3. User enters new data.
- Steps 1a1-1a3 are repeated until the data entered are valid.
- Use case resumes from step 2.
- 1b. The given arguments are incorrectly formatted.
- 1b1. CohortConnect shows an error message.
- 1b2. CohortConnect requests for correct format.
- 1b3. User enters new data.
- Steps 1b1-1b3 are repeated until the data entered are valid.
- Use case resumes from step 2.
- 1c. The contact at given index is already favorited.
- 1c1. CohortConnect shows an error message.
- 1c2. User enters new data.
- Steps 1c1-1c2 are repeated until the data entered are valid.
- Use case resumes from step 2.
Use Case 18: Import contacts from JSON or CSV file
MSS
- User enters command to import from a JSON or CSV file.
-
CohortConnect shows the updated list of contacts.
Use case ends.
Extensions
- 1a. CohortConnect cannot find the JSON or CSV file.
- 1a1. CohortConnect shows an error message.
- 1a2. User enters new filename.
- Steps 1a1-1a2 are repeated until the filename received is valid.
- 1b. Filename entered does not end with
.json
or.csv
- 1b1. CohortConnect shows an error message.
- 1b3. User enters new filename.
- Steps 1b1-1b2 are repeated until the filename received is valid.
- 1c. The JSON or CSV file is formatted wrongly.
- 1c1. CohortConnect shows an error message.
Use case ends.
Use Case 19: Export contacts to JSON or CSV file
MSS
- User enters command to export contacts to a named JSON or CSV file.
-
CohortConnect shows a success message.
Use case ends.
Extensions
- 1a. File name already exists.
- 1a1. CohortConnect shows an error message.
- 1a2. CohortConnect prompts for new filename.
- 1a3. User enters new filename.
- Steps 1a1-1a3 are repeated until the filename received is valid.
- 1b. The given arguments are incorrectly formatted.
- 1b1. CohortConnect shows an error message.
- 1b2. CohortConnect requests for correct format.
- 1b3. User enters new data.
- Steps 1b1-1b3 are repeated until the data entered are valid.
- Use case resumes from step 2.
Use Case 20: Opening a contact’s GitHub
MSS
- User navigates to the contact using Find (UC8-11) and / or Show command (UC12-15).
- User clicks on the contact’s GitHub username, or enters the command to open GitHub page.
-
The contact’s GitHub profile is shown in a new browser.
Use case ends.
Extensions
- 2a. The contact list is empty and the user enters the GitHub command.
- 2a1. CohortConnect shows an error message prompting the user to select a user.
Use case ends.
- 2b. The contact list is empty and the user tries to click the GitHub username.
- 2b1. The contact details will be empty and there will be nothing for the user to click.
Use case ends.
- 2c. The contact has an invalid GitHub username.
- 2c1. The browser shows GitHub’s 404 page.
Use case ends.
Use Case 21: Opening a contact’s Telegram
MSS
- User navigates to the contact using Find (UC8-11) and / or Show command (UC12-15).
- User clicks on the contact’s Telegram username, or enters the command to open Telegram.
-
The contact’s Telegram profile is shown in a browser window, and redirected to the Telegram app if it is installed.
Use case ends.
Extensions
- 2a. The contact list is empty and the user enters the Telegram command.
-
2a1. CohortConnect shows an error message prompting the user to select a user.
Use case ends.
-
- 2b. The contact list is empty and the user tries to click the Telegram username.
-
2b1. The contact details will be empty and there will be nothing for the user to click.
Use case ends.
-
- 2c. The contact has an invalid Telegram username.
-
2c1. The invalid Telegram profile is shown in the browser window, and Telegram shows an error message when opening the profile in the Telegram application.
Use case ends.
-
Use Case 22: Using the Find A Buddy Feature
MSS
- User switches to the Find A Buddy Tab
- CohortConnect gives the top 5 matches to your data
Extensions
- 1a. GitHub data is still being gathered.
- 1a1. CohortConnect shows a loading screen while gathering data in the background.
- 1a2. Once loaded, Use case resumes at Step 2
Use Case 23: Opening the Help Window
MSS
- User uses the keyboard shortcut, or types in the command, or clicks on help in the Menu Bar.
-
CohortConnect shows the Help Window.
Use case ends.
Extensions
- 1a. User types in command with wrong format.
- 1a1. CohortConnect shows an error message.
- 1a2. CohortConnect requests for correct format.
- 1a3. User enters new data.
- Steps 1a1-1a3 are repeated until the data entered are valid.
- Use case resumes from step 2.
- *a. At any time, the User chooses to close the app.
-
*a1. CohortConnect closes.
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 persons without a noticeable sluggishness in performance for typical usage.
- 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.
- Data should be saved locally.
- Product is not required to handle communication between users.
Glossary
- Mainstream OS: Windows, Linux, Unix, OS-X
- Private contact detail: A contact detail that is not meant to be shared with others
- Main Success Scenario (MSS): The most straightforward interaction for a given use case, which assumes that nothing goes wrong.
- JSON: JavaScript Object Notation, is a common file format which stores data in key-value pairs and arrays.
- Regex: Regular Expression
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.
-
Deleting a person
-
Deleting a person while all persons are being shown
-
Prerequisites: List all persons using the
list
command. Multiple persons in the list. -
Test case:
delete 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 0
Expected: No person is deleted. Error details shown in the status message. Status bar remains the same. -
Other incorrect delete commands to try:
delete
,delete x
,...
(where x is larger than the list size)
Expected: Similar to previous.
-
Saving data
-
Dealing with missing/corrupted data files
-
Navigate to and open
/data/addressbook.json
in a text editor. -
Delete one line of code from a Person object, such as
"name" : "Alex Yeoh"
. -
Restart CohortConnect.
-
CohortConnect will start with no contacts.
-
An error message will be shown in the
ResultDisplay
, stating that the file is corrupted.
-