CampusConnect is adapted from the AddressBook Level-3 created by the SE-EDU initiative.
Below are the Java libraries used in this project:
Refer to the guide Setting up and getting started.
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
(consisting of classes Main
and MainApp
) is in charge of the app launch and shut down.
The bulk of the app's work is done by the following 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.Commons
represents a collection of classes used by multiple other components.
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),
interface
with the same name as the Component.{Component Name}Manager
class (which follows the corresponding API interface
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.
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
, 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,
Logic
component.Model
data so that the UI can be updated with the modified data.Logic
component, because the UI
relies on the Logic
to execute commands.Model
component, as it displays Person
object residing in the Model
.API : Logic.java
Here's a (partial) class diagram of the Logic
component:
The sequence diagram below illustrates the interactions within the Logic
component, taking execute("delete 1")
API call as an example.
Note: The lifeline for DeleteCommandParser
should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
How the Logic
component works:
Logic
is called upon to execute a command, it is passed to an AddressBookParser
object which in turn creates a parser that matches the command (e.g., DeleteCommandParser
) and uses it to parse the command.Command
object (more precisely, an object of one of its subclasses e.g., DeleteCommand
) which is executed by the LogicManager
.Model
when it is executed (e.g. to delete a person).CommandResult
object which is returned back from Logic
.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:
AddressBookParser
class creates an XYZCommandParser
(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 a XYZCommand
object (e.g., AddCommand
) which the AddressBookParser
returns back as a Command
object.XYZCommandParser
classes (e.g., AddCommandParser
, DeleteCommandParser
, ...) inherit from the Parser
interface so that they can be treated similarly where possible e.g, during testing.API : Model.java
Here's a class diagram of the Model
component:
Below is a class diagram on the Person
class and the classes related to its attributes:
The Model
component,
Person
objects (which are contained in a UniquePersonList
object).Person
objects (e.g., results of a search query) as a separate filtered list which is exposed to outsiders as an unmodifiable ObservableList<Person>
that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change.UserPref
object that represents the user’s preferences. This is exposed to the outside as a ReadOnlyUserPref
objects.Model
represents data entities of the domain, they should make sense on their own without depending on other components)Note: An alternative (arguably, a more OOP) model is given below. It has a 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.
API : Storage.java
The Storage
component,
AddressBookStorage
and UserPrefStorage
, which means it can be treated as either one (if only the functionality of only one is needed).Model
component (because the Storage
component's job is to save/retrieve objects that belong to the Model
)Classes used by multiple components are in the seedu.address.commons
package.
This section describes some noteworthy details on how certain features are implemented.
The notifications feature is centered around Event
instances.
Event
can represent any type of event with a specific date and time.
This could be a birthday, an upcoming meeting or a deadline.
Event
also encapsulates timings where a reminder should be created.
On startup, EventFactory#createEvents(model)
is used to generate Event
instances from the initial state of the model.
Any future events can be added to the data model as well during runtime.
Three public methods for Event
are important for its usage
Event#addMember(Person)
— Adds a Person
as associated with this event.Event#addReminder(Duration)
— Sets a reminder for the event one Duration
before the time of the actual event. For example, if Duration
is set to a day, the reminder will be a day in advance.Event#getNotificationAtTime(LocalDateTime)
— Check if any notifications should be generated based on a specific time, usually the current time should be passed as the parameter.Below is the class diagram for the Event
class and it's interactions with the other classes.
The startup sequence for creating initial events is given below as well.
On a high level, the MainApp#initEvents()
will use EventFactory#createEvents(model)
to generate Event
instances from the intial state of the model, then add all of these events to the model.
Based on the Event
instances present in the data model, you can call Model#getLatestNotifications(LocalDateTime)
, passing in the current datetime, in order to get a list of Notification
instances representing notifications that should be displayed to the user.
The UI system will make use of this API to check if any notifications should be displayed to the user at startup.
The flow for the startup notifications is described by the following sequence diagram.
Aspect: Generic design
A generic event system was created, even though CampusConnect only requires a specific Birthday notification system at the moment.
Alternative 1 (current choice): Generic event system.
Alternative 2: Birthday notification system.
The addalt
feature involves creating a new Person
object with additional contact details to replace the previous Person
object.
This is done using the AddAltPersonDescriptor
class; AddAltPersonDescriptor
object stores the additional contact information to be added to the previous Person
object.
As a result, the existing Person
class in AB3's implementation is enhanced to have the capacity of containing more attributes. The Person
object is now composed of the following additional attributes due to the addalt
feature on top of the existing attributes from AB3's implementation:
Email
: The secondary email address of the contact.Linkedin
: The linkedin of the contact.Telegram
: The telegram handle of the contact.Birthday
: The birthday of the contact.The java.util.Optional<T>
class is utilised to encapsulate the optional logic of the above attributes.
To add these additional attributes into a Person
object, an INDEX
parameter, followed by the prefixes that represent the attributes needs to be specified for the addalt
command.
INDEX
represents the index number of the contact to be edited in the contact list.
While all the fields are optional, at least one needs to be given to the addalt
command.
The flow for the addalt
command is described by the following sequence diagram:
INDEX
provided is valid and each of the additional attribute input follows the pre-determined formats defined in their respective classes. It also checks that the added secondary email does not result in the contact to have duplicate emails.Person
objected with the updated attributes is created and stored in the AddressBook
.Aspect: Generic design
The additional attributes to be added into a Person
object on top of the original AB3 attributes are encapsulated into their respective classes: Linkedin
, Telegram
and Birthday
. These classes are implemented similarly to the other existing attributes of Person
, but they are modified according to the respective input patterns that model the real world.
As these attributes are additional information for a Person
object, every attribute has been made optional in the case when user only keys in several, but not all of the additional attributes into the command. However, the purpose of using this command only exists when users would like to add additional attributes to a Person
in the contact list. Thus, the addalt
command is designed to ensure that the user adds at least one of the additional attributes aforementioned.
As this command merely adds additional attributes to a Person
object, this can be done by enhancing the add
command.
addalt
.
Person
is added by the add
command.Person
.add
command.
Person
entry.The edit
feature is similar to the implementation of addalt
; it involves creating a new Person
object with edited contact details to replace the previous Person
object.
This is done using the EditPersonDescriptor
class; EditPersonDescriptor
object stores the contact information to be updated to the previous Person
object.
The edit
command has similar input fields to the addalt
command with the difference being that it is able to edit all the attributes of a Person
object except:
Note
: The notes of the contact. Read Notes feature
for more details.Avatar
: The profile picture of the contact. Read Update photo feature
for more details.Balance
: The amount that the contact owes. Read Payments feature
for more details.While all the fields are optional, at least one needs to be given to the edit
command. Users who wishes to edit
empty additional attributes of Person
object should use addalt
instead.
addalt
, the application will validate the arguments supplied by the user; whether the INDEX
provided is valid and each of Person
attribute input follows the pre-determined formats defined in their respective classes. However, it also checks that the edit
command does not update any empty additional attributes of Person
and update Person
object to have same primary and secondary email.Person
and the new Person
object with the edited attributes is the same.edit
command does not update the Person
object.Person
objected with the updated attributes is created and stored in the AddressBook
.The following activity diagram shows the logic of a user using the edit
command:
The flow for the edit
command is described by the following sequence diagram:
Since edit
command updates attributes of a Person
object, setting the values directly to the Person
object could be another viable option.
Person
object.
Person
class, reducing possible bugs.Person
object is always created even if for instance, only one attribute of Person
object is changed.Person
object.
The find command feature allows users to locate specific Person instances within the application based on keywords provided. It is significantly revamped from AB3's implementation in three ways:
This is accomplished using a custom-built tokenizer / lexer (FindFilterStringTokenizer
) and parser (FindExpressionParser
).
The existing FindCommandParser
is used as a harness to tie the two together and provide a single entry point for the feature.
The FindFilterStringTokenizer
class is responsible for parsing complex boolean find filter strings into tokens, which can later be used to construct a filter expression tree.
It is reminiscent of lexers used in interpreters and compilers, and is implemented using a state machine.
The class also includes a Token
inner class that represents a token in the filter string. Each Token
has a Type
and a text representing the token in the filter string.
The class takes a filter string as input and tokenizes it into a list of Token
objects.
Each Token
object represents a component in the boolean filter string and has a Type
(AND
, OR
, NOT
, LPAREN
, RPAREN
, CONDITION
) and a text representing the token in the filter string.
The tokenize
method is the main method in this class.
It iterates over the characters in the filter string and based on the current character, it creates a new Token
object and adds it to the list of tokens.
The method handles different types of tokens including AND, OR, NOT operators, parentheses for grouping, and conditions in the form of FIELD/KEYWORD
or FIELD/"KEYWORDS AS QUOTED STRING"
.
Ultimately, the tokenize
method returns a list of Token
objects that represent the tokens in the filter string.
The FindExpressionParser
class is responsible for constructing a filter expression tree from a list of tokens, which is reminiscent of parsers used in interpreters and compilers.
The parseToPredicate
method is the main method in this class.
It uses a recursive descent parsing algorithm to parse the list of tokens into a filter expression tree.
The class also converts it into a Predicate<Person>
that can be directly used to filter the list of persons.
Specifically, the class takes a list of Token
objects from FindFilterStringTokenizer
and parses it into a filter expression tree.
These nodes are represented as subclasses of the ExprNode
inner class, which is an abstract class that represents a node in the filter expression tree.
Each ExprNode
has a Type
and a text
representing the node in the filter string, as well as a toPredicate
method which outlines how to convert that node into a Predicate<Person>
which can actually be used to filter through a person list.
The subclasses are:
BinaryOpNode
: Represents a binary operation (AND
, OR
) between two other nodes (which can be any type of ExprNode).NotNode
: Represents a NOT
operation on another node (which can be any type of ExprNode).ConditionNode
: Represents a condition in the form of FIELD/KEYWORD
or FIELD/"KEYWORDS AS QUOTED STRING"
.The parsing conducted by the parseToPredicate
method follows the structure of a boolean expression grammar, which is defined as follows:
OR
and a term.AND
and a factor.NOT
, or an expression enclosed in parentheses.This structure ensures that the AND
operator has higher precedence than the OR
operator, and the NOT
operator has higher precedence than both AND
and OR
.
This is because a term (which can contain AND
operations) is treated as a single unit in an expression, and a factor (which can contain NOT
operations) is treated as a single unit in a term.
Ultimately, the parse
method returns an ExprNode
object that represents the root of the filter expression tree.
This tree structure represents the logical structure of the user's input and is used to evaluate whether a record matches the filter conditions.
The flow of the find feature is described by the following high-level sequence diagram:
Aspect: Command Flexibility vs. Complexity
Alternative 1 (current choice): Support boolean operations in FindCommand
.
Alternative 2: Only allow simple keyword-based searches.
Aspect: Tokenizer library
Alternative 1 (current choice): Custom-built tokenizer and parser.
Alternative 2: Use a third-party library.
Aspect: Tokenization Strategy
Alternative 1 (current choice): Custom tokenizer.
Alternative 2: Regular expression-based tokenizer.
Aspect: Supported Logical Operators
Alternative 1 (current choice): Use standard boolean operators (&&
, ||
, !
).
Alternative 2: Support more advanced operators or functions (e.g., nearness search, regex patterns).
Aspect: Handling Invalid Inputs
The UpdatePhotoCommand
feature allows users to update photo of a specific contact. This functionality is essential for forgetful users who want to store photos of contacts to remember them easier.
Two key classes are involved in this implementation:
UpdatePhotoCommand
: Handles the logic for updating photo.Avatar
: Represents the photo of a contact.Photos are stored as an Avatar
within the Person model.
The Avatar
class contains a String
representing the path to the chosen photo.
When a user inputs a command to update photo of a contact, the UpdatePhotoCommandParser
parses the user input and creates a new UpdatePhotoCommand
object. This object is then executed, which results in the update of the contact's photo.
The process can be summarized in the following logic flow:
UpdatePhotoCommand
object.UpdatePhotoCommand
, which involves:
Avatar
.CommandResult
indicating the outcome.Key methods in this implementation include:
UpdatePhotoCommand::UpdatePhotoCommand(Index index, String path)
: Constructor to initialize the command.UpdatePhotoCommand::execute(Model model)
: Updates the photo as well as the model.The Payments feature allows users to keep track of the money they owe to and are owed by their contacts. This feature is useful for users who want an easy way to figure out how to settle their payments in one future transaction, and ensure that all transactions are tracked and eventually settled.
Three key classes are involved in this implementation:
Balance
: Represents money amounts owed by or to a contact.PayCommand
: Handles the logic to track that you have paid / lent money to a contact.PaybackCommand
: Handles the logic to track that you have received / borrowed money from a contact.Additionally, the ParserUtil::parseBalance
method is used to parse a human-friendly string representation of a balance into an int
value representing a monetary value in cents.
Money is represented with a Balance
class, which is effectively a wrapper around an int
value representing a monetary value in cents. However, users interact with the Balance
class through a human-friendly string representation, with a dollars and cents component, that may optionally include a dollar sign.
Most validation is handled by the Balance
class, through three static methods:
isValidDollarString(String test)
: Checks if a given string is a valid human-friendly string representation of a balance using a validation regex.isWithinBalanceLimit(Integer balanceInCents)
: Checks if a given integer is a valid balance value that is a field of a contact. CampusConnect restricts users to only be able to track a maximum payment owed to / from a contact of $10,000.isWithinTransactionLimit(Integer amountInCents)
: Checks if a given integer is a valid money value that is used in a single transaction. Due to the $10,000 balance limit, any individual transaction cannot transact more than $20,000, since that will always lead to the balance limit being exceeded. This check occurs whenever a Balance
object is instantiated, since there should never be any Balance
objects with a value greater than $20,000.The PayCommand
and OweCommand
classes are extremely similar. Both classes simply update the Person's balance with the amount given.
The following activity diagram shows the logic of the validation checks that occur when a user uses the pay
command:
The series of validation checks in the activity diagram also applies to the owe
command.
Aspect: Money representation data type
There are multiple options to represent money values in the application within the Balance class.
int
to represent money value in cents.
double
to represent money value in dollars.
Aspect: Specialization of commands
The payments feature could be implemented as a single command, with a negative sign to specify whether the payment is a pay
or owe
command.
pay
and owe
commands to represent positive and negative transactions.
The AddNoteCommand
feature allows users to add personalized notes to a specific contact in the Address Book. This functionality is essential for users who wish to record additional information about their contacts.
Three key classes are involved in this implementation:
AddNoteCommand
: Handles the logic for adding the note.Index
: Represents the index of the person in the filtered person list to whom the note will be added.Note
: Represents the content of the note to be added.Notes are stored as an ObservableList<Note>
within the Person model. This is done to simplify storage and retrieval of notes, as well as to enable the use of JavaFX components to display the notes with the Observer
pattern.
The Note
class is a simple wrapper class that contains a String
representing the content of the note.
When a user inputs a command to add a note, the NoteCommandParser
parses the user input and creates a new AddNoteCommand
object. This object is then executed, which results in the addition of the specified note to the targeted contact.
The process can be summarized in the following logic flow:
AddNoteCommand
object.AddNoteCommand
, which involves:
CommandResult
indicating the outcome.Key methods in this implementation include:
AddNoteCommand(Index index, Note note)
: Constructor to initialize the command.execute(Model model)
: Adds the note and updates the model.The Notes feature also includes a user interface component to allow users to interactively add, view, and remove notes from contacts.
The user interface for the Notes feature is implemented using JavaFX components. The main components include:
PersonCard
: Displays individual person's details and includes a button for accessing notes.NotesWindow
: A pop-up window that displays all notes associated with a person.The PersonCard
component includes a button (notesButton
) that, when clicked, triggers the handleNotesButtonClick
method in the controller. This method creates and shows a new NotesWindow
.
The NotesWindow
is responsible for displaying the list of notes and is populated using a ListView
component. The controller for NotesWindow
handles the population of this list and the closing of the window. NotesWindow
has a button (closeButton
) that, when clicked, triggers the handleCloseButtonClick
method in the controller. This method closes the window. The NotesWindow
is also closed when the user presses the ESC
key.
Aspect: Integration with Existing Data Model
Person
class and Model
interface.
Person
class becomes more complex as more features are added.Person
class simpler and more focused on contact information.Choosing alternative 1 aligns with the principle of maximizing code reuse and maintaining consistency across the application, even though it slightly increases the complexity of the Person
class.
Aspect: User Interaction and Experience
PersonCard
.
Choosing alternative 1 provides a balance between functionality and user interface simplicity, ensuring that the notes feature does not overwhelm the main contact viewing experience.
The UI components are tested using TestFX to ensure that they behave as expected. Test cases include verifying the display of notes, interaction responses, and the proper functioning of the close button. Ensuring thorough testing is vital for maintaining the reliability and user-friendliness of the application.
Take note that UI tests have to be run on the JavaFX
thread, so UI tests have to extend ApplicationTest
from TestFX
to run properly.
preferences.json
file created by the application before running the application again.add
/edit
command, if you try to add/edit a new/existing contact with the same properties of another saved contact (Note: 2 names are considered the same if both of them have the same casing and whitespaces) in your contact list, CampusConnect allows you to do so. We plan to enhance the add
/edit
command such that it takes into account what makes a contact unique in your contact list.addalt
command, if you input other prefixes that are not accepted by the command format, the error message shown does not prompt you to remove those prefixes and adhere strictly to the command format. We will be working on this in the future to improve the specificity of error messages.PERSON_INDEX
, if you did not input an appropriate index, the error message shown is generic; CampusConnect informs you the format of the command you should adhere to instead of prompting you to input a positive index. We will be working on this in the future to improve the specificity of error messages.updatephoto
command, if the PERSON_INDEX
contains characters besides 0-9
, CampusConnect will be unresponsive as we assume that you will input a valid integer for PERSON_INDEX
. Moreover, successful execution of the same updatephoto
command with the same image will still result in Photo updated
even though the photo is not updated. In addition, you can input multiple valid paths and the command will update your contact profile with the last image. We will be working on handling more errors and improving the specificity of messages in the future.add
not accepting slashes). We plan to improve our internationalization support in the future, allowing for Unicode characters to be used throughout the app since users could have contacts with names including diacritics or non-alphabetic characters (e.g. Tamil, Arabic or Chinese names).find
command, if you input a FIND_EXPRESSION
that is not accepted by the command format, the error message shows fairly general error messages. We will be working on this in the future to improve the specificity of error messages.find
command, it is impossible to search for keywords that include the double-quote character ("
) under any circumstance. We will be working on this in the future to support searching for the double-quote character, which could appear in fields such as notes.find
command, the behavior of the bal
field is not intuitive, especially for users who do not read the User Guide in-depth. We will be working on this in the future to improve the ergonomics of bal
field, by implementing >
and <
operators so users can search for balance amounts below or above the keywords.addressbook.json
is unable to be parsed by the application as a valid AddressBook
(e.g. data is corrupt), the app may overwrite it on the next exit, causing data loss. We will rewrite our data storage code to be more robust when dealing with this issue by backing up data files even when they are corrupt, allowing users to retrieve them in the future.Target user profile:
Value proposition: manage contacts faster than a typical mouse/GUI driven app
Priorities: High (must have) - * * *
, Medium (nice to have) - * *
, Low (unlikely to have) - *
Priority | As a … | I want to … | So that I can… |
---|---|---|---|
* * * | user | add new contacts with basic contact information | keep track of the people I know. |
* * * | user | add alternative contact information such as telegram, email and linkedin | connect with friends through my preferred channels |
* * * | user | delete contacts | reduce clutter and keep my contact list organised. |
* * * | user | add notes associated with my contacts | remember important information regarding my contacts. |
* * * | user | delete notes associated with my contacts | remove unwanted information. |
* * | forgetful user | update photos for my contacts | visually remember them. |
* * * | forgetful user | add the birthday of my contact | keep track and remember my contacts’ birthdays. |
* * * | forgetful user | receive a notification when it is the day before my contact’s birthday | remember to celebrate his/her birthday. |
* * * | user | opt out of receiving notifications | keep myself from being distracted by the notifications. |
* * * | user | record money owed to a contact | remember to settle the debt. |
* * * | user | record money owed by a contact | remember to collect the money. |
* * * | user | search through my contacts based on their respective contact information | quickly access the information required. |
* * * | user | search my contacts by name | quickly find a person without scrolling through my entire list. |
* * * | user | search my contacts by phone number | identify who is calling me from an unfamiliar number. |
* * * | international in-campus resident | add Singapore’s emergency contact details | access them quickly in urgent situations. |
* * * | in-campus resident | add campus specific information to my contacts, in particular, a tag called RA (Residential Assistant) and SOS (Security Officer) | quickly reach out to them when required. |
* * * | on-campus student | quickly list the emergency contacts I have previously registered | contact them in an emergency. |
(For all use cases below, the System is CampusConnect
and the Actor is a NUS student who stays in campus
, unless specified otherwise)
Use case: UC1 - Add new contact
MSS
Extensions
Use case: UC2 - List all contacts
MSS
Extensions
Use case: UC3 - Add alternative information to existing contact
MSS
Extensions
Use case: UC4 - Edit information of existing contact
MSS
Extensions
Use case: UC5 - Delete contact
MSS
Extensions
Use Case UC6 - Add note
MSS
Extensions
Use Case UC7 - Remove note
MSS
Extensions
Use Case UC8 - Record payment
MSS
Extensions
Use Case UC9 - Search for contact
MSS
Extensions
Use Case UC10 - Update contact photo
MSS
Extensions
Prefix | Meaning | Example |
---|---|---|
n/ | Name of contact | n/John Doe |
p/ | Phone number of contact | p/98765432 |
e/ | Email of contact | e/johndoe@gmail.com |
a/ | Address of contact | a/16 Bukit Timah Road, S156213 |
t/ | Tags of contact | t/friend |
li/ | Linkedin of contact | li/john-doe |
tg/ | Telegram handle of contact | tg/@johndoe |
e2/ | Secondary email of contact | e2/johndoe@hotmail.com |
b/ | Birthday of contact | b/23/10 |
path/ | Path to the photo of contact | path/D:/images/john-doe.png |
Given below are instructions to test the app manually.
Note: These instructions only provide a starting point for testers to work on; testers are expected to do more exploratory testing.
Initial launch
Download the CampusConnect.jar
file and copy into an empty folder
Double-click the CampusConnect.jar
file Expected: Shows the GUI with a set of sample contacts. The window size may not be optimal.
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 Person
:
Test case: add n/John Doe p/98765432 e/johndoe@gmail.com a/John street, block 123, #01-01
Expected: A new Person
is successfully created with name "John Doe", phone "98765432", email "johndoe@gmail.com" and address "John street, block 123, #01-01". Success details shown in the status message. Moreover, this new Person
is visible in CampusConnect.
Test case: add n/John Doe p/98765432 e/johndoe@gmail.com a/
Expected: No new Person
is created. Error details shown in the status message.
Test case: add n/John Doe p/98765432 e/johndoe@gmail.com
Expected: No new Person
is created. Error details shown in the status message.
To see if the Person
is added, use the list
command and verify the last Person
in CampusConnect.
Adding alternative contact to a Person
:
Prerequisites: List all Persons
in CampusConnect using the list
command.
Test case: addalt 1 tg/@johndoe_123 e2/johndoe@hotmail.com li/john-doe-b9a38128a b/31/10
Expected: The first Person
in the list is added with the following alternative contact information: telegram "@johndoe_123", secondary email "johndoe@hotmail.com", linkedin "john-doe-b9a38128a" and birthday "31/10". Success details shown in the status message. The aforementioned happens only if the fields are initially empty. Otherwise, error details will be shown in status message.
Test case: addalt 1 tg/@johndoe_123 e2/
Expected: The first Person
in the list will not be added with the following alternative contact information: telegram "@johndoe_123". Error details shown in the status message.
Test case: addalt 1 tg/@johndoe_123 tg/@johnjohn
Expected: The first Person
in the list will not be added with the following alternative contact information: telegram "@johndoe_123" or "@johnjohn". Error details shown in the status message.
To see if the Person
is added with alternative contact information, use the find command to search for the Person
and verify the details.
To know exactly what are the alternative details of a Person
that can be added, see this.
Editing a Person
:
Prerequisites: List all Persons
in CampusConnect using the list
command.
Test case: edit 1 p/98765411 e/johndoe@gmail.com
Expected: The first Person
in the list will have phone edited to "98765411" and email edited to "johndoe@gmail.com". Success details shown in the status message.
Test case: edit 1 tg/@johndoe e2/johndoe@gmail.com
Expected: The first Person
in the list will have telegram edited to "@johndoe" and secondary email edited to "johndoe@gmail.com" only if the fields were not initially empty; success details shown in the status message. Otherwise, error details will be shown in status message.
Test case: edit 1 tg/@johndoe_123 tg/@johnjohn
Expected: The first Person
in the list will not have telegram edited. Error details shown in status message.
To see if the Person
's details are edited, use the find command to search for the Person
and verify the details.
To know exactly what are the details of a Person
that can be edited, see this.
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.
Updating photo of a Person
:
Prerequisites: List all Persons
in CampusConnect using the list
command.
Test case: updatephoto 1 path/docs/images/johndoe.png
Expected: The first Person
in the list will have profile photo updated to the picture "johndoe.png". Success details shown in the status message.
Test case: updatephoto 1 path/C:/documents/test.docx
Expected: The first Person
in the list will not have profile photo updated. Error details shown in the status message.
To see if the Person
's profile photo is updated, use the find command to search for the Person
and verify the profile photo.
Adding note to a Person
:
Prerequisites: List all Persons
in CampusConnect using the list
command.
Test case: addnote 1 This is a sample note for the person.
Expected: The first Person
in the list will have a new note stating "This is a sample note for the person.". Success details shown in the status message.
Test case: addnote 1 This person is very funny! 😀
Expected: The first Person
in the list will not have a new note stating "This person is very funny! 😀". Error details shown in the status message.
To see if the new note is added to Person
, use the viewnotes command and verify the details of the notes.
Paying money to a Person
:
Prerequisites: List all Persons
in CampusConnect using the list
command.
Test case: pay 1 10
Expected: The first Person
in the list will be paid $10. Success details shown in the status message.
Test case: pay 1 10.555
Expected: The first Person
in the list will not be paid $10.555. Error details shown in the status message.
Test case: pay 1 50000
Expected: The first Person
in the list will not be paid $50000. Error details shown in the status message.
To see if the money is paid to Person
, use the find command to search for the Person
and verify the money paid.
Owing money to a Person
:
Prerequisites: List all Persons
in CampusConnect using the list
command.
Test case: owe 1 10
Expected: You owe the first Person
in the list $10. Success details shown in the status message.
Test case: owe 1 10.555
Expected: You will not owe the first Person
in the list $10.555. Error details shown in the status message.
Test case: owe 1 50000
Expected: You will not owe the first Person
in the list $50000. Error details shown in the status message.
To see if the money the Person
owes is recorded, use the find command to search for the Person
and verify the money owed.
Finding a Person
while all persons are being shown:
Test case: find n/do
Expected: CampusConnect displays a list of Person
that has name that contains the substring do
.
Test case: find t/friends
Expected: CampusConnect displays a list of Person
that has tag that is equal to the tag friends
.
Test case: find a/John street, block 123, #01-01
Expected: CampusConnect will not display any Person
. Error details shown in the status message.
Test case: find a/"John street, block 123, #01-01"
Expected: CampusConnect will display a list of Person
with address John street, block 123, #01-01
.
CampusConnect is an advanced extension of the foundational AB3, enriched with sophisticated new features and a more advanced UI. These enhancements are complex and significantly expand the application's functionality beyond the original AB3.
Our project encountered numerous challenges:
Person.java
. AB3's extensive codebase posed a significant learning curve.addressbook.json
.NotesWindow.java
, as well as figuring out how to modify already-existing UI elements like PersonCard.java
.find
function was enhanced to support complex, fully expressive queries. This involved developing a custom tokenizer, parser and expression evaluator, which was a significant undertaking.Person
class to incorporate additional data fields crucial for the addalt
feature.Overall, significant effort was spent in firstly evaluating our user requirements, and then coming up with ways in which we could extend AB3 to better suit their needs. The implementation of these features often required us to extend AB3 well beyond its original capabilities, and in some cases write entirely new systems in order to support them. Development work was carried out without sacrificing code quality, and we maintained a high standard of testing throughout the process, only merging PRs if they demonstrated sufficient test coverage.