PROJECT: Coupon Stash


Overview

Coupon Stash is a desktop coupon manager that allows users to keep track of their coupons. The user interacts with it using a Command Line Interface (CLI) in the application, with an aesthetically pleasing GUI created with JavaFX. It is written in Java with about 15kLoC. This application caters to fast-typists as all of the commands and features can be executed using the CLI.

Summary of contributions

  • Major enhancements:

    • Added the ability to search for coupons by its expiry date/ month year (Pull requests #82 , #198 , #202)

      • What it does: Allows the user to view all his/ her coupons by the expiry date or the month.

      • Justification: This feature is essential for users to keep track of the expiry dates of coupons. The enhancement, with the calendar, will ensure that users know exactly when their coupons expire and will never forget to use them.

      • Highlights: This enhancement ties in closely with the calendar, which indicates if there is any coupon expiring on each date. It also ties in with other coupon fields to ensure the validity of a coupon (i.e. not expired), such as savings from the coupon. This feature is critical to the functioning of Coupon Stash as it directly affects the primary functions of Coupon Stash such as the automatic archiving of expired coupons and setting of a reminder 3 days before the expiry date.

    • Added Calendar Pane (Pull request #123)

      • What it does: allows user to have a bird eye’s view of the expiry dates of his/ her coupons on the calendar.

      • Justification: This feature is important for users to have a clearer view of the expiry dates of coupons with highlights on the calendar.

      • Highlights: This enhancement ties in closely with expiry date, as the calendar will highlight a date on the calendar if there is any coupon expiring on the specific date. The Calendar Pane changes with the observable list in the coupon list panel, which follows the Observer Design Pattern.

  • Minor enhancements:

    • Added start date field that allows coupons to have a starting date & to tie in with the expiry date (Pull requests #105 , #226)

    • Added the copy command that allows the user to copy the details of a coupon as an add command to the user’s clipboard (Pull requests #134 , #198 (Renamed from export to copy))

    • Added the goto command that allows the user to navigate the calendar using the CLI (Pull request #202)

  • Code contributed: Click on this RepoSense link to view the code that I have contributed to the project!

  • Other contributions:

    • Front-End Developer:

      • Created and styled most of the UI classes (i.e. Calendar, Tabs, Result Display) (Pull requests #123 , #172 , #305)

    • Project management:

      • Created new issues when bugs are identified (Issues #217 , #229 , #321)

    • Enhancements to existing features:

      • Expanded on the list command to list active, archived or active (i.e. not expired/ not archived) coupons (Pull request #198)

    • Documentation:

      • Edited the logical flow of the User Guide (Pull request #179)

      • Edited parts of User Guide (Pull requests #6 , #7 , #151)

      • Edited parts of Developer Guide (Pull requests #159 , #167)

    • Tool:

      • Integrated a continuous integration workflow (Netlify) on GitHub into team repo

Contributions to the User Guide

Given below are sections I contributed to the User Guide.

Listing all expiring coupons: expiring

Shows a list of all your coupons expiring on a date or during a month year.

This command will change the month year on display in the calendar to the month year of specified expiry date or month year.
For example, expiring e/30-9-2020 & expiring my/9-2020 both changes the month year on display in the calendar to September 2020.

Format: expiring e/EXPIRY_DATE or expiring my/MONTH_YEAR

Examples:

  • expiring e/30-9-2020
    Shows you all the coupons that expire on 30 September 2020.

  • expiring my/9-2020
    Shows you all the coupons that expire during September 2020.

The expiring command will include archived coupons in its results too. Coupons that are expiring in the archives are displayed below the non-archived matches.

Listing coupons: list

Shows a list of coupons in the Coupon Stash.

There are 3 types of lists:

  • List of active coupons

  • List of archived coupons

  • List of used coupons

This command will change the month year on display in the calendar to the system’s date (usually today’s date).

Format: list [PREFIX]

  • PREFIX can be either a/ or u/.

  • Using a/ lists all coupons that are archived, while u/ lists all used coupons (including archived coupons at the end of the list).

PREFIX can be left blank to list all active coupons (coupons that are not fully used/ not archived (except for coupon(s) that are unarchived on purpose)).

Examples:

  • list
    Shows a list of all active coupons in Coupon Stash.

  • list a/
    Shows a list of all archived coupons in Coupon Stash.

  • list u/
    Shows a list of all used coupons in Coupon Stash.

Copying a coupon: copy

Copies a coupon as an add command to your clipboard so that you can easily share it with your friends/family!

Format: copy INDEX

  • Copies the coupon at the specified INDEX.

  • The add command of the coupon will be copied to your clipboard. Simply Ctrl + V to paste it! (Cmd + V for macOS)

  • Statistics like number of times used or amount of savings accumulated will not be copied.

  • The copied add command will only contain the following fields:

    • Name

    • Savings

    • Expiry Date

    • Limit

    • Promo Code (if any)

    • Condition (if any)

Below is a short demonstration of the copy command:

copy command
Figure 1. Animation of copy command operation.

Examples:

  • list
    copy 2
    Copies the second coupon in the resulting coupon list of the list command to an add command and copies it to your clipboard.

  • find chicken
    copy 1
    Copies the first coupon in the resulting coupon list of the find command to an add command and copies it to your clipboard.

Going to a month-year on the calendar: goto

Goes to the specified month and year on the calendar by changing the month year on display.

Format: goto my/MONTH_YEAR

Example:

  • goto my/12-2020
    Goes to December 2020 on the calendar by changing the month year on display to December 2020.

Contributions to the Developer Guide

Given below are sections I contributed to the Developer Guide.

Calendar

Current Implementation

The Calendar component provides a visual representation of the stored coupons that are expiring over a month. It is facilitated by CalendarPane, DateCell, ObservableList<Coupon> and ObservableMonthView.

The CalendarPane is the controller of the Calendar on display. Users can change the month on display to show the coupons that expire during a specific month year by clicking on the arrows at the sides of the calendar’s title or by using the goto command.

Each DateCell represents each date of the month that is currently on display. Each DateCell uses the ObservableList<Coupon> to keep a list of the coupon(s) that expires on each date. A DateCell with coupon(s) expiring on the date are highlighted in red and a Datecell that represents the system’s date is highlighted blue.

The ObservableList<Coupon> is the list of filtered coupons that are currently on display in the CouponListPanel. They are obtained by calling the Logic#getFilteredCouponList() method. The list can be filtered to view all active, archived or used coupons using the expiring command .

The ObservableMonthView is the current month & year on display in the Calendar Pane. It is obtained by calling the Logic#getMonthView() method.

The class diagram below shows the interaction between classes that affects the Calendar:

CalendarClassDiagram
Figure 2. Overview of the class diagram representation of the Calendar.

The sequence diagrams below show how the Calendar works:

CalendarSequenceDiagram
Figure 3. Sequence diagram representation of the Calendar on the startup of Coupon Stash.
CalendarSequenceDiagram Ref SetDetailsOfDateCell
Figure 4. Sequence diagram representation of the Set details of DateCell ref frame of Calendar (Applicable to the next two diagrams).

The two scenarios below are examples of how the Calendar mechanism behaves at each step of each scenario.

Updating the Calendar with an Updated List

The Calendar updates with the current ObservableList<Coupon> with commands such as the add, archive, clear, delete, edit, expiring, find, list, redo, unarchive, undo and used. The following steps describes how this behavior is implemented.

Step 1. The user launches the application for the first time.

The Calendar displayed will render the saved coupon data, triggered by the initiation of the UiManager.

Step 2. The user executes a command that alters the ObservableList<Coupon> (any command listed above).

When a command alters the observable coupon list, the listener of the observable list detects the change and the Calendar will be updated accordingly to the list by calling the CalendarPane#fillUpCalendar() method.

For example, the find command alters the observable coupon list. It calls the FindCommand#execute(Model, String) method, which calls the Model#updateFilteredCouponList(Predicate) method. It then calls the FilteredList<Coupon>#setPredicate(Predicate) method that alters the observable coupon list.

If a command fails its execution, it will not call the FilteredList<Coupon>#setPredicate(Predicate) method. Hence, the observable coupon list will not be altered and the calendar will not be altered.

The following sequence diagram shows how the Calendar updates with the observable coupon list:

CalendarFindCommandSeqDiagram
Figure 5. Sequence diagram representation of the update of the Calendar with the Coupon List for the "find chicken" Command.
Updating the Calendar with a Different Month View

The Calendar updates with the current ObservableMonthView with commands such as goto, expiring and list or by clicking on the arrows at the sides of the calendar title. The following steps describes how this behavior is implemented.

Step 1. The user launches the application for the first time.

The Calendar displayed will render the saved coupon data, triggered by the initiation of the UiManager. The default calendar display will be set to the system’s month year.

Step 2. The user executes a command that alters the ObservableMonthView (any command listed above).

When a command alters the observable month view, the listener of the observable month view detects the change and the month view display of the calendar will be updated according by calling the CalendarPane#updateCalendarWithYearMonth method.

For example, the goto command calls the GoToCommand#execute(Model, String) method, which calls the Model#updateMonthView(String) method. It then calls the ObservableMonthView#setValue(String) method that alters the observable month view.

  • expiring command

    • For the expiring command, the Calendar will be updated accordingly to the month year of the specified date or month year with the command.

    • For example, entering these expiring commands expiring my/9-2020 or expiring e/11-9-2020 will change the month year on display to September 2020.

  • list command

    • For the list command, the Calendar will be updated to the system’s month year.

      If a command fails its execution, it will not call the ObservableMonthView#setValue(String) method. Hence, the observable month view will not be altered and the calendar will not be altered.

The following sequence diagram shows how the Calendar updates with the observable month view:

CalendarExpiringCommandSeqDiagram
Figure 6. Sequence diagram representation of the update of the Calendar’s MonthView for the "expiring chicken" Command.

Or alternatively, instead of step 2,

Step 3. The user clicks on the arrows at the sides of the calendar title to change the month year displayed.

When a click alters the observable month view, the listener of the observable month view detects the change and the month view display of the calendar will be updated according by calling the CalendarPane#updateCalendarWithYearMonth method.

For example, clicking on the arrow on the right calls the CalendarPane#changeCalendarToNextMonth method, which calls CalendarPane#updateCalendarToNextMonth. It then calls the ObservableMonthView#setValue method that alters the observable month view.

Design Considerations

Aspect: Information displayed on the Calendar
  • Alternative 1 (current choice): Show expiring coupons by highlighting the dates with expiring coupon(s)

    • Pros: Cleaner view of the Calendar with minimal information & may take up less space on the Main Window

    • Cons: Lesser information provided with a glance

  • Alternative 2: Show a condensed version of the coupons' details within the cell of each date

    • Pros: More information provided with a glance

    • Cons: Messy to look at when there are multiple coupons expiring on a date & may take up more space on the MainWindow

We decided on alternative 1, to show coupons expiring on specific dates with highlights. This is because a coupon contains much information and the calendar may look cluttered and messy, which may be aesthetically unpleasant to the user. Furthermore, the user can use the expiring command to search for coupons expiring on a date or month year and have a more detailed view of the coupons in the CouponListPanel.

Aspect: Whether the Calendar should update with the list
  • Alternative 1 (current choice): Calendar updates with the filtered list

    • Pros: User can easily relate and reference to the coupons shown in the Calendar to the CouponListPanel

    • Cons: May overlook some coupons if the list is filtered

  • Alternative 2: Calendar shows all the coupons in CouponStash

    • Pros: View of all coupons and will not overlook any coupons even when the coupon list is filtered

    • Cons: User may be confused if he/she sees a highlighted date on the Calendar when there is no coupon expiring on that date in the CouponListPanel

We decided on alternative 1, for the calendar to update with the list in the CouponListPanel. This is because this follows the Observer Pattern Design Principle. Furthermore, this will not confuse the user when the user sees a highlighted date on the Calendar when there is no coupon expiring on that date in the CouponListPanel.

Coupon Reminder

To ensure users are aware of expiring coupons and maximise their saving, Coupon Stash reminds the user through a pop-up window, upon launching the application.

To achieve this feature, the following methods in the RemindDate and RemindWindow classes are used.

  • RemindDate#isToday() - Check if the RemindDate is today.

  • RemindWindow#filterRemindCoupons() - Filters out all RemindDates that are not today from RemindWindow.

  • RemindWindow#constructRemindCoupons() - Creates a String of coupons that have their RemindDates today. This String is used in the displayed reminder window.

  • RemindWindow#showIfAny() - Shows the reminder window if there are coupons to be reminded of today. If there are no coupons that have to be reminded today, no window will be shown.

RemindClassDiagram
Figure 7. Overview of the class diagram representation of the reminder checking implementation.

To make sense of how coupon reminder functions, let’s dive into the specifics of RemindDate class, RemindCommand class and RemindWindow class.

Implementation of editing a coupon’s RemindDate

The following activity diagram depicts what happens when the user runs a edit command to edit a coupons’s RemindDate.

EditRemindActivityDiagram
Figure 8. Activity Diagram representation of the flow of editing the remind date of a coupon.

Implementation of reminder pop up

After establishing the remind dates for all the coupons, the next step is ensure that there will be a reminder pop up (if necessary) upon opening the application.

The following steps describe how to reminder pop up works:

  1. The user launches Coupon Stash. The start method in MainApp class will kick start the program by setting up the stage, along with the saved data.

  2. This will trigger the start method in UiManager, which leads to the creation of a new RemindWindow instance, with a List of all coupons currently stored passed in as a parameter.

  3. In the constructor of RemindWindow, coupons that do not have a remind date of today are filtered out.

  4. After filtering the coupons, if there are coupons to be reminded today, their information will be concatenated into a String that is displayed in the reminder window.

RemindSequenceDiagram
Figure 9. Sequence diagram describing the process of opening the reminder window.

Design consideration

Aspect: How to keep track of coupon remind date

  • Alternative 1 (current choice): Coupon contains a Remind field.

    • Pros: Code implementation is easier and this makes the remind date more visible to the user since it is a field.

    • Cons: Coupon display may get very cluttered with the addition of this extra field.

  • Alternative 2 : Store the remind dates of all coupons in a separate data file. Coupons can be stored in a hash table with their remind dates as keys.

    • Pros: No need to clutter coupon display with additional fields. Plus, it is efficient to list all coupons that have to be reminded for on a certain day as the coupons are stored in a hash table.

    • Cons: Hard to maintain two separate data files that have shared components (in this case coupons)

All in all, we chose alternative 1 as we feel that it is good for users to be able to view the remind date of a coupon in the coupon view. Additionally, all speed-ups and efficiency of storing the remind dates in a separate data file is nullified by the fact that we still need to loop through all coupons to display their remind dates on the calendar component. Thus, to make it easier to extend the program in the future, we decided against adding another data file which can make extension more complicated, and chose to work with alternative 1.