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
inconfig.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>
, andUniqueEntityList<T extends Person>
, so as to reduce 600+ lines of repetitive code (Pull Request #95).
-
-
Documentation:
-
Community:
-
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
Installing
-
Ensure you have Java 11 or above installed in your Computer.
-
Download the latest release here.
-
Copy the file to the folder you want to use as the home folder for your Deliveria.
-
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 substitute22/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:
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
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.
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
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
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
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-overlappingEventTime
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.
After the above checks has passed, assign
command will:
-
Set the
Driver
andEventTime
attributes in theTask
-
Add the proposed
EventTime
to theDriver
'sSchedule
Similarly, calling free
command will:
-
Remove the existing
EventTime
from theDriver
'sSchedule
-
Set the
Driver
andEventTime
attributes tonull
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
API : Model.java
The Model
,
-
stores a
UserPref
object that represents the user’s preferences. -
stores the
DriverManager
,TaskManager
,CustomerManager
andIdManager
-
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.