Developer Guide
- Acknowledgements
- Setting up, getting started
- Design
- Implementation
- Documentation, logging, testing, configuration, dev-ops
-
Appendix: Requirements
- Product scope
- User stories
- Use cases
- Use case: UC01 - Add a new client into SWEE
- Use case: UC02 - Edit a client
- Use case: UC03 - Delete a client
- Use case: UC04 - View a client’s information
- Use case: UC05 - List all saved clients
- Use case: UC06 - Find a client by name
- Use case: UC07 - Find a client by Tag
- Use case: UC08 - Schedule an appointment
- Use case: UC09 - Unschedule an appointment
- Non-Functional Requirements
- Glossary
- Appendix: Planned Enhancements
- Appendix: Instructions for manual testing
Acknowledgements
- This project is based on (forked from) the AddressBook-Level3 project created by the SE-EDU initiative
- Libraries used: JavaFX, Jackson, JUnit5
- AI Usage: ChatGPT by OpenAI, Github Copilot
- Used to answer design questions and write skeleton code.
Setting up, getting started
Refer to the guide Setting up and getting started.
Design
![:bulb: :bulb:](https://github.githubassets.com/images/icons/emoji/unicode/1f4a1.png)
.puml
files used to create diagrams in this document docs/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
(consisting of classes Main
and MainApp
) is in charge of the app launch and shut down.
- At app launch, it initializes the other components in the correct sequence, and connects them up with each other.
- At shut down, it shuts down the other components and invokes cleanup methods where necessary.
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 del 1
.
Each of the four main components (also shown in the diagram above),
- defines its API in an
interface
with the same name as the Component. - implements its functionality using a concrete
{Component Name}Manager
class (which follows the corresponding APIinterface
mentioned in the previous point.
For example, the Logic
component defines its API in the Logic.java
interface and implements its functionality using the LogicManager.java
class which follows the Logic
interface. Other components interact with a given component through its interface rather than the concrete class (reason: to prevent outside component’s being coupled to the implementation of a component), as illustrated in the (partial) class diagram below.
The sections below give more details of each component.
UI component
The API of this component is specified in Ui.java
The UI consists of a MainWindow
that is made up of parts e.g.CommandBox
, ResultDisplay
, ClientListPanel
, 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 displaysClient
object residing in theModel
.
Logic component
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("del 1")
API call as an example.
![:information_source: :information_source:](https://github.githubassets.com/images/icons/emoji/unicode/2139.png)
DeleteCommandParser
should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline continues till the end of diagram.
How the Logic
component works:
- When
Logic
is called upon to execute a command, it is passed to anAddressBookParser
object which in turn creates a parser that matches the command (e.g.,DeleteCommandParser
) and uses it to parse the command. - This results in a
Command
object (more precisely, an object of one of its subclasses e.g.,DeleteCommand
) which is executed by theLogicManager
. - The command can communicate with the
Model
when it is executed (e.g. to delete a client).
Note that although this is shown as a single step in the diagram above (for simplicity), in the code it can take several interactions (between the command object and theModel
) to achieve. - The result of the command execution is encapsulated as a
CommandResult
object which is returned back fromLogic
.
Here are the other classes in Logic
(omitted from the class diagram above) that are used for parsing a user command:
How the parsing works:
- When called upon to parse a user command, the
AddressBookParser
class creates anXYZCommandParser
(XYZ
is a placeholder for the specific command name e.g.,AddCommandParser
) which uses the other classes shown above to parse the user command and create aXYZCommand
object (e.g.,AddCommand
) which theAddressBookParser
returns back as aCommand
object. - All
XYZCommandParser
classes (e.g.,AddCommandParser
,DeleteCommandParser
, …) inherit from theParser
interface so that they can be treated similarly where possible e.g, during testing.
Model component
API : Model.java
The Model
component,
- stores the address book data i.e., all
Client
objects (which are contained in aUniqueClientList
object). - stores the currently ‘selected’
Client
objects (e.g., results of a search query) as a separate filtered list which is exposed to outsiders as an unmodifiableObservableList<Client>
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)
![:information_source: :information_source:](https://github.githubassets.com/images/icons/emoji/unicode/2139.png)
Tag
list in the AddressBook
, which Client
references. This allows AddressBook
to only require one Tag
object per unique tag, instead of each Client
needing their own Tag
objects.![](images/BetterModelClassDiagram.png)
Note that this image omitted note and appointment
Storage component
API : Storage.java
The Storage
component,
- can save both address book data and user preference data in JSON format, and read them back into corresponding objects.
- inherits from both
AddressBookStorage
andUserPrefStorage
, which means it can be treated as either one (if only the functionality of only one is needed). - depends on some classes in the
Model
component (because theStorage
component’s job is to save/retrieve objects that belong to theModel
)
Common classes
Classes used by multiple components are in the seedu.addressbook.commons
package.
Implementation
This section describes some noteworthy details on how certain features are implemented.
Add command
This section describes how the add commands work which will serve as a basic understanding of how commands are implemented.
AddressBookParser : AddressBookParser
AddCommandParser : AddCommandParser.java
AddCommand : AddCommand.java
The sequence diagram below illustrates the interactions within the Logic
component, taking execute("add --name=Name --phone=2103 --email=hello@Test --addr=Address --tags=hello --tags=Hi --note=NA")
API call as an example.
![:information_source: :information_source:](https://github.githubassets.com/images/icons/emoji/unicode/2139.png)
AddCommandParser
should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline continues till the end of diagram.
Note: [ARGS]
refer to the arguments of the add command that have been parsed. It have been shortened for brevity
How an add
command is executed:
- When Logic is called upon to execute a command, it is passed to an
AddressBookParser
. object which in turn creates a parser that matches the commandAddCommandParser
). and uses it to parse the arguments passed to the command. -
AddCommand
will check that the fields:name
,phone
,email
,address
andnote
are present. The prefixes for these options are defined in CliSyntax.java.- Parsing of the arguments is done by
ArgumentTokenizer.java
(not shown here). - This returns a
ArgumentMultiMap
which supports further operations.
- Parsing of the arguments is done by
- If any of the required prefixes are missing, it will throw a ParseException, which will print a message indicating the correct usage and missing fields, if any.
- It will then create the
Client
to represent the Client to be created. - The
Client
is used as an argument to create anAddCommand.java
. - The
AddCommand
is returned by the parser which is passed on byAddressBookParser
which passes it toLogicManager
. -
LogicManager
calls theexecute
method ofAddCommand
with its model object as a parameter. -
AddCommand
returns aCommandResult
. - LogicManager will call
getAddressBook
ofModel
and then use it as an argument to callsaveAddressBook
of itsstorage
variable.
View command
This section describes how the view
command works and is implemented.
The Object, Sequence and Activity UML diagrams belows shows the objects created as well as their interactions (e.g. method calls) when the view
command is activated.
Class Diagram
Sequence Diagram
![:information_source: :information_source:](https://github.githubassets.com/images/icons/emoji/unicode/2139.png)
ViewCommandParser
should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline continues till the end of diagram.
Activity Diagram
How a view
command is executed:
- User Input Parsing
- When the user enters a command, it is first parsed by the
AddressBookParser
. - The
parseCommand(String userInput)
method inAddressBookParser
splits the user input into the command word and arguments using a regular expression. - If the command word matches “view”, the parsing is delegated to the
ViewCommandParser
.
- When the user enters a command, it is first parsed by the
- View Command Parsing:
- The
ViewCommandParser
expects the arguments to contain an index, which represents the position of the item to view in the list. - The
parse(String args)
method inViewCommandParser
parses the index from the arguments usingParserUtil.parseIndex(args)
.
- The
- Command Execution
- Once the index is parsed successfully, a new
ViewCommand
object is created with the parsed index. - The
ViewCommand
Object is returned to theAddressBookParser
and then to theLogicManager
. - The
LogicManager
executes the command and generates aCommandResult
.
- Once the index is parsed successfully, a new
-
MainWindow
creates aCommandBox
with the resulting command output, allowing the user to view a contact’s information.
Delete command
This section describes how the del
command works and is implemented.
The Sequence and Activity UML diagrams belows shows the objects created as well as their interactions (e.g. method calls) when the del
command is activated.
Sequence Diagram
![:information_source: :information_source:](https://github.githubassets.com/images/icons/emoji/unicode/2139.png)
DeleteCommandParser
should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline continues till the end of diagram.
Activity Diagram
How a del
command is executed:
- User Input Parsing
- When the user enters a command, it is first parsed by the
AddressBookParser
. - The
parseCommand(String userInput)
method inAddressBookParser
splits the user input into the command word and arguments using a regular expression. - If the command word matches “del”, the parsing is delegated to the
DeleteCommandParser
.
- When the user enters a command, it is first parsed by the
- Delete Command Parsing:
- The
DeleteCommandParser
expects the arguments to contain an index, which represents the position of the client to delete in the list. - The
parse(String args)
method inDeleteCommandParser
parses the index from the arguments usingParserUtil.parseIndex(args)
.
- The
- Command Execution
- Once the index is parsed successfully, a new
DeleteCommand
object is created with the parsed index. - The
DeleteCommand
Object is returned to theAddressBookParser
and then to theLogicManager
. - The
LogicManager
executes the command and generates aCommandResult
.
- Once the index is parsed successfully, a new
-
MainWindow
creates aCommandBox
with the resulting command output, deleting the client at the specified index from the Address Book.
Find-Tag Command
This section describes how the find-Tag
command works and is implemented.
The Sequence and Activity UML diagrams belows shows the objects created as well as their interactions (e.g. method calls) when the find-Tag
command is activated.
Class Diagram
Sequence Diagram
>
![:information_source: :information_source:](https://github.githubassets.com/images/icons/emoji/unicode/2139.png)
FindTagCommandParser
should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline continues till the end of diagram.
Activity Diagram
How a find-tag
command is executed:
- The User inputs a command in the format of “find-tag [TAG]” to find clients by tag.
-
LogicManager
receives the user command and parses the command to theAddressBookParser
. -
AddressBookParser
parses this command to theFindTagCommandParser
. -
FindTagCommandParser
creates a new instance ofFindTagCommand
using the tag as a predicate.
-
-
FindTagCommand
instance is returned to theLogicManager
.-
LogicManager
calls the execute() method of theFindTagCommand
instance. - A
CommandResult
is generated and returned toMainWindow
.
-
-
MainWindow
creates aCommandBox
with the resulting command output, listing our all clients with the specified tag.
Appointments
To keep things simple, Client
will keep an immutable ArrayList
of Appointment
(this is to preserve the immutability of Client).
This list is maintained in sorted order according to Appointment::compareTo
(Appointment
implements Comparable<Appointment>
) which is currently defined as increasing from then to date and time.
There are only two methods for modifying the appointments, all of which will preserve the order when inserting or removing.
Two helper methods within Client, withNewAppointment
and removeAppointment
, are provided to add and remove an appointment from the Client’s list respectively.
These helper methods essentially creates a copy of the existing list, modify it and create a new instance of Client with the new modified list.
Within Appointment itself, it reuses the Address
class that is also used in Client
. Parsing wise, it should behave the same as in any other command.
It is guaranteed that the fields, from
come before or is the same as to
.
The date and time of from
and to
are stored in LocalDateTime
and truncated to the minute
level. I.e. seconds and any units smaller than that are ignored.
Class Diagram
![:information_source: :information_source:](https://github.githubassets.com/images/icons/emoji/unicode/2139.png)
Documentation, logging, testing, configuration, dev-ops
Appendix: Requirements
Product scope
Target user profile:
- has a need to manage a significant number of contacts
- has to keep track of a significant number of client appointments and statuses
- 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
- centralized and organized platform for storing and managing contact information and critical stakeholder details
User stories
Priorities: High (must have) - * * *
, Medium (nice to have) - * *
, Low (unlikely to have) - *
Priority | As a … | I want to … | So that I can… |
---|---|---|---|
* * * |
Social Worker | Add a new client | Store my new client’s information |
* * * |
Social Worker | Delete a client | Remove clients that I no longer work with |
* * * |
Social Worker | View a client’s information | Conveniently access client’s details in a easily readable manner |
* * * |
Social Worker | List out all current client’s contacts | View all of the current contacts I have saved |
* * * |
Social Worker who has physical meetings with my clients | Add appointments that I have with each client | Keep track of the physical meetings I have with my clients |
* * * |
Social Worker who has physical meetings with my clients | Delete appointments that I have with each client | Remove any appointments that I no longer have |
* * * |
Social Worker who has physical meetings with my clients | View an appointment’s information | Be informed of the details that the appointment involves |
* * * |
Social Worker who has physical meetings with my clients | List out my appointments in chronological order | Keep track of my upcoming appointments and which one to prepare for |
* * |
Social Worker with a diverse range of clients | Add a tag to selected clients | Highlight any important information about the client’s current status |
* * |
Social Worker with a diverse range of clients | Delete tags from selected clients | Remove any tags which no longer apply to the client |
* * |
Social Worker with a diverse range of clients | View the tag attached to the selected clients | Readily identify the information I wanted to highlight about the client |
* * |
Social Worker | Edit a client’s information | Update the contact if there are any changes to a client’s information |
* |
Social Worker with many clients | Search for a client by name | Efficiently find a client without having to scroll through the address book |
* |
Social Worker with many clients | Search for clients by their tags | Categorise clients who share a common tag |
* |
Social Worker with clients that have medical records | Add information of their medical history | Keep track of any known conditions, allergies or medications |
Use cases
(For all use cases below, the System is SWEE
and the Actor is the user
, unless specified otherwise)
Use case: UC01 - Add a new client into SWEE
MSS
- User requests to add a client.
- User inputs details of the client.
- SWEE adds the client as a contact.
-
SWEE lists out all current contacts that have been saved (UC05).
Use case ends.
Extensions
-
2a. The user’s given input format for the client’s details is invalid.
- 2a1. SWEE prompts user to input the client’s details in the correct format.
- 2a2. User inputs in the client’s details again.
-
2a3. Repeat steps 1a1-1a2 until the input is in the correct format.
Use case resumes at step 3.
Use case: UC02 - Edit a client
MSS
- User requests to edit a client’s information.
- User inputs the fields to be edited.
- SWEE changes the client’s information.
-
SWEE lists out all current contacts that have been saved (UC05).
Use case ends.
Extensions
-
2a. The user’s given input format is invalid.
- 2a1. SWEE shows an error message.
- 2a2. User inputs in the client’s index and edited details again.
-
2a3. Repeat steps 2a1-2a2 until the input is in the correct format.
Use case resumes at step 3.
Use case: UC03 - Delete a client
MSS
- User requests to delete a specific client in the list.
- User inputs the index of the client to delete.
-
SWEE deletes the client.
Use case ends.
Extensions
- 1a. The list is empty.
-
1a1. SWEE shows an error message.
Use case ends.
-
-
1b. The given client index is invalid.
- 1b1. SWEE shows an error message.
- 1b2. User inputs in the client index again.
-
1b3. Repeat steps 1b1-1b2 until the input is a valid index.
Use case resumes at step 3.
Use case: UC04 - View a client’s information
MSS
- User requests to view a specific client’s information in the list.
- User inputs the index of the client to view.
-
SWEE displays the client’s information to the user.
Use case ends.
Extensions
-
1a. The list is empty.
Use case ends.
-
2a. The given client index is invalid.
- 2a1. SWEE shows an error message.
- 2a2. User inputs in the client index again.
-
2a3. Repeat steps 2a1-2a2 until the input is a valid index.
Use case resumes at step 3.
Use case: UC05 - List all saved clients
MSS
- User requests to see the list of all current clients in SWEE.
-
SWEE presents the user with all the currently saved clients in alphabetical order.
Use case ends.
Use case: UC06 - Find a client by name
MSS
- User requests to search for the client by name.
- User inputs name.
-
SWEE returns all clients that match the specified name.
Use case ends.
Extensions
-
2a. No client matches the specified name.
-
2a1. SWEE shows that there are no clients matching the specified name.
Use case ends.
-
Use case: UC07 - Find a client by Tag
MSS
- User requests to search for the client by tag.
- User inputs tag.
-
SWEE returns all clients that match the specified tag.
Use case ends.
Extensions
-
2a. No client matches the specified tag.
-
2a1. SWEE shows that there are no clients matching the specified tag.
Use case ends.
-
Use case: UC08 - Schedule an appointment
MSS
- User requests to add a new appointment for a specific client.
- User inputs in appointment details.
- SWEE updates the specified client with the newly added appointment.
-
SWEE lists out all current contacts that have been saved (UC05).
Use case ends.
Extensions
-
2a. The user’s given input format is invalid.
- 2a1. SWEE shows an error message.
- 2a2. User inputs in the client’s index and appointment details again.
-
2a3. Repeat steps 2a1-2a2 until the input is in the correct format.
Use case resumes at step 3.
Use case: UC09 - Unschedule an appointment
MSS
- User requests to delete an existing appointment for a specific client.
- User inputs in the client and appointment index.
- SWEE updates the specified client by deleting the specified appointment.
-
SWEE lists out all current contacts that have been saved (UC05).
Use case ends.
Extensions
-
2a. The user’s given input format is invalid.
- 2a1. SWEE shows an error message.
- 2a2. User inputs in the client’s index and appointment index again.
-
2a3. Repeat steps 2a1-2a2 until the input is in the correct format.
Use case resumes at step 3.
Non-Functional Requirements
- Should work on any mainstream OS as long as it has Java
11
or above installed. - Should come in a standalone JAR file that does not require anything beyond Java 11 to run (i.e. installing additional libraries or frameworks).
- A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse.
- Should have an intuitive and user-friendly interface, making it easy for the user to navigate, input, and retrieve client information and appointments efficiently.
- Should come with comprehensive documentation and user support to assist users in using the app effectively and troubleshooting any issues that may arise.
- Should be stable and dependable, minimizing the risk of crashes or data loss, and ensuring that appointments and client information are accurately stored and retrieved.
- Should be able to accommodate the growing number of clients and appointments without a significant decrease in performance or usability.
Glossary
- CLI: Command Line Interface, User interact with SWEE by typing commands
- Mainstream OS: Windows, Linux, Unix, MacOS
- Private contact detail: A contact detail that is not meant to be shared with others
- Client: People who are experiencing various challenges in life and receiving assistance by working with the User
- Appointment: When User meets with the client to assist them
Appendix: Planned Enhancements
Team size: 5
- Allow editing appointment information, i.e. changing the title, address or date/times.
- Use strict datetime resolver, so it will reject invalid dates like “31/02/2024” instead of trying to fit to the nearest valid date.
Appendix: Instructions for manual testing
Given below are instructions to test the app manually.
![:information_source: :information_source:](https://github.githubassets.com/images/icons/emoji/unicode/2139.png)
Launch and shutdown
-
Initial launch
-
Download the jar file and copy into an empty folder
-
Double-click the jar file or open a command line terminal in the home directory and run
java -jar SWEE.jar
. Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum.
-
-
Saving window preferences
-
Resize the window to an optimum size. Move the window to a different location. Close the window.
-
Re-launch the app by double-clicking the jar file.
Expected: The most recent window size and location is retained.
-
Deleting a client
-
Deleting a client while all clients are being shown
-
Prerequisites: List all clients using the
list
command. Multiple clients in the list. -
Test case:
del 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:
del 0
Expected: No client is deleted. Error details shown in the status message. Status bar remains the same. -
Other incorrect delete commands to try:
del
,del x
,...
(where x is larger than the list size)
Expected: Similar to previous.
-
New User tutorial
-
Run through the entire new user tutorial in
User Guide > Quick Start > New User Tutorial
.- The guide will take you through all essential features of SWEE.
- Each section can exist as a self-contained test case of a feature.
- Each section give a brief overview of what to expect.
- Expected: Successful running of all valid commands demonstrated in the User Guide.