PROJECT: UniLA


Overview

UniLA is a desktop utilities application designed for university students who are typing oriented to easily manage their contact and event lists. The application is primarily concerned with CLI (Command Line Interface) Interaction, with a simple and intuitive GUI provided.

Summary of contributions

  • Major enhancement: upgrade the find command and implement findE command

    • What it does: These two commands support different search patterns. The find allows users to search for people in contact list easily. Users can search a person through exact keyword match, fuzzy keyword match and wildcard keyword match. The findE command allows users to search for events in event list. Users can search for an event through exact keyword match, search for events before, on or after a certain date, and search for the event with a certain duration.

    • Justification: This feature is important and efficient for users to manage a large list of contacts and events.

    • Highlights: The enhancement and implementation involves reorganizing different classes, adding new predicates classes and command classes. The implementation is challenging because different search patterns are used and the optimal algorithm is chosen after a detailed analysis of the alternatives.

    • Credits: Levenshtein Distance is used to calculate the similarity between keywords. [Original Blogger]

  • Minor enhancement:

    • Implement photo command which allows user to add photo to the person in the contact list.

    • Implement personInfo fxml to display personal information on contact list.

  • Code contributed: [RepoSense Project Code Dashboard]

  • Other contributions:

    • Project Management:

      • Setup netlify test.

      • Added issues to issue tracker.

      • Updated User Guide, Developer Guide, About Us pages.

      • Helped teammates with their problems regarding github and the project.

    • Community:

      • PRs reviewed: #117, #210

      • Reported bugs and suggestions for other teams in the class: #184, #177, #164

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.

Locating persons by any field: find

Finds people whose fields contain any of the input keywords. Searching can be done with or without prefix. When searched without prefix, people whose fields that contain any matching keywords will be returned. When searched with prefix, people whose corresponding fields that contain any matching keywords will be returned.

Format1: find KEYWORD [MORE_KEYWORDS]

Format2: find prefix/KEYWORD, [MORE KEYWORDS]

  • The search is case insensitive. e.g hans will match Hans.

  • The order of the keywords does not matter. e.g. Hans Bo will match Bo Hans.

  • If there is no prefix, all fields will be searched.

These are 3 search patterns that are utilized be the find command:

  1. Exact keyword match - Matches the input keyword with exact keywords found in people’s fields.

  2. Fuzzy keyword match - Matches the input keyword to people’s fields that have higher than 0.7 similarity. The similarity is calculated based on Levenshtein Distance. Further explanation is provided in the developer guide.

  3. Wildcard keyword match - Matches the input keyword with wildcard character *. * represents any number of alphanumeric characters consisting of 0-9, a-z and A-Z.

Searching results are displayed in the following format:
n persons listed:
Exact Search:
[NAME]…
Fuzzy Search:
[NAME]…
Wildcard Search:
[NAME]…

There are three parts of executing find command. The input keywords are first searched with exact string match, then fuzzy search, and finally wildcard search. Any results displayed in the previous searching stage will not be shown in the next searching stage.
For example, if person Alice is found in exact string match, she will not be displayed in fuzzy search result. This is to avoid message duplication.

Examples:

  • find victoria
    In exact search: Returns any person whose fields that contain keyword victoria in exact search. For example, person whose name is Victoria or person who lives in Victoria Street.
    In fuzzy search: Returns any person whose fields that have keywords similar to victoria. For example, person whose tags containing the keyword Victory.

  • find Serangon doctor
    In exact search: Returns any person whose fields that contain keywords Serangon or doctor
    In fuzzy search: Returns any person whose fields that have keywords similar to Serangon or doctor

  • find t/owemoney friends
    In exact search: Returns any person whose tags that contain keywords owemoney or friends
    In fuzzy search: Returns any person whose tags that have keywords similar to to owemoney or friends

  • find *@gmail.com
    In exact search: Returns any person whose fields that contain keywords *@gmail.com
    In fuzzy search: Returns any person whose fields that have keywords similar to *@gmail.com
    In wildcard search: Returns any person whose fields that have keywords match regex *@gmail.com

