PROJECT: Deliveria

Overview

Deliveria is a desktop application that allows a delivery manager to manage and assign delivery tasks efficiently. While it consists of a Graphical User Interface (GUI) that is user-friendly, Deliveria is optimized for those who prefer to work with a Command Line Interface (CLI) which allows fast management of the delivery tasks in an organisation.

Our team of four students worked on the software for the past two months. We started with the code base of AddressBook Level 3, an open-source JavaFX application for Software Engineering education purposes. Over the past months, we changed the AddressBook to suit the needs of our target users.

We follow a semi-Agile approach, and we have released four milestones on a biweekly release schedule. The latest version is v1.4.

Summary of contributions

  • Major enhancement: added the feature to schedule delivery tasks, with automatic suggestions based on the driver’s existing schedule and task specifications.

    • What it does:

      • Allows the user to automatically schedule a task to be completed by a driver in the future, without causing time clashes with the existing tasks of the driver.

      • Optimizes the driver and time slot allocated such that, the drivers’s schedules are fully utilised, and tasks that deliver to the same address are delivered by the same driver.

    • Justification: This is a core feature of the product. By enabling this feature, we help the managers to better utilise the human resources, save time in planning the task, and deliver the orders with satisfaction.

    • Highlights: The command will automatically suggest the driver and a time for a task, so as to ensure that drivers' schedules are optimised on a company level. At the same time, we are giving the managers the freedom to exercise their own judgement, and override the automatic suggestions.

    • Where to find it:

      • User-facing command: suggest, assign, free

      • Logic-level component: ScheduleOptimizer, Candidate

      • Model-level component: Schedule, EventTime, SchedulingSuggestion

  • Minor enhancement:

    • Implemented a GlobalClock object for the ease of development, testing and debugging:

      • by switching the testEnv in config.json, the user can freeze the time at a specific point, so that the time-based methods have deterministic behaviour

    • Improved the presentation of the driver’s unavailable time in the GUI:

      • instead of showing two connecting time (eg. 1500 - 1600, 1600 - 1700), the program will concatenate the two connecting time and show as one (eg. 1500 - 1700).

  • Code contributed: [RepoSense]

  • Other contributions:

    • Project management:

      • Created the team repo, enabled Continuous Integration, Github Pages Deployment.

      • Implemented code review policy and enabled branch protection rules, and worked on various work flow related chores to maintain a clean commit history.

      • Spearheaded the feature planning (Brainstorming Board, Dev Board).

      • Managed releases v1.1 - v1.4 (4 releases) on Github.

    • Enhancements to existing features:

      • Changed three existing classes to generic classes, ie. EntityManager<T extends Person>, ReadOnlyEntityManager<T extends Person>, and UniqueEntityList<T extends Person>, so as to reduce 600+ lines of repetitive code (Pull Request #95).

    • Documentation:

      • Updated User Guide, Developer Guide (Pull Request #236, #108)

    • Community:

      • Reviewed more than 90% of the team members' pull requests: PR with non-trivial review comments: #91, #102, #115

      • Contributed to forum discussions (Link: 1)

      • Reported bugs and suggestions for other teams in the class (examples: 1, 2)

    • Tools:

      • Written a Makefile to simplify the execution of unit testing and style checks (#62)

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.

Getting Started

Ui
Figure 1. UI

Installing

  1. Ensure you have Java 11 or above installed in your Computer.

  2. Download the latest release here.

  3. Copy the file to the folder you want to use as the home folder for your Deliveria.

  4. Double-click the file to start the app. The GUI should appear in a few seconds.

Scheduling Your First Task

Now you are in! We have prepared a few sample customers and drivers for you to play around.

On a busy day, you might be having many orders coming in, and you want to get your drivers busy delivering them. Let’s go through the process of creating and assigning a task.

  • Create a new task by typing addT g/20 boxes of utensils c/1 dt/22/11/2019 in the command box. You need to substitute 22/11/2019 with the date today. Press Enter to execute the command.

  • You can see the newly created task in the Incomplete Delivery Task box:

adding task
Figure 2. The new task shows up in the Incomplete Tasks box
Take note of the Task ID (i.e. #6), which is used to refer to this task.
  • Execute command suggest 2 t/6, where

    • 2 is the number of hours that you estimate this task will take to complete

    • 6 is the ID of the task that you want to assign

  • You can see Task #6 is assigned to Aloysius Chan at 2:00PM - 4:00PM

suggest task
Figure 3. The task is successfully scheduled
The suggested time slot will either start from now or in the future.
We will optimize the driver and the time based on the drivers' availability and the task. Learn more about the rule in Automatically scheduling a task: suggest
  • If you are not satisfied with the suggestion, you can overwrite it by typing assign force t/6 d/1 at/1500 - 1600, where:

    • 6 is the Task ID

    • 1 is the Driver ID

    • 1500 - 1600 is the time that you want to change to

Learn more about the assign command in Manually assigning a task: assign
  • We now have rescheduled the task to 3-4 pm.

reschedule task
Figure 4. The task is rescheduled
The drivers, by default, works from 9 AM to 9 PM. You cannot assign a time beyond the working hours.

You have created and scheduled your first task! Refer to [Features] for details of each command.

Automatically scheduling a task: suggest

Find the most suitable driver and an available time slot to schedule a task, based on the following rules.

  • If there are drivers who deliver to the same customer as the one in this task, the driver who has the earliest available time slot for this task will be selected.

  • If the above rule fails to select a driver, the system will choose among all drivers, and find the driver who has the earliest available time slot for the task.

Format: suggest HOURS t/TASK_ID

  • The HOURS field is in the format of hh:MM or in decimal format. For example, one and a half hours can either be 1:30 or 1.5.

  • The TASK_ID is a positive integer, and the task must exist in the Incomplete Tasks.

  • Use the assign command if you want to assign the task to a specific driver at a specific time.

  • The suggested time slot will only start from now or in the future.

Examples:

  • suggest 2 t/1
    Find the best driver, using the rules above, who has a two-hour time slot to deliver Task #1.

  • suggest 1:20 t/2
    Find the best driver, using the rules above, who is available for 1 hour and 20 minutes to deliver Task #2.

Manually assigning a task: assign

Assign a driver to a task with a proposed time.
Format: assign [force] t/TASK_ID d/DRIVER_ID at/hMM - hMM

  • You cannot assign a time that clashes with the driver’s existing schedule, or is outside their working hours (9 AM - 9 PM)

  • You cannot propose a time slot that started in the past

  • If there is an earlier time slot that the driver is available, the program will suggest the earlier time slot

  • Use assign force to overwrite a task which already has a driver and a scheduled time

  • The TASK_ID is a positive integer, and the task must exist in the Incomplete Tasks.

  • The DRIVER_ID is a positive integer, and the driver must exist in the driver list.

Examples:

  • assign t/1 d/1 at/900-1200
    Schedule Task #1 from 9 am to 12 pm, and assign it to Driver #1.

  • assign force t/2 d/1 at/1600-1700
    Schedule task #2 from 4 pm to 5 pm, and assign it to driver #1, regardless whether the task is already assigned.

Removing assigned driver from a task: free

Remove the time slot and driver from a task, and free the driver from this time slot in their schedule.
Format: free t/TASK_ID

  • You cannot free a task that has no driver or time slot assigned to it.

Examples:

  • free t/1
    Remove the assigned driver and time slot from the task, and free the driver’s schedule.

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.

Driver-Level Task Scheduling

Design Considerations

  • A Schedule should be a collection of non-overlapping EventTime object, and is always sorted

  • Should be able to notify the user if a better time slot is available, while giving users the liberty to exercise their own judgments

Implementation

Every Driver keeps track of a Schedule class, which is backed by a naturally sorted, TreeSet of EventTime objects.

Before a new EventTime is added to the schedule, the method checks against the TreeSet of EventTime to ensure the addition will not result in overlapping EventTime in the schedule. This operation works in logarithmic time thanks to the tree structure.

In order to better utilise a driver, we implement a method to suggest an earlier alternative time slot in a schedule. When adding a time to a schedule, this method will:

  • Calculate the duration of proposed EventTime

  • Perform a linear greedy search in the schedule, to find the first slot that can fit the duration

Since the schedule guarantees no overlapping EventTime, there is no complication in handling the start and end times.

In order to enforce the optimised scheduling method above, the program will block every assign command that has a suboptimal proposed time, unless the user uses the force argument. Moreover, the assign and free command are the only commands that modify the Driver and EventTime attributes of a Task, so that all drivers will have an optimised schedule, unless force assign is used.

The following activity diagram summarizes the checks happened when user executes an assign command.

AssignActivityDiagram
Figure 5. The Activity Diagram for assign command

After the above checks has passed, assign command will:

  • Set the Driver and EventTime attributes in the Task

  • Add the proposed EventTime to the Driver 's Schedule

Similarly, calling free command will:

  • Remove the existing EventTime from the Driver 's Schedule

  • Set the Driver and EventTime attributes to null

Global-Level Task Scheduling

Design Considerations

  • The global-level scheduler needs to consider the task’s details (eg. the customer and address) and all the drivers' schedules, in order to optimize manpower allocation

  • The scheduler needs to be extendable, ie. easily incorporate new rules

Implementation

The ScheduleOptimizer class is a component in the Logic Layer, that have access to the entire model, in order to gather information about all drivers and tasks. The object instantiates with Model and Task, and is designed to be discarded after use.

It will return a Candidate object, which is a wrapper class of javafx.util.Pair<Driver,Optional<EventTime>>. This can be used by the caller to assign the task to the suggested driver at the suggested time.

It exposes a convenient ScheduleOptimizer#start method, to start optimizing, based on the rules implement in the Optimizer. The start method uses a functional pipeline approach, by piping the Optional output of the rules. It is implemented so that, the next stage of the pipeline will only be triggered when this stage of the pipeline fails to find a candidate (ie. returns an empty Optional).

The Optimizer consists of two rules as of now: ScheduleOptimizer#driverEarliestFit and ScheduleOptimizer#prioritizeSameCustomer. Both methods are nullary functions that return a Optional<Candidate>.

Any new rule just need to follow the same method signature as the existing rules, and be added to the pipeline in the ScheduleOptimizer#start method.

Model component

ModelClassDiagram
Figure 6. Structure of the Model Component

API : Model.java

The Model,

  • stores a UserPref object that represents the user’s preferences.

  • stores the DriverManager, TaskManager, CustomerManager and IdManager

  • exposes unmodifiable ObservableList that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change.

  • does not depend on any of the other three components.