This project is based on the AddressBook-Level3 project created by the SE-EDU initiative.
AI was used throughout the development of this project:
NUS Event Mailer Pro (NUS EMP) is a desktop application designed for NUS event organizers to manage contacts and events efficiently. The application features a graphical user interface (GUI) with an integrated command terminal for keyboard-driven input, built using Java 17 and JavaFX.
Core Capabilities:
Refer to the guide Setting up and getting started for detailed installation and setup instructions.
The application follows a clean architecture pattern with five main components:
The Main class handles application lifecycle:
API: Ui.java
The UI component consists of:
MainWindow: Primary application windowCommandBox: User input interfaceResultDisplay: Command feedback displayCommandBox and ResultDisplay are automatically hidden, unless the user presses the terminal button located on the sidebar or presses the keyboard shortcut (Ctrl + T) which will bring them upContactListPanel/EventListPanel: Data presentation panels that fetch their data from the model componentStatusBarFooter: Status information displayAll UI components inherit from UiPart abstract class and use FXML for layout definitions.
API: Logic.java
The Logic component manages:
Key classes:
LogicManager: Main logic coordinatorAppParser: Routes commands to appropriate parsersCommand hierarchy: Individual command implementations
API: Model.java
The Model component maintains application state:
UniqueContactList, UniqueEventListParticipantMapFor more details, refer to Section 3.
API: Storage.java
The Storage component handles persistence:
The Commons component provides shared utilities:
The components interact through well-defined interfaces:
The Model component,
Contacts and Events as a filtered list, exposed to outsiders as an unmodifiable ObservableList, which can be observed for UI updatesUserPref object that represents the user’s preferences, e.g. dark theme, window sizesThe Model component manages three core entity types:
Contact, Event and ParticipantStatus.
Both Contact and Event classes share several design characteristics:
Name, Email, Phone, Address, Tag, Date, EventStatusContact, name for EventinvalidationToggle to provide invalidation mechanism for observable list updatesFor the field classes:
Contact or Event objects, even optional fields.Phone and Address) are represented using empty values instead.Participant links between contacts and events are handled through a ParticipantMap:
ContactKey and EventKey, which are simple classes that only store the primary key fields (i.e. case-insensitive email for ContactKey and name for EventKey).
The command system follows a consistent pattern:
AppParser routes commands to specific parsersCommand objectscontact add: Add new contact with name, email, phone, address, and tagscontact delete: Remove contact by indexcontact edit: Modify contact details (name, email, phone, address, tags)contact find: Search contacts by keywords or specific fieldscontact list: Display all contacts with indicescontact show: View contact details and associated eventsevent add: Create new event with name, date, address, and tagsevent delete: Remove event by indexevent edit: Modify event details (name, date, address, status, tags)event list: Display all events with indices and statusevent show: View event details and associated contactsevent link: Associate contacts with eventsevent unlink: Remove contact associations from eventsevent export: Export all contacts from an event to clipboardevent rsvp: Update contact's RSVP status for an eventhelp: Display command help (opens user guide in browser)exit: Terminate application
The parser system uses a direct interface implementation approach:
Parser<T>: Generic interface for all command parsersAppParser: Main command router using regex patterns and CommandType enumParser<SpecificCommand> directlyCommandType enum: Routes commands to appropriate parsing logic (CONTACT, EVENT, HELP, EXIT, UNKNOWN)Key Components:
BASIC_COMMAND_FORMAT: Initial command type separationCONTACT_COMMAND_FORMAT: Contact command parsing patternEVENT_COMMAND_FORMAT: Event command parsing patternArgumentTokenizer: Handles prefix-based argument parsing (--name, -n, etc.)
Error handling follows these principles:
ParseExceptionCommandExceptionMessages class constants for consistent error feedbackLogsCenter for debuggingException Types:
ParseException: Input format and validation errorsCommandException: Command execution errorsIllegalValueException: Base class for parsing-related errors
Steps:
contact add commandRefer to Add Contact Sequence Diagram above.
Steps:
event add command with event details
Steps:
event link command with contact and event identifiers
Steps:
contact find or event find command with search criteria
Steps:
Steps:
Steps:
contact edit or event edit command with index and new valuesRefer to Edit/Update Sequence Diagram above.
Steps:
event rsvp --event EVENT_INDEX --contact CONTACT_INDEX --status STATUS command
Steps:
The application uses JavaFX's observable pattern for real-time UI updates:
// In ModelManager
private final ObservableList<Contact> filteredContacts = FXCollections.observableArrayList();
// UI components bind to this list
contactListPanel.setItems(model.getFilteredContactList());
Key Features:
Command base classStorage interfaceStep-by-step process:
Create Command Class
public class NewCommand extends Command {
@Override
public CommandResult execute(Model model) throws CommandException {
// Implementation
}
}
Create Parser Class
public class NewCommandParser implements Parser<NewCommand> {
public NewCommand parse(String args) throws ParseException {
// Parsing logic
}
}
Update AppParser
Add Tests
Update Documentation
For Contact fields:
Contact classFor Event fields:
Event class@Test
void execute_contactAddCommand_success() {
// Arrange
Model expectedModel = new ModelManager();
Contact validContact = new Contact(...);
expectedModel.addContact(validContact);
// Act
CommandResult result = new ContactAddCommand(validContact).execute(model);
// Assert
assertEquals(expectedModel, model);
assertEquals(String.format(MESSAGE_ADD_CONTACT_SUCCESS, validContact),
result.getFeedbackToUser());
}
Event organizers at NUS who manage large contact databases, send bulk emails regularly, prefer keyboard-driven CLI interfaces for efficiency, and need automated mailing recommendations for targeted communication with event participants and stakeholders.
Streamlines event communication workflow by integrating contact management with intelligent mailing recommendations and bulk email capabilities in a fast, keyboard-centric CLI interface, enabling event organizers to efficiently manage contacts and execute targeted email campaigns without switching between multiple tools.
Priorities: High (must have) - * * *, Medium (nice to have) - * *, Low (unlikely to have) - *
| Priority | As a … | I want to … | So that I can… |
|---|---|---|---|
* * * | event organizer | add a contact with standard fields (Name, Phone, Email, Address) | build my core contact database |
* * * | event organizer | associate a specific tag (e.g., 'Speaker', 'Attendee', 'VIP', 'Sponsor') | categorize and filter my contacts effectively |
* * * | event organizer | delete a contact from the address book | remove outdated or irrelevant entries |
* * * | event organizer | create a new event with name, date, time, and venue | start organizing my contacts around it |
* * * | event organizer | view a list of all my events, showing key details and their status | get an overview of upcoming, past, or cancelled events |
* * * | event organizer | associate contacts from my address book with a specific event | build an attendee list for the event |
* * * | event organizer | get a list of contacts defined by tags, or event association | target communications and manage groups efficiently |
* * | event organizer | set RSVP status for a contact for a specific event | track attendance commitments |
* * | event organizer | edit any field of an existing contact, including tag and RSVP status | keep contact information up-to-date |
* * | event organizer | view all details of a contact, including tags, and associated events | see a clean, readable summary |
* * | event organizer | view all details of an event, including tags, and associated contacts | see a clean, readable summary |
* * | event organizer | find contacts by searching any field (Name, Tag, Email) | quickly locate specific individuals |
* * | event organizer | add multiple tags to a contact | perform complex filtering |
* * | event organizer | filter contact list by tags | create highly specific lists |
* * | event organizer | list all contacts, with option to sort by Name or tags | get a general overview |
* * | event organizer | remove a contact from an event without deleting from address book | manage event participation flexibly |
* * | event organizer | archive a past event | keep main view uncluttered but retain data |
* * | event organizer | set event status to 'Cancelled' | exclude it from mailings |
* * | event organizer | import contacts from a CSV file | quickly populate my address book |
* * | event organizer | export contacts or filtered subset to CSV | backup or use in another application |
* * | user | create a complete backup of application data | safeguard my information |
* * | user | restore application data from a backup file | recover from data loss |
* * | user | specify storage location for data and backups | organize my files as needed |
* | user | sort contacts by name | locate a person easily |
* | user | manage additional entity types related to contacts (tasks, loans, grades) | extend functionality as needed |
For all use cases below, the System is NUS Event Mailer Pro (NUS EMP) and the Actor is the user, unless specified otherwise.
| Use Case | Preconditions | Main Success Scenario | Extensions | Postconditions |
|---|---|---|---|---|
| UC01: Add a contact | System is running | 1. User inputs contact details with name and email 2. System validates required fields 3. Contact added to the system 4. Success message displayed Use case ends. | 1a. Required fields missing or invalid - System shows error message with missing fields - User can retry with correct input - Use case ends. 2a. Contact already exists - System shows error about duplicate contact - User can add with different email - Use case ends. | Contact added to system |
| UC02: Delete a contact | Contact exists in displayed list | 1. User selects contact by index 2. User confirms deletion 3. Contact removed from system and all associated events 4. Confirmation message displayed Use case ends. | 1a. Invalid contact index - System shows error about invalid index - User can try with correct index - Use case ends. | Contact removed from system and all events |
| UC03: Edit a contact | Contact exists in displayed list | 1. User selects contact by index 2. User edits the contact 3. System updates the contact fields. 4. Confirmation message displayed Use case ends. | 1a. Invalid contact index - System shows error about invalid index - User can try with correct index - Use case ends. 1b. Invalid command format for editing fields - System shows error about invalid command format, and a usage message - User can try with correct correct format - Use case ends. | Contact is updated with new fields |
| UC04: Create an event | System is running | 1. User inputs event details with name and date 2. System validates date format 3. Event created with PENDING status 4. Success message displayed Use case ends. | 1a. Required fields missing - System shows error about missing name or date - User can provide missing information - Use case ends. 2a. Invalid date format - System shows specific date format error (DD-MM-YYYY HH:mm) - User can correct date format - Use case ends. | Event added to system |
| UC05: Delete an event | System is running | Similar to UC02, but contact is replaced with event, and vice versa | Similar to UC02, but contact is replaced with event, and vice versa | Event removed from system and all contacts |
| UC06: Edit an event | System is running | Similar to UC03, but contact is replaced with event, and vice versa | Similar to UC03, but contact is replaced with event, and vice versa | Event is updated with new fields |
| UC07: List contacts | System is running | 1. User requests to list contacts 2. System displays all contacts 3. Each contact shown with index and details Use case ends. | 1a. No contacts exist - System shows "No contacts found" - User can add contacts using add command - Use case ends. | Contact list displayed |
| UC08: List events | System is running | 1. User requests to list events 2. System displays all events 3. Each event shown with index, date, and status Use case ends. | 1a. No events exist - System shows "No events found" - User can create events using add command - Use case ends. | Event list displayed |
| UC09: Filter contacts by criteria | Contacts exist in system | 1. User inputs search keywords or field criteria 2. System filters contacts matching criteria 3. Filtered list displayed with new indices Use case ends. | 1a. No contacts match criteria - System shows "No contacts found" - User can try different search terms - Use case ends. 2a. Invalid search syntax - System shows error about search format - User can correct search syntax - Use case ends. | Filtered contact list displayed |
| UC10: Filter events by criteria | Events exist in system | Similar to UC09, but replace contacts with events | Similar to UC09, but replace contacts with events | Filtered event list displayed |
| UC11: Associate contacts with event | Event and contacts exist in system | 1. User selects event and contact by indices 2. System links contact to event 3. Contact marked as UNKNOWN for event 4. Confirmation message displayed Use case ends. | 1a. Invalid event or contact index - System shows error about non-existent event/contact - User can select valid indices - Use case ends. 2a. Contact already linked to event - System shows that association already exists - User can select different contact - Use case ends. | Contact-event association created |
| UC12: Remove contacts from event | System is running | 1. User selects event and contact by indices 2. System unlinks contact from event 3. Confirmation message displayed Use case ends. | 1a. Invalid event or contact index - System shows error about non-existent event/contact - User can select valid indices - Use case ends. 1b. Contact not linked to event - System shows that contact is already unlinked to event -Use case ends. | Contact-event association removed |
| UC13: Update RSVP status for contact | Contact already linked to event | 1. User selects event, contact, and RSVP status 2. System updates contact's status for event 3. Confirmation message displayed with new status Use case ends. | 1a. Invalid event or contact index - System shows error about non-existent selection - User can select valid indices - Use case ends. 2a. Invalid RSVP status - System shows valid status options (available/unavailable/unknown) - User can select valid status - Use case ends. 3a. Contact not linked to event - System shows contact not in event - User can link contact first - Use case ends. | RSVP status updated for contact-event relationship |
| NFR ID | Category | Description | Metric/Target | Priority |
|---|---|---|---|---|
| NFR-T01 | Technical | Runtime environment compatibility | Java 17, Windows/macOS/Linux | High |
| NFR-T02 | Technical | Deployment method | Single JAR, no installer | High |
| NFR-T03 | Technical | Network dependency | Offline functionality | High |
| NFR-P01 | Performance | Command response time | < 500ms for typical commands | High |
| NFR-P02 | Performance | Application startup time | < 3 seconds on standard hardware | High |
| NFR-P03 | Performance | Memory usage with 1000 contacts | < 1000MB | Medium |
| NFR-P04 | Performance | Data storage size for 1000 contacts | < 5MB | Medium |
| NFR-P05 | Performance | Supported data capacity | 1000 contacts, 1000 events | High |
| NFR-UX01 | User Experience | Interface design | Keyboard-driven, clean and simple | High |
| NFR-UX02 | User Experience | Command syntax consistency | Consistent format across all operations | High |
| NFR-UX03 | User Experience | Error message quality | Clear, actionable guidance | High |
| NFR-UX04 | User Experience | Command response usefulness | Clear success/failure indication | High |
| NFR-F01 | Features | Data file format | Human-editable (JSON) | Medium |
| NFR-F02 | Features | Data corruption handling | Recovery warnings and manual recovery options | Medium |
| NFR-D01 | Development | Code coverage requirement | ≥ 70% code coverage | Medium |
| NFR-D02 | Development | Testing framework | JUnit 5, comprehensive test suite | Medium |
| Requirement Category | Must Have (High) | Should Have (Medium) | Could Have (Low) | Won't Have |
|---|---|---|---|---|
| Contact Management | Add, edit, delete contacts | Advanced search/filtering | Custom fields | N/A |
| Event Management | Create, view events | Event archiving | Recurring events | N/A |
| Contact-Event Linking | Link/unlink contacts | RSVP status tracking | Bulk operations | N/A |
| Data Management | Data persistence | Import/Export functionality | Custom storage locations | N/A |
| Performance | < 500ms response time | < 3GB memory usage | Batch optimization | N/A |
| User Experience | Keyboard interface | Consistent syntax | Advanced UI features | N/A |
| Technical | Cross-platform compatibility | Human-readable files | Cloud integration | N/A |
Note: These instructions provide a starting point for testing; testers should perform additional exploratory testing.
Initial Launch
Window Preference Persistence
Add Contact
contact add --name John Doe --email john@example.com --phone 12345678 --address "123 Main St"contact add -n John Doe -e john@example.com -p 12345678 -a "123 Main St"Delete Contact
contact delete 1 (where 1 is valid index)Find Contact
contact find Johncontact find --email gmail --tag friendEdit Contact
contact edit 1 --phone 98765432 --email newemail@gmail.comInvalid Commands
contact add (missing required fields)Create Event
event add --name Team Meeting --date 15-01-2024 14:00 --address Meeting Room 1Link Contact to Event
event link --event 1 --contact 1Update RSVP Status
event rsvp --event 1 --contact 1 --status availableExport Event Contacts
event export 1Unlink Contact from Event
event unlink --event 1 --contact 1List Events
event listData Recovery
Corruption Handling
Invalid Index
contact delete 999Duplicate Data
Invalid Formats
event add --name Test --date invalid-dateMissing Required Fields
contact add --name "John" (missing email)Invalid RSVP Status
event rsvp --event 1 --contact 1 --status invalidInvalid Event/Contact Combination
event link --event 999 --contact 1Difficulty Level: Medium
Challenges Faced:
Achievements of the project:
Team size: 5
preferences.json, and the second screen will have a much larger x-coordinate, causing the window to be off-screen. We plan to fix this by detecting the full window screen size and placing the app window within the screen boundaries.preferences.json, not the maximized dimensions.contact add only allows adding single contacts. We plan to enhance contact adding by allowing users to define a file path inside the command, and document the csv file format so users can easily add contacts in batch.--replace, so users still can remove tags using --replace.event add by allowing users to define the status. Currently, event add adds events with a default status of PENDING. We plan to enhance event adding by allowing users to define the initial status as well, so users can add ONGOING or DONE events in one shot without executing event edit afterwards.event export by allowing exports for multiple participant status. Currently, event export only allows exports for participants with one specific status from AVAILABLE, UNAVAILABLE and UNKNOWN. We plan to enhance event exporting by allowing users to define multiple statuses, or a special --status all option to export all participants regardless of status