PROJECT: UniLA


Overview

UniLA is a desktop utilities application designed for NUS students who are typing oriented. UniLA provides an efficient and convenient solution for managing contact list and event list, contact interactions, planning meetings, setting up reminders, etc. The application is primarily concerned with CLI (Command Line Interface) Interaction, with a simple and intuitive GUI provided.

Summary of contributions

  • Major enhancement: added the ability to automatically create a meeting event with persons

    • What it does: It allows users to create a meeting event among certain people, by automatically

      • searching for the earliest available timeslot among the requested people to meet

      • creating the meeting event in the event list

      • connecting the requested people to the event and

      • setting the details of the event (such as name, description etc.) as requested.

    • Justification: This feature improves the product significantly because as the user’s contact and event list grows, there may be too many people and events stored and it will be difficult for the user to find a suitable timing to meet the people he needs to. Thus the app must be able to conveniently help the user do so.

    • Highlights: This enhancement affects existing commands and commands to be added in future. It required an in-depth analysis of design alternatives. The implementation too was challenging as it required changes to existing commands.

  • Minor enhancement: added a switch command which allows the user to switch between person and events list view. All commands will also automatically switch accordingly or terminate depending on the current view of the window as the command was entered.

  • Code contributed: [RepoSense Project Code Dashboard]

  • Other contributions:

    • Improved UI

    • Did cosmetic tweaks to existing contents of the README and updated UI

    • PRs reviewed (with non-trivial review comments): [#100]

    • Helped team to fix bugs

    • Maintained issue tracker

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.

Planning a meeting : meet

This command allows users to automatically create a meeting event among certain people, removing the need to siphon through all the events just to find a common available timeslot among contacts to meet. The command will:

  • search for the earliest available timeslot among the requested people to meet

  • create the meeting event in the event list

  • connect the requested people to the event and

  • set the details of the event (such as name, description etc.) as requested.

Format: meet INDEX TAGS n/NAME d/DESCRIPTION v/VENUE l/LABEL s/STARTDATETIME e/ENDDATETIME duration/D H M S block/BLOCK where

  • INDEX denotes the indices of the people in the contact list the user wants to meet. For example, entering indices 1 2 will retrieve the 1st and 2nd persons in the contact list.

  • TAGS denotes the tags of the people in the contact list the user wants to meet. For example, entering tags t/friends will retrieve all persons with the friends tag.

  • NAME is the name of the new meeting event

  • DESCRIPTION is the description of the new meeting event

  • VENUE is the venue of the new meeting event, and

  • LABEL is the label given to this new meeting event.

  • D is the number of days the event will have

  • H is the number of hours the event will have

  • M is the number of minutes the event will have, and

  • S is the number of seconds the event will have.

  • STARTDATETIME is the earliest possible start date and time of the new meeting event

  • ENDDATETIME is the latest possible end date and time of the new meeting event, and

  • BLOCK denotes the block of time that the new meeting event must be in.

and must abide by these restrictions:

  • INDEX must be positive integers 1, 2, 3…​

  • TAGS must be valid alphanumeric tag names, each with the t/ prefix, such as t/tagOne t/tagTwo

  • Duplicates are allowed but are ignored

  • At least two valid persons were specified

  • NAME must be a valid event name

  • DESCRIPTION must be a valid event description

  • VENUE must be a valid event venue and

  • LABEL must be a valid alphanumeric event label.

  • D, H, M, S are all integers

  • Individually, D, H, M and S can be negative

  • The specified duration in its entirety is nonnegative.

  • Both STARTDATETIME and ENDDATETTIME are valid `DateTime`s

  • BLOCK is a valid Block (see Defining a Valid Block).

The earliest this event would be created will be at the start of the next hour from the time the command was run. That is, if user ran the command on 1st Jan 2019 at 4:59pm, the earliest meeting event that could be created would be on 1st Jan 2019 at 5pm.

Note: All fields are optional, but at least two valid persons must be specified. If a field is unspecified, a default value is used.

Note: Entering multiple tags create and/or relationships, more specifically, if a user specifies two tags, tagone and tagtwo, then every person who has tagone and/or tagtwo will be retrieved (think of it as a union of two sets, not the intersection).

Example:

meet 1 2 t/friends t/colleagues n/Snooker competition d/Playing snooker again v/SAFRA Toa Payoh l/snooker duration/0 8 -30 0 s/2019-05-01 00:00:00 e/2019-05-07 00:00:00 block/09:00 18:00

creates an event whereby:

  • The event will have the first and second people in the contact list, and all persons with the friends or colleagues tag, or both

  • The event’s name is Snooker competition

  • The event’s description is Playing snooker again

  • The event venue is SAFRA Toa Payoh

  • The event is labeled as snooker

  • The event will last 7 hours and 30 minutes

  • The event will start at or after 1st May 2019 at 12am

  • The event will end at or before 7th May 2019 at 12am

  • The event must fall within 9am to 6pm of the same day.

Defining a Valid Block:

A block denotes the block of time that the new meeting event must be in.

It contains two timings, and possibly a ! symbol, "negating" the block.

A block must be written as: block/<Optional ! symbol><Either two sets of timings in HH:MM separated by space, or a keyword>

Examples:

  • block/00:00 13:32
    indicates that the new meeting event must fall within 12am to 1.32pm within the same day (counting clockwise).

  • block/!22:00 04:00
    indicates that the new meeting event must not fall within 10pm to 4am the next day, or effectively, the new meeting event must fall within 4am to 10pm within the same day.

Instead of entering two timings, users may opt to enter a keyword instead. The following is the full list of available keywords:

  • morning denotes 6am to 12pm

  • afternoon denotes 12pm to 6pm

  • evening denotes 6pm to 8pm

  • night denotes 8pm to 12am the next day

  • midnight denotes 12am to 6am

  • school denotes school hours, i.e. 8am to 6pm

  • breakfast denotes 7am to 10am

  • lunch denotes 12pm to 2pm

  • dinner denotes 5pm to 8pm

  • supper denotes 9pm to 1am the next day

  • brunch denotes 10am to 1pm.

Examples:

  • block/breakfast
    indicates that the new meeting event must fall within breakfast hours, defined to be 7am to 10am.

  • block/!night
    indicates that the new meeting event must not fall within night time, defined to be 8pm to 12am the next day. Effectively, the event must fall within 12am to 8pm of the same day.

Switching views: switch

Changes the view in the window. The default upon startup is in Contacts view. Running this command will toggle between contacts and events view.

Format: switch

Commands that require indices of one particular view as arguments may require you to switch to that view first. In those cases, switch to the appropriate view before running the command.

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.

Meeting Planner

The mechanism is supported by the MeetCommand class. This command allows users to automatically create a meeting event among certain people, removing the need to siphon through all the events just to find a common available timeslot among contacts to meet. The command will:

  • search for the earliest available timeslot among the requested people to meet

  • create the meeting event in the event list

  • connect the requested people to the event and

  • set the details of the event (such as name, description etc.) as requested.

Format: meet INDEX TAGS n/NAME d/DESCRIPTION v/VENUE l/LABEL duration/DURATION s/STARTDATETIME e/ENDDATETIME block/BLOCK

Example:

meet 1 2 t/friends t/colleagues n/Snooker competition d/Playing snooker again v/SAFRA Toa Payoh l/snooker duration/0 8 -30 0 s/2019-05-01 00:00:00 e/2019-05-07 00:00:00 block/09:00 18:00

creates an event whereby:

  • The event will have the first and second people in the contact list, and all persons with the friends or colleagues tag, or both

  • The event’s name is Snooker competition

  • The event’s description is Playing snooker again

  • The event venue is SAFRA Toa Payoh

  • The event is labeled as snooker

  • The event will last 7 hours and 30 minutes

  • The event will start at or after 1st May 2019 at 12am

  • The event will end at or before 7th May 2019 at 12am

  • The event must fall within 9am to 6pm of the same day.

How it works:

  1. MeetCommandParser parses meet command. The MeetCommandParser will ensure that:

    • at least one index or tag was entered

    • all indices are valid

    • all other errors caused by invalid arguments will be thrown by ParserUtil and ParserUtilForEvent

  2. All the fields are passed as arguments into the constructor of MeetCommand, which stores all arguments as instance fields.

  3. MeetCommand retrieves the specified persons from model. If a person is not able to be retrieved from the contact list, or if it discovers that less than two valid persons were entered, a CommandException is thrown.

  4. Next, a check will be done to ensure that the specified start time entered is non-trivial, that is, the earliest event created must start at least from the next hour from the time the user enters the command. If the user enters a date and time before the date and time when the command is entered, the next hour after the current date and time will be used instead.

  5. The full event list will be retrieved from model, and piped into a Stream.

  6. Events will be filtered off from the Stream, based on whether the event involves any of the people specified by the user. This is so that a common free slot can be found.

  7. The events will then be sorted in order of start time, so that the earliest available common free time slot can be found.

  8. The earliest common time slot that fits the given block and duration of the event is searched for.

  9. This event is created, added, and the addressbook is then committed.

The sequence of interactions is shown in the following diagram:

MeetCommandSequenceDiagram

The loop portion is not actually implemented with a for or while loop. Instead, a Stream was used as it was able to filter and reduce each event sequentially.

The following describes the logic behind points 4 till 9.

Suppose the user enters meet 1 2 3 s/2019-05-14 03:00 duration/0 4 0 0 block/09:30 18:00. In this case, the indices, tags, name, description, venue and label are trivial to the logic. Further suppose the user enters this command at some date and time before 03:00 on 14 May 2019.

In this user’s current events list, there are six events, all involving either persons 1, 2 or 3. These events are "in the way". They are depicted as red boxes in the following diagrams. All other events in the list have been filtered away by the Stream.

The yellow region signifies the block entered by the user, i.e. the block of time which the event must fall in.

MeetCommandLogic1

Because the start time is after the next hour from the time the command was entered, an event with a duration of 4 hours that starts on 3am on 14 May 2019 is created. This is labeled as meeting in light blue below.

MeetCommandLogic2

meeting is first transformed to fit in the first block. This is done by the transformEventToFitBlock(Event e) method.

At this point, all existing events in the events list (shown in red) will be piped into a Stream object. This stream is filtered, such that all events that do not have any of the proposed meeting participants are filtered out.

In this case, because all events in the user’s events list involve either persons 1, 2 or 3, none of them are filtered out. Now, reduce is applied on this Stream object.

The following is the code snippet for the reduce method.

.reduce(meeting, (x, y) -> {
    LocalDateTime xEnd = toDateTime(x.getEndDateTime());
    LocalDateTime yStart = toDateTime(y.getStartDateTime());
    LocalDateTime yEnd = toDateTime(y.getEndDateTime());
    if (toDateTime(x.getStartDateTime()).isAfter(yEnd)
            || !xEnd.isAfter(yStart)) {
        return x;

    }
    LocalDateTime start = yEnd;
    return transformEventToFitBlock(new Event(name, description, venue,
            new DateTime(start.format(DateTime.DATE_TIME_FORMATTER)),
            new DateTime(start.plus(duration).format(DateTime.DATE_TIME_FORMATTER)), label));
});

In this case, x will always be the meeting event, shown in blue. Event y will be highlighted green.

Because the stream has already been sorted in order of start DateTime s, y will be selected in order of left to right in the diagram.

The logic behind reduce is as follows:

  • If x does not clash with y, then (x, y) reduces to x.

  • Else, create a new meeting that starts at the end DateTime of y, then transform it to fit the block.

Pictorially, it will look like the following.

MeetCommandLogic3

The left-most event is selected as y. In the first use of reduce, the identity was set to be the initial meeting:Event, in blue.

Because the event in blue does not clash with the event in green, both events simply reduce to the one in blue. The next left-most event is selected as the next y.

MeetCommandLogic4

In this case, the event in blue clashes with the event in green. Thus, a new event, with a duration of 4 hours (same as before), that starts at the end of y, is created. This event is then transformed to fit the block, which in this case already does. The old blue and green events are reduced to this new event.

MeetCommandLogic5

Here, the event in blue clashes with the event in green. A new event with a duration of 4 hours, that starts at the end of y, is created. This event is then transformed to fit the block, in this case, it is pushed all the way to start at 09:30 the next day.

MeetCommandLogic6
MeetCommandLogic6a

The event here does not clash with meeting. Both events reduce to meeting.

MeetCommandLogic7

Here, the event in blue clashes with the event in green. Just as before, the two old events are reduced to a new meeting event that starts right after the end of y and is within the block.

MeetCommandLogic8

Finally, the last event does not clash with the event in blue. Both events reduce to the event in blue.

MeetCommandLogic9

This event is then tested whether its end DateTime is later than the latest end DateTime specified by the user, and whether there are duplicate events. I would have written a lot more, but I’m out of space in my PPP.