Overview
U-Schedule is a desktop calendar application for university students written for the project for the module CS2103 Software Engineering. The user interacts with it using a CLI, and it has a GUI created with JavaFX for user feedback. It is written in Java, and has about 10 kLoC.
Summary of contributions
-
Major enhancement: added fuzzy search with filters
-
What it does: Allows the user to search for commands without having to type the exact matching words - e.g. "find event proj" to find a calendar event with "Project" in its name. Also allows the user to filter their search by date and time, and by tags, for more precise searching. Finally, it displays the most relevant results first.
-
Justification: This feature improves the product significantly because it allows users to search for events more conveniently and more precisely. This would improve the overall user experience as well as the ease of navigation for users of U-Schedule, since the search function also makes the edit and select functions more convenient.
-
Highlights: This feature expands upon the existing find command in Address Book (Level 4), by revamping the Parser and Model to allow extra functionality (filtering by multiple Predicates and Sorting). This feature has the potential to be extended further, to allow users to filter the search by even more criteria or to incorporate more fine-grained filtering with boolean logic.
-
Credits: Uses third party library fuzzywuzzy for the fuzzy search
-
-
Minor enhancement: added feature that switches the GUI to the appropriate tab - either To Do List or Calendar Events - when a todo or event command is entered respectively
-
Other contributions:
-
Project management:
-
Enhancements to existing features:
-
Modified classes in the original AddressBook app to help transform it into our U-Schedule app. e.g. renaming AddressBook to Scheduler, modifying CalendarEvent and the various parsers to accept and store events' start and end date/time (Pull Requests: #11, #39)
-
Updated the GUI color scheme (Pull request #111)
-
Wrote additional tests and test cases for existing features, increasing coverage to 91% (Pull request #131)
-
-
Documentation:
-
Community:
-
Contributions to the User Guide
Given below are sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users. |
Searching for events: find event
Finds calendar events whose title, venue or description match any of the given keywords.
Also optionally filters the search by the specified date & time interval as well as the specified tags.
Format: find event [KEYWORD MORE_KEYWORDS…] [from/DATE & TIME] [to/DATE & TIME] [tag/TAG] [tag/MORE_TAGS]…
Examples:
-
find event lecture
Returns list of events whose title, venue or description matches the keywordlecture. -
find event cs2103 assignment tag/Project
Returns list of events matching the keywordscs2103andassignment, and having the tagProject -
find event exam from/29 nov 8am to/7 dec 5pmReturns list of events matching the keywordexam, that is ongoing at some point between 8am on 29th November and 5pm on 7th December. -
find event project tag/CS2103 from/15 nov 10am ` Returns list of events matching the keyword `project, having the tagCS2103, that is ongoing at some point after 10am on 15th November. -
find event tag/cs2103 tag/lectureReturns list of events having both the tags 'CS2103' and 'Lecture' (i.e. events with only theCS2103tag but not theLecturetag would be excluded).
Contributions to the Developer Guide
Given below are sections I contributed to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project. |
Fuzzy Search and Filter by Date/Time and Tags
Current Implementation
Overview
The Fuzzy Search enhancement for the find event command is achieved by modifying the Predicate used to filter the FilteredList already present in the ModelManager, by using a fuzzy match algorithm to check the similarity of the event’s Title, Description and Venue text with each input keywords, and seeing if it exceeds a given threshold.
The Filter by Date/Time and Tags feature is achieved by allowing the aforementioned FilteredList to accept multiple predicates at a time, by taking the logical AND of all the input predicates.
The feature to show the most relevant search results first is achieved by sorting the FilteredList, by wrapping it with a SortedList, and changing the Comparator used based on the input command.
The FindEventCommandParser and the ArgumentTokenizer were modified to accept commands that may or may not have keywords (i.e. preamble), and handle having varying numbers of prefixes in each command.
Finally, the GUI CalendarPanel was modified to access the new SortedList instead of the FilteredList so as to display the most relevant results first.
Model - Predicates, Comparators and SortedList
To allow for fuzzy matching, the fuzzywuzzy API was used. This allows us to compare strings using using a fuzzy algorithm based on Levenshtein distance between the strings. It assigns an integer match score from 0-100 for each pair of strings. If this score was above the threshold of 70, the retooled FuzzySearchFilterPredicate would allow it to be seen. This score was also used to compare events in our FuzzySearchComparator.
To implement the extra filters in conjunction with the fuzzy search, the UpdateFilteredCalendarEventList method was modified to allow for variable number (at least 1) of input Predicate<CalendarEvent> arguments. The method would then combine the input Predicates into a single Predicate by taking their logical AND. The combined Predicate would then be used to filter the FilteredList.
To allow users to filter by date/time, a new predicate, DateTimePredicate was created with 2 DateTime objects to set the dateFrom and dateTo for the filter. The DateTime objects were allowed to be null - this allowed for only 1 DateTime (either from or to) to be specified, so as to reduce the restrictions on users. If both DateTime objects were null, it would always return true - allowing it to be AND-ed with the other Predicates by the ModelManager without interfering with the filters.
To allow users to filter by tags, a new predicate, TagsPredicate was created. This stored a Set of input tag Strings, and if all of them had a full (but case-insensitive) match with at least one of the event’s Tags, then the event would be shown. The Set is allowed to be empty - similar to the implementation of the DatePredicate, this case would always return true.
The existing ModelManager attributes and methods were insufficient to show the most relevant search results first, as this would require sorting. As a result, we created a new FuzzySearchComparator to sort the CalendarEvents based on the aforementioned fuzzy match score, in descending order. In order to let users see this, we created a new SortedList to the ModelManager, to sort the existing FilteredList based on the FuzzySearchComparator.
Logic - ArgumentTokenizer and Parser
The new FindEventCommand required modifications to the parser to allow for commands with varying levels of inputs - it could have preamble and prefixes, only preamble with no prefixes or no preamble and only prefixes. In the latter case, we noticed that the ArgumentTokenizer would end up capturing the initial prefix as the "preamble". In order to correctly detect the absence of a preamble, the ArgumentTokenizer was modified, so as to correctly detect the absence of a preamble (i.e. when a prefix is at index 0 of the input arguments), and accordingly not add the 'preamble' (actually the first prefix) to the ArgumentMultimap.
After modifying the ArgumentTokenizer, the FindEventCommandParser also needed to be modified to generate the correct FindEventCommand from the inputs. As mentioned earlier, for each of the Predicates and Comparators, we implemented a 'default case', when the Predicate always returned true and the Comparator always returned zero - e.g. when both the DateTime for the DatePredicate are null, or when the List of keywords for the FuzzySearchFilterPredicate and FuzzySearchComparator are empty.
This allowed us to deal with the cases where some of the inputs were missing. If the user did not enter any tag/ prefix, then the FindEventCommand created by the FindEventCommandParser will have a TagPredicate containing an empty Set of tag strings.
GUI
In order for the new search features to be visible to the user, the getFilteredAndSortedCalendarEventList method was modified to allow the GUI CalendarPanel to access the SortedList rather than the FilteredList when initializing its ListView.
Tabbed Interface
Since the left panel would be shared between the TaskListPanel and the CalendarPanel, we realised that sometimes the panel would be showing the wrong list when a command is entered. Hence, after updating the FilteredList and SortedList, we made the execute method of the FindEventCommand post a SwitchToSearchTabEvent to the EventsCenter. The MainWindow handles the SwitchToSearchTabEvent and switches to the Calendar Events tab, so that the user can see the search results.
Design Considerations
Aspect: How Predicates are Managed and Applied to the FilteredList
-
Alternative 1 (current choice): Use a simple rule (AND) to combine all the
Predicatesinto a single one, then apply them to theFilteredListas normal.-
Pros: Easy to implement, since it mostly leverages existing functionality (only need to modify 1 method). As such it is also less likely to lead to regression errors.
-
Cons: Only capable of limited functionality
-
-
Alternative 2: Use a separate class to store and manage the
Predicates, combine them in the desired fashion and apply the resultingPredicateto theFilteredList.-
Pros: Will be capable of combining multiple
Predicatesin various different ways, allowing for more complex boolean expressions (see the next section for possible implementations) -
Cons: More difficult to implement, since it would add an extra layer of integration between the new class and the ModelManager. Would also need to ensure that the list of predicates is appropriately reset when each new command is entered.
-
Aspect: How Best to Show the User the Most Relevant Search Result
-
Alternative 1 (current choice): Sort the
FilteredListofCalendarEventsin descending order of fuzzy match score-
Pros: More reliable, has high chance of achieving the desired goal
-
Cons: Slower (since it needs to sort), more complicated to implement
-
-
Alternative 2: Apply a more aggressive filter (with higher threshold for acceptance) to hide less relevant results
-
Pros: Faster than sorting
-
Cons: Can be difficult to calibrate the filters properly to achieve the desired result. May end up being too aggressive, and could remove correct/desired results in some scenarios.
-
Further Extensions
Additional Filters
The FindEventCommand could be enhanced by including more Predicates to further filter the events based on other properties. For instance, we could allow users to specifically filter based on whether Venue or Description matched an input keyword, e.g. find event v/COM2 or find event d/Wear formal clothes.
Custom Filters using Boolean Logic
The FindEventCommand could be modified to allow users to specify custom filters using boolean logic, e.g. a possible command could be find event t/exam && v/MPSH5 or find event t/lecture || d/lecture. This would allow users to perform much more fine-grained searches. It is likely to appeal to our users, since they are more tech-savvy university students who are comfortable with CLI, and thus are probably somewhat familiar with boolean logic.
In order to implement this feature, we could create a new class specifically to take in the list of input Predicates as well as the specific rules by which to apply the AND/OR, and then to generate the appropriate predicate based on those instructions. This could then be fed to the ModelManager to update the FilteredList.