PROJECT: NUS Hangs

Overview

NUS Hangs is a desktop Schedule Manager application. It has a GUI but most user interactions occur on CLI (Command Line Interface). It is written in Java by developers of F11-4, to target small/mid-size interest/study groups in NUS.

Summary of contributions

  • Major enhancement: Created a group class and added corresponding group functions:

    • add_group: Add a new group which will appear in the right group panel of UI.

    • delete_group: Delete an existing group and remove all current members from that group.

    • edit_group: Edit an existing group’s name/description.

    • register: Register an existing person as a member of the group.

    • delete_member: Delete an existing member of the group.

    • find_group: Find a group based on its name.

    • view_group: View all members of the group in UI.

    • Justification: This feature improves the product’s functionality significantly because a user can now better organise his contacts into groups and easily coordinate common time slots to meet.

    • Highlights: This enhancement affects existing and future commands. The implementation was challenging due to:

      • Integration of Group and Person Class. For instance, delete_group requires the group to be deleted and all reference to the group in all Persons registered to be deleted.

      • Changes in the UI and Storage. For instance, GroupPanel has to be created to display groups. Group data have to stored after the app closes and reloaded into the app as Group objects when app is launched.

  • Minor enhancement: Enhance list command to list both groups and persons. Added a list INDEX command to show all the groups that the person at INDEX is currently in. (#123)

  • Minor enhancement: Created short forms for commands. (#208)

  • Minor enhancement: Created Abstract class Entity which Group and Person extends from to allow for polymorphism: Refactored the ModelManager and AddressBook function: E.g. addPerson() -→ add() which performs add on Group or Person class based on polymorphism. Also, refactored UniquePersonList into generic UniqueList<T>. (#80)

  • Minor enhancement: Created a defensiveSetGroups method in Person class enforcing compulsory association of Person and groupList- instead of setGroup() which can alter a private groupList field in Person and also checks for null entries of groupList (#222).

  • Code contributed: [Functional and Test code]

  • Other contributions:

    • Project management:

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

      • Edited the badges: Codacy badge, Coverage and Build status on README.adoc.

    • Enhancements to existing features:

      • Wrote additional tests to increase coverage by >5% (Pull requests #222, #80, #123, #146)

    • Documentation:

      • Update the Model, Storage Class diagrams Logic and High level Sequence Diagrams in DG: #202, #200, #213

    • Community:

      • PRs reviewed (with non-trivial review comments): #71, #155)

      • Parts of the group feature adopted by other teammates (#160)

      • Create a CommandUtil.java with common functions reused by different commands in AB4 and those used by other teammates (#211)

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.

Listing all groups a person is in : list (Melodies)

Short form/Alias: l

Shows a list of all groups the person specified by his PERSON_INDEX is in.
Format: list PERSON_INDEX

(Remember to list first)

  • List the groups that the person at the specified INDEX is currently in. The index refers to the index number shown in the displayed person list. The index must be a positive integer 1, 2, 3, …​

Example:

  • list 1

(lists all the groups that the person at index 1 is added to previously)

Groups (Melodies)

Adding a group: add_group (Short form/Alias: ag)

Add a group to the System
Format: add_group n/GROUPNAME [d/GROUPDESCRIPTION]

Examples:

  • add_group n/happyfriends d/a group of happy friends

  • `add_group n/Bestfriends

Editing a group : edit_group (Short form/Alias: eg)

Edit a group’s details. OLDGROUPNAME must be entered. There must be at least 1 field non-empty. If field is empty, system assumes that the field remains unedited.

Format: edit_group OLDGROUPNAME [n/NEWGROUPNAME] [d/NEWGROUPDESCRIPTION]

Examples:

  • add_group n/happyfriends d/a group of happy friends

  • edit_group happyfriends n/sadfriends d/sad because of cs2103 (Changes the name of group from "happyfriends" to "sadfriends" and description)

  • edit_group sadfriends d/very very miserable (Changes only description.)

Locating group by name: find_group (Short form/Alias: fg)

Find groups whose names contain any of the given keywords
Format: find_group KEYWORD [MORE_KEYWORDS]

  • Only the group name is searched.

  • Only full words will be matched e.g. Family will not match Families

Examples:

  • find_group John
    Returns john family and John House

  • find_group Betsy Tim John
    Returns any group having names Betsy, Tim, or John

Deleting a group : delete_group (Short form/Alias: dg)

Delete a group in the system by name
Format: delete_group n/GROUPNAME

Examples:

  • delete_group n/happyfriends

Add a person to group : register (Short form/Alias: r)

Adds a person using the person index to a group if group exists
Format: register PERSON_INDEX n/GROUP_NAME

  • Remember to list before using register command.

Examples: (Adds the first person in the displayed person panel to the group happyfriends)

list
register 1 n/happyfriends

List all persons of a group : view_group (Short form/Alias: vg)

Displays a list of all persons in a group
Format: view_group n/GROUP_NAME

Examples: (lists the persons added to group happyfriends in the displayed person list)

  • ` view_group n/happyfriends`

Delete a person from group : delete_member (Short form/Alias: dm)

Delete a person from a group using person index displayed with view_group.
Format: delete_member PERSON_INDEX n/GROUP_NAME

  • Remember to view_group before using register command.

  • The PERSON_INDEX is the index of the displayed person list AFTER view_group command is performed.

Examples: (deletes the 1st person displayed in view_group command.)

  • view_group n/happyfriends

  • delete_member 1 n/happyfriends

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.

Group Feature

The Group class extends the Entity abstract class just like a Person class does. An Entity contains an abstract method isSame that is necessary for the class to be used in UniqueList<T extends Entity>. Group class is an immutable class that is contained inside Model.

As shown in the diagram below, group and person classes are associated. Each group contains a UniqueList<Person>, a list of its members. Likewise, each person contains a UniqueList<Group>, a list of all groups he is in. Due to these mutual association, commands editing/deleting instances of group or person may have to edit the corresponding member/group lists respectively. (Refer to DeleteGroupCommand example below.)

GroupPersonClassDiagram
Figure 1. Class Diagram of Group and Person

Group features make use of Storage to load information on groups added by the user before the UI is closed. XmlAdaptedGroup class helps the convert groups detail from xml files to the AddressBook when MainApp starts and similarly convert Group objects into xml files.

Group features also updates the Group Panel inside the UI using a predicate.

Current Implementation

The current group commands added are:

  • add_group/ag — adds a group with an optional description

  • delete_group/dg — deletes a group and all references from its members to it

  • edit_group/eg — edit name/description of a group while maintaining uniqueness of all group names

  • find_group/fg — search for groups using keywords that must match exactly a word in Group name

  • register/r — register a member into an existing group and include a reference to the group in the member (Person class)

  • delete_member/dm — delete an existing member from an existing group and remove reference to that group in the member

  • view_group/vg — view the existing members in the group and is updated whenever any member is added or deleted.

  • list [person_index]/l [person_index] — lists all the groups the person at [person_index] is currently in.

These functions and their parsers are handled in Logic, before updating Model and Storage and displaying the updated result on the UI.

An Example: DeleteGroupCommand

The DeleteGroupCommand is a good example of how Group and Person classes are interacting (dependent and associated with each other). An example usage scenario of how delete_group behaves at each stop is explained below.

Step 1: The user launches the application. (We assume that the user has already added a group and person.) VersionedAddressBook will be loaded with the final addressbook state before the application was closed. Lists of groups and persons added previously will be loaded from addressbook.xml file into Model using XmlAdaptedPerson and XmlAdaptedGroup classes. Updated lists will be displayed in the UI.

DeleteGroupCommandLogicSD
Figure 2. Sequence Diagram of DeleteGroupCommand (Logic component)

Step 2: The user enters the command dg n/Family to delete the existing group with group name Family.

  • The LogicManager class parses the command to AddressBookParser. AddressBookParser creates a DeleteGroupCommandParser instance which parses the String "Family" into a Name name. The parser creates a Group gr with the name (Family). This group is needed for the function below.

  • The parser creates a DeleteGroupCommand object with parameters gr. Subsequently, retrieveGroupFromName(m, gr.getName()) is called to the Model component in order to retrieve the actual group with the name "Family".

DeleteGroupCommandModelSD
Figure 3. Sequence Diagram of DeleteGroupCommand (Model component)
  • After retrieving the actual group g with name "Family" from Model m, the model performs deletion of the group using an internal function delete(g).

  • However, the group still has to be deleted from the groupList from all of its members. (Refer to GroupPersonClassDiagram above.) deleteGroupFromMembers(m, g) is called from DeleteGroupCommand object to do so. This method calls the UnModifiableObservableList() method in its UniqueList<Person> to return a list of members in g. For every member in the list of members (for loop), updatePersonDeleteGroupFromGroupList(m, g, member) deletes the reference to g in member.

  • Finally, the address book is committed with all the changes.

Step 4: FilteredPersonList and FilteredGroupList is also updated correspondingly to display the new groups and persons in the UI. A successful message is also displayed to the user below the UI’s command box.

Step 5: Before the application is closed, all groups and persons are stored in addressbook.xml using XmlAdaptedGroup and XmlAdaptedPerson classes. (Refer to High level sequence diagrams Figure 3 and 4 for a similar illustration of the concept.)

Design Considerations

Aspect: How to implement Group class

Alternative 1 (current choice): Group as an immutable class.

Pros: Immutable objects are good Map keys and Set elements, since these typically do not change once created. Immutability makes it easier to write, use and reason about the code.

Cons: Doing so might restrict the way one can call the class and its methods. It may be slower as you have to create new objects with every command.

Alternative 2: Setter methods for Groups

Pros: Easier and less code for methods involving groups. Faster as do not have to create new objects each time you change a Group (e.g. edit its description or group members).

Cons: Miss out on the advantages of immutable object (above). Good practice to use immutable objects.

Aspect: Interactions between person and groups

Deleting a person from a group will affect the person’s reference to that group and vice versa (similar for adding and editing).

Alternative 1 (current choice): Having a UniqueList<Groups> in Person and UniqueList<Person> in Group.

Pros: Easy to retrieve groups from Person and persons from Group. Existing UniqueList class available (since already used in Model).

Cons: Have to update both lists in most group commands (e.g. registering a new member in a group). Issue of enforcing referential integrity - defensive programming.

Alternative 2 Just update the list of groups in Model and have person refer to that list of group as to whether it is a member of the group.

Pros: Less issues with enforcing referential integrity (see alternative 1).

Cons: Can be more expensive to look for groups for a particular person.