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.
Step 6. The user executes `clear`, which calls `Model#commitAddressBook()`. Since the `currentStatePointer` is not pointing at the end of the `addressBookStateList`, all address book states after the `currentStatePointer` will be purged. Reason: It no longer makes sense to redo the `buyer n/David …` command. This is the behavior that most modern desktop applications follow.
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 Persons object into Buyers and
Sellers, 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
exitAdding a buyer contact
clear command to prevent conflicts from prior testing.buyer n/Bobbuyer 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/owesMoneybuyer 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/Bobseller 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/highseller 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.combedit 1 p/invalidphonebedit 99999 p/12345bedit 0 p/12345bedit, bedit 1, bedit p/12345Editing a seller contact
sedit 1 p/12345 e/example@email.comsedit 1 p/invalidphonesedit 99999 p/12345sedit 0 p/12345sedit, sedit 1, sedit p/12345Deleting 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 hbprio 1 medium, bprio 1 mbprio 1 low, bprio 1 lbprio 1 nil, bprio 1 nbprio 99999 highbprio 0 highbprio 0 invalidpriobprio, bprio high, bprio 1Setting a seller's priority
sprio 1 high, sprio 1 hsprio 1 medium, sprio 1 msprio 1 low, sprio 1 lsprio 1 nil, sprio 1 nsprio 99999 highsprio 0 highsprio 0 invalidpriosprio, sprio high, sprio 1clear command and add buyers named "John", "John Doe", "JohnDoe", and "Doe" with the buyer command.filter Johnfilter DoefilterDisplaying a buyer contact's information
blist 1blist, blist 0, blist 99999Displaying a seller contact's information
slist 1slist, slist 0, slist 99999Sorting buyer contacts
bsort prio/dbsort qwerty, bsort invalidprefix/invalidorderbsort command will ignore the invalid parameters and prefixes, and do a default sort.bsort qwerty n/a, bsort invalidprefix/invalidorder n/absort command will ignore the invalid parameters and prefixes, and sort by name ascending.bsort n/a qwerty, bsort n/a invalidprefix/invalidorderbsort n/invalidorderbsort 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/dssort qwerty, ssort invalidprefix/invalidorderssort command will ignore the invalid parameters and prefixes, and do a default sort.ssort qwerty n/a, ssort invalidprefix/invalidorder n/assort command will ignore the invalid parameters and prefixes, and sort by name ascending.ssort n/a qwerty, ssort n/a invalidprefix/invalidorderssort n/invalidorderssort 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.