Welcome to my project portfolio. I am Benjamin and I am a computing science student. This project portfolio will detail the contributions I have made to various Software Engineering projects.

PROJECT: MeetingBook

MeetingBook is a desktop scheduler application targeted at university students involved in multiple Co-Curricular Activities and Project-Based modules. The application is a modified contact book with features to group contacts and assign meetings to such groups. MeetingBook is written by a team of 5 students from National University of Singapore’s School of Computing. It is forked from AddressBook-Level4 written by SE-EDU.

Summary of contributions

  • Major enhancement: enhanced the find command.

    • What it does prior: the find command filters the displayed list of persons based on the keywords given. Upon execution, the list displays persons whose name consists at least one of the keywords given.

    • What it does after enhancement:

      • The user can add an additional parameter specifying whether to search for groups, meetings, or persons.

      • The user can put the keywords into 3 categories: a set of keywords where the results must match all given keywords, a set of keywords where the results must match at least one of the given keywords, and a set of keywords where the results must not match any of the given keywords.

    • Justification: The extension of the find command allows the user to better manage their MeetingBook. When there is a large amount of data in the MeetingBook, the user will have an easier time searching for what they need.

  • Minor enhancement: added a sort command that allows the user to sort the displayed list of persons by name, phone number, email address, or home address by lexicographical order.

  • Code contributed:

  • Other contributions:

    • Project management:

      • Managed releases v1.1 - v1.3 (3 releases) on GitHub

      • Wrote tests to increase coverage from 93% to 94% (Pull request)

    • Enhancements to existing features:

    • Documentation:

      • Updated User Guide and Developer Guide: #161, #95

    • 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.

Sorting the list of displayed persons: sort

The sort command sorts the list in the display of persons. The user can specify whether to sort by name, phone number, email address, or home address, in lexicographical order or numerical order.

Format:
sort <name|phone|email|address>

  • <name|phone|email|address>: this parameter specifies whether to sort by name, phone, email address, or home address in lexicographical order or numerical order.

Examples:

  • sort name: Sorts the list of persons by name in lexicographical order.

  • sort phone: Sorts the list of persons by phone number in numerical order.

  • sort email: Sorts the list of persons by email address in lexicographical order.

  • sort address: Sorts the list of persons by home address in lexicographical order.

Finding a group / person / meeting: find

The find command searches the MeetingBook for specified person, group, or meeting, and displays the results on the panel.

Format:
Longhand: find <person|group|meeting> [a/KEYWORDS] [s/KEYWORDS] [n/KEYWORDS]
Shorthand: find <p|g|m> [KEYWORDS]

  • <person|group|meeting>, <p|g|m>: this parameter specifies whether to search for persons, groups, or meetings. This parameter is required to execute this command.

    • person, p: specifies to search for persons.

    • group, g: specifies to search for groups.

    • meeting, m: specifies to search for meetings.

  • KEYWORDS: this tag contains any number of keywords separated by a space.

  • [all], [a]: this parameter specifies that the results must contain every keyword specified in <keywords>. This parameter may be omitted.

  • [some], [s]: this parameter specifies that the results must contain at least one keyword specified in <keywords>. This parameter may be omitted.

  • [none], [n]: this parameter specifies that the results must not contain any keyword specified in <keywords>. This parameter may be omitted.

  • When the shorthand format is used, the keywords will be placed in the a/ parameter.

Examples:

findCommandAllExample
  • find p Alex: finds all persons whose name contains Alex. (Refer to diagram above)

findCommandSomeExample
  • find p s/yu li: finds all persons whose name contains one of yu or li. (Refer to diagram above)

findCommandNoneExample
  • find p n/Alex Bernice: finds all persons whose name does not contain Alex or Bernice (Refer to diagram above)

  • find p Betty Charles: finds all persons whose name contains Betty and Charles.

  • find p s/David Eric n/James: finds all persons whose name contains one of David or Eric, and whose name does not contain James.

  • find group project: finds all groups whose title contains project.

  • find g a/team n/school: finds all groups whose title contains team and whose title does not contain school.

  • find meeting s/official important: finds all meetings whose title contains one of official or important.

  • find m a/urgent n/basketball: finds all meetings whose titles contains urgent and whose title does not contain basketball.

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.

Sort and Find feature

The Sort and Find commands modifies the ObservableList<Person> in Model that the UI is bound to. This section will detail how the sort and find commands are implemented.

Implementation of find command

Structure of find command
FindCommandClassDiagram

