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
, DisplayableListPanel
, 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 Displayable
objects 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("sdelete 1")
API call as an example.
Note: The lifeline for DeleteSellerCommandParser
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., DeleteSellerCommandParser
) and uses it to parse the command.Command
object (more precisely, an object of one of its subclasses e.g., DeleteSellerCommand
) which is executed by the LogicManager
.Model
when it is executed (e.g. to delete a seller).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:
When called upon to parse a user command, the AddressBookParser
class creates an XYZCommandParser
(XYZ
is a placeholder for the specific command name e.g., AddSellerCommandParser
) which uses the other classes shown above
to parse the user command and create a XYZCommand
object (e.g., AddSellerCommand
) which the AddressBookParser
returns back as a Command
object.
AddressBookParser
also creates a CommandWarnings object, which is used to handle user errors that do not require the system
to fail execution of the command (e.g. if user is trying to add seller with a non-alphanumeric name.) If the command inherits
from the Parser
interface, then it will use this object to store warnings to output into the Command and eventually into
the resulting CommandResult. It is also used by LogicManager to log warnings. Classes which do not implement Parser
do
not use warnings (because they either execute successfully or fail; there is no potential for user misinput.)
All 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,
Displayable
objects (which are contained in the appropriate UniqueDisplayableList<T extends Displayable>
object, in this case only buyers and sellers).Displayable
objects (e.g., results of a search query) as a separate filtered list which
is exposed to outsiders as an unmodifiable ObservableList<Buyer>
/ObservableList<Seller>
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.addressbook.commons
package.
This section describes some noteworthy details on how certain features are implemented.
Given below is an example usage scenario and how the edit mechanism behaves at each step.
Step 1. The user types in the bedit
or sedit
keyword, followed by the index of the buyer or seller that they want
to edit. Following that, they type one or more of /PREFIX
, where PREFIX
is a field that they want to edit.
Note: If the index or field is invalid, no command will be executed.
The following sequence diagram shows how the edit operation works:
Note: The lifeline for EditBuyerCommand
should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
Aspect: How edit executes:
Alternative 1 (current choice): Creates an EditBuyerDescriptor or an EditSellerDescriptor to abstract away
all the Optional functionality
edit
commands easier to read and debug.Alternative 2: edit
command parsers pass Optionals directly to the Buyer Command
The priority feature allows the user to assign priority levels to their clients in the address book, using
the SetBuyerPriority
and SetSellerPriority
commands. These commands act as a shortcut for conveniently
assigning priority levels to clients, without having to use the edit command (bedit
or sedit
).
Also, when the priority feature is used with the sorting command (bsort
or ssort
), this allows users to
view high priority clients at the top of the list first.
To implement this feature, the Priority
field is firstly added to Person
, and its corresponding UI Label is
rendered by modifying the PersonCard.java
controller and the respective BuyerCard and SellerCard FXML files.
The priority field is optional when instantiating buyers and sellers, and is initially set to a default priority
level of nil
.
Priority
is implemented as an optional field is elaborated below under 'Design considerations'.PersonCard.java
based on the buyer/seller's priority field. For instance, its color is red for high
priority,
orange for medium
, green for low
, and not rendered for nil
.To accommodate saving of buyers and sellers with the new priority fields in storage, JsonAdaptedBuyer
and other
relevant files are modified to include these fields in JSON format, and to be readable and loaded back into Model
in
subsequent RTPM initialisations.
Given below is an example usage scenario for setting priorities for buyers in the address book's buyer list.
Step 1. The user launches the application and executes the bprio 2 high
command, which sets the priority level of the
2nd person in the buyer list to high
. The bprio
command calls LogicManager
, which gets AddressBookParser
to parse and obtain a SetBuyerPriorityCommand
, before executing it. The command execution calls ModelManager
to
update the address book's buyer list with the newly assigned buyer priority, which is reflected on the UI too.
Finally, LogicManager
calls StorageManager
to update the JSON file.
The following sequence diagram shows how the undo operation works:
Note: The lifeline for SetBuyerPriorityCommand
should end at the destroy marker (X), but due to a
limitation of PlantUML, the lifeline reaches the end of diagram.
Step 2. To unassign the priority level of the 2nd person, the user can execute the bprio 2 nil
command, which
runs a similar flow as illustrated in the sequence diagram above.
The same logic can be used for assigning priorities to sellers instead of buyers, by using sprio
instead
of bprio
.
Aspect: How the optional priority field is implemented
Initial implementation: Overload the Buyer
/Seller
constructors.
Current implementation: Assign a default value for all non-compulsory fields in AddBuyer
and AddSeller
(for example, default phone number = 123, default priority = nil, and so on), and only assign these optional
fields if the user supplies arguments for them, which would be available in ArgumentMultimap
after parsing
the user input.
Buyer
/Seller
is needed for multiple optional fields instead of having
to overload the constructors.The sort mechanism is facilitated by SortedList
in ModelManager
. A SortedList
wraps an ObservableList
and sorts
its content. In ModelManager
, we have SortedList<Buyer>
and SortedList<Seller>
which wrap around
FilteredList<Buyer>
and FilteredList<Seller>
, allowing the user to filter the buyer and seller lists as well as
sort them at the same time. Additionally, ModelManager
implements the following operations:
ModelManager#updateFilteredSortedBuyerList(Comparator<Buyer> comparator)
- Sets the buyer SortedList
with a
comparator that denotes the order of this list.ModelManager#updateFilteredSortedSellerList(Comparator<Seller> comparator)
- Sets the seller SortedList
with a
comparator that denotes the order of this list.These operations are exposed in the Model
interface as
Model#updateFilteredSortedBuyerList(Comparator<Buyer> comparator)
and
Model#updateFilteredSortedSellerList(Comparator<Seller> comparator)
, which are executed by SortBuyerCommand
and
SortSellerCommand
respectively to sort the buyer and seller lists.
The comparator
passed into the methods above defines the way buyers and sellers are sorted. This sorting logic is
handled by the BuyerComparator
and SellerComparator
classes, which come with predefined implementations for the
compare
method of Comparator<T>
. Based on the prefix and sort order (ascending/descending) that is passed after
the bsort/ssort
keyword, an instance of BuyerComparator
/SellerComparator
with the corresponding implementation of
compare
method will be constructed and passed into the SortedList
through the sort command.
Given below is an example usage scenario and how the sort mechanism behaves at each step.
Step 1. The user launches the application for the first time. The VersionedAddressBook
will be initialized with the
initial address book state, and the currentStatePointer
pointing to that single address book state.
Step 2. The user executes buyer n/Amy
, buyer n/Bob
and buyer n/Carla
to add three new buyers.
Step 3. The user executes bsort n/d
to sort the buyer list by name in descending order. Inputting bsort n/d
calls
LogicManager
, which gets AddressBookParser
to create an instance of SortBuyerCommandParser
. If there is a valid
prefix, SortBuyerCommandParser
parses its SortOrder
, and creates a SortBuyerCommand
with a BuyerComparator
for
the prefix and sort order. If there is no valid prefixes, the SortBuyerCommand
will have a null BuyerComparator
.
The bsort
command is then executed, updating the SortedList
in this case by passing the BuyerComparator
instance that sorts by name descending. These changes are reflected in the UI, showing a list of buyers that is sorted
by name in descending order. Finally, LogicManager
calls StorageManager
to update the JSON file.
The following sequence diagram shows how the sort operation works:
Step 4. To sort the buyer list by its default order, the user can execute bsort
, which runs a similar flow as
illustrated in the sequence diagram above, except passing a null BuyerComparator
into the SortedList
for a default
sorting.
The same logic can be used for sorting sellers instead of buyers, by using ssort
instead of bsort
.
The undo/redo mechanism is facilitated by VersionedAddressBook
. It extends AddressBook
with an undo/redo history, stored internally as an addressBookStateList
and currentStatePointer
. Additionally, it implements the following operations:
VersionedAddressBook#commit()
— Saves the current address book state in its history.VersionedAddressBook#undo()
— Restores the previous address book state from its history.VersionedAddressBook#redo()
— Restores a previously undone address book state from its history.These operations are exposed in the Model
interface as Model#commitAddressBook()
, Model#undoAddressBook()
and Model#redoAddressBook()
respectively.
Given below is an example usage scenario and how the undo/redo mechanism behaves at each step.
Step 1. The user launches the application for the first time. The VersionedAddressBook
will be initialized with the initial address book state, and the currentStatePointer
pointing to that single address book state.
Step 2. The user executes bdelete 5
command to delete the 5th buyer in the address book. The bdelete
command calls Model#commitAddressBook()
, causing the modified state of the address book after the bdelete 5
command executes to be saved in the addressBookStateList
, and the currentStatePointer
is shifted to the newly inserted address book state.
Step 3. The user executes buyer n/David …
to add a new buyer. The buyer
command also calls Model#commitAddressBook()
, causing another modified address book state to be saved into the addressBookStateList
.
Note: If a command fails its execution, it will not call Model#commitAddressBook()
, so the address book state will not be saved into the addressBookStateList
.
Step 4. The user now decides that adding the buyer was a mistake, and decides to undo that action by executing the undo
command. The undo
command will call Model#undoAddressBook()
, which will shift the currentStatePointer
once to the left, pointing it to the previous address book state, and restores the address book to that state.
Note: If the currentStatePointer
is at index 0, pointing to the initial AddressBook state, then there are no previous AddressBook states to restore. The undo
command uses Model#canUndoAddressBook()
to check if this is the case. If so, it will return an error to the user rather
than attempting to perform the undo.
Note: The lifeline for UndoCommand
should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
The redo
command does the opposite — it calls Model#redoAddressBook()
, which shifts the currentStatePointer
once to the right, pointing to the previously undone state, and restores the address book to that state.
Note: If the currentStatePointer
is at index addressBookStateList.size() - 1
, pointing to the latest address book state, then there are no undone AddressBook states to restore. The redo
command uses Model#canRedoAddressBook()
to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo.
Step 5. The user then decides to execute the command list
. Commands that do not modify the address book, such as list
, will usually not call Model#commitAddressBook()
, Model#undoAddressBook()
or Model#redoAddressBook()
. Thus, the addressBookStateList
remains unchanged.
The following activity diagram summarizes what happens when a user executes a new command:
Aspect: How undo & redo executes:
Alternative 1 (current choice): Saves the entire address book.
Alternative 2: Individual command knows how to undo/redo by itself.
bdelete
, just save the buyer being deleted).In previous versions of the app and in the original brownfield project AB3, fields such as Name
or Email
had a validation method on instantiation, which would throw an IllegalArgumentException
when
the provided string did not fit the regex. Although useful, this would often be overzealous, causing potential
frustration. Furthermore, this exception, as it halts execution, only informs you of the first field that fails
to pass, so if you had multiple errors you would have to resolve and re-execute each time.
In 1.3, we implemented a group of static methods for each parameter, per convention
named isAppropriate(Field), which has a looser regex. The result of this boolean check,
if it fails in ParserUtil
, then passes a warning string to theCommandWarnings
instance, which collects
and stores them in an internal Set
At the end of the execute() method, if the
command encountered any warnings, then they are output through the getWarningMessage()
method
into the returned CommandResult.
This is then passed through LogicManager into MainWindow for display to the user.
LogicManager will also log any such warnings using its logger.
Note: The lifeline for SetBuyerPriorityCommand
should end at the destroy marker (X), but due to a
limitation of PlantUML, the lifeline reaches the end of diagram.
Aspect: How to implement the warnings
Alternative 1 (current choice): Use a CommandWarnings class to store strings representing warnings for inappropriate but valid fields.
Alternative 2 (possible future enhancement): Have CommandWarnings hold a set of predefined Warning singletons instead of Strings.
Alternative 3 (proposed): Use an exception such as InappropriateFieldException, which would be thrown by ParserUtil and caught by the add buyer/seller command parsers, which would then pass a String warning to the command for it to output as the CommandResult.
Target user profile:
This product is for student/junior realtors who have many clients to keep track of. They are relatively tech-savvy and prefer the keyboard over the mouse, prefer concise commands as opposed to full sentences, and would like to customise the software to suit their preferences.
Value proposition:
Our free and open-source app helps realtors to keep track of their clients’ preferences and details in one place. Unlike generic apps such as Google Sheets, our app is optimised for typical realtor workloads and databases.
Priorities: High (must have) - * * *, Medium (nice to have) - * *, Low (unlikely to have) - *
Priority level is based on current iteration
Priority | As a … | I want to … | So that I can… |
---|---|---|---|
* * * | realtor | add home-buyer clients into the app | keep track of them and their requirements |
* * * | realtor | add home owners and their houses into the app | keep track of them and relevant details (such as the price they are looking for, etc.) |
* * * | realtor | view my contacts | easily find contacts I want to talk to |
* * * | user who has been using the app for a long time | delete/archive old contacts | declutter my list from outdated information |
* * * | realtor | save contact data to my computer | refer to it when I reopen my app |
* * * | realtor | add houses into the app together with their price, furnishings, etc. | quickly list the features to my clients |
* * * | realtor who wants to pack light on the move | solely use the keyboard and not need to carry a mouse around to use the app | quickly access and update information without the fuss of using a mouse |
* * | realtor with many contacts | view personal contacts separately from work contacts | I can focus on work when I need to |
* * | realtor with many client contacts | sort my client contacts based on priority (time, importance, etc.) | I can focus on the most important clients first |
* * | realtor | add prospective rental clients into the app | keep track of them and their requirements |
* * | realtor who spends a lot of time at house viewings | I want the app to start up and respond quickly | use the app to note down any of my client’s preferences while talking to them |
* * | realtor who is flexible with scheduling | reschedule or postpone my meetings easily in the app | so I can avoid the hassle of constantly deleting and making new meetings |
* * | realtor | add time to tasks related to each of my clients | remember to do them |
* * | busy realtor with other activities in my life | enter my schedule | account for overlaps with any meetings |
* | forgetful user | be reminded if I have any upcoming or late meetings | follow up on my clients |
* | realtor | track tasks related to each of my clients | remember what I need to do to follow up on each of them |
* | realtor | be reminded of upcoming tasks or late tasks | do them before meeting clients |
* | power user | modify the syntax of (at least some) commands | enter them faster |
* | lazy user | be able to automatically match appropriate houses to prospective buyers | avoid doing it manually |
(For all use cases below, the System is our app RTPM (RealtorTrackerPlusMax)
and the Actor is the user
,
unless specified otherwise)
Use case: UC1 - Add homeowner and house info
System: RTPM
Actor: User
MSS
User enters command to add homeowner and details of the house they are selling.
System adds the entry to the list.
System saves file.
Use case ends.
Extensions:
1a. User enters invalid parameters.
3a. Failure to update savefile.
Use case: UC2 - Add homebuyer and preferences
System: RTPM
Actor: User
MSS
Extensions:
1a. User enters invalid parameters.
3a. Failure to update savefile.
Use case: UC3 - View buyers
System: RTPM
Actor: User
MSS
Use case ends.
Extensions:
Use case: UC4 - View sellers
System: RTPM
Actor: User
MSS
User enters the list-s command.
System displays list of sellers.
Use case ends.
Extensions
Use case: UC5 - Delete a buyer/seller
System: RTPM
Actor: User
MSS
User enters command to delete a buyer or a seller.
System deletes item.
System updates savefile.
System returns an indicator of execution success.
Use case ends.
Extensions
Use case: UC6 - Enter an invalid command
System: RTPM
Actor: User
MSS:
NFRs taken from the given constraints found here: The marking of NFRs as fulfilled/unfulfilled below is accurate for v1.4. -[x] The product should be optimized for keyboard users who can type fast and prefer typing over other means of input. -[x] The data should be stored locally in a human editable text file, instead of in a database. -[x] The software should primarily follow OOP. -[x] The software should work on the Windows, Linux, and OS-X platforms (hence shouldn’t depend on OS-specific libraries). -[x] The software should work on a computer that has version 11 of Java i.e., no other Java version installed. -[x] The software should work without requiring an installer. -[x] The use of third-party frameworks/libraries/services is allowed but only if they are free, open-source (this doesn't apply to services), and have permissive license terms.
The GUI should work well (i.e., should not cause any resolution-related inconveniences to the user) for
In addition, the GUI should be usable (i.e., all functions can be used even if the user experience is not optimal) for
Additional NFRs
We consider that as of the current release (v1.4), substantial effort has been put in by our team to rework the original AB3 project into a product that is useful for realtors and contains all the features required, including sorting, flexible command typing, reordering and undoing/redoing commands.
The first difficulty we encountered was in modifying the UI to fit our requirements. As developers newly introduced
to the brownfield AB-3 project, we had to spend quite a fair bit of time familiarising ourselves with
the project architecture, and the initial process of integrating some basic new commands
and tests (such as the one given in the AB-3 tutorial) was already rather tedious, let alone adding our own features to
the project and changes to the UI. For example, when trying to separate the initial Person
s object into Buyer
s and
Seller
s, the app wasn't able to launch due to having a broken test codebase, which required us to refactor at least
20 files across different directories in the project in order for operations to resume.
After much refactoring and tinkling with the JavaFX GUI, we were finally more familiar
and comfortable making changes to the AB-3 project, and were able to begin system testing of our app in
preparation for our very first release of RTPM, in the v1.2 release.
One of the things that we believe show the effort that we put into the project was the restructuring in the back-end to allow Model to hold, and UI to display, multiple lists of different types. In AB3, the application only needed to deal with one type of object, while in our case, we wanted to add 3 (Houses, Buyers and Sellers, although we only ended up adding the latter 2). Hence, we decided to abstract out the responsibility for displaying the object to the object itself (so we would not need a class to hold and display every type we wanted to add.
Displayable is an interface that allows the UniqueDisplayableList to abstractify the actual displaying and maintaining of uniqueness to the contained class itself. Thus, we can reduce the number of repetitive classes required to contain others.
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.
Exiting the program
exit
Adding a buyer contact
clear
command to prevent conflicts from prior testing.buyer n/Bob
buyer n/John Doe p/98765432 e/johnd@example.com ah/311, Clementi Ave 2, #02-25 i/Central Area 5 Room Condominium prio/medium t/friends t/owesMoney
buyer n/Bob
(this test case proceeds the one in ii, without clearing)buyer n/Tom p/phone e/email
Expected: Similar to previous.Adding a seller contact
clear
command to prevent conflicts from prior testing.seller n/Bob
seller n/Ryan p/91234567 e/ryan@gmail.com ah/My Secret Home as/47D Lor Sarhad, Singapore 119164 i/4 Room Flat in Sarhad Ville prio/high
seller n/Bob
(this test case proceeds the one in ii, without clearing)seller n/Tom p/invalidphone e/invalidemail
Expected: Similar to previous.Editing a buyer contact
bedit 1 p/12345 e/example@email.com
bedit 1 p/invalidphone
bedit 99999 p/12345
bedit 0 p/12345
bedit
, bedit 1
, bedit p/12345
Editing a seller contact
sedit 1 p/12345 e/example@email.com
sedit 1 p/invalidphone
sedit 99999 p/12345
sedit 0 p/12345
sedit
, sedit 1
, sedit p/12345
Deleting a buyer contact while all persons are being shown
Prerequisites: List all persons using the list
command. Multiple persons in the list.
Test case: bdelete 1
Expected: First buyer is deleted from the buyer list. Details of the deleted contact shown in the status message.
Test case: bdelete 0
Expected: No buyer is deleted. Error details shown in the status message.
Other incorrect delete commands to try: bdelete
, bdelete x
, ...
(where x is larger than the list size)
Expected: Similar to previous.
Deleting a seller contact while all persons are being shown
Prerequisites: List all persons using the list
command. Multiple persons in the list.
Test case: sdelete 1
Expected: First contact is deleted from the seller list. Details of the deleted contact shown in the status message.
Test case: sdelete 0
Expected: No person is deleted. Error details shown in the status message.
Other incorrect delete commands to try: sdelete
, sdelete x
, ...
(where x is larger than the list size)
Expected: Similar to previous.
Setting a buyer's priority
bprio 1 high
, bprio 1 h
bprio 1 medium
, bprio 1 m
bprio 1 low
, bprio 1 l
bprio 1 nil
, bprio 1 n
bprio 99999 high
bprio 0 high
bprio 0 invalidprio
bprio
, bprio high
, bprio 1
Setting a seller's priority
sprio 1 high
, sprio 1 h
sprio 1 medium
, sprio 1 m
sprio 1 low
, sprio 1 l
sprio 1 nil
, sprio 1 n
sprio 99999 high
sprio 0 high
sprio 0 invalidprio
sprio
, sprio high
, sprio 1
clear
command and add buyers named "John", "John Doe", "JohnDoe", and "Doe" with the buyer
command.filter John
filter Doe
filter
Displaying a buyer contact's information
blist 1
blist
, blist 0
, blist 99999
Displaying a seller contact's information
slist 1
slist
, slist 0
, slist 99999
Sorting buyer contacts
bsort prio/d
bsort qwerty
, bsort invalidprefix/invalidorder
bsort
command will ignore the invalid parameters and prefixes, and do a default sort.bsort qwerty n/a
, bsort invalidprefix/invalidorder n/a
bsort
command will ignore the invalid parameters and prefixes, and sort by name ascending.bsort n/a qwerty
, bsort n/a invalidprefix/invalidorder
bsort n/invalidorder
bsort prio/d prio/a
Expected: The buyer list is not updated. Error details shown in the status message indicating duplicate prefixes.bsort prio/d n/a
Expected: Buyer list is sorted by name in ascending order. bsort
chooses one of the provided prefixes based on
this order: 1. Name, 2. Home address, 3. House info, 4. Priority.Sorting seller contacts
ssort prio/d
ssort qwerty
, ssort invalidprefix/invalidorder
ssort
command will ignore the invalid parameters and prefixes, and do a default sort.ssort qwerty n/a
, ssort invalidprefix/invalidorder n/a
ssort
command will ignore the invalid parameters and prefixes, and sort by name ascending.ssort n/a qwerty
, ssort n/a invalidprefix/invalidorder
ssort n/invalidorder
ssort prio/d prio/a
Expected: The seller list is not updated. Error details shown in the status message indicating duplicate prefixes.ssort prio/d n/a
Expected: Seller list is sorted by name in ascending order. ssort
chooses one of the provided prefixes based on
this order: 1. Name, 2. Home address, 3. House info, 4. Priority.Dealing with missing/corrupted data files
data/rtpm.json
file is created.Extension: missing data
Instead of filling it with invalid data, delete the JSON file.
Expected: The app will recognize that there is no stored file, and will default to providing a typical sample of a contact list.
Test case: Delete the file data/rtpm.json
or the folder data
Expected: The app will launch with sample data in the buyer and seller lists
Given below are the enhancements that will be implemented in a future version.
Currently, the UI text is cut off if the entries are too long. While this should not usually happen since the user
can decide what to enter (nicknames, abbreviations, acronyms, etc.), we plan to accommodate overly long names,
phone numbers, addresses, emails and house info entries within the UI.
As a current workaround, users can call the blist
/slist
commands to display the text representation of the entry in
the results box.
Extremely long inputs can cause the program to hang or crash. This is a minor issue, since users are unlikely to enter such long fields into the app. A possible enhancement is to prevent overly long entries by blocking the command execution.
Currently, if the user makes a spelling or spacing mistake, the intended prefix of another field is regarded as part of the argument for the previous field. We plan to check for misspelled prefixes and prefixes provided as arguments of other fields and warn the user.
Currently, the user is not warned if addresses, names, or house info entries contain only numbers and special symbols. We plan to expand warnings to include warnings for addresses, names and house info entries containing only non-alphabetical characters.
As of v1.4, we have received reports that a warning is thrown even when there are no names that users considered similar. After testing, we determined that users in fact had two names that were very short, and this caused a discrepancy between commonly expected behavior and actual implementation. We defined distance between similar names as either one name contains the other entirely, or the Levenshtein distance between the two names is 2 or less (It takes 2 or fewer substitutions/additions/removals to turn one of the names into the other.) An unintended effect was that, for example, if you had short names (e.g "d", "hi", in the original case for us), the names would match despite normal users probably not defining these two names as similar. Possible future enhancements would be to make it percentage-based, so that short names are not producing warnings unnecessarily.
Currently, for bprio
and sprio
,
bprio 1 high low
, the app
accepts the input and sets the first buyer's priority level to high
instead of warning the user about extra
arguments which would be ignored. As such, we plan to warn the user if any extra arguments are supplied for the
user to double check that their priority input is correct.(?i)(h[igh]{0,3}|m[edium]{0,5}|l[ow]{0,2}|n[il]{0,2})$
(?i)
refers to case-insensitive matching(h[igh]{0,3}|m[edium]{0,5}|l[ow]{0,2}|n[il]{0,2})
means that the string input can match one of four possible
options below, with each option separated by a |
:
h[igh]{0,3}
accepts a string with a first letter 'h', followed by 0 to 3 letters after 'h', which can be
any of the letters inside the square brackets, so h
, hi
, hhh
and hggi
are all appropriate inputs.m[edium]{0,5}
accepts a string with a first letter 'm', followed by 0 to 5 letters after 'm', which can be
any of the letters inside the square brackets, so m
, mii
and mdmiue
are all appropriate inputs.l[ow]{0,2}
accepts a string with a first letter 'l', followed by 0 to 2 letters after 'l', which can be
any of the letters inside the square brackets, so l
, lw
, and lww
are all appropriate inputs.n[il]{0,2}
accepts a string with a first letter 'n', followed by 0 to 2 letters after 'n', which can be
any of the letters inside the square brackets, so n
, nl
, and nll
are all appropriate inputs.$
demarcates the end of the matchingInitially, the regex above was meant to allow for user typos, such as hgih
or meduim
, but in hindsight,
this regex is unnecessary as it doesn't value add much to the user experience,
and only made it harder to test for invalid priority inputs.
As such, we plan to
change the validation regex to only accept h
, m
, l
, or nil
as inputs
for priority in the future.
Our filter command currently only matches entire sections of a name. A possible enhancement is to allow filter to
search for partial matches, or have an additional command parameter to enable this; e.g. match hi
with Ibrahim
.
Our filter command also can only search for names as of v1.4. Another enhancement is to allow users to search for the
specific field they want to, by indicating it in the command parameters. One possible format is filter p/202
if you
wanted to filter by phone number, for example.
sort
keyword and before the next
valid prefix (n
, ah
, i
or prio
). Instead of allowing these inputs, the sort command can be changed to not allow
these inputs and show an error message to the user indicating these invalid parameters and prefixes. Moreover, sort
currently allows the user to input more than one valid prefix, in which case it'll take the prefix based on the order:
1. Name, 2. Home address, 3. House info, 4. Priority. The sort command can be changed to allow only a
single prefix, decreasing ambiguity on which prefix it is sorting by.