Overview
SugarMummy is a desktop application used to manage a type-II diabetic lifestyle. The user interacts with it using a CLI, and it has a GUI created with JavaFx. It is written in Java and has about 30 kLOC.
Summary of contributions
-
Major enhancement: added a calendar system with real-time reminder
-
What it does: allows the user to add calendar entries(reminder and event) and automatically generate real-time reminders.
-
Justification: This feature improves the product significantly because a diabetic patients could be busy with their work or study already and they may need to keep track of important events, such as appointment with doctor, buying medicine regularly and be reminded at certain time so they do not forget, such as injecting insulin.
-
Highlights: This enhancement deals with date and time and scheduling tasks using the java multithreaded API
ScheduledExecutorService
. It provides both dynamic and static viewing of calendar entries. When the app is running concurrently, reminders are automatically shown in the side pane according to the time set by the user without interrupting user typing other commands. Reminder list can be updated at times during the running time of the app, it requires the scheduler to update scheduled tasks. Also reminders can recur daily or weekly so scheduler needs to adapt those reminders to an one-time reminder on certain date. Moreover, to avoid potential duplicate reminder shown in the side pane, the calendar checks any reminders that overlap with each other.
-
-
Minor enhancement:
-
added a calendar command that allows the user to view monthly calendar and a list of calendar entries on a day or each day in a month or in a week.
-
added a pane for showing reminders that the user might miss.
-
-
Code contributed: [View RepoSense]
-
Other contributions:
-
Project management:
-
Documentation:
-
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. |
CALENDAR AND REMINDER
Command arguments for calendar and reminder:
-
d/DESCRIPTION
Description can take any values, and it should not be blank. If more than one are present, the last argument is taken. -
dt/DATETIME
The format of date time is:yyyy-mm-dd hh:mm
and the number should be valid. If more than one are present and the command only accepts onedt/
, the last argument is taken. Otherwise, see below inevent
command for more details. -
r/REPETITION
Repetition can only take valueonce
,daily
orweekly
(case insensitive). If more than one are present, the last argument is taken. -
td/TIME_DURATION
The format of time duration is:hh:mm
and it should not be blank. e.g.01:30
represents 1 hour and 30 minutes. If more than one are present, the last argument is taken. -
ym/YEAR_MONTH
The format of year month is:yyyy-mm
and the number should be valid. If more than one are present, the last argument is taken. -
ymd/YEAR_MONTH_DAY
The format of year month day is:yyyy-mm-dd
and the number should be valid. If more than one are present, the last argument is taken. -
ymw/YEAR_MONTH_DAY
The format of year month day is:yyyy-mm-dd
and the number should be valid(same as year month day). If more than one are present, the last argument is taken. See below incalendar
command for more details if more than one ofym/
,ymd
,ymw/
are present.
Add a reminder: reminder
Format: reminder d/DESCRIPTION dt/DATETIME [r/REPETITION]
Add a reminder at a specific time and date. There is no repetition by default and user can specify a daily or weekly or no repetition optionally.
If it is a repeated reminder, the date inputted is the starting date of the reminder(reminder with date time before the current date time is allowed).
And it provides duplicate and conflicting checking as well as auto merging for reminders added.
For example, reminder d/insulin inject dt/2019-11-25 17:30 r/daily
adds a new reminder of insulin injection at 17:30 everyday from 2019 Nov 25.
-
More on duplicate and conflicting check and auto merging
-
To avoid duplicate reminders, the app will not accept reminders that already exist in the system(same description, date time and repetition). Similarly, reminders that are completely covered by an existing reminder will not be added again. For example, if the previous reminder command is successful,
reminder d/insulin inject dt/2019-12-01 17:30 r/weekly
will not add the new reminder because it is completely covered (whenever the later one shows up, the previous one shows up at the same time with the same description).reminder d/insulin inject dt/2019-12-01 12:00 r/daily
is not considered covered because there could be two injections one day. -
Moreover, new reminder that covers one or more existing reminders can be added and those existing ones will be removed automatically. For example, if
reminder d/insulin inject dt/2019-11-20 17:30 r/daily
is typed after the previous one, it replaces the insulin inject from 2019-11-25 because whenever the previous reminder shows up, the new reminder shows up with the same description. -
However, if the new reminder overlaps with existing ones but they cannot be merged, it will not be added. To illustrate,
reminder d/insulin inject dt/2019-11-18 17:30 r/weekly
is rejected because on 2019 Nov 18 it shows up at 17:30 alone. After 2019 Nov 20, this reminder will be a duplicate of the previous one every Monday.
-
-
Reminder list side pane
-
On the right side of the app window, there is a pane for reminder list with the date showing on the top. The reminder list pane is split into two parts: a real-time reminder list on top of a probably missed reminder list.
-
The real-time reminder list is empty if there is no reminder set at current time. If the app is open, the reminders will show up in the pane at the time set by the user. For example, if the user opens the app at 17:00 on 2019 Nov 30. A reminder of insulin injection will show up at 17:30 if the user has not closed the app.
-
The probably missed reminder list includes all reminders that should exist in at the time between the starting of day and the time when the user opens the app. For example, if the user opens the app at 17:00 on 2019 Nov 30, it lists all reminders from 00:00 to 16:59 on that day. However, if the user adds a reminder on 2019 Nov 30 17:10 at 17:20 on that day, it will neither exist in the real-time reminder list pane nor the missed reminder list pane.
-
Add an event: event
Format: event d/DESCRIPTION dt/DATETIME [dt/DATETIME] [td/TIME_DURATION]
Add an event with a starting time and an optional ending time(starting time before the current date time is accepted). There is no reminder for an event by default.
User can also create a reminder for this event by specifying a time duration and a one time reminder will show up at the starting time of the event minus this time duration.
For example, event d/appointment dt/2019-12-20 14:00 dt/2019-12-20 15:00 td/01:30
adds an appointment event on 2019 Dec 20 from 14:00 to 15:00 to the calendar and a reminder for it at
12:30 on that day.
If only one dt/
is present, it will be recognized as the starting date time. If there are more than one dt/
, the first and second one are interpreted as starting and ending
date time. Starting date time should come before ending date time. If they are equal, then ending date time is discarded.
A duplicate event(same description and starting date time) will be rejected. However, events that overlap with existing ones are accepted with a message shown to the user.
For example, event d/appointment dt/2019-12-20 14:00 dt/2019-12-20 16:00
will be rejected after the previous event commands. event d/test dt/2019-12-20 13:00 dt/2019-12-20 14:10
is accepted with a message shown.
View calendar entries: calendar
Format: calendar [ym/YEAR_MONTH] [ymw/YEAR_MONTH_DAY] [ymd/YEAR_MONTH_DAY]
View all calendar entries or calendar entries in a month or in a week or on a day.
-
If none of the three arguments is present, it will show all calendar entries added by the user.
-
If one or more of the three arguments are present, it accepts the one with the smallest time unit. e.g. if
ymw
andymd
are both typed in, it only shows entries on the given date if the date is valid.-
ymd/
shows the monthly calendar of given date and below the calendar is a list of reminders and events on that day. Particularly, if no date is provided, it will show calendar entries on the current date. -
ymw/
shows the monthly calendar of given date and below the calendar is a list of reminders and events on each day in the week.ymw/
accepts a date also and the week picked is 7 days from Monday to Friday which include the given date. Particularly, if no date is provided, it will show calendar entries in the current week. For example,calendar ymw/2019-12-03
will show a monthly calendar of 2019 December and calendar entries from Dec 2 to Dec 8. -
ym/
shows the monthly calendar of given month and below the calendar is a list of reminders and events on each day in the month.ym/
accepts a month. Particularly, if no date is provided, it will show calendar entries in the current week. -
For the monthly calendar shown, the subscript number beside each date is the number events and reminders on that day. And the current date is in a different color.
-
Snooze reminder: snooze
[coming in v2.0]
Format: snooze [tp/TIME_PERIOD]
Silence the current reminder and activate it after a time period.
Complete a task: complete
[coming in v2.0]
Format: complete [INDEX] [t/TIME]
Mark all the reminders before now as completed if no argument provided. Otherwise, mark only those tasks with indices provided or before the specific time as completed.
Set time zone: timezone
[coming in v2.0]
Format: timezone tz/TIME_ZONE [t/TIME] [t/TIME_END]
Set the time zone of the application permanently or in any time interval(e.g. For travelling).
Cancel an entry in calendar [coming in v2.0]
Format: cancel [et/ENTRYTYPE] [d/DESCRIPTION] [dt/DATETIME] [dt/DATETIME]
Delete an event that you do not want to keep track anymore.
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. |
Calendar feature
Implementation
Overview
The calendar feature is mainly supported by Calendar
along with a Scheduler
. Calendar
stores internally a calendarEntries
list, a pastReminders
list and a Scheduler
.
It also handles checking for duplicate and overlapping calendar entries.
Calendar entries consists of Reminder`s and `Event`s. `calendarEntries
list is an unique list for all calendar entries added
by the user. Aside from it, pastReminders
list is dynamically determined by time. The Scheduler
, which utilizes java ScheduledExecutorService
is responsible for adding reminders at specific time to the
pastReminders
list and all reminders in this list is shown to the user. The real-time reminder works parallel with all other features in this app. Also Scheduler
keeps track of the current date and the starting
time of running the app.
Calendar implements the following operations:
-
Calendar#addCalendarEntry
— Adds a new calendar entry to the calendar. -
Calendar#addPastReminder
— Adds a reminder to the past reminders list. -
Calendar#getCalendarEntryList
— Gets a list of calendar entries. -
Calendar#getPastReminderList
— Gets a list of past reminders. -
Calendar#schedule
— Schedules a series of upcoming reminders.
These operations are exposed in the Model
interface as Model#addCalendarEntry()
, Model#addPastReminder()
,
Model#getFilteredCalendarEntryList()
, Model#getPastReminderList()
and Model#schedule()
respectively.
Reminder and Event class
Basically, both Reminder
and Event
which extend from the abstract class CalendarEntry
consist of a Description
and a DateTime
field.
For Reminder
, the DateTime
field represents the time of the reminder and the date from which the reminder starts.
It has a field of Repetition
, which is an enum class representing Daily
, Weekly
or Once
repetition type of the reminder.
For Event
, it has another optional DateTime
field and an optional Reminder
field. The compulsory DateTime
field is the starting date time while the optional one is the ending date time. The Reminder
is for an auto reminder created by the app if the user requires in the command.
See the class diagram below for calendar related classes.
EventCommand and ReminderCommand class
To add events or reminders, the calendar system implemented EventCommand
and ReminderCommand
. Before adding an event, the calendar system will check whether any duplicate event exists and any events overlap
with the new event by comparing the DateTime
attributes.
The execution of the ReminderCommand
involves checking whether any duplicate reminder exists also. Due to probable recurrence of reminders, it also
checks if any reminder can fully cover the new reminder by comparing DateTime
and Repetition
. In this case, the new reminder will not be added. Besides, if the new reminder can cover other reminders, new reminder
will replace them. For those overlap but cannot be resolved, the system will not add the new reminder either.
Scheduler class
To show reminders at certain time, the Scheduler
utilizes java ScheduledExecutorService
to schedule a future task with a delay. It creates two inner classes implementing the Runnable
interface for tasks. The ReminderAdder
class represents a task of adding a reminder to the past reminder list. Initializer
class represents a task of initializing the scheduler at the beginning of a day.
The following activity diagram shows how scheduler works when it is called to schedule tasks:
-
Range of tasks scheduled
-
To calculate the accurate delay time for each reminder, the scheduler keeps track of starting time for all the scheduled tasks and the delay is the time duration between this starting time and the reminder time. Apart from the starting time, there is a deadline for the scheduler which limits the time range for scheduled reminder tasks. So the scheduler only schedules tasks for reminders between the starting time and the deadline. In addition, before scheduling tasks, all reminders with the same time are grouped together by using a
TreeMap
to map from each time to a list of reminders so they can be added to the past reminder list together. -
After the app is launched, the
Scheduler
is called to initialize tasks. The starting time is set to be the starting date time and the deadline is set to be 23:59 on the same day. Thus only reminders on the current date is scheduled. During the app running,Scheduler
can be called to reschedule tasks because of adding new reminders or removing reminders. This will adjust the starting time for scheduling to the current time while the deadline remains the same. Due to the limited number of threads and unknown number of upcoming reminders, the scheduler will cancel all the tasks that have not been executed and then schedule upcoming reminders that fall between the new starting time and the deadline. Each time any reminder being added or removed, the scheduler is triggered to reschedule tasks.
-
-
How to move on to next day
Besides scheduling tasks of adding reminders, the scheduler always schedules a task withInitializer
class for initializing right after the current deadline, so that it can transfer smoothly to the next day if the app is open overnight. This initializer will set the deadline to be end of the next day, update date and schedule tasks.
The following activity diagram shows an example of how event command and scheduler work together:
Example usage scenario
Given below is an example usage scenario and how the calendar behaves at each step.
Step 1. The user launches the application for the first time on Dec 14 2019 09:00(local time). The Calendar
will be initialized with the initial calendar state, which includes an empty calendar
entry list and an empty past reminder list.
Step 2. The user executes reminder d/insulin injection dt/2019-12-14 17:30 r/daily
command to add a new reminder of 'insulin inject' at 17:30 every day. The reminder
command calls Model#addCalendarEntry()
,
causing the modified state of the calendar after the reminder command executes to be saved in the calendarEntries
list. Subsequently, it calls Model#schedule()
which forces the scheduler to update the upcoming
reminders.
Step 3. The user executes event d/meeting dt/2019-12-14 14:30 tp/00:30
command to add an new event with an auto reminder scheduled 30 minutes
before the event. It calls Model#addCalendarEntry()
, causing a new event as well as a new reminder saved in the calendarEntries
list. Subsequently, it calls Model#schedule()
which forces the scheduler to
update the upcoming reminders.
If an event or reminder command fails its execution, it will not call Model#addCalendarEntry() , so the calendar state will not be saved into the calendarEntryList .
|
Step 4. At 14:00, a scheduled task is executed to call Calendar#addPastReminder()
and it adds the dinner event reminder to the pastReminders
list.
Step 5. At 17:30, a scheduled task is executed to call Calendar#addPastReminder()
and it adds the dinner event reminder to the pastReminders
list.
The following sequence diagram shows how a single reminder
command works:
CalendarCommand class
CalendarCommand
is implemented for showing calendar entries and monthly calendar. It consists of a YearMonth
, an optional YearMonthDay
, isShowingWeek
and isShowingRaw
fields. The result of executing a CalendarCommand
is a subclass CalendarCommandResult
extending from CommandResult
, which will be considered
differently in the MainWindow
. MainWindow
will create a pane depending on the attributes of CalendarCommandResult
.
Design Considerations
Aspect: How scheduler updates upcoming reminders
-
Alternative 1 (current choice): Cancels all scheduled reminders and reschedule according to the updated reminder entries.
-
Pros: Easy to implement.
-
Cons: May have to do duplicate work of scheduling. May have performance issues in terms of time.
-
-
Alternative 2: Updates scheduled reminders according to the newly added reminder.
-
Pros: Will has less repeated work.
-
Cons: More work to do on deciding which tasks to cancel.
-
Aspect: Period of updating scheduler.
-
Alternative 1 (current choice): Updates scheduler at 23:59(local time) every day.
-
Pros: Good consistency.
-
Cons: May have a large number of scheduled tasks which will not be executed before the application is closed.
-
-
Alternative 2: Updates scheduler every hour.
-
Pros: More flexible scheduling without concerning date and less scheduled tasks.
-
Cons: May cause overhead due to frequently updating.
-
Aspect: Resolution of overlapping reminders
-
Alternative 1 (current choice): Force to replace subset reminders with new superset reminders which fully cover existing reminders.
-
Pros: Avoid duplicate reminders added which the user may not be aware of.
-
Cons: May remove some reminders that the user was not intent to do.
-
-
Alternative 2: Asks for user’s permission before proceeding.
-
Pros: Can avoid unintentionally reminders deleting.
-
Cons: May cause some duplicate reminders.
-