When the user executes a find command, the FindCommandParser returns either a FindPersonCommand, a FindMeetingCommand, or a FindGroupCommand, depending on the user’s input. FindPersonCommand, FindMeetingCommand, and FindGroupCommand extends the FindCommand<Person>, FindCommand<Meeting>, and FindCommand<Group> abstract classes respectively. The FindCommand<E> abstract class extends the Command abstract class.

public abstract class FindCommand<E> extends Command {

    public static final String COMMAND_WORD = "find";

    public static final String MESSAGE_USAGE = ...

    protected final Predicate<E> predicate;

    FindCommand(Predicate<E> predicate) {...}

    @Override
    public boolean equals(Object other) {...}
}

ModelManager maintains 3 lists of type FilteredList. The lists are filteredPersons, filteredMeetings, and filteredGroups. When the find command is executed, a predicate is passed into the respective FilteredList. The elements in the list that passes the predicate test will be displayed in the UI.

Referring to the code snippet above, FindCommand<E> implements the equals() method and contains a predicate of type Predicate<E>. This predicate will be applied to the filteredPersons list if FindPersonCommand is executed, filteredGroups list if FindGroupCommand is executed, and filteredMeetings list if FindMeetingCommand is executed. Each of FindPersonCommand, FindGroupCommand, and FindMeetingCommand implements the execute() method required by the Command abstract class.

Structure of Predicates
PredicateClassDiagram

The predicates are created using the abstract class EntityContainsKeywordsPredicate<E>. PersonNameContainsKeywordsPredicate extends EntityContainsKeywordsPredicate<Person>, GroupTitleContainsKeywordsPredicate extends EntityContainsKeywordsPredicate<Group>, and MeetingTitleContainsKeywordsPredicate extends EntityContainsKeywordsPredicate<Meeting>.

public abstract class EntityContainsKeywordsPredicate<E> implements Predicate<E> {
    private final List<String> allKeywords;
    private final List<String> someKeywords;
    private final List<String> noneKeywords;

    private final Function<E, Predicate<String>> testKeywordPredicateGetter;

    public EntityContainsKeywordsPredicate(List<String> allKeywords, List<String> someKeywords, List<String> noneKeywords, Function<E, String> entityKeywordGetter) {...}

    @Override
    public boolean test(E element) {...}

    @Override
    public boolean equals(Object other) {...}
}

As seen in the code snippet above, EntityContainsKeywordsPredicate maintains 3 lists of String, the allKeywords list, the someKeywords list, and the noneKeywords list. The lists of strings are generated by the FindCommandParser that parses the find command entered. testKeywordPredicateGetter is used to generate the predicate that will be used to test whether each keyword matches the element being tested.

Each of PersonNameContainsKeywordsPredicate, GroupTitleContainsKeywordsPredicate, and MeetingTitleContainsKeywordsPredicate contains a predefined function used to retrieve the Person name, Group title, or Meeting title. This predefined function will be used passed into the constructor of EntityContainsKeywordsPredicate to generate testKeywordPredicateGetter.

When the predicate is passed into the FilteredList in ModelManager, the test() method in this predicate will be used to test the elements in the list.

How the sort command works with the find command

In the current implementation, the sort command only applies on the list of persons.

ModelManager maintains a sortedPersons list of type SortedList<Person> and a filteredPersons list of type FilteredList<Person>.

sortedList filteredList persons

Illustrated in the figure above, sortedPersons list wraps the filteredPersons list and filteredPersons list wraps the persons list of type ObservableList<Person> maintained by versionedMeetingBook. persons list in versionedMeetingBook contains all the persons stored in the MeetingBook.

The list displayed in the UI is bound to the sortedPersons list. Hence, the persons list has two layers of modification before being displayed in the GUI as sortedPersons.

Before the user executes any find or sort commands, no Comparator<Person> and no Predicate<Person> is applied to the persons list. The UI will display the all the persons in the MeetingBook as expected.

When the user executes a find command, an appropriate Predicate<Person> will be created based on the user’s input, and the Predicate<Person> will be applied to the persons list. This modification will be reflected in the sortedPersons list and in the displayed list in the GUI.

Similarly, when a user executes a sort command, an appropriate Comparator<Person> will be applied to the filteredPersons list. This modification will be reflected in the displayed list in the UI.

Design considerations

Due to this two-layered structure. The user can chain find and sort commands back-to-back to display the desired list.

findThenSortExample

An example can be seen in the diagram above by executing the command find p s/yu li followed by sort name. After applying the sort command, the find filter remains applied. The next example will demonstrate a different order.

sortThenFindExample

The diagram above shows another example of back-to-back execution. The diagram shows the execution of sort name followed by find p s/yeoh li. As can be seen in the diagram, the sorting order remains even after executing the find command.