Locating events by any field: findE

Finds events whose fields contain any of the input keywords. Searching can be done with or without prefix. When searched without prefix, events whose fields that contain any matching keywords will be returned. When searched with prefix, events whose corresponding fields that contain any matching keywords will be returned.

Format1: find KEYWORD [MORE_KEYWORDS]
Format2: find prefix/KEYWORD, [MORE KEYWORDS]

  • The search is case insensitive. e.g talk will match Talk.

  • The order of the keywords does not matter. e.g. cs meeting will match meeting cs.

  • Only exact keywords will be matched e.g. talk will not match talks.

  • If there is no prefix, all fields will be searched.

  • Events matching at least one keyword will be returned (i.e. OR search). e.g. talk will return google talk and career talk.

Examples:

  • findE google
    Returns any events having fields that contain keywords google. For example, event whose name is 'Google talk' or event whose venue is 'Google building'.

  • findE pgp library
    Returns any events having fields that contain keywords pgp or library. For example, events with venue 'pgp' or 'central library'.

  • findE l/important
    Returns any event having label that contains keyword important.

There are two sub command of findE command which are findE time/ and findE duration/:
1. findE time/ finds events whose starting date are before, equal to or after the searching date. Alternatively alias (ytd, today or tmr) can be used to search for events whose starting date is yesterday, today or tomorrow.

Format3: findE time/operatorDATE
Format4: findE time/alias(ytd, today or tmr)

  • operator should be of type <, = or >.

  • DATE should be a valid date.

  • DATE should in format YYYY-MM-DD.

  • operator and DATE should have no blank space in between.

Examples:

  • findE time/>2019-12-01
    Returns all the events whose starting dates are after 2019,12,1.

  • findE time/tmr
    Returns all the events that starting tomorrow.

2. findE duration/ finds events whose duration is smaller, equal to or larger than the searching period.

Format5: findE duration/operatorHOURS

  • operator should be of type <, = or >.

  • HOURS is an integer representing the duration in hours.

  • HOURS should be a positive integer within range [1,24].

  • operator and HOURS should have no blank space in between.

Examples:

  • findE duration/<2
    Returns all the events which are shorter than 2 hours.

  • findE duration/>4
    Returns all the events which are longer than 4 hours.

Adding photo to a person : photo

Adds photo to a person in the contact list (identified by the index number used in the last listing).
Format1: photo INDEX IMAGE_PATH

Removes photo from a person in the contact list by the sub-command clear. Photo of the person will reset to the default photo.
Format2: photo INDEX clear

  • The index refers to the index number shown in the most recent listing.

  • The index must be a positive integer 1, 2, 3, …​

  • The given path must be a valid image path.

  • The size of the photo should be smaller than 20MB.

Examples:

  • list
    photo 3 /users/alice/desktop/photo.png (in mac)
    photo 3 C:\Users\william\Desktop\photo.jpg (in windows)
    Adds photo to the 3rd person in the UniLA.
    photo 3 clear
    Removes the photo from the 3rd person in the UniLA. Photo is reset to the default photo.

The added photo will be copied to the program. Thus, if the photo in the original path is moved, renamed or deleted, UniLA will not be affected.

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.

Find feature

Current Implementation

The mechanism is facilitated by findCommand, findCommandParser and different Predicate classes. It allows users to search with 3 different search patterns. During the execution of find command, 3 search patterns will be executed one by one.

Given below is the process of executing find command:

Step 1. The exact search: This is a base method of matching string. It’s implemented by String.equals().

Step 2. The fuzzy search: This is based on similarity comparison. First edit distance between input keywords and the string in people’s fields is calculated based on Levenshtein Distance algorithm. Subsequently, similarity is calculated by s = 1 - Levenshtein_Distance/Max_Length_Of_Two_Strings. The similarity threshold is set to 0.7. If a person’s fields contain keyword which have more than 0.7 similarity to the input keywords, he or she will be returned in fuzzy search result.

