toDisplayValue method in the LeaseAmount class was adapted
from this StackOverflow answer to format currency using commas for every
thousand digit.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, TenantListPanel,
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.Note:
MainWindow stores a reference to a Model interface instance. This is to pass the Model instance to
TenantListPanel as well as to retrieve job information for JobListPanel.TenantCard has a stronger association with the Model component because it stores a reference to a Model
interface instance. However, this is used only to retrieve and display jobs linked to a tenant.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 DeleteTenantCommandParser 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 EstateMateParser object which in turn
creates a parser that matches the command (e.g., DeleteTenantCommandParser or AddJobCommandParser) and uses it to
parse the command.Command object (more precisely, an object of one of its subclasses e.g., DeleteTenantCommand)
which is executed by the LogicManager.Model when it is executed (e.g. to delete a tenant or add a job).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:
EstateMateParser class creates an XYZCommandParser (XYZ is a
placeholder for the specific command name e.g., AddTenantCommandParser) which uses the other classes shown above to
parse the user command and create a XYZCommand object (e.g., AddTenantCommand) which the EstateMateParser
returns back as a Command object.XYZCommandParser classes (e.g., AddTenantCommandParser, DeleteTenantCommandParser, ...) 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).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)API : Storage.java
The Storage component,
EstateMateStorage 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.estatemate.commons package.
This section describes some noteworthy details on how certain features are implemented.
This section explains how Jobs are modelled, parsed, stored, and presented.
Core Types
Job fields: id:int, description:Description, isDone:boolean.Description validates non-blank descriptions.UniqueJobList maintains all jobs; provides add/remove/mark/unmark and an unmodifiable observable view.Tenant References
Person keeps List<Integer> jobs (job ids).jobs lists.
Commands
AddJobCommand (job d/<desc>) parses Description, allocates id via Model#nextJobId(), adds job.EditJobCommand (ejob <id> d/<desc>) updates job description for the given id; preserves isDone.DeleteJobCommand (djob <id>) removes job and triggers cascade unlink from tenants.MarkJobCommand / UnmarkJobCommand (mark <id> / unmark <id>) toggles completion.FindJobCommand (fjob KEYWORDS…) filters by case-insensitive keywords with JobContainsKeywordsPredicate.Parsers
AddJobCommandParser, EditJobCommandParser, DeleteJobCommandParser,
MarkJobCommandParser, UnmarkJobCommandParser, FindJobCommandParser
tokenize arguments, validate fields, and construct commands.Post-mutation behaviour
ModelManager resets the job filter to PREDICATE_SHOW_ALL_JOBS. Mark/unmark just update the job’s status and keep the current filter.
JSON Mapping
JsonAdaptedJob <-> Job with fields{ "id": number (>0), "description": string, "isDone": boolean }.JsonSerializableEstateMate rejects duplicates by id. Jobs with the same description are allowed and will all be loaded. Load errors surface as
DataLoadingException; the app then starts with an empty dataset (AB-3 fallback path).Save Flow
LogicManager persists via StorageManager#saveEstateMate(...).
Lists & Cards
JobListPanel renders the ObservableList<Job>.JobCard shows id, description, and a completion badge derived from isDone.ModelManager's filteredJobs changes.
Design Notes
Target user profile:
Value proposition: EstateMate helps property managers stay organized and in control of their operations. It provides a centralized and efficient way to manage tenants, rental records, and maintenance jobs all from a simple, keyboard-friendly interface. With EstateMate, users can:
Priorities: High (must have) - * * *, Medium (nice to have) - * *, Low (unlikely to have) - *
| Priority | As a ... | I want to ... | So that I can... |
|---|---|---|---|
| '***' | Property Manager | Add new tenants and their information | manage tenants for contacting and tracking their information |
| '***' | Property Manager | Delete tenants and their information | remove people who are no longer tenants |
| '***' | Property Manager | List all tenants | see what tenants I am keeping track of |
| '***' | Property Manager | Add new maintenance jobs | keep track of tasks that need to be carried out |
| '***' | Property Manager | Delete maintenance jobs | remove tasks that are no longer needed |
| '***' | Property Manager | List all jobs | see what jobs I am keeping track of |
| '***' | Property Manager | Mark jobs as completed | monitor the progress and ensure maintenance tasks are resolved |
| '***' | Property Manager | Unmark jobs as not completed | keep track of tasks that still need to be followed up on |
| '***' | Property Manager | View available commands | refer to usage instructions when needed to avoid mistakes |
| '***' | Property Manager | Exit the application | safely close the system and ensure all changes are saved |
| --- | --- | --- | --- |
| '**' | Property Manager | Search a tenant | quickly locate a tenant without scrolling through the entire tenant list |
| '**' | Property Manager | Search a maintenance job | quickly locate a maintenance job without scrolling through the entire job list |
| '**' | Property Manager | Edit tenant details | update their information when changes occur |
| '**' | Property Manager | Edit maintenance jobs | update maintenance job description when changes occur |
| '**' | Property Manager | Link jobs to specific tenants | know which tenant is associated with each maintenance request |
| '**' | Property Manager | Clear all tenants from the system | reset the tenant database or start fresh |
(For all use cases below, the System is the 'TenantManager' and the Actor is the 'user', unless specified otherwise)
MSS
Use case ends.
Extensions
MSS
Use case ends.
Extensions
MSS
Use case ends.
Extensions
MSS
Use case ends.
Extensions
MSS
Use case ends.
Extensions
MSS
Use case ends.
Extensions
MSS
Use case ends.
Extensions
MSS
Use case ends.
Extensions
MSS
Use case ends.
Extensions
MSS
Use case ends.
Extensions
MSS
Extensions
MSS
Use case ends.
Extensions
MSS
Use case ends.
Extensions
MSS
Use case ends.
Extensions
MSS
Use case ends.
MSS
Use case ends.
17 or above installed.Note: within 1 second for typical operations like listing, searching, adding, etc.
Note: Standard screen resolutions - 1920x1080 and higher, screen scales 100% and 125%.
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 estatemate.jar and copy into an empty folder
cd to the folder with the jar file and running the command java -jar estatemate.jar to launch the app
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.
Proper shutdown
Open the help window by entering help into the command box or using the button at the top left,
Close the main application window.
Expected: Both the help window and the main window should close.
Deleting a tenant while all tenants are being shown
Prerequisites: List all tenants using the list command. Multiple tenants in the list.
Test case: delete 1
Expected: First tenant is deleted from the list. Details of the deleted tenant shown in the status message.
Test case: delete 0
Expected: No person is deleted. Error details shown in the status message. Tenant list 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: No specific prerequisites.
Test case: job d/ Pest infestation
Expected: Job is added to the job list. Details of the job are shown in the status message. Switch to a job
list
screen where the added job can be seen
Prerequisites: At least one tenant with name containing the word Alex, and no tenant with name containing the
word Michael.
Test case: find alex
Expected: Only tenants with names containing the word Alex (non-case sensitive) are displayed.
Test case: find michael
Expected: An empty tenant list is displayed.
Prerequisites: At least one job with name containing the word pest, and no job with name containing the word
pipe.
Test case: fjob pest
Expected: Only jobs with descriptions containing the word pest (non-case sensitive) are displayed.
Test case: fjob pipe
Expected: An empty job list is displayed.
Linking an existing tenant and job to each other
Prerequisites: List all tenants using the list command. At least one tenant in the list. List all jobs using
the ljob command. At least one job (job id: 1) in the list.
Test case: link 1 j/1 Expected: Job is linked to the tenant. Status message shows the job number and name of tenant linked. Job is
displayed in the tenant's maintenance information section.
Linking a nonexistent tenant and a job to each other.
Prerequisites: List all tenants using the list command. At most 4 tenants in the list. List all jobs using the
ljob command. At least one job (job id: 1) in the list.
Test case: link 5 j/1 Expected: Job is not linked to the tenant. Error details shown in status message indicating invalid tenant
index.
Linking a tenant and a nonexisting job to each other.
Prerequisites: List all tenants using the list command. at least 1 tenant in the list. List all jobs using the
ljob command. At most 3 (max job id: 3) in the list.
Test case: link 1 j/4 Expected: Job is not linked to the tenant. Error details showing in status message indicating invalid job
index.
Team Size: 5
Anne-marie, O'Brien, José .help command to show a quick, brief reference to available commands without the need to access external links.find command to no longer require full words to match.
find Ale will display the tenant Alex Yeoh.fjob command to no longer require full words to match.
fjob leak will display the job with description Pipe leakage.link command)tenant command, specifically involving the a/ tag.
a/ a/ is not accepted, while one of a/a/ is accepted.tenant command
999(())9999 or 99-----9 should be rejected.clear command.
The effort required and difficulty level was high, as we generally did not reuse any code (except for 1 method adapted
from StackOverflow).
While AB3 deals with only one entity type (Persons), EstateMate needs to handle two (Tenants and Jobs). The tenant class
is also much more detailed with more fields compared to the Person class from AB3.
add command to allow for adding tenants