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 keywordscs2103
andassignment
, and having the tagProject
-
find event exam from/29 nov 8am to/7 dec 5pm
Returns 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/lecture
Returns list of events having both the tags 'CS2103' and 'Lecture' (i.e. events with only theCS2103
tag but not theLecture
tag 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
Predicates
into a single one, then apply them to theFilteredList
as 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 resultingPredicate
to theFilteredList
.-
Pros: Will be capable of combining multiple
Predicates
in 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
FilteredList
ofCalendarEvents
in 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
.