Introduction

zhuanyu

[GitHub]

The purpose of this portfolio is to document my contributions to Car Park Finder, a software engineering team project to be completed as part of the CS2103T Software Engineering module in National University of Singapore (NUS).

My fellow team members are: Keith | Yurou | Delong

1. Project: Car Park Finder

1.1. Overview

Car Park Finder is a desktop command-line application for all car owners seeking a hassle-free way to find Housing & Development Board (HDB) car parks in Singapore.

Our application allows you to view vital information such as parking lots availability for each car park, so that you can plan ahead of your trip.

If you use the computer frequently and commute by driving, why not try out Car Park Finder. No installation is required and getting started is as simple as typing out a text message.

Main Features of Car Park Finder:

  • Find by location helps to narrows down the car parks near your destination

  • Filter through the list of car parks with the use of flags to get the preferred choice of car park

  • Receive notifications on how many parking lots are still available for a car park

  • Use of autocomplete to simplify overcomplicated commands by prompting correct format

1.2. Summary of contributions

This section is to provide a summary of my contributions to the project.

My main task for Car Park Finder was to query data from the database and update the information of all the HDB car parks.

This allows users of the application to:

  • Find car parks based on their postal code, address or car park number

  • Filter car parks using information like night parking as flags etc

  • View and click on car parks in the Google Maps found inside Car Park Finder


  • Major enhancement: Query data to provide real-time updates

    • What it does: This enhancement is broken down into two commands, namely query and notify. Basically both commands retrieve data from the database and either add or update the car parks in Car Park Finder. Users will then be able to find and filter the car parks base on their preferences and pick one that is most suitable for them.

    • Justification: This feature is part of the core requirements for the application to work properly. Without it, users would not be able to view all the HDB car parks in Singapore easily or receive notifications about them.

    • Highlights: This enhancement requires an in-depth analysis of design alternatives as it is written from scratch. It serves as a basis for other features and without it, there would not be any car park(s) data. This affects features to be added in future, since a change in car park information could require changing the whole system. The implementation too was challenging as it required threading to ensure a fast and efficient load time while querying the database.

    • Credit: Gson library for converting JSON data from data.gov.sg database to Java Objects for use in the project.

  • Minor enhancements:

    • Added marker clustering for the Google Maps to limit the number of markers on the map. It also zooms in onto the location when clicked on.

      • Credit: This enhancement uses Javascript and was based off the tutorial here provided by Google.

    • Updated the status bar to show the total number of car park(s) in Car Park Finder. I also added a check for List and Clear commands to either show all the car park(s) or display a "no car parks(s) available" message instead of displaying zero.

  • Code contributed: Click here to view my code on the CS2103T Project Code Dashboard.

  • Other contributions:

    • Project management:

      • Managed releases v1.1 - v1.4 (7 releases) on GitHub

      • Handled deliverables and deadlines for the entire team

    • Enhancements to existing features:

      • Updated the GUI layout and aesthetics (Pull requests #113, #122)

    • Documentation:

      • Did cosmetic tweaks to existing contents of the User Guide and Developer Guide. (PR #134)

      • Updated README to include introduction, value proposition, main features and standardise acknowledgements. (PR #134)

    • Community:

    • Tools:

      • Integrated a third party library (Gson) to the project (PR #3)

      • Integrated two new Github plugin (AppVeyor & Coveralls) to the team repository (Commit)

      • Integrated Reposense to track individual code contribution (PR #28)

2. User Guide Contributions

For my main contributions to the User Guide, they consist of the sections documenting Query and Notify commands. They showcase my ability to write documentation targeting end-users in a clear and concise manner.


2.1. Querying data: query

On your first time running Car Park Finder, you will see nothing on your screen. Do not panic! By type query into the Command Box, it will start to fill the application with all the available HDB car parks.

Format Abbreviation

query

q / qu / que / quer

  • If you wish to update all the car parks at a later date, just type this command again.

  • Remember that Car Park Finder does not automatically execute this command at the start.


2.1.1. Example: Let’s get some car parks!

It might be your first time using Car Park Finder, or you decided to clear out all the car parks. Whatever the case, its time to get some car parks into the application.

Message: Turning on notification

Loading…​ please wait…​

Step 1. Type query into the Command Box. The message above should appear in the Message Box as confirmation that you typed in correctly. Take note that you cannot type anything else once you press Enter.

Step 2. Wait for Car Park Finder to finish loading. It will only take awhile, so why not just sit back and relax?

zy query
Figure 1. Car Park Finder with 2099 car parks
Message: Finished loading

2099 car parks updated

Step 3. Once it has finish loading, the message above should appear. Please refer to Figure 1 to check if you are successful in getting the car parks.

2.1.2. Example: Query error(s)

Encountered a query error? Take a look below to see what went wrong.

Message: Query unable to retrieve car parks

Unable to retrieve car parks from data.gov.sg
Please check your internet connection and try again

This error occurs when there is a connection problem to data.gov.sg. Please check your internet connection and try again.

2.2. Enabling Notification : notify

After selecting a car park, you can choose to receive notifications on how many parking lots are still available. It sends an update periodically, so once it is enabled you do not need to type the command again into the Command Box.

Format Abbreviation Example(s)

notify TIME_SECONDS

n / no / not / noti / notif

notify 0
notify 10

  • If you did not select a car park beforehand, Car Park Finder would not know which car park needs to be updated.

  • You can set within a range of 10 to 600 seconds (10 minutes). Decimal values are not allowed.

  • Typing query or clear will disable the notification.

  • As notify is based on the index of select, you can select another car park to receive notification while it is enabled.

  • This means if you type find hougang and the original car park is gone but the index is still valid, it will update that car park instead.


2.2.1. Example: Receive notification every 10 seconds

This example assumes you have already decided on a car park. For more information on how you can choose a suitable car park in Car Park Finder, please click here.

It is time to head to your destination. Before you do so, why not check if the car park is full?

zy select notify
Figure 2. Selected Car Park AM18

Step 1. Select your car park from the list. In this example, we will choose the 5th car park as shown in Figure 2.

Message: Turning on notification

Notification enabled for car park AM18
Interval: every 10 seconds

Step 2. Type notify 10 into the Command Box. The message above should appear in the Message Box as confirmation that you typed in correctly.

zy notify ba
Figure 3. Before and after receiving notification
Message: Notification with no change in lot(s) available

Car park AM18 has 159 lot(s) available
Interval: every 10 seconds

Message: Notification with lot(s) freed

Car park AM18 has 167 lot(s) available
8 lot(s) freed since last check
Interval: every 10 seconds

Message: Notification with lot(s) taken

Car park AM18 has 152 lot(s) available
7 lot(s) taken since last check
Interval: every 10 seconds

Step 3. Now that the notification is enabled, you do not have to do anything else. It will inform you whether there is a change in parking lots availability through visual updates like these messages above and Figure 3.

2.2.2. Example: Turn off notification

Now that you have gotten tired of the notification, it is time to disable it.

Message: Turning off notification

Notification disabled

The message above will be displayed when you type notify 0 into the Command Box. You can also exit Car Park Finder to turn off notifications.

Message: Already disabled

Notification already disabled

If you are unsure whether you have already disabled the notification, the message above will be shown if you have done so.

2.2.3. Example: Notify error(s)

Encountered a notify error? Take a look below to see what went wrong.

Message: Invalid command format

Invalid command format!
notify: Set when to receive notification about the lot availability of a car park.
Parameters: SECONDS
0 to disable, range is 10 to 600 seconds (decimals not allowed)
Example: notify 10

This error occurs when you type the command wrongly in the Command Box. Either try again or just copy the command here.

Remember that notify 0 is how you disable the notification.

Message: Notification without selecting a car park first

Cannot notify without selecting a car park first

This error occurs when you did not select a car park and tried to enable notification. Please click here for more information.

Message: No car park data found

Unable to retrieve car park information from data.gov.sg
Unfortunately, the data is not available. We apologise for any inconvenience caused

This error occurs when the data is not available from data.gov.sg. Therefore, it is not possible to enable notification for that particular car park.

Message: Notification unable to retrieve car parks

Unable to retrieve car parks from data.gov.sg
Please check your internet connection and try again

This error occurs when there is a connection problem to data.gov.sg. Please check your internet connection and try again.

3. Developer Guide Contributions

For my main contributions to the Developer Guide, they consist of the sections documenting Query, Notify and Weather commands. They showcase my ability to write technical documentation and the technical depth of my contributions to the project in a clear and concise manner.


3.1. Query feature

The query feature updates the information of every car park using the latest information provided by data.gov.sg database.

3.1.1. Overview

The mechanism does an API call to the website data.gov.sg to obtain car park information in JSON format. An external library Gson is used to parse the data in GsonUtil. The data is stored internally as a CarparkJson object.

Some notable methods that GsonUtil implement are:

  • GsonUtil#getCarparkData() — Get the basic car park information from the API.

  • GsonUtil#getCarparkAvailability() — Get the total number of parking lots as well as the availability of the parking lots from another API.

  • GsonUtil#fetchCarparkInfo() — Return a list of car parks with populated data.

  • GsonUtil#loadCarparkPostalCode — Return a list of postal code information, with hashed coordinate data.

Only GsonUtil#fetchCarparkInfo() is used in QueryCommand, inside QueryCommand#readCarpark() method.

A local copy of the data is saved at the end. Users only need to execute this command if they want to get the most recent information from the database.

3.1.2. Example

Given below is an example usage scenario of how the query mechanism behaves at each step.

Step 1. The user launches the application, where the initial state of Car Park Finder is not up-to-date with the latest data published by data.gov.sg.

Step 2. The user executes the query command to fetch the latest data. The query command calls GsonUtil#fetchCarparkInfo() which in turn runs GsonUtil#getCarparkData() and GsonUtil#getCarparkAvailability().

Step 3. The user waits for data to be updated. GsonUtil#getCarparkData() establishes a connection with the API to read JSON data containing basic car park information.

Step 4. The JSON data is parsed using Gson library and stored inside CarparkJson. A HashSet is used to consolidate all the car parks and prevent duplicate entries.

Step 5. Once GsonUtil#getCarparkData() is done getting all the basic car park information, GsonUtil#getCarparkAvailability() retrieves additional details of the parking lot. The process is similar to how GsonUtil#getCarparkData() retrieves data from the API.

Step 6. GsonUtil#getCarparkAvailability() appends the additional the parking lot details using CarparkJson#addOn()

The loading of postal code, GsonUtil#loadCarparkPostalCode() is called inbetween here. Please refer here for more information on how it works.

Step 7. Next, a final check is done to see if there is any car park with no parking lot data. The value '0' is added if there is no data.

Step 8. Finally an ArrayList<ArrayList<String>> is returned from GsonUtil#fetchCarparkInfo() to update the car park finder state. The line of text at the bottom of the application then will show that the application is updated.

If GsonUtil#getCarparkData() or GsonUtil#getCarparkAvailability() fails to read from the API, IOException would be thrown.

Please refer to the Figure 4 below for how the query operation works.

zy seq
Figure 4. Interactions for query operation
For more details on the internal workings of Model, please refer to Undo/Redo.

3.1.3. Design Considerations

To find out why certain designs were chosen for the query feature, please read the following section for the explanation and reason behind such choices.

Aspect: How query executes
  • Alternative 1 : Wait for data to be queried sequentially.

    Pros

    No side effects as everything is done sequentially. If an error occurred, it is easy to trace the source.

    Cons

    The application hangs while data is being queried due to long processing time.

  • Alternative 2 (current choice) : Data is queried using a separate thread.

    Pros

    Application can provide feedback to the user as the data is being fetched in the background.

    Cons

    Reading the car park list while querying might cause unintended side effects if not handled properly.

Aspect: Data structure to support query command
  • Alternative 1 (current choice): Use ArrayList<ArrayList<String>> to store car park information.

    Pros

    Easy to maintain and iterate through an array list of array lists to get a specific car park.

    Cons

    Using an ArrayList<ArrayList<String>> can be confusing and unintuitive. Accessing elements is also not that efficient.

  • Alternative 2 : Use a HashMap<String,Carpark> to store data.

    Pros

    Much more efficient in accessing elements by using a key and better code readability.

    Cons

    HashMap does not provide an ordered collection. Since order of insertion is not known, the output for the list of car parks might be different every time.

3.2. Notify feature

The notify feature updates the lot availability of the car park selected by the user. It also displays a notification message periodically to inform the user.

3.2.1. Overview

The notify mechanism is facilitated by NotifyCommand and NotifyCommandParser. It enables notifications at a given interval and disables notification when it detects that the interval is set to '0'.

The NotifyCommandParser implements Parser with the following operation:

  • NotifyCommandParser#parse() — This checks the validity of the argument on whether it is non-negative integer and between 10 to 600, including 0. It throws a ParseException when then user input does not conform the expected format.

When notification is enabled:

  • NotifyCommand calls ScheduledExecutorService#scheduleAtFixedRate() to start NotifyTimeTask#run() and repeat the execution at a fixed interval.

  • At every interval, NotifyTimeTask will call GsonUtil#getSelectedCarparkInfo() to get the lot details from the API and update the specific car park using Carpark#setLots().

  • It will also create an event called NewResultAvailableEvent() to update the user on how many parking lots are available by displaying a message.

Take a look before at the code snippet below for more details on how NotifyTimeTask works.

NotifyTimeTask.java
@Override
public void run() {
    try {
        //...
        // Get the data from the database
        List<String> updateData = new ArrayList<>(GsonUtil.getSelectedCarparkInfo(
                selectedNumber.toString()));

        // Update the specific car park
        model.getCarparkFinder().getCarparkList().parallelStream()
                .filter(carpark -> carpark.getCarparkNumber()
                .equals(selectedNumber))
                .findFirst().ifPresent(carpark -> carpark.setLots(
                new LotsAvailable(updateData.get(1)), new TotalLots(updateData.get(2))));
        // This event updates the UI for the CarparkListPanel to account for any changes
        // in lotsAvailable variable
        EventsCenter.getInstance().post(new NotifyCarparkRequestEvent());
        model.commitCarparkFinder();

        // Check if notification is enabled
        if (CarparkListPanel.getTimeInterval() > 0) {
            // show notification...
        }
    }
    //...
}

3.2.2. Example

Given below is an example usage scenario of how the notify mechanism behaves at each step.

To enable it, notify must be used in conjunction with the select command. This means that it will only notify the current selected car park and not all the car parks.

Step 1. The user executes select 10 to select the 10th car park in the list.

Step 2. The user then executes notify 10, indicating the interval to be 10 seconds.

Step 3. The NotifyCommandParser#parse() gets called to parse the arguments supplied. It checks if the interval is valid non-negative integer, in the range of 10 to 600 and including 0.

Step 4. A new instance of NotifyCommand is created by NotifyCommandParser, and the argument then gets stored as int targetTime in NotifyCommand.

Step 5. Inside NotifyCommand, ScheduledExecutorService#scheduleAtFixedRate() starts NotifyTimeTask#run() with targetTime as one of the parameters passed in. This sets NotifyTimeTask to run and repeat every targetTime.

Step 6. When NotifyTimeTask#run() is active, GsonUtil#getSelectedCarparkInfo() gets called and fetches the data from the database. It returns the specific car park to be updated.

Step 7. To find which car park to be updated in the list, parallelStream() is used to filter through. Once it is found, Carpark#setLots() gets called to update the TotalLots and LotsAvailable variables.

Step 8. To show that the list of car parks is updated, NotifyCarparkRequestEvent() and NewResultAvailableEvent() are created to update the UI and display a message respectively.

Step 9. Every 10 seconds, the user will receive a notification on the number of available lots left for the 10th car park.

Step 10. The whole process will repeat itself starting from Step 6, until ScheduledExecutorService#shutdownNow() gets called by notify 0 which disables notification.

Turning notification off goes through the same process as above, but instead of running ScheduledExecutorService#scheduleAtFixedRate(), it check for '0' then runs ScheduledExecutorService#shutdownNow() to stop NotifyTimeTask.

Figure 5 below summarises what happens when a user executes the notify command:

zy act
Figure 5. Executing a notify command

3.2.3. Design Considerations

To find out why certain designs were chosen for the notify feature, please read the following section for the explanation and reason behind such choices.

Aspect: How notify executes
  • Alternative 1 : Set an interval for data to be queried sequentially.

    Pros

    No side effects as everything is done sequentially. If an error occurred, it is easy to trace the source.

    Cons

    Performance issues as the application might stall, despite only updating one car park.

  • Alternative 2 (current choice): Data is queried using a separate thread every interval.

    Pros

    Application can provide feedback to user as the data is being fetched in the background.

    Cons

    Reading the car park list while updating on a separate thread might cause unintended side effects if not handled properly.

Aspect: Class to run NotifyTimeTask
  • Alternative 1 : Use Timer class to run and schedule NotifyTimeTask.

    Pros

    Using Timer class allows for notify to run in the background thread and handles the repeated calls as well.

    Cons

    If the user were to change the system clock, it might affect how Timer class works. This is because the class relies on the system time to calculate when to run the task again.

  • Alternative 2 (current choice): Use ExecutorService class to run and schedule NotifyTimeTask.

    Pros

    ScheduledThreadPoolExecutor supports the use of multiple threads, whereas Timer only has one. Also, ScheduledThreadPoolExecutor is not affected by the system clock.

    Cons

    Unable to handle more complex operations if the application continues to add more features. For example, handling updates to multiple car parks with different intervals.