Project: GreenMileageEfforts

Green Mileage Efforts (GME) is an efficient carpooling management solution designed to help corporations reduce their carbon footprint. The GME system allows for the simple creation and management of weekly carpooling groups of employees looking to carpool to and from their office. These pools of employees can then carpool from the office regularly on the specified days and times every week. Through the GME system, users can find employees based on their carpooling preferences and quickly group them with drivers. The system also maintains a database of the arranged carpooling groups for easy management.

GME is a platform that follows a Command-Line Interface (CLI) such that power users that are familiar can efficiently navigate the program.

Given below are my contributions to the project.

  • New Feature: Added the ability to drive passengers.
    • What it does: allows the user to select passengers to be driven by a driver.
    • Justification: This is a core feature of the product whereby we assign drivers to passengers
    • This feature has since been refactored to the pool command.
  • Code contributed: RepoSense link

  • Project management:
    • Setup team repo and organisation
    • Designed icon and branding for product
    • Ensured PRs and issues are linked to the correct author and milestone for proper tracking
    • Managed releases v1.1 - v1.4 (4 releases) on GitHub
  • Enhancements to existing features:
    • Augmented Person and Index to make them more testable (PR #57)
    • Wrote additional tests for existing features to increase coverage by 1.63% (PR #57), 2.56% (PR #78), 2.81% (PR #286)
    • Refactored code using Java Optionals (PR #225)
    • Prevent Passengers from being deleted if they are being referenced by a Pool (PR #132)
    • Fix Index being parsed incorrectly, resulting in wrong error message (PR #269)
    • Refactored the serialisation of Drivers and Pools such that they are stored as proper JSON (PR #125, #117)
    • Refactored toModelType of Passenger to better fit SLAP (PR #117)
    • Added sanity checking for JSON read to ensure manually edited files have no errors (PR #256, #298)
  • Community:
    • PRs reviewed (with non-trivial review comments): #16, #133
    • Substantial number of code related PRs merged, with code quality comments taken offline (and sadly not on github)
  • Tools:
    • Patched security vulnerabilities in nokogiri and kramdown versions
    • Added Codacy static analysis to repo
    • Setup Codecov check to PRs to ensure we attempt to maintain coverage
  • Documentation:
    • User Guide:
      • Added documentation for the features drive (PR #62 #32)
      • Did cosmetic tweaks to existing documentation examples: (PR #62)
      • Change layout to use Github pages functionality (PR #32)
      • General cosmetic and formatting issues
    • Developer Guide:
      • Added diagrams for Model, delete
      • Added implementation details for Model, Storage
      • Added manual testing instructions for edit

Excerpts from UG/DG

User Guide


3.1.1 User Interface

The various sections of the User Interface are described as in the picture below.

Ui_labelled


3.1.6 Editing the data file

GME data is saved as a JSON file [JAR file location]/data/GMEdata.json. Advanced users are welcome to update data directly by editing that data file.

  • Ensure that the following constraints are met if you decide to edit the file:
    • There are no duplicate Passengers
    • There are no duplicate Pools
    • Only one Pool can reference a Passenger
    • The Passenger referenced in Pool can be found in the Passenger object
    • The Passenger in a Pool must have the same Trip Day as the Pool

:warning: GME will replace the JSON file with a new one if it cannot read the file

  • Make a backup before any changes
  • Edit at your own risk

Format: delete INDEX [INDEX INDEX...]

Format: pool n/DRIVER_NAME p/DRIVER_PHONE d/TRIPDAY t/TRIPTIME c/INDEX [c/INDEX c/INDEX ...] [tag/TAG]


Developer Guide

Diagrams:


Structure of the Model Component

BetterModelClassDiagram


Model

The class diagram for the Model can be seen above in the Design section. Such a design was chosen after a few iterations on other designs. One such design is briefly documented as below:

v1.2

v1.2 Model

In v1.2, Passenger has-an optional Driver, which was initially chosen for its ease of implementation and storage. However, it was spotted that this would lead to issues in future when implementing trips on multiple days, since each Driver would have their own times, leading to a lot of duplication of Drivers. Further, this was not an easy format to display to the user intuitively, and would require a traverse of the whole Passenger list just to group Passengers by Drivers.

Implementation

Therefore, the decision was made to encapsulate the details of each trip (which is a trip by 1 driver with multiple passengers), into a Pool class. This Pool class would have it’s own CRUD, and would contain a Driver, Passengers, TripDay and TripTime.

This is done to facilitate Storage and UI, and also from a Users perspective, to allow for Pools with timings that may be slightly different from the Passengers preferred time.

A package Pool containing class Pool and UniquePoolList was created. This package performs a function that is similar to the Passenger package, exposing CRUD operations through ModelManager.

The decision was also made to make Passenger and Driver extend Person, so that for future iterations, we can support a Driver who is also a Passenger with minimal changes to the code.

Storage

As above, with regards to Model, the initial implementation of Storage was simply to store a Driver as a String field inside Passenger.

However, this involved extra parsing to ensure that the Driver String was still a valid Driver on load. Therefore, Driver was also made into a Jackson JsonProperty.

The class diagram for Storage can be found above.

Implementation

As of v1.3, the relation between Driver and Passenger is encapsulated in a Pool. All of Driver, Passenger, Pool and Tag are stored as JSON Objects. The choice was also made to store all data in 1 file, for ease of portability, such that the user only needs to manage 1 file.

At a high level, the JSON file is structured as such:

High level Storage Class Diagram

The Passengers are duplicated between the Passenger list and each of the Pools that reference the Passenger simply so we can reuse the JsonSerializablePassenger structure. This association would be much better represented in a RDBMS, which would be an easy change for a potential future iteration.

JsonSerializableAddressBook implements checks to ensure the following from the read JSON file:

  • There are no duplicate Passengers
  • There are no duplicate Pools
  • No more than one Pool can reference a single Passenger
  • There are no Pools that reference a Passenger that does not exist

This is to ensure the sanitization of data that is read from JSON, if the user decides to edit the JSON file directly.


Delete feature

This feature was adapted from AB-3. It allows users to delete Passengers.

Design considerations include being able to delete multiplePassengers with one command, and prevent the deletion of any Passengers that are currently in a Pool. This is done to prevent any accidental deletions of Passengers without either party being informed.

Given below is the Sequence Diagram for interactions within the Logic component for the delete 1 2 command

Interactions inslide the Logic Component for the Delete command

:information_source: Note: The lifeline for DeleteCommandParser and DeleteCommand should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.

From the diagram illustrated above:

  1. LogicManager has its execute() method called when a user enters the "delete 1 2" command.
  2. Object of AddressBookParser class is then accessed, which then subsequently creates DeleteCommandParser class object to help parse the user’s command.
  3. AddressBookParser would then invoke the parse() method of DeleteCommandParser, with parameters 1 2.
  4. DeleteCommandParser invokes the parseDeleteIndex() method of ParserUtil, with the arguments "1 2", which splits the arguments into tokens via whitespace.
  5. ParserUtil self invokes parseIndex() on each token, which is used for parsing single indexes, and returns all the Index objects created to DeleteCommandParser as indexes.
  6. DeleteCommandParser then instantiates a DeleteCommand object with indexes as a parameter. The DeleteCommand object is then returned to LogicManager.
  7. LogicManager would subsequently invoke the execute() method of DeleteCommand, which in turn calls the getFilteredPassengerList() method in Model, to get the current passenger list being shown to the user as lastShownList.
  8. lastShownList is then iterated through to and each passenger is passed to Model via hasPoolWithPassenger(), to check if that passenger is indeed currently being Pooled.
  9. If any Passenger is found to be contained in a Pool, a new CommandException is thrown, informing the user as such.
  10. After checking that it is indeed safe to delete all the Passengers in lastShownList, each Passenger is then deleted in Model via passing it to the deletePassenger() method.
  11. A CommandResult object is then created with a message which includes the names of the Passengers deleted, in lastShownList
  12. Finally, the CommandResult object is returned to LogicManager.