Step 3. The wildcard search: This is based on regular expression. It recognizes character *. The character * matches any number of alphanumeric characters consisting of 0-9, a-z and A-Z.

  • The following code snippet is from TagsContainsKeywordPredicate class. It shows an example of how predicate class works:

    @Override
    public boolean test(Person person) {
        return keywords.stream()
                .anyMatch(keyword -> {
                    String name = person.getName().fullName;
                    String tags = person.getTagsAsStringNoBracket();
                    if (StringUtil.containsWordIgnoreCase(tags, keyword)) {
                        if (!exactSearchList.contains(name)) {
                            exactSearchList.add(name);
                        }
                        return true;
                    }
                    if (StringUtil.matchFuzzySearch(tags, keyword)) {
                        if (!fuzzySearchList.contains(name)) {
                            fuzzySearchList.add(name);
                       }
                        return true;
                    }
                    if (StringUtil.matchWildcardSearch(tags, keyword)) {
                        if (!wildcardSearchList.contains(name)) {
                            wildcardSearchList.add(name);
                        }
                        return true;
                    }
                    return false;
                });
    }

Design Considerations

Aspect: How fuzzy search is executed
  • Alternative 1 (current choice): Use similarity to compare the keywords

    • Pros: The accuracy is higher because the differences and the total length of the strings are proportional.

    • Cons: Less easy to implement.

  • Alternative 2: Use edit distance to compare the keywords

    • Pros: Easy to implement.

    • Cons: Lower accuracy especially when the string is short.

Different search patterns optimize the 'find' command for different kinds of users. They help with users when they cannot remember the exact spelling or users who are prone to typos.

Photo feature

Current Implementation

The mechanism is facilitated by Photo, PhotoCommand class. A Photo class is added to Model. It allows users to add a photo to the person in the contact list.

Given below is the process of executing photo command:

Step 1. The command will be checked whether it contains sub command clear. If it is a photo clear command, the photo of the contact list will reset to default photo. The photo command execution is finished. Else, following steps will be executed.

Step 2. The input file path will be checked for whether the file exists.

Step 3. The input file path will be checked for whether it can be opened as an image.

Step 4. The size the of photo will be checked for whether it is within the range (smaller than 20MB).

Step 5. The photo will be copied to the program and it is saved to the target person.

  • The following code snippet is from PhotoCommand class. It shows an example of how photo command executes:

if (photo.getPath().equals(COMMAND_SUB)) {
    photo.setPath(DEFAULT_PHOTOPATH);
    Person personToEdit = lastShownList.get(targetIndex.getZeroBased());
    String path = personToEdit.getPhoto().getPath();
    File file = new File(path);
    file.delete();

    } else {
    if (!isValidPhotoPath(photo.getPath())) {
         return new CommandResult(MESSAGE_INVALID_PHOTOPATH);
    }
    if (!isImage(photo.getPath())) {
         return new CommandResult(MESSAGE_FILE_NOT_IMAGE);
    }
    if (!isPhotoSizeWithinRange(photo.getPath())) {
         return new CommandResult(MESSAGE_SIZE_EXCEED);
    }

    String user = System.getProperty("user.name");
    String dir = "data/";
    String copyPath = FileUtil.copyFile(photo.getPath(), String.format(dir, user));
    photo.setPath(copyPath);
    }

Design Considerations

Aspect: How photo is stored in UniLA
  • Alternative 1 (current choice): Copy the photo to the program.

    • Pros: The address book will not be affected if the photo in the original path is moved, renamed or deleted. Stability of the program is ensured.

    • Cons: Consumes more memory because the photo is copied to the program.

  • Alternative 2: Save the path and load the photo from the path every time when user opens the app.

    • Pros: Use less memory. Easy to implement.

    • Cons: The photo will be not be displayed if the photo in the original path is moved, renamed or deleted.