Developer Guide
- Acknowledgements
- Introduction
- Setting up, getting started
- Design
- Implementation
- Documentation
- Logging
- Testing
- Configuration
- Dev-ops
- Appendix: Requirements
- Appendix: Glossary
- Appendix: Planned Enhancements
-
Appendix: Instructions for manual testing
- Launch and shutdown
- Adding a contact
- Editing a contact
- Finding contacts
- Listing contacts
- Removing a contact’s field
- Deleting a contact
- Clearing all contacts or tags
- Sorting contacts
- Viewing a contact’s detailed information
- Adding a project
- Editing a project
- Deleting a project
- Removing a project’s field
- Clearing all projects
- Sorting projects
- Assigning a contact to a project
- Unassigning a contact from a project
- Saving data
Acknowledgements
- This project is based on the AddressBook-Level3 project created by the SE-EDU initiative.
Introduction
This Developer Guide serves as documentation of the architecture and design of SOCket, an application for managing contacts and projects quickly using CLI (Command Line Interface), with some key implementation details of SOCKet’s features. The current version of SOCket is v1.4
.
The Developer Guide is written to aid present and future developers of SOCket with understanding the architecture, design and non-trivial implementations of SOCket’s features. In doing so, this guide imparts the knowledge required for developers to further modify and extend the features and functionality of SOCket beyond its current state.
The code for SOCket is hosted on GitHub here.
Refer to the following section for how to set up and get started with SOCket.
Setting up, getting started
Refer to the guide Setting up and getting started.
Design
.puml
files used to create diagrams in this document can be found in the diagrams folder. Refer to the PlantUML Tutorial at se-edu/guides to learn how to create and edit diagrams.
Architecture
The Architecture Diagram given above explains the high-level design of the App.
Given below is a quick overview of main components and how they interact with each other.
Main components of the architecture
Main
has two classes called Main
and MainApp
. It is responsible for,
- At app launch: Initializes the components in the correct sequence, and connects them up with each other.
- At shut down: Shuts down the components and invokes cleanup methods where necessary.
Commons
represents a collection of classes used by multiple other components.
The rest of the App consists of four components.
-
UI
: The UI of the App. -
Logic
: The command executor. -
Model
: Holds the data of the App in memory. -
Storage
: Reads data from, and writes data to, the hard disk.
How the architecture components interact with each other
The Sequence Diagram below shows how the components interact with each other for the scenario where the user issues the command delete 1
.
Each of the four main components (also shown in the diagram above),
- defines its API in an
interface
with the same name as the component. - implements its functionality using a concrete
{Component Name}Manager
class (which follows the corresponding APIinterface
mentioned in the previous point.
For example, the Logic
component defines its API in the Logic.java
interface and implements its functionality using the LogicManager.java
class which follows the Logic
interface. Other components interact with a given component through its interface rather than the concrete class (reason: to prevent outside component’s being coupled to the implementation of a component), as illustrated in the (partial) class diagram below.
The sections below give more details of each component.
UI component
The API of this component is specified in Ui.java
Shown below is the class diagram for the UI
component:
The UI consists of a MainWindow
that is made up of parts e.g.CommandBox
, ResultDisplay
, PersonListPanel
, StatusBarFooter
etc. All these, including the MainWindow
, inherit from the abstract UiPart
class which captures the commonalities between classes that represent parts of the visible GUI.
The UI
component uses the JavaFx UI framework. The layout of these UI parts are defined in matching .fxml
files that are in the src/main/resources/view
folder. For example, the layout of the MainWindow
is specified in MainWindow.fxml
The UI
component,
- executes user commands using the
Logic
component. - listens for changes to
Model
data so that the UI can be updated with the modified data. - keeps a reference to the
Logic
component, because theUI
relies on theLogic
to execute commands. - depends on some classes in the
Model
component, as it displaysPerson
andProject
object residing in theModel
.
Logic component
The API of this component is specified in 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 theSocketParser
class to parse the user command. - This results in a
Command
object (more precisely, an object of one of its subclasses e.g.,AddCommand
) which is executed by theLogicManager
. - The command can communicate with the
Model
when it is executed (e.g. to add a person). - The result of the command execution is encapsulated as a
CommandResult
object which is returned back fromLogic
.
The Sequence Diagram below illustrates the interactions within the Logic
component for the execute("delete 1")
API call.
DeleteCommandParser
should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
Here are the other classes in Logic
(omitted from the class diagram above) that are used for parsing a user command:
How the parsing works:
- When called upon to parse a user command, the
SocketParser
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 theSocketParser
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.
Shown below is the activity diagram for how SOCket parses a user input:
Model component
The API of this component is specified in Model.java
Shown below is the class diagram for the Model
component:
The Model
component,
- stores the
Socket
data, i.e., allPerson
objects (contained in aUniquePersonList
object) and allProject
objects (contained in aUniqueProjectList
object). - stores the
VersionedSocket
data, i.e.,Socket
states to restore, either toundo
orredo
changes - 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 the
Project
objects as a separate filtered list which is exposed to outsiders as an unmodifiableObservableList<Project>
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)
Language
and Tag
lists in Socket
, which Person
references. This allows Socket
to only require one Language
/Tag
object per unique language/tag, instead of each Person
needing their own Language
/Tag
objects. Storage component
The API of this component is specified in Storage.java
Shown below is the class diagram for the Storage
component:
The Storage
component,
- can save both
Socket
data and user preference data in json format, and read them back into corresponding objects. - can save data on
Person
members of aProject
and restore references to samePerson
objects inUniquePersonList
upon readingSocket
data back into corresponding objects. - inherits from both
SocketStorage
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.socket.commons
package.
Implementation
This section describes some noteworthy details on how certain features are implemented.
Undo/Redo feature
Implementation
This feature was implemented as proposed in AddressBook-Level3
The undo/redo mechanism is facilitated by VersionedSocket
. It extends Socket
with an undo/redo history, stored internally as an socketStateList
and currentStatePointer
. Additionally, it implements the following operations:
-
VersionedSocket#commit()
— Saves the current ‘Socket’ state in its history. -
VersionedSocket#undo()
— Restores the previous ‘Socket’ state from its history. -
VersionedSocket#redo()
— Restores a previously undone ‘Socket’ state from its history.
These operations are exposed in the Model
interface as Model#commitSocket()
, Model#undoSocket()
and Model#redoSocket()
respectively.
Given below is an example usage scenario and how the undo/redo mechanism behaves at each step.
Step 1. The user launches the application for the first time. The VersionedSocket
will be initialized with the initial Socket
state, and the currentStatePointer
pointing to that single Socket
state.
Step 2. The user executes delete 5
command to delete the 5th person in ‘Socket’. The delete
command calls Model#commitSocket()
, causing the modified state of Socket
after the delete 5
command executes to be saved in the socketStateList
, and the currentStatePointer
is shifted to the newly inserted Socket
state.
Step 3. The user executes add n/David …
to add a new person. The add
command also calls Model#commitSocket()
, causing another modified Socket
state to be saved into the socketStateList
.
Model#commitSocket()
, so the Socket
state will not be saved into the socketStateList
.
Step 4. The user now decides that adding the person was a mistake, and decides to undo that action by executing the undo
command. The undo
command will call Model#undoSocket()
, which will shift the currentStatePointer
once to the left, pointing it to the previous Socket
state, and restores Socket
to that state.
currentStatePointer
is at index 0, pointing to the initial Socket
state, then there are no previous Socket
states to restore. The undo
command uses Model#canUndoSocket()
to check if this is the case. If so, it will return an error to the user rather
than attempting to perform the undo.
The following sequence diagram shows how the undo operation works:
UndoCommand
should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
The redo
command does the opposite — it calls Model#redoSocket()
, which shifts the currentStatePointer
once to the right, pointing to the previously undone state, and restores Socket
to that state.
currentStatePointer
is at index socketStateList.size() - 1
, pointing to the latest Socket
state, then there are no undone Socket
states to restore. The redo
command uses Model#canRedoSocket()
to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo.
Step 5. The user then decides to execute the command list
. Commands that do not modify the Socket
, such as list
, will usually not call Model#commitSocket()
, Model#undoSocket()
or Model#redoSocket()
. Thus, the socketStateList
remains unchanged.
Step 6. The user executes clear
, which calls Model#commitSocket()
. Since the currentStatePointer
is not pointing at the end of the socketStateList
, all Socket
states after the currentStatePointer
will be purged. Reason: It no longer makes sense to redo the add n/David …
command. This is the behavior that most modern desktop applications follow.
The following activity diagram summarizes what happens when a user executes a new command:
Design considerations
Aspect: How undo & redo executes:
-
Alternative 1 (current choice): Saves the entire
Socket
.- Pros: Easy to implement.
- Cons: May have performance issues in terms of memory usage.
-
Alternative 2: Individual command knows how to undo/redo by
itself.
- Pros: Will use less memory (e.g. for
delete
, just save the person being deleted). - Cons: We must ensure that the implementation of each individual command are correct.
- Pros: Will use less memory (e.g. for
List Feature
Implementation
The list
feature allows user to display a list of persons. The user can filter the list by tag or language. list
filters the list by AND search. If no argument is given, then list by default displays all persons in SOCket.
The feature is mainly facilitated by the ListCommand
class and with the help of Predicate classes.
ListCommand
extends Command
and implements the following operation:
-
ListCommand#execute()
- Displays the list of persons in SOCket that contains the given keyword of each respective fields.
The ListCommandParser
class is used to parse and verify the user input to create the list command.
Once the input is parsed by ListCommandParser
, a list of keywords for each respective field is then used to create the respective Predicate class to check if any keyword matches the given field of a Person.
The Predicates relevant to ListCommand
differ from FindCommand
in the way that it looks for full keyword matches in Person(s). If no fields are given, list of persons will be update by PREDICATE_SHOW_ALL_PERSONS
/ a true predicate.
Otherwise, this list of Predicate classes include:
ListCommandLanguagePredicate
ListCommandTagPredicate
This Predicate class will return True as long as any of the Predicate classes inside it returns True. The Predicate classes works using an AND search, persons will be shown in the resulting list only if all the keywords given should match to the Person.
The following sequence diagram shows how the list
operation works:
Design considerations
Aspect: Filtering by other fields
- Initially considered list to not have additional arguments but as decided to filter through Language and Tag due to:
- Find currently does a partial keyword match, thus list allows user to have the option to do full keyword matches
- As language(s) and tag(s) are the only fields which can belong to more than one person, it makes sense to use list to do full keyword matches on these fields only.
Sort Feature
Implementation
The sort feature allows users to sort the list of persons and projects in the application.
The feature is facilitated by the SortCommand
and SortProjectCommand
classes.
They extend Command
and implements the following operations:
-
SortCommand#execute()
— Sorts the list of persons in the application. -
SortProjectCommand#execute()
— Sorts the list of projects in the application.
The SortCommandParser
and SortProjectCommandParser
classes are used to parse the user input and create the respective commands.
The respective classes verify that the user input is valid and create the commands accordingly.
If no argument is provided, Persons will be sorted by name and Projects will be sorted by deadline by default.
The input is then passed to the sort
function in UniquePersonList
and UniqueProjectList
respectively.
The sort
makes use of a comparator that sorts the persons or projects by the category specified by the user.
If the person or project does not have that field, they are sorted at the back. If there are multiple persons or contacts where the field is empty, they are sorted by name.
The following sequence diagram shows how the sort operation works for persons (implementation is similar for projects):
Find Feature
Implementation
The find feature allows users to display a list of contacts from SOCket that contains the given keyword of each respective fields.
The feature is facilitated by the FindCommand
class mainly but Predicate classes are also used.
FindCommand
extends Command
and implements the following operation:
-
FindCommand#execute()
— Finds and displays the list of persons in the application that contains the given keyword of each respective fields.
The FindCommandParser
class is used to parse & verify the user input to create the find command.
Once the input is parsed by FindCommandParser
, a list of keywords for each respective field is then used to create a Predicate class that checks if any keyword matches the given field of a Person.
This list of Predicate classes include:
FindCommandAddressPredicate
FindCommandEmailPredicate
FindCommandLanguagePredicate
FindCommandNamePredicate
FindCommandPhonePredicate
FindCommandProfilePredicate
FindCommandTagPredicate
Each of the above Predicate class will return True as long as any keyword in the list of keywords matches any word in the respective field.
All the above Predicate classes will be enclosed inside a FindCommandPersonPredicate
class.
This Predicate class will return True as long as any of the Predicate classes inside it returns True.
The Predicate classes works using an OR search, as long as a keyword matches any word in the respective field, that person will be shown in the resulting list from find command.
FindCommandPersonPredicate
is used to enclosed all the other Predicate class as the updateFilteredPersonList
for ModelManager
class only accepts 1 Predicate<Person>
class.
Find command now checks on all fields, but we cannot pass in each Predicate class for the different fields into updateFilteredPersonList
without resetting the previous filtered list.
While it may be possible to modify updateFilteredPersonList
to accept the multiple Predicate class without resetting the previous filtered list, we decided against it as:
- Heavy modification to
updateFilteredPersonList
is required which is highly likely to cause many unintended bugs and even break the entire program. - Many other commands use
updateFilteredPersonList
as well, changing the behavior ofupdateFilteredPersonList
to suit find command would mean that we have to also change how the other commands useupdateFilteredPersonList
.
If no argument is provided, an empty list will be shown.
The following sequence diagram shows how the find
operation works:
(“Initializing Predicate for other fields” reference frame is omitted as it is just initialization of the other Predicate classes)
Design considerations
Aspect: AND search or OR search
- An AND search has been considered for find initially but ultimately dropped in favor of OR search due to the following reasons:
- List command already does an AND search. Though only on Tag & Language currently, it can be extended to include the other fields eventually, making find a duplicate command of list command should find command use AND search as well.
- We intend for find command to be a more broad search, getting all persons that matches just a keyword. This is to help users narrow down their search should they forgot the exact name of a contact they are looking for.
Aspect: Full keyword match or Partial keyword match
- We have also considered a partial match of the keyword (For example:
han
keyword will match field with the valuehans
). However we decide to implement a full match due to the following reason:- Having partial match may bring out unintended matches as the possible range of results is broadened. We fear that doing a partial match may be too broad for find command to function as a way for users to narrow down their search.
Remove Feature
Implementation
The remove feature allows users to remove a given value in Person’s and Project’s respective fields.
The feature is facilitated by the RemoveCommand
class and RemoveProjectCommand
classes.
They extend Command
and implement the following operations:
-
RemoveCommand#execute()
— Removes the respective Person’s field value based on the given input. -
RemoveProjectCommand#execute()
— Removes the respective Project’s field value based on the given input.
The RemoveCommandParser
and RemoveProjectCommandParser
classes are used to parse the user input and remove the value that matches to the existing value in the respective field using RemovePersonDescriptor
and RemoveProjectDescriptor
respectively.
RemovePersonDescriptor
and RemoveProjectDescriptor
objects are created and pass with the Index
(parsed from the user input) into respective commands.
The respective classes verify that the user input is valid and create the commands accordingly.
If no argument is provided, an exception will be thrown. Otherwise, if only predicate is present, the corresponding field of the Person/Project will be emptied.
Design considerations
Remove a specific field value
- While an edit feature that allows users to add or remove field values can be useful in certain contexts, it may not be appropriate or necessary in all situations:
- If user decides to remove 1 out of many field values in a person’s tag field, user will have a difficult time to type all the must-have value. WIth remove feature, user just need to type out the tag that he/she wish to remove.
- Currently, edit feature for language field is cumulative, language will not be removed, so remove feature helps to resolve the issue on language removal.
Project feature:
Implementation
This feature allows users to create and track ongoing projects. Details of each Project
are recorded as attributes of the Project
, which include ProjectName
, ProjectRepoHost
, ProjectRepoName
, ProjectDeadline
, ProjectMeeting
, and a Set<Person>
, which stores the Person
objects involved with the Project
. Project
objects are stored in a UniqueProjectList
in Socket
.
Any changes to Person
objects that are associated with a Project
object automatically updates the outdated Person
object in the associated Project
object (i.e., edit
, remove
, delete
).
The following sequence diagram shows how updates are made to the associated Project
object when updates are made to Person
objects on execution of a DeleteCommand
:
Step 1. The DeleteCommand#execute()
method is called.
Step 2. The ModelManager#deletePerson(p)
method of the ModelManager
implementing the Model
interface is then called.
Step 3. This calls the method Socket#removePerson(p)
, which:
- calls
UniquePersonList#remove(p)
to delete the personp
from theUniquePersonList
persons
. - calls
UniqueProjectList#removeMemberInProjects(p)
which removes any stored references to the deleted personp
from theProject
objects in theUniqueProjectList
projects
.
Step 4. The execution of the DeleteCommand
then continues, and the result is encapsulated as a CommandResult
(not shown) and returned.
commitSocket()
and creation of a CommandResult
portions of the execution as they are not relevant to the discussion.
Updates to Person
objects associated with a Project
object through the other commands are handled similarly.
The following classes handle operations on Project
objects:
-
XYZProjectCommand
- represents a command operating onProject
objects, i.e.,AddProjectCommand
,EditProjectCommand
, etc. -
XYZProjectCommandParser
- parses the respectiveXYZProjectCommand
that operates onProject
objects. -
AssignCommand
,UnassignCommand
- represent commands for assigning/unassigning aPerson
object to aProject
object.
The JsonAdaptedProject
class handles the reading/writing to storage for Project
objects. It makes use of JsonAdaptedPerson
to store the Person
objects associated with the Project
. Storage for Person
objects associated with a Project
object are stored in the .json
file by making a copy of the data for the Person
in the “persons” list in the “members” list under the respective Project
object in the “projects” list.
When JsonSerializableSocket#toModelType
is called to convert the stored data into a Socket
object, the restored Person
objects in the UniquePersonList
are matched to the duplicate Person
objects in the Project
objects and the duplicate Person
objects are replaced with the exiting objects in the UniquePersonList
.
Design considerations:
Aspect: How Project
objects are stored:
-
Alternative 1 (current choice): Stores the
Project
objects with thePerson
objects in a single.json
file.- Pros: Fewer files to manage, simpler to write data to upon any changes to
Socket
. - Cons: Corrupt data in “projects” will cause entire file to be discarded, deleting “persons” data.
- Pros: Fewer files to manage, simpler to write data to upon any changes to
-
Alternative 2: Stores the
Project
objects with thePerson
objects in separate.json
files.- Pros: Corrupt data in “projects” will only cause “projects” file to be discarded.
- Cons: More files to manage, harder to write data to upon any changes to
Socket
.
Documentation
The Documentation guide covers how documentation is managed for SOCket.
Logging
The Logging guide covers how logging is used in SOCket.
Testing
The Testing guide covers types of test cases used in SOCket, as well as how to run them.
Configuration
The Configuration guide covers how certain properties of SOCket can be managed through the configuration file.
Dev-ops
The DevOps guide covers build automation and steps to create releases for SOCket.
Appendix: Requirements
Product scope
Target user profile:
- has a need to manage a significant number of contacts (peers, professors in NUS Computing)
- has a need to group and manage groups of contacts
- has a need to view the skills and proficiencies of contacts (programming languages, modules)
- has a need to access the GitHub profiles/repositories of contacts
- has a need to track deadlines and responsibilities associated with contacts
- prefer desktop apps over other types
- can type fast
- prefers typing to mouse interactions
- is reasonably comfortable using CLI apps
Value proposition: manage contacts faster than a typical mouse/GUI driven app, store information relevant to NUS Computing students (GitHub profile, repository, programming language proficiencies, modules taken), quickly find contacts that satisfy certain criteria (taken a particular module, in a group)
User stories
Priorities: High (must have) - * * *
, Medium (nice to have) - * *
, Low (unlikely to have) - *
Priority | As a … | I can… | So that I can… |
---|---|---|---|
* * * |
new user | see usage instructions | refer to instructions when I forget how to use the App |
* * * |
user | add a new person | |
* * * |
user | delete a person | remove entries that I no longer need |
* * * |
user | find a person by name | locate details of persons without having to go through the entire list |
* * * |
student with many friends | save my friend’s details | easily contact them |
* * * |
student who likes to ask questions | save my professors’ details | easily contact them to ask questions |
* * * |
student with fast typing speed | use command based inputs to search for contacts | quickly pull out contacts without needing to spend time moving my mouse |
* * |
user | hide private contact details | minimize the chance of someone else seeing them by accident |
* * |
student involved in project work | search contacts belonging to a certain group | contact my groupmates easily |
* * |
student with many assignments | tag deadlines and responsibilities to my contacts | easily keep track of which tasks are more urgent and who is taking care of it |
* * |
organised user | group contacts into different groups | manage my contacts easily |
* * |
software engineering student with many SE projects | access the github repositories of my peers | easily keep track of the github repos that I’m involved and interested in |
* * |
student interested in hackathons | find students based on skills | form groups with them |
* * |
software engineering student | find repositories of group projects I am involved in | easily access team repositories |
* * |
student who is organised | sort all my peers’ contact information | have only one platform where I know my contacts are organized |
* * |
software engineering student | tag contacts with their skills | know what skills they have and can easily find those with a particular skill |
* * |
advanced user | use the shortcut keys | get things done more effectively |
* * |
busy software engineering student | create shortcuts to long commands | not type out long commands repeatedly to save time |
* * |
student constantly getting into new projects with other members | quickly remove tags of specific groups and delete contacts | not be flooded with too much irrelevant contacts which can cause accidental wrong contact |
* * |
software engineering student with many peers | save and access my peers’ github information | easily access their profiles and view their repositories |
Use cases
(For all use cases below, the System is the SOCket
and the Actor is the user
, unless specified otherwise)
Use case: UC01 - Add a contact
MSS:
- User requests to add a contact.
-
SOCket adds the contact.
Use case ends.
Extensions:
-
1a. There are no details provided.
-
1a1. SOCket shows an error message.
Use case resumes at step 1.
-
-
1b. There is no name provided.
-
1b1. SOCket shows an error message.
Use case resumes at step 1.
-
Use case: UC02 - Edit a contact
MSS:
- User requests to list contacts.
- SOCket shows a list of contacts.
- User requests to edit a specific contact’s details in the list.
- SOCket edits the contact’s details.
Extensions:
-
2a. The list is empty.
Use case ends.
-
3a. The given index is invalid.
-
3a1. SOCket shows an error message.
Use case resumes at step 2.
-
-
3b. There are no details provided.
-
3b1. SOCket shows an error message.
Use case resumes at step 2.
-
-
3c. There are no tags provided.
-
3b1. SOCket removes all tags associated with the contact.
Use case ends.
-
-
3d. There are tag(s) provided.
-
3b1. SOCket removes existing tags associated with the contact and adds the tag(s) provided.
Use case ends.
-
Use case: UC03 Delete a contact
MSS:
- User requests to list contacts.
- SOCket shows a list of contacts.
- User requests to delete a specific contact in the list.
-
SOCket deletes the contact.
Use case ends.
Extensions:
-
2a. The list is empty.
Use case ends.
-
3a. The given index is invalid.
-
3a1. SOCket shows an error message.
Use case resumes at step 2.
-
Use case: UC04 Sort contacts
MSS:
- User requests to list contact.
- SOCket shows a list of contacts.
- User requests to sort the list by a category.
-
SOCket sorts the contacts by that category and displays the sorted contact list.
Use case ends.
Extensions:
-
2a. The list is empty.
Use case ends.
-
3a. The given category is invalid.
-
3a1. SOCket shows an error message.
Use case resumes from step 2.
-
-
3b. No category is given.
-
3b1. SOCket sorts the list by name and displays the sorted list.
Use case ends.
-
Use case: UC05 Find contact(s)
MSS:
- User request to find a contact with the given keywords in their respective fields.
-
SOCket shows a list of contacts that contains any of the given keyword in their respective fields.
Use case ends.
Extensions:
-
1a. No contact matches any of the given keywords in their respective fields.
-
1a1. SOCket shows an empty list of contacts.
Use case ends.
-
-
1b. No keywords are given for all fields.
-
1b1. SOCket shows an empty list of contacts.
Use case ends.
-
Use case: UC06 Clear contacts/tag
MSS:
- User chooses to clear off group of contacts.
- SOCket requests for confirmation to delete the contacts.
- User confirms.
- SOcket deletes all the contacts.
Use case ends.
Extensions:
-
1a. All the given tags are present in SOCket.
- 1a1. SOCket request to delete all the contacts under the tags.
-
1b. Some given tags are not present in SOCket.
-
1b1. SOCket shows an error message, listing all the wrong tags.
-
1b2. SOCket request to delete all the contacts under the correct tags.
Use case resumes at step 2.
-
-
1c. All the given tag are not present in SOCket.
-
1c1. SOCket shows an error message, listing all the wrong tags.
-
1c2. SOCket requests for correct tags.
Steps 1c1-1c2 are repeated until a tag is entered correctly.
Use case resumes at step 2.
-
-
1d. No tag is provided.
-
1d1. SOCket request to delete all the contacts.
Use case resumes at step 2.
-
-
*a. At any time, User choose to cancel the operation.
-
*a1. SOCKet stops the operation.
Use case ends.
-
Use case: UC07 List contacts
MSS:
- User requests to list contacts.
- SOCket shows a list of contacts.
- User requests to list contacts by language(s) and tag(s).
-
SOCket shows a list of contacts with given language(s) and tag(s).
Use case ends.
Extensions:
-
3a. The given tag(s) or language(s) is invalid.
-
3a1. SOCket shows an error message.
Use case resumes at step 2.
-
-
3b. No contact matches the given language(s) or tag(s).
-
3b1. SOCket shows an empty list of contacts.
Use case resumes at step 2.
-
-
3c. No category is given.
Use case resumes at step 2.
Use case: UC08 Access Help page
MSS:
- User requests for help.
-
SOCket shows the help page.
Use case ends.
Use case: UC09 - Exit SOCket
MSS:
- User requests to exit SOCket.
-
SOCket exits.
Use case ends.
Use case: UC10 Hide contact details
MSS:
- User requests to list contact.
- SOCket shows a list of contacts.
- User requests to hide a category of a specific contact.
-
SOCket hides the specific category of that user.
Use case ends.
Extensions:
-
2a. The list is empty.
Use case ends.
-
3a. The given category is invalid.
-
3a1. SOCket shows an error message.
Use case resumes at step 2.
-
-
3b. The contact does not have that category.
-
3b1. SOCket shows an error message.
Use case resumes at step 2.
Use case ends.
-
Use case: UC11 Using shortcut command
MSS:
- User binds a command to a given keyword of their choice.
- SOCket shows a message to confirm shortcut key created.
- User types the keyword bound to the command.
- SOCket will execute the command. Use case ends.
Extensions:
-
3a. Command bounded to the keyword is not valid.
-
3a1. SOCket shows an error message.
Use case ends.
-
Use case: UC12 Access peer GitHub information
MSS:
- User requests to list contact.
- SOCket shows a list of contacts.
- User requests to view a specific contact’s github information.
-
SOCket opens the contact’s github profile in a browser.
Use case ends.
Extensions:
-
2a. The list is empty.
Use case ends.
-
3a. The contact does not have github information.
Use case resumes at step 2.
Use case: UC13 Undo recent changes
MSS:
- User requests to undo changes.
-
SOCket undoes changes.
Use case ends.
Extensions:
-
1a. No changes exist.
-
1a1. SOCket shows an error message.
Use case ends.
-
Use case: UC14 Redo recent undone changes
MSS:
- User requests to restore recent undone changes.
-
SOCket restores undone changes.
Use case ends.
Extensions:
-
1a. No undone changes exist.
-
1a1. SOCket shows an error message.
Use case ends.
-
Use case: UC15 Tagging a contact
MSS:
- User find contact(s) (UC05) they wish to tag.
- User tags the list of contacts currently shown with a tag name.
-
SOCket tags all contacts in the list currently shown, displays all contacts with the given tag name to the user.
Use case ends.
Use case: UC16 Set deadlines & responsibilities
MSS:
- User has a team project and creates an event.
- SOCket creates the event.
- User request to groups all the team members contact under the event created.
- SOCket groups the contacts together.
- User enters a deadline date to each milestone of the project.
- SOCket allocates the date to respective milestone.
- User chooses to give role to each team members.
-
SOCket assigned the given roles to respective team members.
Use case ends.
Extensions:
-
5a. SOCket detects a syntax error in the entered date.
-
5a1. SOCket shows an error message.
Use case resume at step 6 when the date entered is correct.
Use case resumes at step 2.
-
Use case: UC17 Removing tag/language(s)
MSS
- User found that he/she has place a wrong tag to a person.
- User request to remove the specific tag.
-
SOCket find the person and removes the tag associated to it.
Use case ends.
Extensions
-
2a. SOCket detects that user inputs an empty field after the tag prefix.
-
2a1. SOCket removes all tags associated to it.
Use case ends.
-
Use case: UC18 Viewing a person’s detail
MSS
- User find contact(s) (UC05) he/she wishes to view.
- SOCket lists out all the contact associated to the category.
- User wishes to view the first contact in the list.
-
SOCket displays all the person’s information at the detail display window.
Use case ends.
Use case: UC19 Sorting project list
Similar to UC04 Sort Contacts, except,
- the list being sorted is the list of projects instead of contacts
- the default sort when no category is provided is by the deadline of the project instead of the name.
Use case: UC20 Assigning a member from a project
- User requests to add a contact to a project.
-
SOCket adds the contact to the project.
Use case ends.
Extensions
- 2a. The chosen contact is already assigned to the project.
-
2a1. SOCket shows an error message.
Use case ends.
-
- 2b. The chosen project does not exist.
-
2b1. SOCket shows an error message.
Use case ends.
-
- 2c. The chosen contact does not exist.
-
2c1. SOCket shows an error message.
Use case ends.
-
Use case: UC21 Unassigning a member from a project
- User requests to remove a member from a project.
-
SOCket removes the member from the project.
Use case ends.
Extensions
- 2a. The chosen member is not assigned to the project.
-
2a1. SOCket shows an error message.
Use case ends.
-
- 2b. The chosen project does not exist.
-
2b1. SOCket shows an error message.
Use case ends.
-
Use case: UC22 - Add a project
Similar to UC01 Add a contact, except,
- A projects is added instead of contact.
- Only meeting date is an optional field, all other fields are compulsory.
Use case: UC23 Delete a project
Similar to UC03 Delete a contact, except,
- a list of projects is shown instead of contacts.
- a project is deleted instead of a contact.
Use case: UC24 Edit a project
Similar to UC02 Edit a contact, except,
- a list of projects is shown instead of contacts.
- a project’s details are edited instead of a contact’s details.
Use case: UC25 Removing a project’s fields
Similar to UC17 Removing tag/language(s), except,
- a project’s details are removed instead of tag/languages(s)
Use case: UC26 Clear projects
Similar to UC06 Clear contacts/tag, except,
- all projects are cleared instead of all contacts.
Non-Functional Requirements
- Should work on any mainstream OS as long as it has Java
11
or above installed. - Should work on 64-bit environments.
- Should be able to hold up to 1000 contacts without a noticeable sluggishness in performance for typical usage.
- Should be able to hold up to 100 projects 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.
Appendix: Glossary
- Mainstream OS: Windows, Linux, Unix, OS-X
-
GitHub profile: GitHub username e.g.
chia-yh
-
GitHub repository: repository path e.g.
AY2223S2-CS2103T-T12-4/tp
Appendix: Planned Enhancements
- The current
VALIDATION_REGEX
for validating theName
field is flawed, and does not consider names with/
,-
or.
valid.- Valid
Name
fields:Hubert Blaine Wolfeschlegelsteinhausenbergerdorff Sr
Alex Yeoh2
- Invalid
Name
fields:Balakrishnan s/o Nagamuthu
Chien-Ming Wang
S. R. Nathan
We plan to update the
VALIDATION_REGEX
to also allow other possible names including, but not limited to, those under “InvalidName
fields” above. TheVALIDATION_REGEX
currently does not restrictName
length to allow for contacts with long names. These cases are handled in the UI by allowing the contact list to be scrollable, and wrapping long names in the detail panel. - Valid
- The current
VALIDATION_REGEX
for validating thePhone
field is flawed, and does not consider phone numbers with+
,-
, `,
(, or
)` valid.- Valid
Phone
fields:995
12345678
- Invalid
Phone
fields:+65 12345678
01234-012345
(01234) 012345
We plan to update the
VALIDATION_REGEX
to allow other possible phone numbers including, but not limited to, those under “InvalidPhone
fields” above, as well as limiting the length of a validPhone
field to follow the longest conventionally accepted phone number. - Valid
- The current
VALIDATION_REGEX
for validatingLanguage
fields is flawed, and does not consider languages with.
or ` ` valid.- Valid
Language
fields:Java
C++
C#
C--
- Invalid
Language
fields:NET.Data
Text Executive Programming Language
We plan to update the
VALIDATION_REGEX
to allow other possible languages including, but not limited to, those under “InvalidLanguage
fields” above. This entails enhancements such as allowing aLanguage
field to consist of multiple words in the case that users choose to store the full name of a language instead of its acronym, or names of languages with multiple words without a conventionally accepted acronym, as well as allowing.
in theLanguage
field. - Valid
- The current restrictions allow for adding a
Project
with an overdueProjectDeadline
and/orProjectMeeting
field, without any indication that the deadline or meeting has passed. As users may want to keep the details of finished projects or meetings for posterity, we plan on continuing to allow theProjectDeadline
andProjectMeeting
fields to be set to past date and time, however, further checks will be added when setting theProjectDeadline
orProjectMeeting
fields to notify users when setting a date and time that has already passed, showing an additional message:The project deadline has already passed.
orThe project meeting has already passed.
- The current
Project
only allows users to set a singleProjectMeeting
, which could be problematic in the case that users would like to store the details of several meetings in advance, as opposed to just the upcoming meeting. We plan to enhance this feature to allow users to set severalProjectMeeting
fields for aProject
, to enable users to manage more meetings for aProject
, such as future meetings past the upcoming meeting, as well as keep a record of previous meetings if such a need arises. - The current UI makes use of a horizontal scroll bar to handle the interaction of overflowing text with tags in the contact panel. To enhance the user experience, we plan to only display the tag count initially. If the user chooses to view a specific contact, the contact card will then expand to show the full list of tags associated with that contact.
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.
-
Adding a contact
-
Adding a contact to the contact list
-
Test case:
add n/John Doe p/98765432 g/johndoe l/Python
Expected: Adds a contact with the nameJohn Doe
and the provided phone, GitHub profile, and language details. -
Test case:
add n/John Doe
Expected: Adds a contact with the nameJohn Doe
and no other details. -
Test case:
add n/John Doe p/abc
Expected: No contact is added. An error message is shown as an invalid phone number was given. An error is logged in the console. -
Other incorrect add commands to try:
add
,add John Doe
Expected: No contact is added. An error message is shown as an invalid command was given. An error is logged in the console.
-
Editing a contact
-
Editing a contact while all contacts are being shown
-
Prerequisites: List all contacts using the
list
command. At least one contact in the list. -
Test case:
edit 1 n/Dohn Joe
Expected: First contact in the list is edited with the nameDohn Joe
-
Test case:
edit 1 t/friend t/owesmoney
Expected: Existing tags of the first contact in the list are replaced with the tagsfriend
andowesmoney
-
Test case:
edit 1 l/Python
Expected: ThePython
language is added to the existing languages of the first contact. An error is logged in the console. -
Test case:
edit 1
Expected: No contact is edited. An error message is shown as no fields to edit were provided. An error is logged in the console. -
Test case:
edit 0
Expected: No contact is edited. An error message is shown as an invalid index was given. An error is logged in the console. -
Other incorrect edit commands to try:
edit
,edit John Doe
Expected: No contact is edited. An error message is shown as an invalid command was given.
-
Finding contacts
-
Finding contacts while all contacts are being shown.
-
Test case:
find n/alex
Expected: Contacts with name that containsalex
is shown. -
Test case:
find l/Java
Expected: Contacts with language that containsJava
is shown. -
Test case:
find n/alex t/neighbours
Expected: Contacts with namealex
or tagneighbours
is shown. -
Test case:
find l/
Expected: An empty contact list is shown. -
Test case:
find
Expected: An empty contact list is shown. -
Test case:
find n/next n/alex
Expected: Same list of contacts as find test case 1i is shown as only the value of the last occurrence of a prefix is used.
-
-
Listing contacts while some contacts are being shown.
-
Prerequisites: Filter the contact list with
find
orlist
command. -
Test case:
find n/alex
Expected: All contacts with name that containsalex
in SOCket (including those not shown before executingfind n/alex
) is shown. -
Test case:
find l/Java
Expected: All contacts with language that containsJava
in SOCket (including those not shown before executingfind l/Java
) is shown. -
Test case:
find n/alex t/neighbours
Expected: All contacts with namealex
or tagneighbours
in SOCket (including those not shown before executingfind n/alex t/neighbours
) is shown. -
Test case:
find l/
Expected: An empty contact list is shown. -
Test case:
find
Expected: An empty contact list is shown. -
Test case:
find n/next n/alex
Expected: Same list of contacts as find test case 2ii is shown as only the value of the last occurrence of a prefix is used.
-
Listing contacts
- Listing contacts while all contacts are being shown.
-
Prerequisites: At least one contact in the list.
-
Test case:
list t/friend
Expected: Contacts tagged withfriend
is shown. -
Test case:
list l/Java
Expected: Contacts with languageJava
is shown. -
Test case:
list t/friend l/Java
Expected: Contacts with tagfriend
and languageJava
is shown. -
Test case:
list l/
Expected: An error is message is shown as an invalid syntax for language is given. -
Test case:
list 12345
Expected: Same list of contacts is shown as this would be regarded as alist
command.
-
- Listing contacts while some contacts are being shown.
-
Prerequisites: Filter the contact list with
find
orlist
command. At least one contact in the list. -
Test case:
list
Expected: All contacts from the unfiltered list is shown. -
Test case:
list t/friend
Expected: Contacts tagged withfriend
in the filtered list is shown.
-
Removing a contact’s field
- Removing a contact’s field while all contacts are being shown
-
Prerequisites: At least one contact in the shown list.
-
Test case:
remove 1 l/
Expected: The language field of the first contact is removed. Language field in the person detail panel becomes empty. -
Test case:
remove 1 l/ t/
Expected: The language and tag field of the first contact are removed. Language and tag field in the person detail card becomes empty. -
Test case:
remove 1 t/friends
Expected: The language field of the first contact is removed only if its tag name previously wasfriends
. Otherwise, an error message is shown as the field does not exist in the SOCKet. -
Test case:
remove 0 t/
Expected: No contact field is removed. An error message is shown as the given index is invalid. An error is logged in the console. -
Test case:
remove 0
Expected: No contact field is removed. An error message is shown as the given syntax is invalid. An error is logged in the console.
-
Deleting a contact
-
Deleting a contact while all contacts are being shown
-
Prerequisites: List all contacts using the
list
command. Multiple contacts 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 contact 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.
-
Clearing all contacts or tags
-
Clearing all contacts or contacts associated with the given tag(s) while all contacts are being shown
-
Test case:
clear
Expected: All contacts are cleared. -
Test case:
clear t/
Expected: All contacts are cleared. -
Test case:
clear t/friends
Expected: Contacts with tagfriends
are cleared. -
Test case:
clear t/enemy
Expected: If there are no contact with the tagenemy
, no contact is cleared. An error message is shown as no such tag was found in the SOCket.
-
Sorting contacts
- Sorting contacts while all contacts are shown
- Prerequisites: At least two contacts in the list.
- Test case: ‘sort’
Expected: Contacts are sorted by name in alphanumerical order. - Test case: ‘sort name’
Expected: Contacts are sorted by name in alphanumerical order. - Test case: ‘sort address’
Expected: Contacts are sorted by address in alphanumerical order. Contacts without an address are at the back. - Other categories to try: ‘phone’, ‘email’, ‘github’
Expected: Contacts are sorted by the given category in alphanumerical order. Contacts without the given category are at the back. - Test case: ‘sort invalid’
Expected: An error message is shown as an invalid category was given. An error is logged in the console.
Viewing a contact’s detailed information
-
Viewing a contact’s detail while all the contacts are being shown
-
Prerequisites: At least one project in the list.
-
Test case:
view 1
Expected: Detailed information of the first contact is displayed in the person detail panel. -
Test case:
view 1 t/
Expected: No contact is displayed in the person details panel. An error message is shown as an invalid command was given. -
Test case:
view 0
Expected: No contact is displayed in the person details panel. An error message is shown as an invalid index was given. An error is logged in the console.
-
Adding a project
- Adding a project to the project list.
-
Prerequisites: No project in the list named
Project 1
,Project 2
,Project 3
orProject 4
. -
Test case:
addpj n/Project 1 h/project-1 r/ProjectOne d/29/04/22-0900 m/24/03/22-1400
Expected: Project with the project nameProject 1
, repo hostproject-1
, repo nameProjectOne
, deadline29/04/22-0900
& meeting24/03/22-1400
is added to the project list. -
Test case:
addpj n/Project 2 h/project-1 r/ProjectOne d/29/04/22-0900 m/24/03/22-1400
Expected: Project with the project nameProject 2
, repo hostproject-1
, repo nameProjectOne
, deadline29/04/22-0900
& meeting24/03/22-1400
is added to the project list. -
Test case:
addpj n/Project 3 h/project-1 r/ProjectOne d/29/04/22-0900
Expected: Project with the project nameProject 1
, repo hostproject-1
, repo nameProjectOne
& deadline29/04/22-0900
is added to the project list. -
Test case:
addpj n/Project 4 h/project-4 r/ProjectThree
Expected: No project is added to the project list as deadline value is missing.
-
Editing a project
- Editing a project while all projects are being shown
-
Prerequisites: At least one project in the list.
-
Test case:
editpj 1 n/Project Bravo
Expected: First project in the list is edited with the project nameProject Bravo
-
Test case:
editpj 1 r/BravoRepo
Expected: Existing repo name of the first project in the list are replaced with the repo nameBravoRepo
-
Test case:
editpj 1
Expected: No project is edited. An error message is shown as no fields to edit were provided. An error is logged in the console. -
Test case:
editpj 0
Expected: No project is edited. An error message is shown as an invalid index was given. An error is logged in the console. -
Other incorrect edit commands to try:
editpj
,editpj Project Alpha
Expected: No project is edited. An error message is shown as an invalid command was given.
-
Deleting a project
-
Deleting a project while all projects are being shown
-
Prerequisites: At least one project in the shown list.
-
Test case:
deletepj 1
Expected: First project is deleted from the list. Details of the deleted project shown in the status message. Timestamp in the status bar is updated. -
Test case:
deletepj 0
Expected: No project is deleted. Error details shown in the status message. Status bar remains the same. -
Other incorrect delete commands to try:
deletepj
,deletepj x
,...
(where x is larger than the list size)
Expected: Similar to previous.
-
Removing a project’s field
- Removing a project while all projects are being shown
-
Prerequisites: At least one project in the shown list.
-
Test case:
removepj 1 h/
Expected: First project repo host field is removed. Repo host in the project card becomesNot Available
. -
Test case:
removepj 1 h/ r/
Expected: First project repo host and repo name fields are removed. Repo host and Repo name in the project card becomesNot Available
. -
Test case:
removepj 1 r/first-project
Expected: First project repo name field is removed only if its repo name previously wasfirst-project
. Otherwise, an error message is shown as the field does not exist in the project. -
Test case:
removepj 0 h/
Expected: No project field is removed. An error message is shown as the given index is invalid. An error is logged in the console. -
Test case:
removepj 0
Expected: No project field is removed. An error message is shown as the given syntax is invalid. An error is logged in the console.
-
Clearing all projects
- Clearing all projects in the list.
-
Test case:
clearpj
Expected: All projects are cleared. -
Test case:
clearpj 123
Expected: All projects are cleared. -
Test case:
clearpj h/
Expected: All projects are cleared.
-
Sorting projects
- Sorting projects while all projects are being shown
- Prerequisites: At least two projects in the list.
- Test case: ‘sortpj’
Expected: Projects are sorted by deadline in chronological order. Projects without a deadline are at the back. - Test case: ‘sortpj name’
Expected: Projects are sorted by name in alphanumerical order. - Test case: ‘sortpj reponame’
Expected: Projects are sorted by repo name in alphanumerical order. Projects without a repo name are at the back. - Other categories to try: ‘repohost’, ‘deadline’, ‘meeting’
Expected: Projects are sorted by the given category in alphanumerical order. Projects without the given category are at the back. - Test case: ‘sortpj invalid’
Expected: Projects are not sorted. An error message is shown as an invalid category was given. An error is logged in the console.
Assigning a contact to a project
- Assigning a person contact to a project while all contacts & projects are being shown.
-
Prerequisites: At least one contacts & exactly two project in their shown list.
-
Test case:
assign 1 1
Expected: First contact in the contact list is assigned to the first project in the project list. -
Test case:
assign 1 3
Expected: First contact in the contact list is not assigned to any project in the project list. An error message is shown as the index provided for the project list is invalid. -
Test case:
assign a 1
Expected: An error message is shown as the given syntax is invalid. -
Test case:
assign 1 b
Expected: An error message is shown as the given syntax is invalid.
-
Unassigning a contact from a project
- Unassigning a contact from a project while all projects are being shown
- Prerequisites: At least one project with an existing contact named ‘Amy Bee’ assigned to the first project
- Test case:
unassign 1 n/Amy Bee
Expected: Amy Bee is unassigned from the first project. - Test case:
unassign 1 n/Bob Choo'
Expected: No contact is unassigned from the first project. An error message is shown as the contact is not assigned to the project.
Saving data
-
Dealing with missing/corrupted data files
- Prerequisites: A
.json
data file containing some data -
Test case: Corrupted
.json
fileOpen the
.json
file with a text editor and change any field to an invalid value (e.g. change a"phone"
field toabc
). Launch SOCket.Expected: SOCket starts with no data. An error is logged in the console.
- Prerequisites: A