This guide documents RACE (Residential Assistant’s Contact Entries). This repository extends the AB3 architecture with additional domain concepts such as room-based resident records, tag registries, comments, and list sorting behaviours that are not present in stock AB3.
When reading older AB3-oriented diagrams or descriptions, interpret them as the high-level structure of the app; refer to the current Model, Storage, and Logic sections for RACE-specific details.
Contributors should prefer updating diagrams and explanations alongside feature changes to avoid documentation drift.
comment feature implementation is adapted from the se-edu tutorial Adding optional fields to AB3.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.PersonCard only when the comment is non-empty, so empty comments do not take up space in the list view.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,3") as an example.
Note: The lifeline for 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:
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).Model) to achieve.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
The Model component,
Person objects (which are contained in a UniquePersonList object). Each Person stores immutable values for Name, Phone, Email, Room, Comment, and tags.CustomTagRegistry inside AddressBook to track known custom tags separately from each Person, while still treating built-in tags as always known.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.UserPrefs object that represents the user’s preferences. This is exposed to the outside as a ReadOnlyUserPrefs object.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,
comment fields as empty comments to preserve compatibility with older saved data.customTags, while also rebuilding missing custom-tag entries from loaded persons so the in-memory model stays usable even if the file is stale.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 sorting feature is integrated into the list command. It allows users to
view all residents ordered by a chosen field (name or room).
The implementation relies on JavaFX's SortedList, which is initialized in ModelManager to wrap around the filteredPersons list. This architectural choice ensures that whenever the filter changes (e.g., via find), the sort order can still be applied to the filtered subset.
ListCommandParser identifies the -sort option and maps supported field
prefixes (n/, r/) to a corresponding Comparator<Person>.ListCommand is created with the field name and its comparator.ListCommand calls Model#updateFilteredPersonList(Predicate, Comparator).ModelManager sets the filter on its FilteredList and the comparator on its SortedList.Model#updateFilteredPersonList(Predicate, Comparator).
Commands that use the single-argument overload reset the sort comparator to null; this includes plain list,
find, and commands such as add and comment that refresh the shown list through updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS).The following sequence diagram shows the main interactions for list -sort n/.
Aspect: How sorting interacts with filtering
find and plain list, but
preserved by add after a prior list -sort <prefix>/.
The tag model separates three concerns: tag identity, built-in tag definitions, and custom-tag existence.
Tag remains an immutable value object that stores the displayed tag name and uses exact string equality. This makes tag behavior predictable: study-group and Study-Group are different tags.DefaultTagEnum defines the built-in tags (vegetarian, vegan, halal, allergies). These are treated as known immediately, but they follow the same case-sensitive matching rule as custom tags.CustomTagRegistry stores the currently known custom tags in an AddressBook, and its lookup logic also treats built-in tags as known immediately. This keeps "does this tag exist already?" in the model layer instead of inside Tag.add and edit only register unknown custom tags when the user explicitly supplies -newtag. Without that flag, both commands reject unknown tags consistently.ParserUtil.parseBooleanFlag(...) and TagCommandUtil.validateKnownTags(...), keep -newtag parsing and tag validation aligned between add and edit.AddressBook also registers custom tags found on persons. This keeps the in-memory registry consistent even if the stored customTags list is incomplete.The following class diagram summarizes the main classes involved in tag management.
Aspect: Where tag existence should be tracked
CustomTagRegistry, while Tag only represents a value.
Aspect: How tag casing should work
Aspect: How to handle incomplete customTags data from storage
Target user profile:
Felix is a Year 3 Soc student and RA at Acacia College. Approachable and proactive, he enjoys connecting with residents and planning events for them regularly. He loves his terminal. However, he has poor memory and gets overwhelmed during orientation, because of the messy process of onboarding residents and collecting data.
Value proposition: RACE provides a single, dedicated system to manage resident details safely, keeping sensitive information private. During orientation, RAs must onboard 40+ residents quickly, and RACE helps to manage resident information efficiently, instead of current scattered and slow workflows.
Priorities: High (must have) - * * *, Medium (nice to have) - * *, Low (unlikely to have) - *
| Priority | As a … | I want to … | So that I can… |
|---|---|---|---|
* * * | beginner, new RA | add a resident with required details | register residents quickly during onboarding |
* * * | expert, RA handling a busy intake | add a resident even if some optional fields are missing | incomplete data does not block onboarding |
* * * | beginner, RA | view a list of all registered residents | see who is currently in the system |
* * * | beginner, forgetful RA | search using partial information | not have to attain all information to pinpoint a resident quickly |
* * * | beginner, RA | update resident details | the information stays accurate over time |
* * * | expert, RA | add, edit or clear a private comment for a resident | keep follow-up notes without changing the resident's main details |
* * * | beginner, RA | remove resident records that are no longer needed | my records do not become cluttered |
* * * | beginner, new RA | understand what the system is meant to help me do (e.g. user manual) | know how it supports my role |
* * | intermediate, RA | tag residents with labels (e.g., allergy, athlete, international) | group them easily |
* * | intermediate, RA | create a new custom tag while adding or editing a resident | record categories specific to my hall or event |
* * | beginner, RA | sort residents alphabetically or by room number | browse faster and make block checks easier |
* * | intermediate, RA | replace or clear a resident's tags | remove outdated group labels from the record |
* * | expert, RA managing semester turnover | delete multiple residents at once | clean up the roster more efficiently |
* * | expert, RA preparing for a new intake cycle | clear all resident records | reset the app quickly for a new semester |
* * | beginner, busy RA | have my data saved automatically after successful commands | not lose updates if I close the app |
* | expert | append notes instead of overwriting them | history is preserved |
* | expert | check if a specific number of residents have been recorded in certain floors | no one is accidentally left out |
* | expert | limit list output to the first N entries | terminal output stays readable |
* | intermediate | select specifically residents who have signed up for an activity and review their details | I feel prepared and can cater to their needs |
* | intermediate | notice missing or incomplete resident information | I know who to follow up with |
* | intermediate | see at a glance residents with special needs | I can be inclusive |
* | intermediate | list all residents under a tag | I can plan targeted outreach |
* | intermediate | filter residents based on specific criteria | I can find relevant groups easily |
* | intermediate | hide certain columns | I can see the fields I want to see at one point more easily |
* | beginner | focus on entering information without visual distractions | I make fewer mistakes |
* | beginner | mark certain fields as sensitive | they are hidden in summary views |
* | beginner | hide certain private information from immediate view | sensitive data is protected |
* | beginner | use command shortcuts | frequent actions are faster |
* | beginner | reuse my last command with small edits | repeated onboarding is efficient |
(For all use cases below, the System is the AddressBook and the Actor is the user, unless specified otherwise)
Use case: Delete a person
MSS
User requests to list persons
AddressBook shows a list of persons
User requests to delete a specific person in the list
AddressBook deletes the person
Use case ends.
Extensions
2a. The list is empty.
Use case ends.
3a. The given index is invalid.
3a1. AddressBook shows an error message.
Use case resumes at step 2.
Use case: Add or clear a resident comment
MSS
User requests to list residents or find a resident.
System shows a list of residents with their indices.
User requests to add a comment to a specific resident in the list.
System updates the resident's comment.
System shows the updated resident list.
Use case ends.
Extensions
2a. The list is empty.
Use case ends.
3a. The given index is invalid.
3a1. System shows an error message.
Use case resumes at step 2.
3b. The user provides c/ with no text.
3b1. System clears the resident's existing comment.
Use case ends.
Use case: Batch Onboarding
MSS
User requests to list all residents to check current occupancy.
System shows a list of residents.
User requests to add a new resident with mandatory and optional fields.
System validates the data and adds the resident to the database.
User requests to add another new resident with mandatory and optional fields.
System adds the second resident.
User verifies the new list.
Use case ends.
Extensions
Use case: Search for Resident During Emergency
MSS
User requests to find residents using one or more keywords.
System shows a filtered list of matching residents with their indices.
User identifies the correct resident and room number from the results.
User requests to update the resident's phone number to the latest provided.
System updates the record and confirms the change.
Use case ends.
Extensions
0 persons listed!.Use case: Mid-Semester Room Swap
MSS
User requests to find the two residents involved in the swap.
System shows the current records for both residents.
User requests to update Resident A to a temporary placeholder room.
User requests to update Resident B to Resident A’s original room.
User requests to update Resident A to Resident B’s original room.
System confirms all updates are successful.
Use case ends.
Extensions
Use case: Bulk Removal of Graduating Residents
MSS
User requests to list all residents.
System shows the full list.
User requests to delete multiple residents by providing comma-separated indices.
System validates all provided indices.
System removes all specified records in one command.
Use case ends.
Extensions
Use case: Managing Medical/Special Needs
MSS
User requests to find residents with a specific tag (e.g., "asthma").
System shows a list of residents matching the tag.
User requests to view the full details of a specific resident.
User requests to add a new specific dietary tag to that resident’s record.
System updates the resident record with the new tag.
Use case ends.
Extensions
17 or above installed.delete 1) to identify a resident from the currently displayed list.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 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.
Prerequisites: Multiple residents with different names, rooms, and phone numbers.
Test case: list -sort r/
Expected: List is updated to show all residents sorted by room number (format: #BLOCK-ROOM[-LETTER]). Status message confirms sorting.
Test case: list -sort x/
Expected: No sorting occurs. Error message "Invalid sort field! Supported field prefixes: n/, r/" is displayed.
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.
Prerequisites: List all persons using the list command. Multiple persons in the list.
Test case: comment 1 c/Requires wheelchair-accessible venue
Expected: The first person's comment is updated. The success message is shown in the result display. The person card shows the new comment.
Test case: comment 1 c/
Expected: The first person's comment is removed. The success message is shown in the result display. The comment label is no longer shown on the person card.
Other incorrect comment commands to try: comment, comment 1, comment 0 c/test, comment x c/test, comment 1 c/first c/second
Expected: No person's comment is changed. Error details are shown in the result display.
Autosaving after a successful command
Prerequisites: App has been launched at least once in a test folder.
Test case: add n/Test Resident r/#09-101 and then close the app.
Re-launch the app.
Expected: The newly added resident is still present in the list. The file data/addressbook.json exists in the app folder and contains the new resident record.
Test case: add n/Another Resident r/#09-102, then enter an invalid command such as delete 999999.
Expected: The invalid command shows an error message and does not modify saved data. After closing and re-launching the app, Another Resident is still present and no unintended changes have been made.
Dealing with a missing data file
Prerequisites: Close the app. In the app folder, ensure data/addressbook.json does not exist by deleting it or moving it elsewhere.
Test case: Launch the app in that folder.
Expected: The app starts successfully and shows the sample residents in the GUI.
Test case: After launch, execute a successful command such as list or add n/Test Resident r/#10-101.
Expected: A new data/addressbook.json file is created in the app folder.
Dealing with a corrupted data file
Prerequisites: Close the app. Open data/addressbook.json in a text editor and replace its contents with invalid JSON such as { invalid json.
Test case: Launch the app.
Expected: The app starts successfully with an empty resident list instead of crashing, because invalid stored data cannot be loaded.
Test case: Execute a successful command such as add n/Recovered Resident r/#11-111.
Expected: The app saves normally again, and data/addressbook.json is replaced with a valid file containing the new resident data.
Team size: 5
help is run while it is minimised: currently, running help again can appear to do nothing if the Help window is minimised. We plan to change this so the same Help window is restored, brought to the front, and focused instead of remaining hidden.delete 1,3,999 fails, the message does not clearly tell the user which index caused the command to be rejected. We plan to report the specific invalid index and clarify that no residents were deleted.add and edit more accurate: currently, unsupported prefixes such as c/ in add n/Amy r/#01-01 c/test may be absorbed into another field and trigger a misleading room or tag format error. We plan to detect unsupported prefixes explicitly and report them directly, e.g. Unsupported prefix in add command: c/.find command: currently, find uses OR logic across all keywords, so users cannot require multiple conditions in one search. We plan to extend find so users can express AND conditions explicitly, while keeping OR searches available, e.g. find John AND Doe or find John AND 14-203.# by automatically prepending it during parsing, and treating alphabetic room suffixes case-insensitively so inputs such as #14-203-d and 14-203-D are accepted consistently.p/ inside the actual name: currently, names like Chloe p/o Tan can be misread because p/ is treated as the phone prefix. We plan to refine parsing so such names can be entered normally without the command being split at p/.Alex Tan (Block 14) to the stored display name so each resident record remains uniquely identifiable.clear removes all residents: currently, clear executes immediately and permanently. We plan to show a confirmation dialog that states all resident records will be deleted and only proceed when the user explicitly confirms.