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:
Format: meet INDEX TAGS n/NAME d/DESCRIPTION v/VENUE l/LABEL s/STARTDATETIME e/ENDDATETIME duration/D H M S block/BLOCK
where
and must abide by these restrictions:
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:
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:
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:
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:
How it works:
-
MeetCommandParser
parsesmeet
command. TheMeetCommandParser
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
andParserUtilForEvent
-
-
All the fields are passed as arguments into the constructor of
MeetCommand
, which stores all arguments as instance fields. -
MeetCommand
retrieves the specified persons frommodel
. 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, aCommandException
is thrown. -
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.
-
The full event list will be retrieved from
model
, and piped into aStream
. -
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. -
The events will then be sorted in order of start time, so that the earliest available common free time slot can be found.
-
The earliest common time slot that fits the given block and duration of the event is searched for.
-
This event is created, added, and the addressbook is then committed.
The sequence of interactions is shown in the following diagram:
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.
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.
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 withy
, then(x, y)
reduces tox
. -
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.
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
.
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.
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.
The event here does not clash with meeting
. Both events reduce to meeting
.
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.
Finally, the last event does not clash with the event in blue. Both events reduce to the event in blue.
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.