> {
+Class HiddenLogic #FFFFFF
+}
+
+Class TimetableWindow
+TimetableWindow *-down-> "1" UnscheduleWindow
+UnscheduleWindow --> Logic
+
+
+UnscheduleWindow *-down-> "1" UnscheduledDeliveryJobListPanel
+UnscheduledDeliveryJobListPanel *-down-> "*" DeliveryJobCard
+@enduml
diff --git a/docs/diagrams/UiSequenceDiagramCompleteWindow.puml b/docs/diagrams/UiSequenceDiagramCompleteWindow.puml
new file mode 100644
index 00000000000..6bcfe70be75
--- /dev/null
+++ b/docs/diagrams/UiSequenceDiagramCompleteWindow.puml
@@ -0,0 +1,56 @@
+@startuml
+!include style.puml
+
+box UI
+participant ":MainWindow" as MainWindow UI_COLOR_T1
+participant ":CompleteWindow" as CompleteWindow UI_COLOR_T3
+participant ":UnscheduledDeliveryJobListPanel" UI_COLOR_T2
+participant ":UnscheduledDeliveryJobListPanel" as UnscheduledDeliveryJobListPanel UI_COLOR_T2
+participant ":DeliveryJobCard" as DeliveryJobCard UI_COLOR
+end box
+
+box Logic LOGIC_COLOR
+participant ":Logic" as Logic LOGIC_COLOR_T1
+participant "filteredDeliveryJobList:FilteredList" as FilteredList LOGIC_COLOR_T2
+end box
+
+
+[-> MainWindow : handleCompletedTimetable()
+activate MainWindow
+MainWindow -> CompleteWindow : fillInnerParts()
+activate CompleteWindow
+
+CompleteWindow -> Logic : getCompletedDeliveryJobList()
+activate Logic
+Logic --> CompleteWindow
+
+CompleteWindow -> FilteredList : addListener()
+activate FilteredList
+FilteredList --> CompleteWindow
+deactivate FilteredList
+
+create UnscheduledDeliveryJobListPanel
+CompleteWindow -> UnscheduledDeliveryJobListPanel
+activate UnscheduledDeliveryJobListPanel
+
+create DeliveryJobCard
+UnscheduledDeliveryJobListPanel -> DeliveryJobCard
+activate DeliveryJobCard
+DeliveryJobCard --> UnscheduledDeliveryJobListPanel
+deactivate DeliveryJobCard
+DeliveryJobCard -[hidden]-> UnscheduledDeliveryJobListPanel
+destroy DeliveryJobCard
+
+UnscheduledDeliveryJobListPanel --> CompleteWindow
+deactivate UnscheduledDeliveryJobListPanel
+UnscheduledDeliveryJobListPanel -[hidden]-> CompleteWindow
+destroy UnscheduledDeliveryJobListPanel
+
+CompleteWindow --> MainWindow
+
+[<--MainWindow
+deactivate MainWindow
+
+
+
+@enduml
diff --git a/docs/diagrams/UiSequenceDiagramTimetableWindow.puml b/docs/diagrams/UiSequenceDiagramTimetableWindow.puml
new file mode 100644
index 00000000000..cb44f4f8a43
--- /dev/null
+++ b/docs/diagrams/UiSequenceDiagramTimetableWindow.puml
@@ -0,0 +1,101 @@
+@startuml
+!include style.puml
+
+box UI
+participant ":MainWindow" as MainWindow UI_COLOR_T1
+participant ":TimetableWindow" as TimetableWindow UI_COLOR_T2
+participant "detail:TimetableDetailPanel" as TimetableDetailPanel UI_COLOR_T3
+participant "weekDate:DayOfWeekPanel" as DayOfWeekPanel UI_COLOR_T4
+participant "monthDate:DayOfMonthPanel" as DayOfMonthPanel UI_COLOR
+participant ":WeekJobListPanel" as WeekJobListPanel UI_COLOR_T2
+end box
+
+box Logic LOGIC_COLOR
+participant ":Logic" as Logic LOGIC_COLOR_T4
+participant "filteredDeliveryJobList:FilteredList" as FilteredList LOGIC_COLOR_T2
+end box
+
+[-> MainWindow : handleTimetable()
+
+MainWindow -> TimetableWindow : fillInnerParts()
+
+
+activate TimetableWindow
+
+create TimetableDetailPanel
+TimetableWindow -> TimetableDetailPanel
+activate TimetableDetailPanel
+
+
+TimetableWindow -> FilteredList : addListener()
+activate FilteredList
+FilteredList --> TimetableWindow
+deactivate FilteredList
+
+TimetableDetailPanel -> TimetableDetailPanel : fillInnerParts()
+activate TimetableDetailPanel
+TimetableDetailPanel -> Logic : getFocusDate()
+activate Logic
+Logic --> TimetableDetailPanel : focusDate
+TimetableDetailPanel -> Logic : updateSortedDeliveryJobListByDate()
+Logic --> TimetableDetailPanel
+TimetableDetailPanel -> Logic : setWeekDeliveryJobList(focusDate)
+Logic --> TimetableDetailPanel
+
+
+
+create WeekJobListPanel
+TimetableDetailPanel -> WeekJobListPanel
+
+activate WeekJobListPanel
+
+WeekJobListPanel -> WeekJobListPanel : addAllPlaceholderJobs();
+
+
+activate WeekJobListPanel
+WeekJobListPanel -> Logic : getDayofWeekJob()
+Logic --> WeekJobListPanel
+deactivate Logic
+
+
+
+
+deactivate WeekJobListPanel
+
+create DayOfMonthPanel
+TimetableDetailPanel -> DayOfMonthPanel
+activate DayOfMonthPanel
+DayOfMonthPanel --> TimetableDetailPanel : monthDate
+deactivate DayOfMonthPanel
+DayOfMonthPanel -[hidden]-> TimetableDetailPanel : monthDate
+destroy DayOfMonthPanel
+
+create DayOfWeekPanel
+TimetableDetailPanel -> DayOfWeekPanel
+activate DayOfWeekPanel
+DayOfWeekPanel --> TimetableDetailPanel : weekDate
+deactivate DayOfWeekPanel
+DayOfWeekPanel -[hidden]-> TimetableDetailPanel : weekDate
+destroy DayOfWeekPanel
+
+WeekJobListPanel --> TimetableDetailPanel
+deactivate WeekJobListPanel
+WeekJobListPanel -[hidden]-> TimetableDetailPanel
+destroy WeekJobListPanel
+deactivate TimetableDetailPanel
+
+
+
+
+TimetableDetailPanel --> TimetableWindow : detail
+deactivate TimetableDetailPanel
+TimetableDetailPanel -[hidden]-> TimetableWindow : detail
+destroy TimetableDetailPanel
+
+TimetableWindow --> MainWindow
+deactivate TimetableWindow
+
+[<--MainWindow
+deactivate MainWindow
+
+@enduml
diff --git a/docs/diagrams/UiSequenceDiagramUnscheduledTimetableWindow.puml b/docs/diagrams/UiSequenceDiagramUnscheduledTimetableWindow.puml
new file mode 100644
index 00000000000..d0780acb152
--- /dev/null
+++ b/docs/diagrams/UiSequenceDiagramUnscheduledTimetableWindow.puml
@@ -0,0 +1,56 @@
+@startuml
+!include style.puml
+
+box UI
+participant ":MainWindow" as MainWindow UI_COLOR_T1
+participant ":UnscheduleWindow" as UnscheduleWindow UI_COLOR_T3
+participant ":UnscheduledDeliveryJobListPanel" UI_COLOR_T2
+participant ":UnscheduledDeliveryJobListPanel" as UnscheduledDeliveryJobListPanel UI_COLOR_T2
+participant ":DeliveryJobCard" as DeliveryJobCard UI_COLOR
+end box
+
+box Logic LOGIC_COLOR
+participant ":Logic" as Logic LOGIC_COLOR_T1
+participant "filteredDeliveryJobList:FilteredList" as FilteredList LOGIC_COLOR_T2
+end box
+
+
+[-> MainWindow : handleUnscheduledTimetable()
+activate MainWindow
+MainWindow -> UnscheduleWindow : fillInnerParts()
+activate UnscheduleWindow
+
+UnscheduleWindow -> Logic : getUnscheduledDeliveryJobList()
+activate Logic
+Logic --> UnscheduleWindow
+
+UnscheduleWindow -> FilteredList : addListener()
+activate FilteredList
+FilteredList --> UnscheduleWindow
+deactivate FilteredList
+
+create UnscheduledDeliveryJobListPanel
+UnscheduleWindow -> UnscheduledDeliveryJobListPanel
+activate UnscheduledDeliveryJobListPanel
+
+create DeliveryJobCard
+UnscheduledDeliveryJobListPanel -> DeliveryJobCard
+activate DeliveryJobCard
+DeliveryJobCard --> UnscheduledDeliveryJobListPanel
+deactivate DeliveryJobCard
+DeliveryJobCard -[hidden]-> UnscheduledDeliveryJobListPanel
+destroy DeliveryJobCard
+
+UnscheduledDeliveryJobListPanel --> UnscheduleWindow
+deactivate UnscheduledDeliveryJobListPanel
+UnscheduledDeliveryJobListPanel -[hidden]-> UnscheduleWindow
+destroy UnscheduledDeliveryJobListPanel
+
+UnscheduleWindow --> MainWindow
+
+[<--MainWindow
+deactivate MainWindow
+
+
+
+@enduml
diff --git a/docs/diagrams/style.puml b/docs/diagrams/style.puml
index fad8b0adeaa..cd05e001824 100644
--- a/docs/diagrams/style.puml
+++ b/docs/diagrams/style.puml
@@ -12,6 +12,7 @@
!define UI_COLOR_T2 #3FC71B
!define UI_COLOR_T3 #166800
!define UI_COLOR_T4 #0E4100
+!define UI_COLOR_T5 #00FF00
!define LOGIC_COLOR #3333C4
!define LOGIC_COLOR_T1 #C8C8FA
diff --git a/docs/empty.csv b/docs/empty.csv
new file mode 100644
index 00000000000..ad239874777
--- /dev/null
+++ b/docs/empty.csv
@@ -0,0 +1,2 @@
+,
+,
diff --git a/docs/images/Addjob.png b/docs/images/Addjob.png
new file mode 100644
index 00000000000..673e9d555b9
Binary files /dev/null and b/docs/images/Addjob.png differ
diff --git a/docs/images/Checked.png b/docs/images/Checked.png
new file mode 100644
index 00000000000..81f546d3873
Binary files /dev/null and b/docs/images/Checked.png differ
diff --git a/docs/images/Completedjobs.png b/docs/images/Completedjobs.png
new file mode 100644
index 00000000000..5598ed0fa59
Binary files /dev/null and b/docs/images/Completedjobs.png differ
diff --git a/docs/images/DeleteDeliveryJobSequenceDiagram.png b/docs/images/DeleteDeliveryJobSequenceDiagram.png
new file mode 100644
index 00000000000..42a0089368d
Binary files /dev/null and b/docs/images/DeleteDeliveryJobSequenceDiagram.png differ
diff --git a/docs/images/Dukedeliveryaddressbook.png b/docs/images/Dukedeliveryaddressbook.png
new file mode 100644
index 00000000000..e24631ae85f
Binary files /dev/null and b/docs/images/Dukedeliveryaddressbook.png differ
diff --git a/docs/images/EditDeliveryJobSequenceDiagram.png b/docs/images/EditDeliveryJobSequenceDiagram.png
new file mode 100644
index 00000000000..00210907a72
Binary files /dev/null and b/docs/images/EditDeliveryJobSequenceDiagram.png differ
diff --git a/docs/images/Inputcommands.png b/docs/images/Inputcommands.png
new file mode 100644
index 00000000000..7ca2bc0c178
Binary files /dev/null and b/docs/images/Inputcommands.png differ
diff --git a/docs/images/ModelClassDiagram.png b/docs/images/ModelClassDiagram.png
index 04070af60d8..66cf0446fcf 100644
Binary files a/docs/images/ModelClassDiagram.png and b/docs/images/ModelClassDiagram.png differ
diff --git a/docs/images/NotificationSequenceDiagram.png b/docs/images/NotificationSequenceDiagram.png
new file mode 100644
index 00000000000..82de9e079f8
Binary files /dev/null and b/docs/images/NotificationSequenceDiagram.png differ
diff --git a/docs/images/Statistics.png b/docs/images/Statistics.png
new file mode 100644
index 00000000000..1c4fe64b3d2
Binary files /dev/null and b/docs/images/Statistics.png differ
diff --git a/docs/images/StatisticsSequenceDiagram.png b/docs/images/StatisticsSequenceDiagram.png
new file mode 100644
index 00000000000..87e439d0c6a
Binary files /dev/null and b/docs/images/StatisticsSequenceDiagram.png differ
diff --git a/docs/images/StorageClassDiagram.png b/docs/images/StorageClassDiagram.png
index 2533a5c1af0..a7ab7ec8057 100644
Binary files a/docs/images/StorageClassDiagram.png and b/docs/images/StorageClassDiagram.png differ
diff --git a/docs/images/Timetable.png b/docs/images/Timetable.png
new file mode 100644
index 00000000000..0c43a9c1fea
Binary files /dev/null and b/docs/images/Timetable.png differ
diff --git a/docs/images/TimetableCompletedSequenceDiagram.png b/docs/images/TimetableCompletedSequenceDiagram.png
new file mode 100644
index 00000000000..8135e36b84e
Binary files /dev/null and b/docs/images/TimetableCompletedSequenceDiagram.png differ
diff --git a/docs/images/TimetableDateSequenceDiagram.png b/docs/images/TimetableDateSequenceDiagram.png
new file mode 100644
index 00000000000..5493b22ca3c
Binary files /dev/null and b/docs/images/TimetableDateSequenceDiagram.png differ
diff --git a/docs/images/TimetableSequenceDiagram.png b/docs/images/TimetableSequenceDiagram.png
new file mode 100644
index 00000000000..fd5b59e94a2
Binary files /dev/null and b/docs/images/TimetableSequenceDiagram.png differ
diff --git a/docs/images/TimetableUnscheduledSequenceDiagram.png b/docs/images/TimetableUnscheduledSequenceDiagram.png
new file mode 100644
index 00000000000..54fa458b6aa
Binary files /dev/null and b/docs/images/TimetableUnscheduledSequenceDiagram.png differ
diff --git a/docs/images/Ui.png b/docs/images/Ui.png
index 5bd77847aa2..5eb4ac2b27e 100644
Binary files a/docs/images/Ui.png and b/docs/images/Ui.png differ
diff --git a/docs/images/UiClassDiagram.png b/docs/images/UiClassDiagram.png
index 785e04dbab4..1e6c2b7e8b0 100644
Binary files a/docs/images/UiClassDiagram.png and b/docs/images/UiClassDiagram.png differ
diff --git a/docs/images/UiClassDiagramCompleteWindow.png b/docs/images/UiClassDiagramCompleteWindow.png
new file mode 100644
index 00000000000..a49470c00d2
Binary files /dev/null and b/docs/images/UiClassDiagramCompleteWindow.png differ
diff --git a/docs/images/UiClassDiagramCreateJob.png b/docs/images/UiClassDiagramCreateJob.png
new file mode 100644
index 00000000000..7d39ed8960f
Binary files /dev/null and b/docs/images/UiClassDiagramCreateJob.png differ
diff --git a/docs/images/UiClassDiagramMainWindow.png b/docs/images/UiClassDiagramMainWindow.png
new file mode 100644
index 00000000000..b8508cdd240
Binary files /dev/null and b/docs/images/UiClassDiagramMainWindow.png differ
diff --git a/docs/images/UiClassDiagramTimetableWindow.png b/docs/images/UiClassDiagramTimetableWindow.png
new file mode 100644
index 00000000000..3cd843d7dcd
Binary files /dev/null and b/docs/images/UiClassDiagramTimetableWindow.png differ
diff --git a/docs/images/UiClassDiagramUnscheduleWindow.png b/docs/images/UiClassDiagramUnscheduleWindow.png
new file mode 100644
index 00000000000..65309a79022
Binary files /dev/null and b/docs/images/UiClassDiagramUnscheduleWindow.png differ
diff --git a/docs/images/UiSequenceDiagramCompleteWindow.png b/docs/images/UiSequenceDiagramCompleteWindow.png
new file mode 100644
index 00000000000..7adf5f461c2
Binary files /dev/null and b/docs/images/UiSequenceDiagramCompleteWindow.png differ
diff --git a/docs/images/UiSequenceDiagramTimetableWindow.png b/docs/images/UiSequenceDiagramTimetableWindow.png
new file mode 100644
index 00000000000..5bf7e8bb818
Binary files /dev/null and b/docs/images/UiSequenceDiagramTimetableWindow.png differ
diff --git a/docs/images/UiSequenceDiagramUnscheduledTimetableWindow.png b/docs/images/UiSequenceDiagramUnscheduledTimetableWindow.png
new file mode 100644
index 00000000000..a37a6c2d84a
Binary files /dev/null and b/docs/images/UiSequenceDiagramUnscheduledTimetableWindow.png differ
diff --git a/docs/images/Unscheduledjobs.png b/docs/images/Unscheduledjobs.png
new file mode 100644
index 00000000000..936242195d5
Binary files /dev/null and b/docs/images/Unscheduledjobs.png differ
diff --git a/docs/images/addressbookEntry.png b/docs/images/addressbookEntry.png
new file mode 100644
index 00000000000..0f1d314c9be
Binary files /dev/null and b/docs/images/addressbookEntry.png differ
diff --git a/docs/images/c0j0s.png b/docs/images/c0j0s.png
new file mode 100644
index 00000000000..28b4474c57f
Binary files /dev/null and b/docs/images/c0j0s.png differ
diff --git a/docs/images/chinjunan.png b/docs/images/chinjunan.png
new file mode 100644
index 00000000000..445d29f7d61
Binary files /dev/null and b/docs/images/chinjunan.png differ
diff --git a/docs/images/comJob.png b/docs/images/comJob.png
new file mode 100644
index 00000000000..5e17080593a
Binary files /dev/null and b/docs/images/comJob.png differ
diff --git a/docs/images/completebutton.png b/docs/images/completebutton.png
new file mode 100644
index 00000000000..4616e120348
Binary files /dev/null and b/docs/images/completebutton.png differ
diff --git a/docs/images/delJob.png b/docs/images/delJob.png
new file mode 100644
index 00000000000..db3fc54ab4e
Binary files /dev/null and b/docs/images/delJob.png differ
diff --git a/docs/images/deletebutton.png b/docs/images/deletebutton.png
new file mode 100644
index 00000000000..4435b79df53
Binary files /dev/null and b/docs/images/deletebutton.png differ
diff --git a/docs/images/dohaduong.png b/docs/images/dohaduong.png
new file mode 100644
index 00000000000..cccf989e5cb
Binary files /dev/null and b/docs/images/dohaduong.png differ
diff --git a/docs/images/editJob.png b/docs/images/editJob.png
new file mode 100644
index 00000000000..d03f28e6886
Binary files /dev/null and b/docs/images/editJob.png differ
diff --git a/docs/images/helpMessage.png b/docs/images/helpMessage.png
index b1f70470137..7d5c94967dd 100644
Binary files a/docs/images/helpMessage.png and b/docs/images/helpMessage.png differ
diff --git a/docs/images/hideDetail.png b/docs/images/hideDetail.png
new file mode 100644
index 00000000000..4e9e313d615
Binary files /dev/null and b/docs/images/hideDetail.png differ
diff --git a/docs/images/johndoe.png b/docs/images/johndoe.png
deleted file mode 100644
index 1ce7ce16dc8..00000000000
Binary files a/docs/images/johndoe.png and /dev/null differ
diff --git a/docs/images/listJob.png b/docs/images/listJob.png
new file mode 100644
index 00000000000..deb46f8ecae
Binary files /dev/null and b/docs/images/listJob.png differ
diff --git a/docs/images/listJobSortFilter.png b/docs/images/listJobSortFilter.png
new file mode 100644
index 00000000000..87a9fc14acc
Binary files /dev/null and b/docs/images/listJobSortFilter.png differ
diff --git a/docs/images/penbutton.png b/docs/images/penbutton.png
new file mode 100644
index 00000000000..6140ff078c4
Binary files /dev/null and b/docs/images/penbutton.png differ
diff --git a/docs/images/reminderListWindow.png b/docs/images/reminderListWindow.png
new file mode 100644
index 00000000000..d39270b3b4f
Binary files /dev/null and b/docs/images/reminderListWindow.png differ
diff --git a/docs/images/reminderNotification.png b/docs/images/reminderNotification.png
new file mode 100644
index 00000000000..794a9d85797
Binary files /dev/null and b/docs/images/reminderNotification.png differ
diff --git a/docs/images/scheduleNotification.png b/docs/images/scheduleNotification.png
new file mode 100644
index 00000000000..4806cf9275b
Binary files /dev/null and b/docs/images/scheduleNotification.png differ
diff --git a/docs/images/unhideDetail.png b/docs/images/unhideDetail.png
new file mode 100644
index 00000000000..a0a5f18a5d6
Binary files /dev/null and b/docs/images/unhideDetail.png differ
diff --git a/docs/images/zhuleyao.png b/docs/images/zhuleyao.png
new file mode 100644
index 00000000000..0ca1b6d62f6
Binary files /dev/null and b/docs/images/zhuleyao.png differ
diff --git a/docs/images/zuohui48.png b/docs/images/zuohui48.png
new file mode 100644
index 00000000000..6043bd279d0
Binary files /dev/null and b/docs/images/zuohui48.png differ
diff --git a/docs/index.md b/docs/index.md
index 7601dbaad0d..b389970ed13 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,17 +1,23 @@
---
layout: page
-title: AddressBook Level-3
+title: Duke Driver
---
-[](https://github.com/se-edu/addressbook-level3/actions)
-[](https://codecov.io/gh/se-edu/addressbook-level3)
+
+[](https://github.com/AY2223S2-CS2103-F11-2/tp/actions)
+

-**AddressBook is a desktop application for managing your contact details.** While it has a GUI, most of the user interactions happen using a CLI (Command Line Interface).
+**Duke Driver is a desktop application for
+managing your delivery job details.**
+
+On top of that, you are able to manage your client details, derive statistics of your deliveries and view a timetable for your deliveries!
+
+
+* If you are interested in using Duke Driver, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start).
+* If you are interested about developing Duke Driver, the [**Developer Guide**](DeveloperGuide.html) is a good place to start.
-* If you are interested in using AddressBook, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start).
-* If you are interested about developing AddressBook, the [**Developer Guide**](DeveloperGuide.html) is a good place to start.
**Acknowledgements**
diff --git a/docs/missingelements.csv b/docs/missingelements.csv
new file mode 100644
index 00000000000..227eba55112
--- /dev/null
+++ b/docs/missingelements.csv
@@ -0,0 +1,2 @@
+R,S,,,,,
+JAB456,JUI789,,,,,
diff --git a/docs/some_na.csv b/docs/some_na.csv
new file mode 100644
index 00000000000..39df2a0f813
--- /dev/null
+++ b/docs/some_na.csv
@@ -0,0 +1,4 @@
+Recipient ID,Sender ID,Delivery date,Delivery slot,Price,Recipient ID,Recipient Name,Recipient Phone,Recipient Email,Recipient Address,Recipient Tag,Sender ID,Sender Name,Sender Phone,Sender Email,Sender Address,Sender Tag
+JUS456,JUL789,2023-01-01,1,5.00,JUS456,Justin,23456789,jus@gmail.com,Sg st 42,Customer,JUL789,Julian,34567890,jul@gmail.com,Sg st 43,na
+JUS456,JUL789,na,na,5.00,JUS456,Justin,23456789,jus@gmail.com,Sg st 42,Customer,JUL789,Julian,34567890,jul@gmail.com,Sg st 43,Customer
+JUS456,JUL789,2023-01-03,1,5.00,JUS456,Justin,23456789,jus@gmail.com,Sg st 42,Customer,JUL789,Julian,34567890,jul@gmail.com,Sg st 43,Customer
diff --git a/docs/team/c0j0s.md b/docs/team/c0j0s.md
new file mode 100644
index 00000000000..701a63fbcb6
--- /dev/null
+++ b/docs/team/c0j0s.md
@@ -0,0 +1,82 @@
+---
+layout: page
+title: c0j0s's Project Portfolio Page
+---
+
+### Project: Duke Driver
+
+Duke Driver - a delivery tasking and planning application used by delivery personnel from DUKE pte ltd. Duke Driver provides bulk management and personalised job listing for drivers.
+
+Given below are my contributions to the project.
+
+* **New Feature**: Added Delivery Job System Storage.
+ * What is does: Handle saving and loading of delivery job list from data files.
+ * Justification: This feature allow delivery job data to be saved in a separate data file to avoid congesting the address book.
+ * Highlights: Largely inline with address book storage structure with modification to json adapted classes to fit our custom delivery job model.
+ * Credits: Address book storage.
+
+* **New Feature**: Delivery Job List and Detail Pane in MainWindow.
+ * What is does: Display a list of pending delivery jobs for user. Able to view the job detail when selected.
+ * Justification: The user needs to see all the available jobs and its details.
+ * Highlights: User can select to view the job detail by mouse or arrow keys.
+ * Credits: MainWindow.
+
+* **New Feature**: List Delivery Job Command.
+ * What is does: List all the available jobs.
+ * Justification: Core function.
+ * Highlights: N.A.
+ * Credits: ListCommand.
+
+* **New Feature**: Add Delivery Job Command.
+ * What is does: Adds a new delivery jobs.
+ * Justification: Core function.
+ * Highlights: Support GUI mode.
+ * Credits: AddCommand.
+
+* **New Feature**: Delete Delivery Job Command.
+ * What is does: Delete a selected jobs.
+ * Justification: Core function.
+ * Highlights: User can select and delete a job using `del` key from the MainWindow.
+ * Credits: DeleteCommand.
+
+* **New Feature**: Edit Delivery Job Command.
+ * What is does: Edit a selected jobs.
+ * Justification: Core function.
+ * Highlights: Able to select jobs using list index or job id. Support GUI mode.
+ * Credits: EditCommand.
+
+* **New Feature**: Find Delivery Job Command.
+ * What is does: Find a selected jobs.
+ * Justification: Core function.
+ * Highlights: All attributes of `DeliveryJob` can be use as a command option for searching.
+ * Credits: FindCommand.
+
+* **New Feature**: Sort and Filter Job List.
+ * What is does: Order job list.
+ * Justification: Allows user to filter out completed or sort by high earning jobs.
+ * Highlights: Support both ascending and descending mode.
+
+* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=c0j0s&breakdown=true)
+
+* **Project management**:
+ * Setting up the GitHub team org/repo
+ * Setup project dashboard.
+ * Setup project milestones.
+ * Add cache support for github actions.
+
+* **Enhancements to existing features**:
+ * Expandable result display box in contact and main window.
+ * Command grouping support to control specific command groups in desired command locations.
+
+* **Documentation**:
+ * User Guide:
+ * Added documentation for the delivery tasking management system features `list`, `add job`, `edit job`, `delete job` and `find job`.
+ * Developer Guide:
+ * Update diagram for `UI Componets`, `Model` and `Storage`.
+ * Added use case for `list delivery job detail`, `delete job`, `edit job` and `find job`.
+ * Added implementation details foe delivery job system in developer guide.
+ * Added manual test cases for delivery jobs.
+
+* **Community**:
+ * Review PRs.
+ * Forum discussions.
diff --git a/docs/team/chinjunan.md b/docs/team/chinjunan.md
new file mode 100644
index 00000000000..e27722c4e5e
--- /dev/null
+++ b/docs/team/chinjunan.md
@@ -0,0 +1,43 @@
+---
+layout: page
+title: Chin Jun An's Project Portfolio Page
+---
+
+### Project: Duke Delivery
+
+Duke Driver - a delivery tasking and planning application for delivery personnel. Duke Driver provides bulk management and personalised job listing for drivers. The user can interact with it using both CLI and GUI. The GUI is created with JavaFX. It is written in Java and has about 10 kLoC.
+
+Given below are my contributions to the project.
+
+* **New Feature**: Added notification function.
+ * What it does: Shows pop up notifications when app starts up, or when certain events/deadline has passed
+ * Justification: This feature is implemented to alert the user of any events from the app. It is used in conjunction with the reminder and scheduling function of the app
+ * Highlights: It was challenging to implement this function as JavaFx did not have any native notification feature. Hence, I had to find an external library that did so. I decided to used ControlFX, an open source project for JavaFX that serves to provide high quality UI controls on top of the existing JavaFX distribution. Through abstraction, this feature can be used by other portions of the applications (i.e. reminders, scheduling, etc). Hence, the implementation extended the functionalities and benefits of the application.
+ * Credits: ControlFx library, Genuine Coder YouTube Channel on implementing the ControlFX notification feature.
+
+* **New Feature**: Added reminder function.
+ * What it does: Allows users to add reminders into the address book. This function is used in conjunction with the notification function, to fire a pop-up notification when an upcoming deadline/schedule is approaching
+ * Justification: This feature helps users with busy schedules to remind them of certain task they might have forgotten to do in the upcoming future. Essentially, it reduces the load of the shoulders of the users in remembering upcoming task/schedules.
+ * Highlights: This feature required an addition to the data structure of the address book (an additional list to record reminders). This led to additional classes that involved managing the storage and reading from local memory and hard disk. Fortunately, not much have to be done as it was similar to existing codes. Further improvement to this feature involves running process threads to fire notifications while the app is running in the background.
+ * Credits: Existing codes from AB3 which involved the models and storage functionality of the app.
+
+* **New Feature**: Added schedule notification function.
+ * What it does: Notifies users of upcoming and current jobs that have been scheduled.
+ * Justification: This feature allows users to keep up with their busy schedule by being notified of upcoming and current jobs. The user is prepared by being reminded every 20 minutes before the next timetable slot.
+ * Highlights: This feature worked in conjunction with the timetable feature done by fellow programmer, Ha Duong. It required the extraction of the daily job listing that have been scheduled for that day. Also, with the help of the notification abstraction, creating this feature went really smooth.
+ * Credits: Ha Duong for her implementation of the timetable feature, and the notification function.
+
+* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=chinjunan&breakdown=true&sort=groupTitle%20dsc&sortWithin=title&since=2023-02-17&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other)
+
+* **Project management**:
+ * Managed releases v1.3 - v1.4 (2 releases) on GitHub
+
+* **Enhancements to existing features**:
+ * Modified existing storage and model to handle storing reminders in the app.
+
+* **Documentation**:
+ * User Guide:
+ * Added documentation for the usage of the add_reminder, delete_reminder, list_reminder features (PR #92)
+ * Developer Guide:
+ * Added implementation details of the notification function. (PR #92)
+ * Added implementation details of the reminder function. (PR #92)
diff --git a/docs/team/dohaduong.md b/docs/team/dohaduong.md
new file mode 100644
index 00000000000..c503637a8eb
--- /dev/null
+++ b/docs/team/dohaduong.md
@@ -0,0 +1,61 @@
+---
+layout: page
+title: Do Ha Duong's Project Portfolio Page
+---
+
+### Project: Duke Driver
+
+Duke Driver is a task and contact management app that aims to help busy delivery men. It is built by implementing CLI and GUI, helping to enhance users' experience.
+
+Given below are my contributions to the project.
+
+* **New Feature**: Added the ability to view timetable of specific week
+ * What it does: Displays timetable of the week specified by users using commands or using GUI mode (clicks on buttons).
+ * Justification: This feature is implemented to help users to structure their plans in the day - they can view upcoming tasks more easily. It is linked with the delivery job list in the app.
+ * Highlight: This feature was challenging at first to implement as I have to decide on the design and GUI of the timetable myself - JavaFX did not have any integrated timetable format. Moreover, as this enhancement is directly linked with job list, it could affect and requires change in existing commands and commands to be added in the future.
+
+* **New Feature**: Added the ability to view list of completed jobs and jobs with invalid date and/or slot
+ * What it does: Allows users to view list of completed jobs and jobs with invalid date and/or slot using the corresponding commands or click on buttons (GUI).
+ * Justification: This features improves the product and enhances the timetable feature significantly, as completed or jobs with invalid dates/slots are not shown in the Timetable. Thus, users can refer to this feature instead if they want to view the mentioned job lists.
+ * Highlight: This feature is directly linked with job list, thus changes made in the existing functions could affect the features directly. Moreover, the implementation also required changes to the existing commands.
+
+* **New Feature**: Added the ability to add jobs with empty delivery date and/or slot
+ * What it does: Allows users to add jobs without specifying its delivery date and slot (making delivery date and slot optional), making the `add_job` command much shorter.
+ * Justification: This features improves the product as the `add_job` command is quite long (with compulsory delivery date and slot), and users may want to add jobs without delivery date and slot. Thus, the app should allow optional delivery date and timeslot.
+ * Highlight: This feature is directly linked with job list and with the existing command/functions, thus it required an in-depth analysis of design alternatives and changes to existing commands.
+
+
+* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=dohaduong&breakdown=true)
+
+* **Project management**:
+ * Managed releases v1.2 and v1.3.trial (2 releases) on GitHub
+ * Update release v1.3 with description on GitHub
+ * Create tags and labels for issues/PRs on GitHub
+
+* **Enhancements to existing features**:
+ * Wrote tests for existing features, such as: all Timetable commands, reminder commands (`add_reminder`, `delete_reminder`, `list_reminder`), jobs commands (`com_job/uncom_job`, `delete_job`,..), etc. (PR #189, #260)
+ * Added and updated JavaDoc comments for public methods (PR #165)
+ * Updated to make feedbacks and command instructions clearer to users (provide examples on how to use the commands in the app) (PR #194)
+ * Added ability to navigate to Help Window from different windows (PR #189)
+
+* **Documentation**:
+ * User Guide:
+ * Added _How to use this Guide?_ and _Windows and Features Overview_ sections (PR #253)
+ * Added documentation for Timetable commands (PR #184)
+ * Added and updated documentation for Delivery Job commands (PR #184, #253)
+ * Added documentation for Job Detail (UI) in Main Window (PR #254)
+ * Added examples for commands in UG and summary table (PR #198, #254)
+ * Re-format, touch up, fix minor bugs and finalize UG (PR #200, #205, #260)
+
+ * Developer Guide:
+ * Added implementation details and diagrams for `UI component/Timetable Window`, `Logic Component`, `Timetable` and `delete_job` (PR #128, #270, #276)
+ * Added user stories for timetable, reminder; use cases for timetable and glossary (PR #20, #23, #34, #270)
+ * Added Appendix: Effort (PR #270)
+
+* **Community**:
+ * PRs reviewed (with non-trivial review comments): example: #180, #156, #264,..
+ * Identified bugs in commands and brought up discussion among the group in weekly meeting
+ * Contributed to forum discussions (examples: #24)
+
+
+
diff --git a/docs/team/johndoe.md b/docs/team/johndoe.md
deleted file mode 100644
index 773a07794e2..00000000000
--- a/docs/team/johndoe.md
+++ /dev/null
@@ -1,46 +0,0 @@
----
-layout: page
-title: John Doe's Project Portfolio Page
----
-
-### Project: AddressBook Level 3
-
-AddressBook - Level 3 is a desktop address book application used for teaching Software Engineering principles. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC.
-
-Given below are my contributions to the project.
-
-* **New Feature**: Added the ability to undo/redo previous commands.
- * What it does: allows the user to undo all previous commands one at a time. Preceding undo commands can be reversed by using the redo command.
- * Justification: This feature improves the product significantly because a user can make mistakes in commands and the app should provide a convenient way to rectify them.
- * Highlights: This enhancement affects existing commands and commands to be added in future. It required an in-depth analysis of design alternatives. The implementation too was challenging as it required changes to existing commands.
- * Credits: *{mention here if you reused any code/ideas from elsewhere or if a third-party library is heavily used in the feature so that a reader can make a more accurate judgement of how much effort went into the feature}*
-
-* **New Feature**: Added a history command that allows the user to navigate to previous commands using up/down keys.
-
-* **Code contributed**: [RepoSense link]()
-
-* **Project management**:
- * Managed releases `v1.3` - `v1.5rc` (3 releases) on GitHub
-
-* **Enhancements to existing features**:
- * Updated the GUI color scheme (Pull requests [\#33](), [\#34]())
- * Wrote additional tests for existing features to increase coverage from 88% to 92% (Pull requests [\#36](), [\#38]())
-
-* **Documentation**:
- * User Guide:
- * Added documentation for the features `delete` and `find` [\#72]()
- * Did cosmetic tweaks to existing documentation of features `clear`, `exit`: [\#74]()
- * Developer Guide:
- * Added implementation details of the `delete` feature.
-
-* **Community**:
- * PRs reviewed (with non-trivial review comments): [\#12](), [\#32](), [\#19](), [\#42]()
- * Contributed to forum discussions (examples: [1](), [2](), [3](), [4]())
- * Reported bugs and suggestions for other teams in the class (examples: [1](), [2](), [3]())
- * Some parts of the history feature I added was adopted by several other class mates ([1](), [2]())
-
-* **Tools**:
- * Integrated a third party library (Natty) to the project ([\#42]())
- * Integrated a new Github plugin (CircleCI) to the team repo
-
-* _{you can add/remove categories in the list above}_
diff --git a/docs/team/zhuleyao.md b/docs/team/zhuleyao.md
new file mode 100644
index 00000000000..4e3e3a9119d
--- /dev/null
+++ b/docs/team/zhuleyao.md
@@ -0,0 +1,61 @@
+ ---
+layout: page
+title: Zhu Le Yao's Project Portfolio Page
+---
+
+### Project: Duke Driver
+
+Duke Driver - A contact and job management app to aid delivery drivers in better managing their jobs and customer information.
+
+Given below are my contributions to the project.
+
+* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=zhuleyao&breakdown=true&sort=groupTitle%20dsc&sortWithin=title&since=2023-02-17&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other)
+
+* **Enhancements implemented**:
+ * Partially implemented backend of data management.
+ * Implemented mass import delivery jobs and customer information function.
+
+* **Contributions to the UG**:
+ * Features related to delivery job
+ * Add job
+ * Mass import job
+ * Edit job
+ * List job
+ * Find job
+ * Delete job
+ * Mark job as completed or uncompleted
+ * Add images and alternative access methods for timetable and statistics features
+ * Update Command Summary with all of the above
+
+* **Contributions to the DG**:
+ * User stories
+ * Use cases
+ * Instructions for manual testing
+
+* **Contributions to team-based tasks**:
+ * Review/mentoring contributions:
+ * Links to PRs reviewed:
+ * https://github.com/AY2223S2-CS2103-F11-2/tp/pull/23
+ * https://github.com/AY2223S2-CS2103-F11-2/tp/pull/33
+ * https://github.com/AY2223S2-CS2103-F11-2/tp/pull/34
+ * https://github.com/AY2223S2-CS2103-F11-2/tp/pull/57
+ * https://github.com/AY2223S2-CS2103-F11-2/tp/pull/180
+ * https://github.com/AY2223S2-CS2103-F11-2/tp/pull/198
+ * https://github.com/AY2223S2-CS2103-F11-2/tp/pull/205
+ * Other ways:
+ * Submitted jar file for PE-D
+ * Help with UG and supplementary images
+ * Contributions beyond the project team:
+ * 10 Bug reported in PE-D:
+ * Feature flaws:
+ * Unused tag function in customer details
+ * List UI not intuitive
+ * List sort case sensitivity
+ * Set tier is customer information but not implemented
+ * Help guide points to address book
+ * Functionality bug:
+ * 3 commands showing up as unknown commands
+ * Edit order feature not working for quantity parameter
+ * Documentation bug:
+ * Incomplete user guide for tutorial section
+
diff --git a/docs/team/zuohui48.md b/docs/team/zuohui48.md
new file mode 100644
index 00000000000..c18346f92bf
--- /dev/null
+++ b/docs/team/zuohui48.md
@@ -0,0 +1,33 @@
+---
+layout: page
+title: Chen Zuo Hui's Project Portfolio Page
+---
+
+### Project: Duke Driver
+
+Duke Driver - a delivery tasking and planning application used by delivery drivers. The user interacts with it using a CLI, and it has a GUI created with JavaFX.
+
+Given below are my contributions to the project.
+
+* **New Feature**: Statistics function
+ * What it does: Shows a list of summary statistics of delivery jobs
+ * Justification: This feature is implemented to allow users to view different statistics about the jobs that they have added
+
+
+* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=zuohui&breakdown=true)
+
+
+* **Documentation**:
+ * User Guide:
+ * Added documentation for the usage of statistics features (PR #130)
+ * Fix documentation bugs
+ * Developer Guide:
+ * Added implementation details and diagrams for statistics component (PR #190)
+ * Fix documentation bugs
+ * Contributions beyond the project team:
+ * Reported 13 bugs for PE-D
+
+* **Team-Based Tasks**:
+ * Brainstorm and add user stories to Duke Driver
+ * Brainstorm product name
+
diff --git a/docs/testimportfile.csv b/docs/testimportfile.csv
new file mode 100644
index 00000000000..51ee10f08e2
--- /dev/null
+++ b/docs/testimportfile.csv
@@ -0,0 +1,4 @@
+Recipient ID,Sender ID,Delivery date,Delivery slot,Price,Recipient ID,Recipient Name,Recipient Phone,Recipient Email,Recipient Address,Recipient Tag,Sender ID,Sender Name,Sender Phone,Sender Email,Sender Address,Sender Tag
+JUS456,JUL789,2023-01-01,1,5.00,JUS456,Justin,23456789,jus@gmail.com,Sg st 42,Customer,JUL789,Julian,34567890,jul@gmail.com,Sg st 43,na
+JUS456,JUL789,2023-01-02,1,5.00,JUS456,Justin,23456789,jus@gmail.com,Sg st 42,Customer,JUL789,Julian,34567890,jul@gmail.com,Sg st 43,Customer
+JUS456,JUL789,2023-01-03,1,5.00,JUS456,Justin,23456789,jus@gmail.com,Sg st 42,Customer,JUL789,Julian,34567890,jul@gmail.com,Sg st 43,Customer
diff --git a/fileEmpty.csv b/fileEmpty.csv
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/src/main/java/seedu/address/AppParameters.java b/src/main/java/seedu/address/AppParameters.java
index ab552c398f3..56c76c7f73f 100644
--- a/src/main/java/seedu/address/AppParameters.java
+++ b/src/main/java/seedu/address/AppParameters.java
@@ -18,14 +18,6 @@ public class AppParameters {
private Path configPath;
- public Path getConfigPath() {
- return configPath;
- }
-
- public void setConfigPath(Path configPath) {
- this.configPath = configPath;
- }
-
/**
* Parses the application command-line parameters.
*/
@@ -43,6 +35,14 @@ public static AppParameters parse(Application.Parameters parameters) {
return appParameters;
}
+ public Path getConfigPath() {
+ return configPath;
+ }
+
+ public void setConfigPath(Path configPath) {
+ this.configPath = configPath;
+ }
+
@Override
public boolean equals(Object other) {
if (other == this) {
diff --git a/src/main/java/seedu/address/Main.java b/src/main/java/seedu/address/Main.java
index 052a5068631..01c1f20205d 100644
--- a/src/main/java/seedu/address/Main.java
+++ b/src/main/java/seedu/address/Main.java
@@ -4,17 +4,17 @@
/**
* The main entry point to the application.
- *
+ *
* This is a workaround for the following error when MainApp is made the
* entry point of the application:
- *
- * Error: JavaFX runtime components are missing, and are required to run this application
- *
+ *
+ * Error: JavaFX runtime components are missing, and are required to run this application
+ *
* The reason is that MainApp extends Application. In that case, the
* LauncherHelper will check for the javafx.graphics module to be present
- * as a named module. We don't use JavaFX via the module system so it can't
+ * as a named module. We don't use JavaFX via the module system so it ctiman't
* find the javafx.graphics module, and so the launch is aborted.
- *
+ *
* By having a separate main class (Main) that doesn't extend Application
* to be the entry point of the application, we avoid this issue.
*/
diff --git a/src/main/java/seedu/address/MainApp.java b/src/main/java/seedu/address/MainApp.java
index 4133aaa0151..554f88fe53f 100644
--- a/src/main/java/seedu/address/MainApp.java
+++ b/src/main/java/seedu/address/MainApp.java
@@ -6,6 +6,7 @@
import java.util.logging.Logger;
import javafx.application.Application;
+import javafx.application.Platform;
import javafx.stage.Stage;
import seedu.address.commons.core.Config;
import seedu.address.commons.core.LogsCenter;
@@ -16,18 +17,22 @@
import seedu.address.logic.Logic;
import seedu.address.logic.LogicManager;
import seedu.address.model.AddressBook;
+import seedu.address.model.DeliveryJobSystem;
import seedu.address.model.Model;
import seedu.address.model.ModelManager;
import seedu.address.model.ReadOnlyAddressBook;
+import seedu.address.model.ReadOnlyDeliveryJobSystem;
import seedu.address.model.ReadOnlyUserPrefs;
import seedu.address.model.UserPrefs;
import seedu.address.model.util.SampleDataUtil;
import seedu.address.storage.AddressBookStorage;
-import seedu.address.storage.JsonAddressBookStorage;
-import seedu.address.storage.JsonUserPrefsStorage;
+import seedu.address.storage.DeliveryJobSystemStorage;
import seedu.address.storage.Storage;
import seedu.address.storage.StorageManager;
import seedu.address.storage.UserPrefsStorage;
+import seedu.address.storage.json.storage.JsonAddressBookStorage;
+import seedu.address.storage.json.storage.JsonDeliveryJobSystemStorage;
+import seedu.address.storage.json.storage.JsonUserPrefsStorage;
import seedu.address.ui.Ui;
import seedu.address.ui.UiManager;
@@ -36,7 +41,7 @@
*/
public class MainApp extends Application {
- public static final Version VERSION = new Version(0, 2, 0, true);
+ public static final Version VERSION = new Version(1, 4, 0, true);
private static final Logger logger = LogsCenter.getLogger(MainApp.class);
@@ -48,7 +53,7 @@ public class MainApp extends Application {
@Override
public void init() throws Exception {
- logger.info("=============================[ Initializing AddressBook ]===========================");
+ logger.info("=============================[ Initializing DukeDriver ]===========================");
super.init();
AppParameters appParameters = AppParameters.parse(getParameters());
@@ -57,7 +62,9 @@ public void init() throws Exception {
UserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(config.getUserPrefsFilePath());
UserPrefs userPrefs = initPrefs(userPrefsStorage);
AddressBookStorage addressBookStorage = new JsonAddressBookStorage(userPrefs.getAddressBookFilePath());
- storage = new StorageManager(addressBookStorage, userPrefsStorage);
+ DeliveryJobSystemStorage deliveryJobSystemStorage = new JsonDeliveryJobSystemStorage(
+ userPrefs.getDeliveryJobSystemFilePath());
+ storage = new StorageManager(addressBookStorage, deliveryJobSystemStorage, userPrefsStorage);
initLogging(config);
@@ -69,28 +76,55 @@ public void init() throws Exception {
}
/**
- * Returns a {@code ModelManager} with the data from {@code storage}'s address book and {@code userPrefs}.
- * The data from the sample address book will be used instead if {@code storage}'s address book is not found,
- * or an empty address book will be used instead if errors occur when reading {@code storage}'s address book.
+ * Returns a {@code ModelManager} with the data from {@code storage}'s address
+ * book and {@code userPrefs}.
+ * The data from the sample address book will be used instead if
+ * {@code storage}'s address book is not found,
+ * or an empty address book will be used instead if errors occur when reading
+ * {@code storage}'s address book.
*/
private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) {
Optional addressBookOptional;
- ReadOnlyAddressBook initialData;
+ ReadOnlyAddressBook initialAddressData;
+
+ Optional deliveryJobSystemOptional;
+ ReadOnlyDeliveryJobSystem initialDeliveryJobSystemData;
+
try {
addressBookOptional = storage.readAddressBook();
if (!addressBookOptional.isPresent()) {
- logger.info("Data file not found. Will be starting with a sample AddressBook");
+ logger.info("[AB] Data file not found. Will be starting with a sample AddressBook");
}
- initialData = addressBookOptional.orElseGet(SampleDataUtil::getSampleAddressBook);
+ initialAddressData = addressBookOptional.orElseGet(SampleDataUtil::getSampleAddressBook);
+
} catch (DataConversionException e) {
- logger.warning("Data file not in the correct format. Will be starting with an empty AddressBook");
- initialData = new AddressBook();
+ logger.warning("[AB] Data file not in the correct format. Will be starting with an empty AddressBook");
+ initialAddressData = new AddressBook();
} catch (IOException e) {
- logger.warning("Problem while reading from the file. Will be starting with an empty AddressBook");
- initialData = new AddressBook();
+ logger.warning("[AB] Problem while reading from the file. Will be starting with an empty AddressBook");
+ initialAddressData = new AddressBook();
+ }
+
+ try {
+ deliveryJobSystemOptional = storage.readDeliveryJobSystem();
+
+ if (!deliveryJobSystemOptional.isPresent()) {
+ logger.info("[DS] Data file not found. Will be starting with a sample DeliveryJobSystem");
+ }
+
+ initialDeliveryJobSystemData = deliveryJobSystemOptional
+ .orElseGet(SampleDataUtil::getSampleDeliveryJobSystem);
+ } catch (IllegalArgumentException | NullPointerException | DataConversionException e) {
+ logger.warning(
+ "[DS] Data file not in the correct format. Will be starting with an empty DeliveryJobSystem");
+ initialDeliveryJobSystemData = new DeliveryJobSystem();
+ } catch (IOException e) {
+ logger.warning(
+ "[DS] Problem while reading from the file. Will be starting with an empty DeliveryJobSystem");
+ initialDeliveryJobSystemData = new DeliveryJobSystem();
}
- return new ModelManager(initialData, userPrefs);
+ return new ModelManager(initialAddressData, initialDeliveryJobSystemData, userPrefs);
}
private void initLogging(Config config) {
@@ -124,7 +158,8 @@ protected Config initConfig(Path configFilePath) {
initializedConfig = new Config();
}
- //Update config file in case it was missing to begin with or there are new/unused fields
+ // Update config file in case it was missing to begin with or there are
+ // new/unused fields
try {
ConfigUtil.saveConfig(initializedConfig, configFilePathUsed);
} catch (IOException e) {
@@ -134,7 +169,8 @@ protected Config initConfig(Path configFilePath) {
}
/**
- * Returns a {@code UserPrefs} using the file at {@code storage}'s user prefs file path,
+ * Returns a {@code UserPrefs} using the file at {@code storage}'s user prefs
+ * file path,
* or a new {@code UserPrefs} with default configuration if errors occur when
* reading from the file.
*/
@@ -155,7 +191,8 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) {
initializedPrefs = new UserPrefs();
}
- //Update prefs file in case it was missing to begin with or there are new/unused fields
+ // Update prefs file in case it was missing to begin with or there are
+ // new/unused fields
try {
storage.saveUserPrefs(initializedPrefs);
} catch (IOException e) {
@@ -167,17 +204,19 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) {
@Override
public void start(Stage primaryStage) {
- logger.info("Starting AddressBook " + MainApp.VERSION);
+ logger.info("Starting DukeDriver " + MainApp.VERSION);
ui.start(primaryStage);
}
@Override
public void stop() {
- logger.info("============================ [ Stopping Address Book ] =============================");
+ logger.info("============================ [ Stopping DukeDriver ] =============================");
try {
storage.saveUserPrefs(model.getUserPrefs());
} catch (IOException e) {
logger.severe("Failed to save preferences " + StringUtil.getDetails(e));
}
+ Platform.exit();
+ System.exit(0);
}
}
diff --git a/src/main/java/seedu/address/commons/core/LogsCenter.java b/src/main/java/seedu/address/commons/core/LogsCenter.java
index 431e7185e76..70a7f56b8b0 100644
--- a/src/main/java/seedu/address/commons/core/LogsCenter.java
+++ b/src/main/java/seedu/address/commons/core/LogsCenter.java
@@ -12,17 +12,17 @@
* Configures and manages loggers and handlers, including their logging level
* Named {@link Logger}s can be obtained from this class
* These loggers have been configured to output messages to the console and a {@code .log} file by default,
- * at the {@code INFO} level. A new {@code .log} file with a new numbering will be created after the log
- * file reaches 5MB big, up to a maximum of 5 files.
+ * at the {@code INFO} level. A new {@code .log} file with a new numbering will be created after the log
+ * file reaches 5MB big, up to a maximum of 5 files.
*/
public class LogsCenter {
private static final int MAX_FILE_COUNT = 5;
private static final int MAX_FILE_SIZE_IN_BYTES = (int) (Math.pow(2, 20) * 5); // 5MB
private static final String LOG_FILE = "addressbook.log";
private static Level currentLogLevel = Level.INFO;
- private static final Logger logger = LogsCenter.getLogger(LogsCenter.class);
private static FileHandler fileHandler;
private static ConsoleHandler consoleHandler;
+ private static final Logger logger = LogsCenter.getLogger(LogsCenter.class);
/**
* Initializes with a custom log level (specified in the {@code config} object)
@@ -95,6 +95,7 @@ private static void addFileHandler(Logger logger) {
/**
* Creates a {@code FileHandler} for the log file.
+ *
* @throws IOException if there are problems opening the file.
*/
private static FileHandler createFileHandler() throws IOException {
diff --git a/src/main/java/seedu/address/commons/core/Messages.java b/src/main/java/seedu/address/commons/core/Messages.java
index 1deb3a1e469..0a9b45b1d62 100644
--- a/src/main/java/seedu/address/commons/core/Messages.java
+++ b/src/main/java/seedu/address/commons/core/Messages.java
@@ -6,8 +6,19 @@
public class Messages {
public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command";
+ public static final String MESSAGE_UNKNOWN_TIMETABLE_COMMAND = "Unknown command "
+ + "\nOnly timetable_date command is allowed in timetable:"
+ + " timetable_date date/YYYY-mm-DD"
+ + "\nFor example: timetable_date date/2023-03-01";
+
public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s";
+ public static final String MESSAGE_EITHER_INDEX_OR_ID = "Select either by list or job id only.";
public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "The person index provided is invalid";
+ public static final String MESSAGE_INVALID_REMINDER_DISPLAYED_INDEX = "The reminder index provided is invalid";
public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!";
+ public static final String MESSAGE_INVALID_JOB_ID = "The job id provided is invalid";
+ public static final String MESSAGE_INVALID_JOB_DISPLAYED_INDEX = "The job index provided is invalid";
+ public static final String MESSAGE_DELIVERY_JOB_LISTED_OVERVIEW = "%1$d jobs listed!";
+ public static final String COMMAND_NOT_ALLOW = "Command not allowed in this window!";
}
diff --git a/src/main/java/seedu/address/commons/core/Version.java b/src/main/java/seedu/address/commons/core/Version.java
index 12142ec1e32..7e897b61257 100644
--- a/src/main/java/seedu/address/commons/core/Version.java
+++ b/src/main/java/seedu/address/commons/core/Version.java
@@ -32,24 +32,9 @@ public Version(int major, int minor, int patch, boolean isEarlyAccess) {
this.isEarlyAccess = isEarlyAccess;
}
- public int getMajor() {
- return major;
- }
-
- public int getMinor() {
- return minor;
- }
-
- public int getPatch() {
- return patch;
- }
-
- public boolean isEarlyAccess() {
- return isEarlyAccess;
- }
-
/**
* Parses a version number string in the format V1.2.3.
+ *
* @param versionString version number string
* @return a Version object
*/
@@ -67,6 +52,22 @@ public static Version fromString(String versionString) throws IllegalArgumentExc
versionMatcher.group(4) == null ? false : true);
}
+ public int getMajor() {
+ return major;
+ }
+
+ public int getMinor() {
+ return minor;
+ }
+
+ public int getPatch() {
+ return patch;
+ }
+
+ public boolean isEarlyAccess() {
+ return isEarlyAccess;
+ }
+
@JsonValue
public String toString() {
return String.format("V%d.%d.%d%s", major, minor, patch, isEarlyAccess ? "ea" : "");
diff --git a/src/main/java/seedu/address/commons/core/index/Index.java b/src/main/java/seedu/address/commons/core/index/Index.java
index 19536439c09..82776e53f4b 100644
--- a/src/main/java/seedu/address/commons/core/index/Index.java
+++ b/src/main/java/seedu/address/commons/core/index/Index.java
@@ -2,7 +2,7 @@
/**
* Represents a zero-based or one-based index.
- *
+ *
* {@code Index} should be used right from the start (when parsing in a new user input), so that if the current
* component wants to communicate with another component, it can send an {@code Index} to avoid having to know what
* base the other component is using for its index. However, after receiving the {@code Index}, that component can
@@ -23,14 +23,6 @@ private Index(int zeroBasedIndex) {
this.zeroBasedIndex = zeroBasedIndex;
}
- public int getZeroBased() {
- return zeroBasedIndex;
- }
-
- public int getOneBased() {
- return zeroBasedIndex + 1;
- }
-
/**
* Creates a new {@code Index} using a zero-based index.
*/
@@ -45,6 +37,14 @@ public static Index fromOneBased(int oneBasedIndex) {
return new Index(oneBasedIndex - 1);
}
+ public int getZeroBased() {
+ return zeroBasedIndex;
+ }
+
+ public int getOneBased() {
+ return zeroBasedIndex + 1;
+ }
+
@Override
public boolean equals(Object other) {
return other == this // short circuit if same object
diff --git a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java b/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java
index 19124db485c..651ce290208 100644
--- a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java
+++ b/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java
@@ -13,7 +13,7 @@ public IllegalValueException(String message) {
/**
* @param message should contain relevant information on the failed constraint(s)
- * @param cause of the main exception
+ * @param cause of the main exception
*/
public IllegalValueException(String message, Throwable cause) {
super(message, cause);
diff --git a/src/main/java/seedu/address/commons/util/AppUtil.java b/src/main/java/seedu/address/commons/util/AppUtil.java
index 87aa89c0326..c0992318360 100644
--- a/src/main/java/seedu/address/commons/util/AppUtil.java
+++ b/src/main/java/seedu/address/commons/util/AppUtil.java
@@ -4,7 +4,6 @@
import javafx.scene.image.Image;
import seedu.address.MainApp;
-
/**
* A container for App specific utility functions
*/
diff --git a/src/main/java/seedu/address/commons/util/CollectionUtil.java b/src/main/java/seedu/address/commons/util/CollectionUtil.java
index eafe4dfd681..942edda97d7 100644
--- a/src/main/java/seedu/address/commons/util/CollectionUtil.java
+++ b/src/main/java/seedu/address/commons/util/CollectionUtil.java
@@ -12,7 +12,9 @@
*/
public class CollectionUtil {
- /** @see #requireAllNonNull(Collection) */
+ /**
+ * @see #requireAllNonNull(Collection)
+ */
public static void requireAllNonNull(Object... items) {
requireNonNull(items);
Stream.of(items).forEach(Objects::requireNonNull);
diff --git a/src/main/java/seedu/address/commons/util/ConfigUtil.java b/src/main/java/seedu/address/commons/util/ConfigUtil.java
index f7f8a2bd44c..0cefc577a11 100644
--- a/src/main/java/seedu/address/commons/util/ConfigUtil.java
+++ b/src/main/java/seedu/address/commons/util/ConfigUtil.java
@@ -6,7 +6,6 @@
import seedu.address.commons.core.Config;
import seedu.address.commons.exceptions.DataConversionException;
-
/**
* A class for accessing the Config File.
*/
diff --git a/src/main/java/seedu/address/commons/util/DateTimeUtil.java b/src/main/java/seedu/address/commons/util/DateTimeUtil.java
new file mode 100644
index 00000000000..11ba3dc2980
--- /dev/null
+++ b/src/main/java/seedu/address/commons/util/DateTimeUtil.java
@@ -0,0 +1,19 @@
+package seedu.address.commons.util;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * Utility methods to parse LocalDateTime to String, vice versa.
+ */
+public class DateTimeUtil {
+ public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
+
+ public static LocalDateTime toDateTime(String s) {
+ return LocalDateTime.parse(s, DATE_TIME_FORMATTER);
+ }
+
+ public static String dateTimeToString(LocalDateTime t) {
+ return t.format(DATE_TIME_FORMATTER);
+ }
+}
diff --git a/src/main/java/seedu/address/commons/util/FileUtil.java b/src/main/java/seedu/address/commons/util/FileUtil.java
index b1e2767cdd9..f811215474c 100644
--- a/src/main/java/seedu/address/commons/util/FileUtil.java
+++ b/src/main/java/seedu/address/commons/util/FileUtil.java
@@ -20,6 +20,7 @@ public static boolean isFileExists(Path file) {
/**
* Returns true if {@code path} can be converted into a {@code Path} via {@link Paths#get(String)},
* otherwise returns false.
+ *
* @param path A string representing the file path. Cannot be null.
*/
public static boolean isValidPath(String path) {
@@ -33,6 +34,7 @@ public static boolean isValidPath(String path) {
/**
* Creates a file if it does not exist along with its missing parent directories.
+ *
* @throws IOException if the file or directory cannot be created.
*/
public static void createIfMissing(Path file) throws IOException {
diff --git a/src/main/java/seedu/address/commons/util/JsonUtil.java b/src/main/java/seedu/address/commons/util/JsonUtil.java
index 8ef609f055d..8d1143d5ce0 100644
--- a/src/main/java/seedu/address/commons/util/JsonUtil.java
+++ b/src/main/java/seedu/address/commons/util/JsonUtil.java
@@ -51,7 +51,8 @@ static T deserializeObjectFromJsonFile(Path jsonFile, Class classOfObject
/**
* Returns the Json object from the given file or {@code Optional.empty()} object if the file is not found.
* If any values are missing from the file, default values will be used, as long as the file is a valid json file.
- * @param filePath cannot be null.
+ *
+ * @param filePath cannot be null.
* @param classOfObjectToDeserialize Json file has to correspond to the structure in the class given here.
* @throws DataConversionException if the file format is not as expected.
*/
@@ -79,6 +80,7 @@ public static Optional readJsonFile(
/**
* Saves the Json object to the specified file.
* Overwrites existing file if it exists, creates a new file if it doesn't.
+ *
* @param jsonFile cannot be null
* @param filePath cannot be null
* @throws IOException if there was an error during writing to the file
@@ -93,6 +95,7 @@ public static void saveJsonFile(T jsonFile, Path filePath) throws IOExceptio
/**
* Converts a given string representation of a JSON data to instance of a class
+ *
* @param The generic type to create an instance of
* @return The instance of T with the specified values in the JSON string
*/
@@ -102,8 +105,9 @@ public static T fromJsonString(String json, Class instanceClass) throws I
/**
* Converts a given instance of a class into its JSON data string representation
+ *
* @param instance The T object to be converted into the JSON string
- * @param The generic type to create an instance of
+ * @param The generic type to create an instance of
* @return JSON data representation of the given class instance, in string
*/
public static String toJsonString(T instance) throws JsonProcessingException {
@@ -128,7 +132,6 @@ protected Level _deserialize(String value, DeserializationContext ctxt) {
* Gets the logging level that matches loggingLevelString
*
* Returns null if there are no matches
- *
*/
private Level getLoggingLevel(String loggingLevelString) {
return Level.parse(loggingLevelString);
diff --git a/src/main/java/seedu/address/commons/util/StringUtil.java b/src/main/java/seedu/address/commons/util/StringUtil.java
index 61cc8c9a1cb..edacbf58bba 100644
--- a/src/main/java/seedu/address/commons/util/StringUtil.java
+++ b/src/main/java/seedu/address/commons/util/StringUtil.java
@@ -14,14 +14,15 @@ public class StringUtil {
/**
* Returns true if the {@code sentence} contains the {@code word}.
- * Ignores case, but a full word match is required.
- *
examples:
+ * Ignores case, but a full word match is required.
+ *
examples:
* containsWordIgnoreCase("ABc def", "abc") == true
* containsWordIgnoreCase("ABc def", "DEF") == true
* containsWordIgnoreCase("ABc def", "AB") == false //not a full word match
*
+ *
* @param sentence cannot be null
- * @param word cannot be null, cannot be empty, must be a single word
+ * @param word cannot be null, cannot be empty, must be a single word
*/
public static boolean containsWordIgnoreCase(String sentence, String word) {
requireNonNull(sentence);
@@ -53,6 +54,7 @@ public static String getDetails(Throwable t) {
* e.g. 1, 2, 3, ..., {@code Integer.MAX_VALUE}
* Will return false for any other non-null string input
* e.g. empty string, "-1", "0", "+1", and " 2 " (untrimmed), "3 0" (contains whitespace), "1 a" (contains letters)
+ *
* @throws NullPointerException if {@code s} is null.
*/
public static boolean isNonZeroUnsignedInteger(String s) {
diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/address/logic/Logic.java
index 92cd8fa605a..b44af38a40c 100644
--- a/src/main/java/seedu/address/logic/Logic.java
+++ b/src/main/java/seedu/address/logic/Logic.java
@@ -1,14 +1,26 @@
package seedu.address.logic;
+import java.io.FileNotFoundException;
import java.nio.file.Path;
+import java.time.LocalDate;
+import java.util.Comparator;
+import java.util.Map;
+import java.util.function.Predicate;
import javafx.collections.ObservableList;
import seedu.address.commons.core.GuiSettings;
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandGroup;
import seedu.address.logic.commands.CommandResult;
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.Model;
import seedu.address.model.ReadOnlyAddressBook;
+import seedu.address.model.jobs.DeliveryJob;
+import seedu.address.model.jobs.DeliveryList;
import seedu.address.model.person.Person;
+import seedu.address.model.reminder.Reminder;
+import seedu.address.model.stats.WeeklyStats;
/**
* API of the Logic component
@@ -16,12 +28,40 @@
public interface Logic {
/**
* Executes the command and returns the result.
+ *
+ * @param commandText The command as entered by the user.
+ * @return the result of the command execution.
+ * @throws CommandException If an error occurs during command execution.
+ * @throws ParseException If an error occurs during parsing.
+ */
+ CommandResult execute(String commandText) throws CommandException, ParseException, FileNotFoundException;
+
+ /**
+ * Executes the command for chosen group and returns the result.
+ *
* @param commandText The command as entered by the user.
+ * @param condition execute when true.
+ * @return the result of the command execution.
+ * @throws CommandException If an error occurs during command execution.
+ * @throws ParseException If an error occurs during parsing.
+ */
+ CommandResult execute(String commandText, Predicate condition)
+ throws CommandException, ParseException, FileNotFoundException;
+
+ /**
+ * Executes specific command object and returns the result.
+ *
+ * @param command Prebuild command object.
* @return the result of the command execution.
* @throws CommandException If an error occurs during command execution.
- * @throws ParseException If an error occurs during parsing.
+ * @throws ParseException If an error occurs during parsing.
*/
- CommandResult execute(String commandText) throws CommandException, ParseException;
+ CommandResult execute(Command command) throws CommandException, ParseException, FileNotFoundException;
+
+ CommandResult executeTimetableCommand(String commandText)
+ throws CommandException, ParseException, FileNotFoundException;
+
+ // ADDRESS BOOK SYSTEM ===================================
/**
* Returns the AddressBook.
@@ -30,7 +70,9 @@ public interface Logic {
*/
ReadOnlyAddressBook getAddressBook();
- /** Returns an unmodifiable view of the filtered list of persons */
+ /**
+ * Returns an unmodifiable view of the filtered list of persons
+ */
ObservableList getFilteredPersonList();
/**
@@ -38,13 +80,127 @@ public interface Logic {
*/
Path getAddressBookFilePath();
+ // REMINDER ===================================
+ /**
+ * Returns an unmodifiable view of the filtered list of reminders
+ */
+ ObservableList getReminderList();
+
+ /**
+ * Sorts reminder list
+ */
+ void sortReminderList();
+
+ // DELIVERY JOB SYSTEM ===================================
+
+ /**
+ * Returns an unmodifiable view of the filtered list of delivery jobs
+ */
+ ObservableList getFilteredDeliveryJobList();
+
+ /**
+ * Returns an unmodifiable view of the sorted list of delivery jobs
+ */
+ ObservableList getSortedDeliveryJobList();
+
+ /**
+ * Returns delivery job list in the week sorted into day
+ */
+ Map getWeekDeliveryJobList();
+
+ /**
+ * Returns job on specific day of week
+ * @param dayOfWeek day of week
+ * @return job list in the specific day
+ */
+ DeliveryList getDayofWeekJob(int dayOfWeek);
+
+ /**
+ * Returns an unmodifiable view of the list of unscheduled delivery jobs,
+ * sorted by time and earning
+ */
+ ObservableList getUnscheduledDeliveryJobList();
+
+ /**
+ * Returns an unmodifiable view of the list of completed delivery jobs,
+ * sorted by time and earning
+ */
+ ObservableList getCompletedDeliveryJobList();
+
+ /**
+ * Gets total earning of all jobs in job list
+ * @param list
+ */
+ double getTotalEarnings(ObservableList list);
+
+ /**
+ * Gets total number of completed jobs in job list
+ * @param list
+ */
+ int getTotalCompleted(ObservableList list);
+
+ int getTotalPending(ObservableList list);
+
+ ObservableList weekJobsList(ObservableList list, LocalDate date);
+
+ boolean sameWeek(DeliveryJob job, WeeklyStats weeklyStats);
+
+ /**
+ * Returns the user prefs' delivery job system file path.
+ */
+ Path getDeliveryJobSystemFilePath();
+
+ /**
+ * Set focus date
+ * @param focusDate
+ */
+ void setWeekDeliveryJobList(LocalDate focusDate);
+
+ /**
+ * Updates filter delivery job list
+ * @return
+ */
+ void updateFilteredDeliveryJobList(Predicate pre);
+
+ /**
+ * Updates sorted delivery job list
+ * @return
+ */
+ void updateSortedDeliveryJobList(Comparator sorter);
+
+ /**
+ * Updates sorted delivery job list
+ * @return
+ */
+ void updateSortedDeliveryJobListByComparator(Comparator sorter);
+
+ /**
+ * Updates sorted delivery job list by date
+ */
+ void updateSortedDeliveryJobListByDate();
+
+ /**
+ * Gets user input focus date
+ * @return focus date
+ */
+ LocalDate getFocusDate();
+
+ // MODEL ===================================
+
+ /**
+ * Gets model
+ * @return model
+ */
+ Model getModel();
+
/**
* Returns the user prefs' GUI settings.
*/
GuiSettings getGuiSettings();
/**
- * Set the user prefs' GUI settings.
+ * Sets the user prefs' GUI settings.
*/
void setGuiSettings(GuiSettings guiSettings);
+
}
diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/address/logic/LogicManager.java
index 9d9c6d15bdc..a5b3c3e84e7 100644
--- a/src/main/java/seedu/address/logic/LogicManager.java
+++ b/src/main/java/seedu/address/logic/LogicManager.java
@@ -1,20 +1,34 @@
package seedu.address.logic;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Path;
+import java.time.LocalDate;
+import java.util.Comparator;
+import java.util.Map;
+import java.util.function.Predicate;
import java.util.logging.Logger;
import javafx.collections.ObservableList;
import seedu.address.commons.core.GuiSettings;
import seedu.address.commons.core.LogsCenter;
+import seedu.address.commons.core.Messages;
import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandGroup;
import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.HelpCommand;
import seedu.address.logic.commands.exceptions.CommandException;
-import seedu.address.logic.parser.AddressBookParser;
+import seedu.address.logic.parser.DukeDriverParser;
import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.logic.parser.timetable.TimetableParser;
import seedu.address.model.Model;
import seedu.address.model.ReadOnlyAddressBook;
+import seedu.address.model.jobs.DeliveryJob;
+import seedu.address.model.jobs.DeliveryList;
+import seedu.address.model.jobs.sorters.SortbyTimeAndEarn;
import seedu.address.model.person.Person;
+import seedu.address.model.reminder.Reminder;
+import seedu.address.model.stats.WeeklyStats;
import seedu.address.storage.Storage;
/**
@@ -22,11 +36,14 @@
*/
public class LogicManager implements Logic {
public static final String FILE_OPS_ERROR_MESSAGE = "Could not save data to file: ";
+ public static final SortbyTimeAndEarn SORTER_BY_DATE = new SortbyTimeAndEarn();
private final Logger logger = LogsCenter.getLogger(LogicManager.class);
private final Model model;
private final Storage storage;
- private final AddressBookParser addressBookParser;
+ private final DukeDriverParser addressBookParser;
+ private final TimetableParser timetableParser;
+
/**
* Constructs a {@code LogicManager} with the given {@code Model} and {@code Storage}.
@@ -34,24 +51,67 @@ public class LogicManager implements Logic {
public LogicManager(Model model, Storage storage) {
this.model = model;
this.storage = storage;
- addressBookParser = new AddressBookParser();
+ addressBookParser = new DukeDriverParser();
+ timetableParser = new TimetableParser();
+ }
+
+ public Model getModel() {
+ return this.model;
}
@Override
- public CommandResult execute(String commandText) throws CommandException, ParseException {
+ public CommandResult execute(String commandText) throws CommandException, ParseException, FileNotFoundException {
logger.info("----------------[USER COMMAND][" + commandText + "]");
- CommandResult commandResult;
Command command = addressBookParser.parseCommand(commandText);
+ return execute(command);
+ }
+
+ @Override
+ public CommandResult execute(String commandText, Predicate condition)
+ throws CommandException, ParseException, FileNotFoundException {
+ logger.info("----------------[USER COMMAND][" + commandText + "]");
+
+ if (condition.test(addressBookParser.parseCommandGroup(commandText))) {
+ return execute(addressBookParser.parseCommand(commandText));
+ } else {
+ return new CommandResult(String.format(Messages.COMMAND_NOT_ALLOW
+ + "\nPlease refer to User Guide for more details: \n"
+ + HelpCommand.MESSAGE_USAGE));
+ }
+ }
+
+ @Override
+ public CommandResult execute(Command command) throws CommandException, ParseException, FileNotFoundException {
+ logger.info("----------------[USER COMMAND][" + command.getClass().getSimpleName() + "]");
+ CommandResult commandResult = command.execute(model);
+ try {
+ storage.saveAddressBook(model.getAddressBook());
+ storage.saveDeliveryJobSystem(model.getDeliveryJobSystem());
+ } catch (IOException ioe) {
+ throw new CommandException(FILE_OPS_ERROR_MESSAGE + ioe, ioe);
+ }
+ return commandResult;
+ }
+
+ @Override
+ public CommandResult executeTimetableCommand(String commandText)
+ throws CommandException, ParseException, FileNotFoundException {
+ logger.info("----------------[USER COMMAND][" + commandText + "]");
+
+ CommandResult commandResult;
+ Command command = timetableParser.parseCommand(commandText);
commandResult = command.execute(model);
try {
storage.saveAddressBook(model.getAddressBook());
+ storage.saveDeliveryJobSystem(model.getDeliveryJobSystem());
} catch (IOException ioe) {
throw new CommandException(FILE_OPS_ERROR_MESSAGE + ioe, ioe);
}
return commandResult;
+
}
@Override
@@ -64,11 +124,75 @@ public ObservableList getFilteredPersonList() {
return model.getFilteredPersonList();
}
+ @Override
+ public ObservableList getFilteredDeliveryJobList() {
+ return model.getDeliveryJobList();
+ }
+
+ @Override
+ public ObservableList getSortedDeliveryJobList() {
+ return model.getSortedDeliveryJobListByComparator();
+ }
+
+ @Override
+ public Map getWeekDeliveryJobList() {
+ return model.getWeekDeliveryJobList();
+ }
+
+ @Override
+ public DeliveryList getDayofWeekJob(int dayOfWeek) {
+ return model.getDayOfWeekJob(dayOfWeek);
+ }
+
+ @Override
+ public double getTotalEarnings(ObservableList list) {
+ return model.getTotalEarnings(list);
+ }
+
+ @Override
+ public int getTotalCompleted(ObservableList list) {
+ return model.getTotalCompleted(list);
+ }
+
+ @Override
+ public int getTotalPending(ObservableList list) {
+ return model.getTotalPending(list);
+ }
+
+ @Override
+ public boolean sameWeek(DeliveryJob job, WeeklyStats weeklyStats) {
+ return model.sameWeek(job, weeklyStats);
+ }
+
+ @Override
+ public ObservableList weekJobsList(ObservableList list, LocalDate date) {
+ return model.weekJobsList(list, date);
+ }
+
+ public ObservableList getUnscheduledDeliveryJobList() {
+ return model.getUnscheduledDeliveryJobList();
+ }
+
+ @Override
+ public ObservableList getReminderList() {
+ return model.getReminderList();
+ }
+
+ @Override
+ public void sortReminderList() {
+ model.sortReminderList();
+ }
+
@Override
public Path getAddressBookFilePath() {
return model.getAddressBookFilePath();
}
+ @Override
+ public Path getDeliveryJobSystemFilePath() {
+ return model.getDeliveryJobSystemFilePath();
+ }
+
@Override
public GuiSettings getGuiSettings() {
return model.getGuiSettings();
@@ -78,4 +202,42 @@ public GuiSettings getGuiSettings() {
public void setGuiSettings(GuiSettings guiSettings) {
model.setGuiSettings(guiSettings);
}
+
+ @Override
+ public void setWeekDeliveryJobList(LocalDate focusDate) {
+ model.updateFocusDate(focusDate);
+ model.updateWeekDeliveryJobList(focusDate);
+ }
+
+ @Override
+ public void updateFilteredDeliveryJobList(Predicate pre) {
+ model.updateFilteredDeliveryJobList(pre);
+ }
+
+ @Override
+ public void updateSortedDeliveryJobList(Comparator sorter) {
+ model.updateSortedDeliveryJobList(sorter);
+ }
+
+ public void updateSortedDeliveryJobListByComparator(Comparator sorter) {
+ model.updateSortedDeliveryJobListByComparator(sorter);
+ }
+
+ @Override
+ public void updateSortedDeliveryJobListByDate() {
+ model.updateSortedDeliveryJobList(SORTER_BY_DATE);
+ model.updateSortedDeliveryJobListByDate();
+ }
+
+ @Override
+ public ObservableList getCompletedDeliveryJobList() {
+ return model.getCompletedDeliveryJobList();
+ }
+
+ @Override
+ public LocalDate getFocusDate() {
+ return model.getFocusDate();
+ }
+
+
}
diff --git a/src/main/java/seedu/address/logic/commands/Command.java b/src/main/java/seedu/address/logic/commands/Command.java
index 64f18992160..c82c4981716 100644
--- a/src/main/java/seedu/address/logic/commands/Command.java
+++ b/src/main/java/seedu/address/logic/commands/Command.java
@@ -1,13 +1,19 @@
package seedu.address.logic.commands;
+import java.io.FileNotFoundException;
+
import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.Model;
+
/**
* Represents a command with hidden internal logic and the ability to be executed.
*/
public abstract class Command {
+ public static final CommandGroup COMMAND_GROUP = CommandGroup.GENERAL;
+
/**
* Executes the command and returns the result message.
*
@@ -15,6 +21,5 @@ public abstract class Command {
* @return feedback message of the operation result for display
* @throws CommandException If an error occurs during command execution.
*/
- public abstract CommandResult execute(Model model) throws CommandException;
-
+ public abstract CommandResult execute(Model model) throws CommandException, ParseException, FileNotFoundException;
}
diff --git a/src/main/java/seedu/address/logic/commands/CommandGroup.java b/src/main/java/seedu/address/logic/commands/CommandGroup.java
new file mode 100644
index 00000000000..5101f163718
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/CommandGroup.java
@@ -0,0 +1,13 @@
+package seedu.address.logic.commands;
+
+/**
+ * Groups commands types.
+ */
+public enum CommandGroup {
+ GENERAL,
+ PERSON,
+ TIMETABLE,
+ REMINDER,
+ STAT,
+ JOB
+}
diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/seedu/address/logic/commands/CommandResult.java
index 92f900b7916..f13dbbda713 100644
--- a/src/main/java/seedu/address/logic/commands/CommandResult.java
+++ b/src/main/java/seedu/address/logic/commands/CommandResult.java
@@ -11,18 +11,112 @@ public class CommandResult {
private final String feedbackToUser;
- /** Help information should be shown to the user. */
+ /**
+ * Help information should be shown to the user.
+ */
private final boolean showHelp;
- /** The application should exit. */
+ /**
+ * Timetable information should be shown to the user.
+ */
+ private final boolean showTimetable;
+
+ /**
+ * Timetable information of specific date should be shown to the user.
+ */
+ private boolean showTimetableDate;
+
+ /**
+ * List of unscheduled jobs should be shown to user.
+ */
+ private final boolean showUnschedule;
+
+ /**
+ * List of completed jobs should be shown to user.
+ */
+ private final boolean showComplete;
+
+ /**
+ * Statistics information should be shown to the user.
+ */
+ private final boolean showStatistics;
+
+ /**
+ * Reminder list should be shown to the user.
+ */
+ private final boolean showReminderList;
+
+ /**
+ * Address book should be shown to the user.
+ */
+ private final boolean showAddressBook;
+
+ /**
+ * The application should exit.
+ */
private final boolean exit;
/**
* Constructs a {@code CommandResult} with the specified fields.
*/
- public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) {
+
+ public CommandResult(String feedbackToUser, boolean showHelp, boolean showTimetable,
+ boolean showUnschedule, boolean showComplete, boolean showReminderList,
+ boolean showStatistics, boolean showAddressBook,
+ boolean exit) {
+ this.feedbackToUser = requireNonNull(feedbackToUser);
+ this.showHelp = showHelp;
+ this.showTimetable = showTimetable;
+ this.showTimetableDate = false;
+ this.showUnschedule = showUnschedule;
+ this.showComplete = showComplete;
+ this.showReminderList = showReminderList;
+ this.showStatistics = showStatistics;
+ this.showAddressBook = showAddressBook;
+ this.exit = exit;
+ }
+
+ /**
+ * Constructs a {@code CommandResult} with the specified fields.
+ */
+
+ public CommandResult(String feedbackToUser, boolean showHelp, boolean showTimetable,
+ boolean showUnschedule, boolean showComplete, boolean showReminderList,
+ boolean showStatistics, boolean exit) {
this.feedbackToUser = requireNonNull(feedbackToUser);
this.showHelp = showHelp;
+ this.showTimetable = showTimetable;
+ this.showTimetableDate = false;
+ this.showUnschedule = showUnschedule;
+ this.showComplete = showComplete;
+ this.showReminderList = showReminderList;
+ this.showStatistics = showStatistics;
+ this.showAddressBook = false;
+ this.exit = exit;
+ }
+
+ /**
+ * Simplified constructor for CommandResult
+ * which does not show Unscheduled job window
+ * @param feedbackToUser
+ * @param showHelp
+ * @param showTimetable
+ * @param showReminderList
+ * @param showStatistics
+ * @param exit
+ */
+ public CommandResult(String feedbackToUser, boolean showHelp, boolean showTimetable,
+ boolean showReminderList,
+ boolean showStatistics, boolean exit) {
+ this.feedbackToUser = requireNonNull(feedbackToUser);
+ this.showHelp = showHelp;
+ this.showTimetable = showTimetable;
+ this.showTimetableDate = false;
+ this.showUnschedule = false;
+ this.showComplete = false;
+ this.showReminderList = showReminderList;
+ this.showStatistics = showStatistics;
+ this.showAddressBook = false;
this.exit = exit;
}
@@ -31,21 +125,88 @@ public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) {
* and other fields set to their default value.
*/
public CommandResult(String feedbackToUser) {
- this(feedbackToUser, false, false);
+ this(feedbackToUser, false, false, false, false, false);
}
+ /**
+ * Returns feedback to user
+ */
public String getFeedbackToUser() {
return feedbackToUser;
}
+ /**
+ * Checks if help window is shown
+ */
public boolean isShowHelp() {
return showHelp;
}
+ /**
+ * Checks if timetable window for any date is shown
+ */
+ public boolean isShowTimetable() {
+ return showTimetable;
+ }
+
+ /**
+ * Checks if timetable window for specific date is shown
+ */
+ public boolean isShowTimetableDate() {
+ return showTimetableDate;
+ }
+
+ /**
+ * Checks if unscheduled job window is shown
+ */
+ public boolean isShowUnschedule() {
+ return showUnschedule;
+ }
+
+ /**
+ * Checks if completed job window is shown
+ */
+ public boolean isShowComplete() {
+ return showComplete;
+ }
+
+
+ /**
+ * Checks if stats window is shown
+ */
+ public boolean isShowStatistics() {
+ return showStatistics;
+ }
+
+ /**
+ * Checks if reminder window is shown
+ */
+ public boolean isShowReminderList() {
+ return showReminderList;
+ }
+
+ /**
+ * Checks if address window is shown
+ */
+ public boolean isShowAddressBook() {
+ return showAddressBook;
+ }
+
+ /**
+ * Checks if main window is exited
+ */
public boolean isExit() {
return exit;
}
+ /**
+ * Sets status of show timetable for specific date
+ * @param isShowTimetableDate
+ */
+ public void setShowTimetableDate(boolean isShowTimetableDate) {
+ this.showTimetableDate = isShowTimetableDate;
+ }
+
@Override
public boolean equals(Object other) {
if (other == this) {
@@ -60,12 +221,15 @@ public boolean equals(Object other) {
CommandResult otherCommandResult = (CommandResult) other;
return feedbackToUser.equals(otherCommandResult.feedbackToUser)
&& showHelp == otherCommandResult.showHelp
+ && showTimetable == otherCommandResult.showTimetable
+ && showStatistics == otherCommandResult.showStatistics
+ && showReminderList == otherCommandResult.showReminderList
&& exit == otherCommandResult.exit;
}
@Override
public int hashCode() {
- return Objects.hash(feedbackToUser, showHelp, exit);
+ return Objects.hash(feedbackToUser, showHelp, showTimetable, showReminderList, showStatistics, exit);
}
}
diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/seedu/address/logic/commands/ExitCommand.java
index 3dd85a8ba90..5a004fb9fde 100644
--- a/src/main/java/seedu/address/logic/commands/ExitCommand.java
+++ b/src/main/java/seedu/address/logic/commands/ExitCommand.java
@@ -13,7 +13,7 @@ public class ExitCommand extends Command {
@Override
public CommandResult execute(Model model) {
- return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true);
+ return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, false, false, false, true);
}
}
diff --git a/src/main/java/seedu/address/logic/commands/HelpCommand.java b/src/main/java/seedu/address/logic/commands/HelpCommand.java
index bf824f91bd0..f4996722342 100644
--- a/src/main/java/seedu/address/logic/commands/HelpCommand.java
+++ b/src/main/java/seedu/address/logic/commands/HelpCommand.java
@@ -16,6 +16,6 @@ public class HelpCommand extends Command {
@Override
public CommandResult execute(Model model) {
- return new CommandResult(SHOWING_HELP_MESSAGE, true, false);
+ return new CommandResult(SHOWING_HELP_MESSAGE, true, false, false, false, false);
}
}
diff --git a/src/main/java/seedu/address/logic/commands/StatisticsCommand.java b/src/main/java/seedu/address/logic/commands/StatisticsCommand.java
new file mode 100644
index 00000000000..75d64913bf5
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/StatisticsCommand.java
@@ -0,0 +1,30 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+
+/**
+ * Shows summary of descriptive statistics to user.
+ */
+public class StatisticsCommand extends Command {
+ public static final CommandGroup COMMAND_GROUP = CommandGroup.STAT;
+
+ public static final String COMMAND_WORD = "stats";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Shows summary of jobs";
+
+ public static final String SHOWING_STATISTICS_MESSAGE = "Opened statistics window.";
+
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+
+
+ return new CommandResult(SHOWING_STATISTICS_MESSAGE, false, false, false, true, false);
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java b/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java
index a16bd14f2cd..f44c0c19186 100644
--- a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java
+++ b/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java
@@ -1,5 +1,7 @@
package seedu.address.logic.commands.exceptions;
+import seedu.address.logic.commands.Command;
+
/**
* Represents an error which occurs during execution of a {@link Command}.
*/
diff --git a/src/main/java/seedu/address/logic/commands/jobs/AddDeliveryJobCommand.java b/src/main/java/seedu/address/logic/commands/jobs/AddDeliveryJobCommand.java
new file mode 100644
index 00000000000..9a96715e26a
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/jobs/AddDeliveryJobCommand.java
@@ -0,0 +1,176 @@
+package seedu.address.logic.commands.jobs;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DELIVERY_DATE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DELIVERY_SLOT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EARNING;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_RECIPIENT_ID;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SENDER_ID;
+
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.jobs.DeliveryJob;
+
+/**
+ * Adds a delivery job to the delivery job system.
+ */
+public class AddDeliveryJobCommand extends DeliveryJobCommand {
+
+ public static final String COMMAND_WORD = "add_job";
+
+ public static final String MESSAGE_SENDER_CONSTRAINT = "Sender ID should contain "
+ + "numeric and alphabetical characters."
+ + "\nAnd it should not be blank.";
+
+ public static final String MESSAGE_RECIPIENT_CONSTRAINT = "Recipient ID should contain numeric"
+ + "and alphabetical characters"
+ + "\nand it should not be blank.";
+
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a job to the delivery job system. "
+ + "\nParameters: "
+ + PREFIX_SENDER_ID + "SENDER ID "
+ + PREFIX_RECIPIENT_ID + "RECIPIENT ID "
+ + PREFIX_EARNING + "Earning ["
+ + PREFIX_DELIVERY_DATE + "DELIVERY DATE] ["
+ + PREFIX_DELIVERY_SLOT + "DELIVERY SLOT] "
+ + "\nExample: " + COMMAND_WORD + " "
+ + PREFIX_SENDER_ID + "ALE874 "
+ + PREFIX_RECIPIENT_ID + "DAV910 "
+ + PREFIX_EARNING + "20"
+ + PREFIX_DELIVERY_DATE + "2023-03-03 "
+ + PREFIX_DELIVERY_SLOT + "3 ";
+
+ public static final String MESSAGE_SUCCESS = "New job added: %1$s";
+ public static final String MESSAGE_SUCCESS_WITH_NO_DATE = "New job added, with"
+ + " unspecified delivery date: \n%1$s";
+ public static final String MESSAGE_SUCCESS_WITH_NO_SLOT = "New job added, "
+ + "with unspecified delivery slot: \n%1$s";
+ public static final String MESSAGE_SUCCESS_WITH_MISSING_2_ARGS = "New job added, with unspecified "
+ + "%s and %s: \n%s";
+ public static final String MESSAGE_SUCCESS_LATE_SLOT = "New job added with "
+ + "slot outside of working hours: \n%1$s"
+ + "\nValid slot should only be in the range [1,5]";
+ public static final String MESSAGE_SUCCESS_LATE_SLOT_NO_DATE = "New job added with: "
+ + "\nslot outside of working hours and unspecified delivery date: \n%1$s"
+ + "\nValid slot should only be in the range [1,5]";
+
+ public static final String MESSAGE_SUCCESS_LATE_SLOT_NO_EARN = "New job added with: "
+ + "\nslot outside of working hours and unspecified earning: \n%1$s"
+ + "\nValid slot should only be in the range [1,5]";
+ public static final String MESSAGE_SUCCESS_LATE_SLOT_NO_DATE_EARN = "New job added with: "
+ + "\nslot outside of working hours, unspecified delivery date and earning: \n%1$s"
+ + "\nValid slot should only be in the range [1,5]";
+
+
+ public static final String MESSAGE_SUCCESS_WITH_NO_EARN = "New job added with unspecified earning: \n%1$s";
+ public static final String MESSAGE_SUCCESS_WITH_NO_DATE_SLOT_EARN = "New job added with unspecified"
+ + " delivery date, slot and earning: \n%1$s";
+
+
+ public static final String MESSAGE_DUPLICATE_JOB = "This job already exists in the delivery job system";
+ public static final String MESSAGE_INVALID_SENDER = "Sender not found.";
+ public static final String MESSAGE_INVALID_RECIPIENT = "Recipient not found.";
+ public static final String MESSAGE_SAME_SENDER_RECIPIENT = "Sender and Recipient cannot be the same.";
+
+ private final DeliveryJob toAdd;
+
+ /**
+ * Creates an AddDeliveryJobCommand to add the specified {@code DeliveryJob}
+ */
+ public AddDeliveryJobCommand(DeliveryJob job) {
+ requireNonNull(job);
+ toAdd = job;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+
+ if (toAdd.getRecipientId().isEmpty()) {
+ throw new CommandException(MESSAGE_INVALID_RECIPIENT);
+ }
+
+ if (model.getPersonById(toAdd.getRecipientId()).isEmpty()) {
+ throw new CommandException(MESSAGE_INVALID_RECIPIENT);
+ }
+
+ if (toAdd.getSenderId().isEmpty()) {
+ throw new CommandException(MESSAGE_INVALID_SENDER);
+ }
+
+ if (model.getPersonById(toAdd.getSenderId()).isEmpty()) {
+ throw new CommandException(MESSAGE_INVALID_SENDER);
+ }
+
+ if (toAdd.getSenderId().equals(toAdd.getRecipientId())) {
+ throw new CommandException(MESSAGE_SAME_SENDER_RECIPIENT);
+ }
+
+ model.addDeliveryJob(toAdd);
+
+ if (toAdd.hasInvalidSlot()) {
+ if (toAdd.hasEarning() && toAdd.hasDate()) {
+ return new CommandResult(String.format(MESSAGE_SUCCESS_LATE_SLOT, toAdd));
+ } else if (toAdd.hasEarning() && (!toAdd.hasDate())) {
+ return new CommandResult(String.format(MESSAGE_SUCCESS_LATE_SLOT_NO_DATE, toAdd));
+ } else if ((!toAdd.hasEarning()) && toAdd.hasDate()) {
+ return new CommandResult(String.format(MESSAGE_SUCCESS_LATE_SLOT_NO_EARN, toAdd));
+ } else {
+ return new CommandResult(String.format(MESSAGE_SUCCESS_LATE_SLOT_NO_DATE_EARN, toAdd));
+ }
+ }
+
+ return getMessageForMissingArgs(toAdd.hasDate(), toAdd.hasSlot(), toAdd.hasEarning());
+ }
+
+ private CommandResult getMessageForMissingArgs(boolean hasDate, boolean hasSlot, boolean hasEarn) {
+ if (hasDate && hasSlot && hasEarn) {
+ return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd));
+ }
+
+ int numberOfMissingArgs = 0;
+ if (!hasDate) {
+ numberOfMissingArgs++;
+ }
+ if (!hasSlot) {
+ numberOfMissingArgs++;
+ }
+ if (!hasEarn) {
+ numberOfMissingArgs++;
+ }
+
+ if (numberOfMissingArgs == 1) {
+ if (!hasDate) {
+ return new CommandResult(String.format(MESSAGE_SUCCESS_WITH_NO_DATE, toAdd));
+ } else if (!hasEarn) {
+ return new CommandResult(String.format(MESSAGE_SUCCESS_WITH_NO_EARN, toAdd));
+ } else if (!hasSlot) {
+ return new CommandResult(String.format(MESSAGE_SUCCESS_WITH_NO_SLOT, toAdd));
+ }
+ } else if (numberOfMissingArgs == 2) {
+ if ((!hasDate) && (!hasSlot)) {
+ return new CommandResult(String.format(MESSAGE_SUCCESS_WITH_MISSING_2_ARGS,
+ "delivery date", "delivery slot", toAdd));
+ } else if ((!hasDate) && (!hasEarn)) {
+ return new CommandResult(String.format(MESSAGE_SUCCESS_WITH_MISSING_2_ARGS,
+ "delivery date", "earning", toAdd));
+ } else if ((!hasSlot) && (!hasEarn)) {
+ return new CommandResult(String.format(MESSAGE_SUCCESS_WITH_MISSING_2_ARGS,
+ "delivery slot", "earning", toAdd));
+ }
+ } else if (numberOfMissingArgs == 3) {
+ return new CommandResult(String.format(MESSAGE_SUCCESS_WITH_NO_DATE_SLOT_EARN, toAdd));
+
+ }
+ return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof AddDeliveryJobCommand // instanceof handles nulls
+ && toAdd.equals(((AddDeliveryJobCommand) other).toAdd));
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/jobs/CompleteDeliveryJobCommand.java b/src/main/java/seedu/address/logic/commands/jobs/CompleteDeliveryJobCommand.java
new file mode 100644
index 00000000000..5b7988f4ff6
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/jobs/CompleteDeliveryJobCommand.java
@@ -0,0 +1,60 @@
+package seedu.address.logic.commands.jobs;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+import java.util.Optional;
+
+import seedu.address.commons.core.Messages;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.model.Model;
+import seedu.address.model.jobs.DeliveryJob;
+
+/**
+ * Sets delivered status for jobs in the delivery job system.
+ */
+public class CompleteDeliveryJobCommand extends DeliveryJobCommand {
+
+ public static final String COMMAND_WORD_MARK = "com_job";
+ public static final String COMMAND_WORD_UNMARK = "uncom_job";
+
+ public static final String MESSAGE_SUCCESS = "Job now %s";
+
+ public static final String MESSAGE_USAGE = "Sets the delivered status as not/complete.\n"
+ + "Parameters: KEYWORD [JOB ID]...\n"
+ + "Example: " + COMMAND_WORD_MARK + "|" + COMMAND_WORD_UNMARK + " ";
+
+ private final String jobId;
+ private final Boolean setDelivered;
+
+ /**
+ * CompleteDeliveryJobCommand。
+ *
+ * @param jobId
+ * @param setDelivered
+ */
+ public CompleteDeliveryJobCommand(String jobId, Boolean setDelivered) {
+ requireNonNull(jobId);
+ requireNonNull(setDelivered);
+ this.jobId = jobId;
+ this.setDelivered = setDelivered;
+ }
+
+ @Override
+ public CommandResult execute(Model model) {
+ requireNonNull(model);
+ List lastShownList = model.getDeliveryJobList();
+ Optional deliveryJobToEdit = lastShownList.stream()
+ .filter(x -> x.getJobId().equals(jobId))
+ .findAny();
+
+ if (deliveryJobToEdit.isPresent()) {
+ DeliveryJob.Builder toEdit = new DeliveryJob.Builder().copy(deliveryJobToEdit.get());
+ toEdit.setDeliveredStatus(setDelivered);
+ model.setDeliveryJob(deliveryJobToEdit.get(), toEdit.build());
+ return new CommandResult(String.format(MESSAGE_SUCCESS, setDelivered ? "completed" : "not completed"));
+ } else {
+ return new CommandResult(Messages.MESSAGE_INVALID_JOB_ID);
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/jobs/DeleteDeliveryJobCommand.java b/src/main/java/seedu/address/logic/commands/jobs/DeleteDeliveryJobCommand.java
new file mode 100644
index 00000000000..106192d03a4
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/jobs/DeleteDeliveryJobCommand.java
@@ -0,0 +1,66 @@
+package seedu.address.logic.commands.jobs;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+import java.util.Optional;
+
+import seedu.address.commons.core.Messages;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.jobs.DeliveryJob;
+
+/**
+ * Deletes a job identified using it's displayed index from the job system.
+ */
+public class DeleteDeliveryJobCommand extends DeliveryJobCommand {
+
+ public static final String COMMAND_WORD = "delete_job";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Deletes the job identified by the job ID.\n"
+ + "Parameters: STRING job ID\n"
+ + "Example: " + COMMAND_WORD + " ALBE29E66F\n"
+ + "Tip: you can copy the job ID by\n"
+ + " selecting the job and press\n"
+ + " Ctrl+C\n"
+ + "Tip: you can delete job directly by\n"
+ + " selecting and press DEL key";
+ public static final String MESSAGE_DELETE_JOB_SUCCESS = "Deleted Job: %1$s";
+
+ private final String jobId;
+
+ /**
+ * Constructs a DeleteDeliveryJobCommand
+ * @param jobId of which job needs to be deleted
+ */
+ public DeleteDeliveryJobCommand(String jobId) {
+ requireNonNull(jobId);
+ this.jobId = jobId;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+
+ List lastShownList = model.getDeliveryJobList();
+ Optional deliveryJobToDelete = lastShownList.stream()
+ .filter(x -> x.getJobId().equals(jobId))
+ .findAny();
+
+ if (deliveryJobToDelete.isEmpty()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_JOB_ID);
+ }
+
+ model.deleteDeliveryJob(deliveryJobToDelete.get());
+ return new CommandResult(String.format(MESSAGE_DELETE_JOB_SUCCESS, deliveryJobToDelete.get().getJobId()));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof DeleteDeliveryJobCommand // instanceof handles nulls
+ && jobId.equals(((DeleteDeliveryJobCommand) other).jobId)); // state check
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/jobs/DeliveryJobCommand.java b/src/main/java/seedu/address/logic/commands/jobs/DeliveryJobCommand.java
new file mode 100644
index 00000000000..dd49caed87c
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/jobs/DeliveryJobCommand.java
@@ -0,0 +1,11 @@
+package seedu.address.logic.commands.jobs;
+
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandGroup;
+
+/**
+ * Defines PersonCommand.
+ */
+abstract class DeliveryJobCommand extends Command {
+ public static final CommandGroup COMMAND_GROUP = CommandGroup.JOB;
+}
diff --git a/src/main/java/seedu/address/logic/commands/jobs/EditDeliveryJobCommand.java b/src/main/java/seedu/address/logic/commands/jobs/EditDeliveryJobCommand.java
new file mode 100644
index 00000000000..f075f598691
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/jobs/EditDeliveryJobCommand.java
@@ -0,0 +1,388 @@
+package seedu.address.logic.commands.jobs;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DELIVERY_DATE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DELIVERY_SLOT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EARNING;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_IS_DELIVERED;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_JOB_ID;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_RECIPIENT_ID;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SENDER_ID;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_DELIVERY_JOBS;
+
+import java.util.List;
+import java.util.Optional;
+
+import seedu.address.commons.core.Messages;
+import seedu.address.commons.core.index.Index;
+import seedu.address.commons.util.CollectionUtil;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.jobs.DeliveryDate;
+import seedu.address.model.jobs.DeliveryJob;
+import seedu.address.model.jobs.DeliverySlot;
+import seedu.address.model.jobs.Earning;
+
+/**
+ * Edits the details of an existing job in the job system.
+ */
+public class EditDeliveryJobCommand extends DeliveryJobCommand {
+
+ public static final String COMMAND_WORD = "edit_job";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the job identified "
+ + "by the index number/job id used in the displayed job list. "
+ + "Existing values will be overwritten by the input values.\n"
+ + "Parameters: INDEX (must be a positive integer) ["
+ + PREFIX_JOB_ID + "JOB_ID] "
+ + "[" + PREFIX_SENDER_ID + "SENDER_ID] "
+ + "[" + PREFIX_RECIPIENT_ID + "RECIPIENT_ID] "
+ + "[" + PREFIX_DELIVERY_DATE + "DELIVERY_DATE] "
+ + "[" + PREFIX_DELIVERY_SLOT + "DELIVERY_SLOT] "
+ + "[" + PREFIX_EARNING + "EARN] "
+ + "[" + PREFIX_IS_DELIVERED + "COMPLETE_STATUS]\n"
+ + "OR\n"
+ + "Parameters: "
+ + PREFIX_JOB_ID + "JOB_ID "
+ + "[" + PREFIX_SENDER_ID + "SENDER_ID] "
+ + "[" + PREFIX_RECIPIENT_ID + "RECIPIENT_ID] "
+ + "[" + PREFIX_DELIVERY_DATE + "DELIVERY_DATE] "
+ + "[" + PREFIX_DELIVERY_SLOT + "DELIVERY_SLOT] "
+ + "[" + PREFIX_EARNING + "EARN] "
+ + "[" + PREFIX_IS_DELIVERED + "COMPLETE_STATUS]\n"
+ + "Example: " + COMMAND_WORD + " 1 "
+ + PREFIX_SENDER_ID + "ALE874 \n"
+ + COMMAND_WORD + " " + PREFIX_JOB_ID + "ALBE29E66F "
+ + PREFIX_IS_DELIVERED + "t";
+
+ public static final String MESSAGE_EDIT_JOB_SUCCESS = "Edited Job: %1$s";
+ public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided.";
+ public static final String MESSAGE_DUPLICATE_JOB = "This job already exists in the address book.";
+
+ private final Optional index;
+ private final EditDeliveryJobDescriptor editDeliveryJobDescriptor;
+
+ /**
+ * @param index of the job in the filtered job list to edit
+ * @param editDeliveryJobDescriptor details to edit the job with
+ */
+ public EditDeliveryJobCommand(Index index, EditDeliveryJobDescriptor editDeliveryJobDescriptor) {
+ requireNonNull(index);
+ requireNonNull(editDeliveryJobDescriptor);
+
+ this.index = Optional.of(index);
+ this.editDeliveryJobDescriptor = new EditDeliveryJobDescriptor(editDeliveryJobDescriptor);
+ }
+
+ /**
+ * @param editDeliveryJobDescriptor details to edit the job with
+ */
+ public EditDeliveryJobCommand(EditDeliveryJobDescriptor editDeliveryJobDescriptor) {
+ requireNonNull(editDeliveryJobDescriptor);
+
+ this.index = Optional.empty();
+ this.editDeliveryJobDescriptor = new EditDeliveryJobDescriptor(editDeliveryJobDescriptor);
+ }
+
+ /**
+ * Creates and returns a {@code job} with the details of
+ * {@code deliveryJobToEdit}
+ * edited with {@code editjobDescriptor}.
+ */
+ private static DeliveryJob createEditedDeliveryJob(DeliveryJob deliveryJobToEdit,
+ EditDeliveryJobDescriptor editjobDescriptor) {
+ assert deliveryJobToEdit != null;
+ DeliveryJob.Builder toEdit = new DeliveryJob.Builder();
+
+ toEdit.setJobId(deliveryJobToEdit.getJobId());
+
+ editjobDescriptor.getRecipient().ifPresentOrElse(val -> {
+ toEdit.setRecipient(val);
+ }, () -> {
+ toEdit.setRecipient(deliveryJobToEdit.getRecipientId());
+ });
+
+ editjobDescriptor.getSender().ifPresentOrElse(val -> {
+ toEdit.setSender(val);
+ }, () -> {
+ toEdit.setSender(deliveryJobToEdit.getSenderId());
+ });
+
+ editjobDescriptor.getDeliveryDate().ifPresentOrElse(val -> {
+ toEdit.setDeliveryDate(val.date);
+ }, () -> {
+ deliveryJobToEdit.getDeliveryDate().ifPresent(val -> {
+ editjobDescriptor.ifClearDeliveryDate(()-> {
+ toEdit.clearDeliveryDate();
+ }, () -> {
+ toEdit.setDeliveryDate(val.date);
+ });
+ });
+ });
+
+ editjobDescriptor.getDeliverySlot().ifPresentOrElse(val -> {
+ toEdit.setDeliverySlot(val.value);
+ }, () -> {
+ deliveryJobToEdit.getDeliverySlot().ifPresent(val -> {
+ editjobDescriptor.ifClearDeliverySlot(()-> {
+ toEdit.clearDeliverySlot();
+ }, () -> {
+ toEdit.setDeliverySlot(val.value);
+ });
+ });
+ });
+
+ editjobDescriptor.getEarning().ifPresentOrElse(val -> {
+ toEdit.setEarning(val.value);
+ }, () -> {
+ deliveryJobToEdit.getEarning().ifPresent(val -> {
+ toEdit.setEarning(val.value);
+ });
+ });
+
+ editjobDescriptor.getDelivered().ifPresentOrElse(val -> {
+ toEdit.setDeliveredStatus(val);
+ }, () -> {
+ toEdit.setDeliveredStatus(deliveryJobToEdit.getDeliveredStatus());
+ });
+
+ editjobDescriptor.getDescription().ifPresentOrElse(val -> {
+ toEdit.setDescription(val);
+ }, () -> {
+ toEdit.setDescription(deliveryJobToEdit.getDescription());
+ });
+
+ return toEdit.build();
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ List lastShownList = model.getDeliveryJobList();
+
+ Optional deliveryJobToEdit;
+ if (index.isPresent()) {
+ if (index.get().getZeroBased() >= lastShownList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+ deliveryJobToEdit = Optional.of(lastShownList.get(index.get().getZeroBased()));
+ } else {
+ if (!editDeliveryJobDescriptor.getJobId().isPresent()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_JOB_ID);
+ }
+ deliveryJobToEdit = lastShownList.stream().filter(x -> x.getJobId().equals(editDeliveryJobDescriptor.jobId))
+ .findAny();
+ }
+
+ if (deliveryJobToEdit.isPresent()) {
+ DeliveryJob editedDeliveryJob = createEditedDeliveryJob(deliveryJobToEdit.get(), editDeliveryJobDescriptor);
+
+ if (!deliveryJobToEdit.get().isSameDeliveryJob(editedDeliveryJob)
+ && model.hasDeliveryJob(editedDeliveryJob)) {
+ throw new CommandException(MESSAGE_DUPLICATE_JOB);
+ }
+
+ model.setDeliveryJob(deliveryJobToEdit.get(), editedDeliveryJob);
+ model.updateFilteredDeliveryJobList(PREDICATE_SHOW_ALL_DELIVERY_JOBS);
+ return new CommandResult(String.format(MESSAGE_EDIT_JOB_SUCCESS, editedDeliveryJob));
+ } else {
+ throw new CommandException(Messages.MESSAGE_INVALID_JOB_ID);
+ }
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ // short circuit if same object
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof EditDeliveryJobCommand)) {
+ return false;
+ }
+
+ // state check
+ EditDeliveryJobCommand e = (EditDeliveryJobCommand) other;
+ return index.equals(e.index)
+ && editDeliveryJobDescriptor.equals(e.editDeliveryJobDescriptor);
+ }
+
+ /**
+ * Stores the details to edit the job with. Each non-empty field value will
+ * replace the
+ * corresponding field value of the job.
+ */
+ public static class EditDeliveryJobDescriptor {
+ // Identity fields
+ private String jobId;
+
+ // Delivery informations
+ private String recipient;
+ private String sender;
+ private DeliveryDate deliveryDate;
+ private DeliverySlot deliverySlot;
+ private Earning earning;
+ private boolean isDelivered;
+ private String description;
+
+ private boolean isDateCleared = false;
+ private boolean isSlotCleared = false;
+
+ public EditDeliveryJobDescriptor() {
+ }
+
+ /**
+ * Copy constructor.
+ * A defensive copy of {@code tags} is used internally.
+ */
+ public EditDeliveryJobDescriptor(EditDeliveryJobDescriptor toCopy) {
+ setJobId(toCopy.jobId);
+ setSender(toCopy.sender);
+ setRecipient(toCopy.recipient);
+ setDeliveryDate(toCopy.deliveryDate);
+ setDeliverySlot(toCopy.deliverySlot);
+ setEarning(toCopy.earning);
+ setDelivered(toCopy.isDelivered);
+ setDescription(toCopy.description);
+ isDateCleared = toCopy.isDateCleared;
+ isSlotCleared = toCopy.isSlotCleared;
+ }
+
+ /**
+ * Returns true if at least one field is edited.
+ */
+ public boolean isAnyFieldEdited() {
+ return CollectionUtil.isAnyNonNull(jobId, sender, recipient, deliveryDate, deliverySlot, isDelivered);
+ }
+
+ public Optional getJobId() {
+ return Optional.ofNullable(jobId);
+ }
+
+ public void setJobId(String jobId) {
+ this.jobId = jobId;
+ }
+
+ public Optional getRecipient() {
+ return Optional.ofNullable(recipient);
+ }
+
+ public void setRecipient(String recipient) {
+ this.recipient = recipient;
+ }
+
+ public Optional getSender() {
+ return Optional.ofNullable(sender);
+ }
+
+ public void setSender(String sender) {
+ this.sender = sender;
+ }
+
+ public Optional getDeliveryDate() {
+ return Optional.ofNullable(deliveryDate);
+ }
+
+ public void setDeliveryDate(DeliveryDate deliveryDate) {
+ this.deliveryDate = deliveryDate;
+ }
+
+ public Optional getDeliverySlot() {
+ return Optional.ofNullable(deliverySlot);
+ }
+
+ public void setDeliverySlot(DeliverySlot deliverySlot) {
+ this.deliverySlot = deliverySlot;
+ }
+
+ public Optional getEarning() {
+ return Optional.ofNullable(earning);
+ }
+
+ public void setEarning(Earning earning) {
+ this.earning = earning;
+ }
+
+ public Optional getDelivered() {
+ return Optional.ofNullable(isDelivered);
+ }
+
+ public void setDelivered(boolean isDelivered) {
+ this.isDelivered = isDelivered;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public Optional getDescription() {
+ return Optional.ofNullable(description);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ // short circuit if same object
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof EditDeliveryJobDescriptor)) {
+ return false;
+ }
+
+ // state check
+ EditDeliveryJobDescriptor e = (EditDeliveryJobDescriptor) other;
+
+ return getJobId().equals(e.getJobId())
+ && getSender().equals(e.getSender())
+ && getRecipient().equals(e.getRecipient())
+ && getDeliveryDate().equals(e.getDeliveryDate())
+ && getDeliverySlot().equals(e.getDeliverySlot())
+ && getEarning().equals(e.getEarning())
+ && getDelivered().equals(e.getDelivered());
+ }
+
+ /**
+ * Sets the clear slot state.
+ */
+ public void clearDeliverySlot() {
+ isSlotCleared = true;
+ }
+
+ /**
+ * Sets the clear date state.
+ */
+ public void clearDeliveryDate() {
+ isDateCleared = true;
+ }
+
+ /**
+ * Handles slot clearing.
+ * @param s
+ * @param f
+ */
+ public void ifClearDeliverySlot(Runnable s, Runnable f) {
+ if (isSlotCleared) {
+ s.run();
+ } else {
+ f.run();
+ }
+ }
+
+ /**
+ * Handles date clearing.
+ * @param s
+ * @param f
+ */
+ public void ifClearDeliveryDate(Runnable s, Runnable f) {
+ if (isDateCleared) {
+ s.run();
+ } else {
+ f.run();
+ }
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/jobs/FindDeliveryJobCommand.java b/src/main/java/seedu/address/logic/commands/jobs/FindDeliveryJobCommand.java
new file mode 100644
index 00000000000..455dedd3870
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/jobs/FindDeliveryJobCommand.java
@@ -0,0 +1,67 @@
+package seedu.address.logic.commands.jobs;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.function.Predicate;
+
+import seedu.address.commons.core.Messages;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.model.Model;
+import seedu.address.model.jobs.DeliveryJob;
+
+/**
+ * Finds and lists all jobs in job system whose name contains any of the argument keywords.
+ * Keyword matching is case insensitive.
+ */
+public class FindDeliveryJobCommand extends DeliveryJobCommand {
+
+ public static final String COMMAND_WORD = "find_job";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all jobs which contain any of "
+ + "the specified keywords (case-insensitive) and displays them as a list with index numbers.\n"
+ + "Parameters: keyword_type/KEYWORD [more_type/MORE_KEYWORDS]...\n"
+ + "Format: " + COMMAND_WORD + " [ji/JOB_ID]" + " [si/SENDER_ID] [ri/RECIPIENT_ID] "
+ + "[date/DELIVER_DATE] [slot/DELIVERY_SLOT] [earn/EARNING]..."
+ + "\nExample: " + COMMAND_WORD + " si/ALE874";
+
+ private final Predicate predicate;
+ private final String query;
+
+ /**
+ * Constructs a FindDeliveryJobCommand.
+ * @param predicate
+ * @param query for feedback
+ */
+ public FindDeliveryJobCommand(Predicate predicate, String query) {
+ this.predicate = predicate;
+ this.query = query;
+ }
+
+ /**
+ * Constructs a FindDeliveryJobCommand without feedback.
+ * @param predicate
+ * @param query for feedback
+ */
+ public FindDeliveryJobCommand(Predicate predicate) {
+ this.predicate = predicate;
+ this.query = "";
+ }
+
+ @Override
+ public CommandResult execute(Model model) {
+ requireNonNull(model);
+ model.updateFilteredDeliveryJobList(predicate);
+ return new CommandResult(
+ String.format(
+ Messages.MESSAGE_DELIVERY_JOB_LISTED_OVERVIEW + "\nQuery options:\n" + query,
+ model.getFilteredDeliveryJobList().size()),
+ false, false, false, false, false, false, false, false);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof FindDeliveryJobCommand // instanceof handles nulls
+ && predicate.equals(((FindDeliveryJobCommand) other).predicate)); // state check
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/jobs/ImportDeliveryJobCommand.java b/src/main/java/seedu/address/logic/commands/jobs/ImportDeliveryJobCommand.java
new file mode 100644
index 00000000000..5af6abe269a
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/jobs/ImportDeliveryJobCommand.java
@@ -0,0 +1,92 @@
+package seedu.address.logic.commands.jobs;
+
+import static java.util.Objects.requireNonNull;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.List;
+
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.logic.parser.jobs.ImportDeliveryJobCommandParser;
+import seedu.address.model.Model;
+import seedu.address.model.jobs.DeliveryJob;
+import seedu.address.model.person.Person;
+
+/**
+ * Mass imports delivery jobs from CSV file to the delivery job system.
+ */
+public class ImportDeliveryJobCommand extends DeliveryJobCommand {
+
+ public static final String COMMAND_WORD = "import_job";
+ public static final String MESSAGE_EMPTY_FILE = "File is empty";
+ public static final String MESSAGE_SUCCESS = "File is imported";
+ public static final String MESSAGE_WRONG_FILE = "File type is unsupported. Please upload a CSV file and check if "
+ + "file extension is named .csv";
+
+ private final File toAdd;
+
+ /**
+ * Creates an ImportDeliveryJobCommand to mass add delivery jobs
+ * from file.
+ */
+ public ImportDeliveryJobCommand(File file) {
+ requireNonNull(file);
+ toAdd = file;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException, ParseException, FileNotFoundException {
+ requireNonNull(model);
+ List listOfCustomers = new ArrayList<>();
+
+ if (!getFileExtensions(toAdd).equals("csv")) {
+ throw new CommandException(MESSAGE_WRONG_FILE);
+ }
+ if (toAdd.length() == 0) {
+ throw new CommandException(MESSAGE_EMPTY_FILE);
+ }
+
+ List listOfAddDeliveryJob;
+ listOfAddDeliveryJob = ImportDeliveryJobCommandParser.parse(toAdd, listOfCustomers);
+
+ for (int i = 0; i < listOfCustomers.size(); i++) {
+ Person customer = listOfCustomers.get(i);
+ if (!model.hasPerson(customer) && !customer.getPersonId().equals("null")) {
+ model.addPerson(customer);
+ }
+ }
+
+ for (int i = 0; i < listOfAddDeliveryJob.size(); i++) {
+ if (!model.hasDeliveryJob(listOfAddDeliveryJob.get(i))) {
+ model.addDeliveryJob(listOfAddDeliveryJob.get(i));
+ }
+ }
+
+ return new CommandResult(MESSAGE_SUCCESS);
+ }
+
+ /**
+ * Gets type of file extension.
+ *
+ * @param file File that extension is checked
+ * @return String file extension name
+ */
+ public String getFileExtensions(File file) {
+ String fileName = file.getName();
+ String extension = "";
+ int i = fileName.lastIndexOf(".");
+ extension = fileName.substring(i + 1, fileName.length());
+ return extension;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof ImportDeliveryJobCommand // instanceof handles nulls
+ && toAdd.equals(((ImportDeliveryJobCommand) other).toAdd));
+ }
+}
+
diff --git a/src/main/java/seedu/address/logic/commands/jobs/ListDeliveryJobCommand.java b/src/main/java/seedu/address/logic/commands/jobs/ListDeliveryJobCommand.java
new file mode 100644
index 00000000000..3ca1b87f85e
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/jobs/ListDeliveryJobCommand.java
@@ -0,0 +1,24 @@
+package seedu.address.logic.commands.jobs;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_DELIVERY_JOBS;
+
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.model.Model;
+
+/**
+ * Lists all jobs in the delivery job system to the user.
+ */
+public class ListDeliveryJobCommand extends DeliveryJobCommand {
+
+ public static final String COMMAND_WORD = "list_job";
+
+ public static final String MESSAGE_SUCCESS = "Listed all jobs";
+
+ @Override
+ public CommandResult execute(Model model) {
+ requireNonNull(model);
+ model.updateFilteredDeliveryJobList(PREDICATE_SHOW_ALL_DELIVERY_JOBS);
+ return new CommandResult(MESSAGE_SUCCESS, false, false, false, false, false, false, false, false);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/address/logic/commands/person/AddCommand.java
similarity index 94%
rename from src/main/java/seedu/address/logic/commands/AddCommand.java
rename to src/main/java/seedu/address/logic/commands/person/AddCommand.java
index 71656d7c5c8..e02d60a603b 100644
--- a/src/main/java/seedu/address/logic/commands/AddCommand.java
+++ b/src/main/java/seedu/address/logic/commands/person/AddCommand.java
@@ -1,4 +1,4 @@
-package seedu.address.logic.commands;
+package seedu.address.logic.commands.person;
import static java.util.Objects.requireNonNull;
import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
@@ -7,14 +7,16 @@
import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
+import seedu.address.logic.commands.CommandResult;
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.model.Model;
import seedu.address.model.person.Person;
+
/**
* Adds a person to the address book.
*/
-public class AddCommand extends Command {
+public class AddCommand extends PersonCommand {
public static final String COMMAND_WORD = "add";
diff --git a/src/main/java/seedu/address/logic/commands/ClearCommand.java b/src/main/java/seedu/address/logic/commands/person/ClearCommand.java
similarity index 65%
rename from src/main/java/seedu/address/logic/commands/ClearCommand.java
rename to src/main/java/seedu/address/logic/commands/person/ClearCommand.java
index 9c86b1fa6e4..7cc2ebf27fd 100644
--- a/src/main/java/seedu/address/logic/commands/ClearCommand.java
+++ b/src/main/java/seedu/address/logic/commands/person/ClearCommand.java
@@ -1,14 +1,17 @@
-package seedu.address.logic.commands;
+package seedu.address.logic.commands.person;
import static java.util.Objects.requireNonNull;
+import seedu.address.logic.commands.CommandGroup;
+import seedu.address.logic.commands.CommandResult;
import seedu.address.model.AddressBook;
import seedu.address.model.Model;
/**
* Clears the address book.
*/
-public class ClearCommand extends Command {
+public class ClearCommand extends PersonCommand {
+ public static final CommandGroup COMMAND_GROUP = CommandGroup.PERSON;
public static final String COMMAND_WORD = "clear";
public static final String MESSAGE_SUCCESS = "Address book has been cleared!";
diff --git a/src/main/java/seedu/address/logic/commands/DeleteCommand.java b/src/main/java/seedu/address/logic/commands/person/DeleteCommand.java
similarity index 92%
rename from src/main/java/seedu/address/logic/commands/DeleteCommand.java
rename to src/main/java/seedu/address/logic/commands/person/DeleteCommand.java
index 02fd256acba..ab4e4ddd90e 100644
--- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java
+++ b/src/main/java/seedu/address/logic/commands/person/DeleteCommand.java
@@ -1,4 +1,4 @@
-package seedu.address.logic.commands;
+package seedu.address.logic.commands.person;
import static java.util.Objects.requireNonNull;
@@ -6,6 +6,7 @@
import seedu.address.commons.core.Messages;
import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.CommandResult;
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.model.Model;
import seedu.address.model.person.Person;
@@ -13,7 +14,7 @@
/**
* Deletes a person identified using it's displayed index from the address book.
*/
-public class DeleteCommand extends Command {
+public class DeleteCommand extends PersonCommand {
public static final String COMMAND_WORD = "delete";
diff --git a/src/main/java/seedu/address/logic/commands/EditCommand.java b/src/main/java/seedu/address/logic/commands/person/EditCommand.java
similarity index 91%
rename from src/main/java/seedu/address/logic/commands/EditCommand.java
rename to src/main/java/seedu/address/logic/commands/person/EditCommand.java
index 7e36114902f..d9c075536b8 100644
--- a/src/main/java/seedu/address/logic/commands/EditCommand.java
+++ b/src/main/java/seedu/address/logic/commands/person/EditCommand.java
@@ -1,4 +1,4 @@
-package seedu.address.logic.commands;
+package seedu.address.logic.commands.person;
import static java.util.Objects.requireNonNull;
import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
@@ -17,6 +17,7 @@
import seedu.address.commons.core.Messages;
import seedu.address.commons.core.index.Index;
import seedu.address.commons.util.CollectionUtil;
+import seedu.address.logic.commands.CommandResult;
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.model.Model;
import seedu.address.model.person.Address;
@@ -29,7 +30,7 @@
/**
* Edits the details of an existing person in the address book.
*/
-public class EditCommand extends Command {
+public class EditCommand extends PersonCommand {
public static final String COMMAND_WORD = "edit";
@@ -54,7 +55,7 @@ public class EditCommand extends Command {
private final EditPersonDescriptor editPersonDescriptor;
/**
- * @param index of the person in the filtered person list to edit
+ * @param index of the person in the filtered person list to edit
* @param editPersonDescriptor details to edit the person with
*/
public EditCommand(Index index, EditPersonDescriptor editPersonDescriptor) {
@@ -65,6 +66,23 @@ public EditCommand(Index index, EditPersonDescriptor editPersonDescriptor) {
this.editPersonDescriptor = new EditPersonDescriptor(editPersonDescriptor);
}
+ /**
+ * Creates and returns a {@code Person} with the details of {@code personToEdit}
+ * edited with {@code editPersonDescriptor}.
+ */
+ private static Person createEditedPerson(Person personToEdit, EditPersonDescriptor editPersonDescriptor) {
+ assert personToEdit != null;
+
+ String personId = editPersonDescriptor.getPersonId().orElse(personToEdit.getPersonId());
+ Name updatedName = editPersonDescriptor.getName().orElse(personToEdit.getName());
+ Phone updatedPhone = editPersonDescriptor.getPhone().orElse(personToEdit.getPhone());
+ Email updatedEmail = editPersonDescriptor.getEmail().orElse(personToEdit.getEmail());
+ Address updatedAddress = editPersonDescriptor.getAddress().orElse(personToEdit.getAddress());
+ Set updatedTags = editPersonDescriptor.getTags().orElse(personToEdit.getTags());
+
+ return new Person(personId, updatedName, updatedPhone, updatedEmail, updatedAddress, updatedTags);
+ }
+
@Override
public CommandResult execute(Model model) throws CommandException {
requireNonNull(model);
@@ -86,22 +104,6 @@ public CommandResult execute(Model model) throws CommandException {
return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, editedPerson));
}
- /**
- * Creates and returns a {@code Person} with the details of {@code personToEdit}
- * edited with {@code editPersonDescriptor}.
- */
- private static Person createEditedPerson(Person personToEdit, EditPersonDescriptor editPersonDescriptor) {
- assert personToEdit != null;
-
- Name updatedName = editPersonDescriptor.getName().orElse(personToEdit.getName());
- Phone updatedPhone = editPersonDescriptor.getPhone().orElse(personToEdit.getPhone());
- Email updatedEmail = editPersonDescriptor.getEmail().orElse(personToEdit.getEmail());
- Address updatedAddress = editPersonDescriptor.getAddress().orElse(personToEdit.getAddress());
- Set updatedTags = editPersonDescriptor.getTags().orElse(personToEdit.getTags());
-
- return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedTags);
- }
-
@Override
public boolean equals(Object other) {
// short circuit if same object
@@ -125,13 +127,15 @@ public boolean equals(Object other) {
* corresponding field value of the person.
*/
public static class EditPersonDescriptor {
+ private String personId;
private Name name;
private Phone phone;
private Email email;
private Address address;
private Set tags;
- public EditPersonDescriptor() {}
+ public EditPersonDescriptor() {
+ }
/**
* Copy constructor.
@@ -152,44 +156,44 @@ public boolean isAnyFieldEdited() {
return CollectionUtil.isAnyNonNull(name, phone, email, address, tags);
}
- public void setName(Name name) {
- this.name = name;
+ public void setPersonId(String personId) {
+ this.personId = personId;
+ }
+
+ public Optional getPersonId() {
+ return Optional.ofNullable(personId);
}
public Optional getName() {
return Optional.ofNullable(name);
}
- public void setPhone(Phone phone) {
- this.phone = phone;
+ public void setName(Name name) {
+ this.name = name;
}
public Optional getPhone() {
return Optional.ofNullable(phone);
}
- public void setEmail(Email email) {
- this.email = email;
+ public void setPhone(Phone phone) {
+ this.phone = phone;
}
public Optional getEmail() {
return Optional.ofNullable(email);
}
- public void setAddress(Address address) {
- this.address = address;
+ public void setEmail(Email email) {
+ this.email = email;
}
public Optional getAddress() {
return Optional.ofNullable(address);
}
- /**
- * Sets {@code tags} to this object's {@code tags}.
- * A defensive copy of {@code tags} is used internally.
- */
- public void setTags(Set tags) {
- this.tags = (tags != null) ? new HashSet<>(tags) : null;
+ public void setAddress(Address address) {
+ this.address = address;
}
/**
@@ -201,6 +205,14 @@ public Optional> getTags() {
return (tags != null) ? Optional.of(Collections.unmodifiableSet(tags)) : Optional.empty();
}
+ /**
+ * Sets {@code tags} to this object's {@code tags}.
+ * A defensive copy of {@code tags} is used internally.
+ */
+ public void setTags(Set tags) {
+ this.tags = (tags != null) ? new HashSet<>(tags) : null;
+ }
+
@Override
public boolean equals(Object other) {
// short circuit if same object
diff --git a/src/main/java/seedu/address/logic/commands/FindCommand.java b/src/main/java/seedu/address/logic/commands/person/FindCommand.java
similarity index 84%
rename from src/main/java/seedu/address/logic/commands/FindCommand.java
rename to src/main/java/seedu/address/logic/commands/person/FindCommand.java
index d6b19b0a0de..15222b7715a 100644
--- a/src/main/java/seedu/address/logic/commands/FindCommand.java
+++ b/src/main/java/seedu/address/logic/commands/person/FindCommand.java
@@ -1,8 +1,9 @@
-package seedu.address.logic.commands;
+package seedu.address.logic.commands.person;
import static java.util.Objects.requireNonNull;
import seedu.address.commons.core.Messages;
+import seedu.address.logic.commands.CommandResult;
import seedu.address.model.Model;
import seedu.address.model.person.NameContainsKeywordsPredicate;
@@ -10,7 +11,7 @@
* Finds and lists all persons in address book whose name contains any of the argument keywords.
* Keyword matching is case insensitive.
*/
-public class FindCommand extends Command {
+public class FindCommand extends PersonCommand {
public static final String COMMAND_WORD = "find";
@@ -30,7 +31,8 @@ public CommandResult execute(Model model) {
requireNonNull(model);
model.updateFilteredPersonList(predicate);
return new CommandResult(
- String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredPersonList().size()));
+ String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredPersonList().size()),
+ false, false, false, false, false, false, false, false);
}
@Override
diff --git a/src/main/java/seedu/address/logic/commands/ListCommand.java b/src/main/java/seedu/address/logic/commands/person/ListCommand.java
similarity index 57%
rename from src/main/java/seedu/address/logic/commands/ListCommand.java
rename to src/main/java/seedu/address/logic/commands/person/ListCommand.java
index 84be6ad2596..91e9633f09a 100644
--- a/src/main/java/seedu/address/logic/commands/ListCommand.java
+++ b/src/main/java/seedu/address/logic/commands/person/ListCommand.java
@@ -1,17 +1,19 @@
-package seedu.address.logic.commands;
+package seedu.address.logic.commands.person;
import static java.util.Objects.requireNonNull;
import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
+import seedu.address.logic.commands.CommandGroup;
+import seedu.address.logic.commands.CommandResult;
import seedu.address.model.Model;
/**
* Lists all persons in the address book to the user.
*/
-public class ListCommand extends Command {
+public class ListCommand extends PersonCommand {
+ public static final CommandGroup COMMAND_GROUP = CommandGroup.GENERAL;
public static final String COMMAND_WORD = "list";
-
public static final String MESSAGE_SUCCESS = "Listed all persons";
@@ -19,6 +21,6 @@ public class ListCommand extends Command {
public CommandResult execute(Model model) {
requireNonNull(model);
model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
- return new CommandResult(MESSAGE_SUCCESS);
+ return new CommandResult(MESSAGE_SUCCESS, false, false, false, false, false, false, true, false);
}
}
diff --git a/src/main/java/seedu/address/logic/commands/person/PersonCommand.java b/src/main/java/seedu/address/logic/commands/person/PersonCommand.java
new file mode 100644
index 00000000000..6b931b16231
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/person/PersonCommand.java
@@ -0,0 +1,11 @@
+package seedu.address.logic.commands.person;
+
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandGroup;
+
+/**
+ * Defines PersonCommand.
+ */
+abstract class PersonCommand extends Command {
+ public static final CommandGroup COMMAND_GROUP = CommandGroup.PERSON;
+}
diff --git a/src/main/java/seedu/address/logic/commands/reminder/AddReminderCommand.java b/src/main/java/seedu/address/logic/commands/reminder/AddReminderCommand.java
new file mode 100644
index 00000000000..dd0a1e3c80c
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/reminder/AddReminderCommand.java
@@ -0,0 +1,62 @@
+package seedu.address.logic.commands.reminder;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DESCRIPTION;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_TIME;
+
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandGroup;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.reminder.Reminder;
+
+/**
+ * Adds a reminder to the address book.
+ */
+public class AddReminderCommand extends Command {
+ public static final CommandGroup COMMAND_GROUP = CommandGroup.REMINDER;
+
+ public static final String COMMAND_WORD = "add_reminder";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a reminder.\n"
+ + "Parameters: ["
+ + PREFIX_DESCRIPTION + "DESCRIPTION(Max 50 characters, including space!)] "
+ + PREFIX_TIME + "YYYY-MM-DD HH:MM"
+ + "\nExample: " + COMMAND_WORD + " "
+ + PREFIX_DESCRIPTION + "go home "
+ + PREFIX_TIME + "2023-03-03 17:00";
+
+ public static final String MESSAGE_SUCCESS = "New reminder added";
+
+ public static final String MESSAGE_DATE_TIME_CONSTRAINT = "Dates must be valid date "
+ + "- only contain numeric characters and spaces, "
+ + "and it should not be blank.\n"
+ + "Date must have format like this: YYYY-mm-DD"
+ + "\nTime must be a valid timing - only contains numeric characters and colon."
+ + "\nTime must be of format HH:mm";
+
+ private final Reminder toAdd;
+
+ /**
+ * Creates an AddReminderCommand to add the specified {@code Reminder}
+ */
+ public AddReminderCommand(Reminder reminder) {
+ requireNonNull(reminder);
+ toAdd = reminder;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ model.addReminder(toAdd);
+ return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof AddReminderCommand // instanceof handles nulls
+ && toAdd.equals(((AddReminderCommand) other).toAdd));
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/reminder/DeleteReminderCommand.java b/src/main/java/seedu/address/logic/commands/reminder/DeleteReminderCommand.java
new file mode 100644
index 00000000000..e7f944242f3
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/reminder/DeleteReminderCommand.java
@@ -0,0 +1,55 @@
+package seedu.address.logic.commands.reminder;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+
+import seedu.address.commons.core.Messages;
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandGroup;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.reminder.Reminder;
+
+/**
+ * Deletes a reminder using it's displayed index from notifications.
+ */
+public class DeleteReminderCommand extends Command {
+ public static final CommandGroup COMMAND_GROUP = CommandGroup.REMINDER;
+
+ public static final String COMMAND_WORD = "delete_reminder";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Deletes the reminder identified by the index number used in the displayed notification.\n"
+ + "Parameters: INDEX (must be a positive integer)\n"
+ + "Example: " + COMMAND_WORD + " 1";
+
+ public static final String MESSAGE_DELETE_REMINDER_SUCCESS = "Deleted Reminder";
+
+ private final int targetIndex;
+
+ public DeleteReminderCommand(int targetIndex) {
+ this.targetIndex = targetIndex - 1;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ List reminderList = model.getReminderList();
+
+ if (targetIndex > reminderList.size() - 1 || targetIndex < 0) {
+ throw new CommandException(Messages.MESSAGE_INVALID_REMINDER_DISPLAYED_INDEX);
+ }
+ model.deleteReminder(targetIndex);
+ return new CommandResult(String.format(MESSAGE_DELETE_REMINDER_SUCCESS));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof DeleteReminderCommand // instanceof handles nulls
+ && targetIndex == (((DeleteReminderCommand) other).targetIndex)); // state check
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/commands/reminder/ListReminderCommand.java b/src/main/java/seedu/address/logic/commands/reminder/ListReminderCommand.java
new file mode 100644
index 00000000000..b56585c3854
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/reminder/ListReminderCommand.java
@@ -0,0 +1,27 @@
+package seedu.address.logic.commands.reminder;
+
+import static java.util.Objects.requireNonNull;
+
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandGroup;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.model.Model;
+
+/**
+ * Lists all reminders as Notifications to the user.
+ */
+public class ListReminderCommand extends Command {
+ public static final CommandGroup COMMAND_GROUP = CommandGroup.REMINDER;
+
+ public static final String COMMAND_WORD = "list_reminder";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Lists all reminders.\n";
+ public static final String SHOWING_REMINDER_LIST_MESSAGE = "Opened reminder list window.";
+
+ @Override
+ public CommandResult execute(Model model) {
+ requireNonNull(model);
+ return new CommandResult(SHOWING_REMINDER_LIST_MESSAGE,
+ false, false, true, false, false);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/timetable/TimetableCommand.java b/src/main/java/seedu/address/logic/commands/timetable/TimetableCommand.java
new file mode 100644
index 00000000000..d0cd9bc0ccc
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/timetable/TimetableCommand.java
@@ -0,0 +1,49 @@
+package seedu.address.logic.commands.timetable;
+
+import static java.util.Objects.requireNonNull;
+
+import java.time.LocalDate;
+
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandGroup;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.jobs.sorters.SortbyTimeAndEarn;
+
+/**
+ * Formats full timetable instructions for every command for display.
+ */
+public class TimetableCommand extends Command {
+ public static final CommandGroup COMMAND_GROUP = CommandGroup.TIMETABLE;
+
+ public static final String COMMAND_WORD = "timetable";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Shows timetable of scheduled/uncompleted jobs, "
+ + "sorted based on increasing timing\n"
+ + "Example: " + COMMAND_WORD;
+
+ public static final String SHOWING_TIMETABLE_MESSAGE = "Opened timetable window of current week.";
+
+ public static final SortbyTimeAndEarn SORTER = new SortbyTimeAndEarn();
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+
+ model.updateFocusDate(LocalDate.now());
+ model.updateSortedDeliveryJobList(SORTER);
+ model.updateSortedDeliveryJobListByDate();
+ model.updateWeekDeliveryJobList(LocalDate.now());
+
+ return new CommandResult(SHOWING_TIMETABLE_MESSAGE, false, true, false, false, false);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || other instanceof TimetableCommand; // instanceof handles nulls
+ }
+}
+
diff --git a/src/main/java/seedu/address/logic/commands/timetable/TimetableCompletedCommand.java b/src/main/java/seedu/address/logic/commands/timetable/TimetableCompletedCommand.java
new file mode 100644
index 00000000000..cd554cbd521
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/timetable/TimetableCompletedCommand.java
@@ -0,0 +1,34 @@
+package seedu.address.logic.commands.timetable;
+
+import static java.util.Objects.requireNonNull;
+
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandGroup;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.jobs.sorters.SortbyTimeAndEarn;
+
+/**
+ * Finds and lists unscheduled jobs - those with invalid slot/date
+ */
+public class TimetableCompletedCommand extends Command {
+ public static final CommandGroup COMMAND_GROUP = CommandGroup.TIMETABLE;
+ public static final SortbyTimeAndEarn SORTER_BY_DATE = new SortbyTimeAndEarn();
+
+ public static final String COMMAND_WORD = "timetable_completed";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Lists completed jobs";
+ public static final String MESSAGE_SUCCESS = "Listed all completed jobs";
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ model.updateSortedDeliveryJobList(SORTER_BY_DATE);
+ model.getCompletedDeliveryJobList();
+
+ return new CommandResult(MESSAGE_SUCCESS, false, false, false, true, false, false, false);
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/commands/timetable/TimetableDateCommand.java b/src/main/java/seedu/address/logic/commands/timetable/TimetableDateCommand.java
new file mode 100644
index 00000000000..b3af6f0858b
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/timetable/TimetableDateCommand.java
@@ -0,0 +1,67 @@
+package seedu.address.logic.commands.timetable;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE;
+
+import java.time.LocalDate;
+
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandGroup;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.jobs.sorters.SortbyTimeAndEarn;
+
+/**
+ * Shows timetable of the requested week based on user's date input
+ */
+public class TimetableDateCommand extends Command {
+ public static final CommandGroup COMMAND_GROUP = CommandGroup.TIMETABLE;
+
+ public static final String COMMAND_WORD = "timetable_date";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Shows timetable of scheduled/uncompleted jobs in the week, "
+ + "based on user's input date\n"
+ + "Parameters: "
+ + PREFIX_DATE + "DATE "
+ + "\nExample: " + COMMAND_WORD + " "
+ + PREFIX_DATE + "2023-03-15";
+
+ public static final String SHOWING_TIMETABLE_MESSAGE = "Showed timetable of the week containing day: %s.";
+
+ public static final SortbyTimeAndEarn SORTER = new SortbyTimeAndEarn();
+ private final LocalDate jobDate;
+
+ /**
+ * Updates and shows timetable of jobs in week based on input date
+ * @param jobDate input date
+ */
+ public TimetableDateCommand(LocalDate jobDate) {
+ requireNonNull(jobDate);
+ this.jobDate = jobDate;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+
+ model.updateFocusDate(jobDate);
+ model.updateSortedDeliveryJobList(SORTER);
+ model.updateSortedDeliveryJobListByDate();
+ model.updateWeekDeliveryJobList(jobDate);
+
+ String showTimetableMessage = String.format(SHOWING_TIMETABLE_MESSAGE, jobDate.toString());
+ CommandResult commandResult = new CommandResult(showTimetableMessage, false, true, false, false, false);
+ commandResult.setShowTimetableDate(true);
+ return commandResult;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof TimetableDateCommand // instanceof handles nulls
+ && jobDate.equals(((TimetableDateCommand) other).jobDate));
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/commands/timetable/TimetableUnscheduleCommand.java b/src/main/java/seedu/address/logic/commands/timetable/TimetableUnscheduleCommand.java
new file mode 100644
index 00000000000..affcb841d2c
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/timetable/TimetableUnscheduleCommand.java
@@ -0,0 +1,34 @@
+package seedu.address.logic.commands.timetable;
+
+import static java.util.Objects.requireNonNull;
+
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandGroup;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.jobs.sorters.SortbyTimeAndEarn;
+
+/**
+ * Finds and lists unscheduled jobs - those with invalid slot/date
+ */
+public class TimetableUnscheduleCommand extends Command {
+ public static final CommandGroup COMMAND_GROUP = CommandGroup.TIMETABLE;
+ public static final SortbyTimeAndEarn SORTER_BY_DATE = new SortbyTimeAndEarn();
+
+ public static final String COMMAND_WORD = "timetable_unscheduled";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Lists unscheduled jobs with invalid slot/date";
+ public static final String MESSAGE_SUCCESS = "Listed all unscheduled jobs with invalid slot/date";
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ model.updateSortedDeliveryJobList(SORTER_BY_DATE);
+ model.getUnscheduledDeliveryJobList();
+
+ return new CommandResult(MESSAGE_SUCCESS, false, false, true, false, false, false, false);
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java b/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java
index 954c8e18f8e..5711b786037 100644
--- a/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java
+++ b/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java
@@ -15,7 +15,9 @@
*/
public class ArgumentMultimap {
- /** Prefixes mapped to their respective arguments**/
+ /**
+ * Prefixes mapped to their respective arguments
+ **/
private final Map> argMultimap = new HashMap<>();
/**
diff --git a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java b/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java
index 5c9aebfa488..3be8e89059d 100644
--- a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java
+++ b/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java
@@ -7,11 +7,11 @@
/**
* Tokenizes arguments string of the form: {@code preamble value value ...}
- * e.g. {@code some preamble text t/ 11.00 t/12.00 k/ m/ July} where prefixes are {@code t/ k/ m/}.
+ * e.g. {@code some preamble text t/ 11.00 t/12.00 k/ m/ July} where prefixes are {@code t/ k/ m/}.
* 1. An argument's value can be an empty string e.g. the value of {@code k/} in the above example.
* 2. Leading and trailing whitespaces of an argument value will be discarded.
* 3. An argument may be repeated and all its values will be accumulated e.g. the value of {@code t/}
- * in the above example.
+ * in the above example.
*/
public class ArgumentTokenizer {
@@ -21,7 +21,7 @@ public class ArgumentTokenizer {
*
* @param argsString Arguments string of the form: {@code preamble value value ...}
* @param prefixes Prefixes to tokenize the arguments string with
- * @return ArgumentMultimap object that maps prefixes to their arguments
+ * @return ArgumentMultimap object that maps prefixes to their arguments
*/
public static ArgumentMultimap tokenize(String argsString, Prefix... prefixes) {
List positions = findAllPrefixPositions(argsString, prefixes);
@@ -33,7 +33,7 @@ public static ArgumentMultimap tokenize(String argsString, Prefix... prefixes) {
*
* @param argsString Arguments string of the form: {@code preamble value value ...}
* @param prefixes Prefixes to find in the arguments string
- * @return List of zero-based prefix positions in the given arguments string
+ * @return List of zero-based prefix positions in the given arguments string
*/
private static List findAllPrefixPositions(String argsString, Prefix... prefixes) {
return Arrays.stream(prefixes)
@@ -62,7 +62,7 @@ private static List findPrefixPositions(String argsString, Prefi
* {@code argsString} starting from index {@code fromIndex}. An occurrence
* is valid if there is a whitespace before {@code prefix}. Returns -1 if no
* such occurrence can be found.
- *
+ *
* E.g if {@code argsString} = "e/hip/900", {@code prefix} = "p/" and
* {@code fromIndex} = 0, this method returns -1 as there are no valid
* occurrences of "p/" with whitespace before it. However, if
@@ -82,7 +82,7 @@ private static int findPrefixPosition(String argsString, String prefix, int from
*
* @param argsString Arguments string of the form: {@code preamble value value ...}
* @param prefixPositions Zero-based positions of all prefixes in {@code argsString}
- * @return ArgumentMultimap object that maps prefixes to their arguments
+ * @return ArgumentMultimap object that maps prefixes to their arguments
*/
private static ArgumentMultimap extractArguments(String argsString, List prefixPositions) {
@@ -114,8 +114,8 @@ private static ArgumentMultimap extractArguments(String argsString, List\\S+)(?.*)");
+
+ /**
+ * Returns the command group before actual parsing.
+ *
+ * @param userInput full user input string
+ * @return the command group on the user input
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public CommandGroup parseCommandGroup(String userInput) throws ParseException {
+ final Matcher matcher = BASIC_COMMAND_FORMAT.matcher(userInput.trim());
+ if (!matcher.matches()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE));
+ }
+
+ final String commandWord = matcher.group("commandWord");
+ switch (commandWord.toLowerCase()) {
+
+ case AddCommand.COMMAND_WORD:
+ case EditCommand.COMMAND_WORD:
+ case DeleteCommand.COMMAND_WORD:
+ case FindCommand.COMMAND_WORD:
+ case ClearCommand.COMMAND_WORD:
+ return FindCommand.COMMAND_GROUP;
+ case ListCommand.COMMAND_WORD:
+ case ExitCommand.COMMAND_WORD:
+ case HelpCommand.COMMAND_WORD:
+ return HelpCommand.COMMAND_GROUP;
+ case AddReminderCommand.COMMAND_WORD:
+ case ListReminderCommand.COMMAND_WORD:
+ case DeleteReminderCommand.COMMAND_WORD:
+ return DeleteReminderCommand.COMMAND_GROUP;
+ case TimetableCommand.COMMAND_WORD:
+ case TimetableDateCommand.COMMAND_WORD:
+ case TimetableUnscheduleCommand.COMMAND_WORD:
+ case TimetableCompletedCommand.COMMAND_WORD:
+ return TimetableCompletedCommand.COMMAND_GROUP;
+ case ListDeliveryJobCommand.COMMAND_WORD:
+ case AddDeliveryJobCommand.COMMAND_WORD:
+ case EditDeliveryJobCommand.COMMAND_WORD:
+ case FindDeliveryJobCommand.COMMAND_WORD:
+ case DeleteDeliveryJobCommand.COMMAND_WORD:
+ case CompleteDeliveryJobCommand.COMMAND_WORD_MARK:
+ case CompleteDeliveryJobCommand.COMMAND_WORD_UNMARK:
+ return CompleteDeliveryJobCommand.COMMAND_GROUP;
+ case StatisticsCommand.COMMAND_WORD:
+ return StatisticsCommand.COMMAND_GROUP;
+ default:
+ throw new ParseException(MESSAGE_UNKNOWN_COMMAND);
+ }
+ }
+
+ /**
+ * Parses user input into command for execution.
+ *
+ * @param userInput full user input string
+ * @return the command based on the user input
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public Command parseCommand(String userInput) throws ParseException {
+ final Matcher matcher = BASIC_COMMAND_FORMAT.matcher(userInput.trim());
+ if (!matcher.matches()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE));
+ }
+
+ final String commandWord = matcher.group("commandWord");
+ final String arguments = matcher.group("arguments");
+ switch (commandWord.toLowerCase()) {
+
+ case AddCommand.COMMAND_WORD:
+ return new AddCommandParser().parse(arguments);
+
+ case EditCommand.COMMAND_WORD:
+ return new EditCommandParser().parse(arguments);
+
+ case DeleteCommand.COMMAND_WORD:
+ return new DeleteCommandParser().parse(arguments);
+
+ case ClearCommand.COMMAND_WORD:
+ return new ClearCommand();
+
+ case FindCommand.COMMAND_WORD:
+ return new FindCommandParser().parse(arguments);
+
+ case ListCommand.COMMAND_WORD:
+ return new ListCommand();
+
+ case ExitCommand.COMMAND_WORD:
+ return new ExitCommand();
+
+ case HelpCommand.COMMAND_WORD:
+ return new HelpCommand();
+
+ case AddReminderCommand.COMMAND_WORD:
+ return new AddReminderParser().parse(arguments);
+
+ case ListReminderCommand.COMMAND_WORD:
+ return new ListReminderCommand();
+
+ case DeleteReminderCommand.COMMAND_WORD:
+ return new DeleteReminderParser().parse(arguments);
+
+ case TimetableCommand.COMMAND_WORD:
+ return new TimetableCommand();
+
+ case TimetableDateCommand.COMMAND_WORD:
+ return new TimetableDateCommandParser().parse(arguments);
+
+ case TimetableUnscheduleCommand.COMMAND_WORD:
+ return new TimetableUnscheduleCommand();
+
+ case TimetableCompletedCommand.COMMAND_WORD:
+ return new TimetableCompletedCommand();
+
+ case ListDeliveryJobCommand.COMMAND_WORD:
+ return new ListDeliveryJobCommand();
+
+ case AddDeliveryJobCommand.COMMAND_WORD:
+ return new AddDeliveryJobCommandParser().parse(arguments);
+
+ case EditDeliveryJobCommand.COMMAND_WORD:
+ return new EditDeliveryJobCommandParser().parse(arguments);
+
+ case FindDeliveryJobCommand.COMMAND_WORD:
+ return new FindDeliveryJobCommandParser().parse(arguments);
+
+ case DeleteDeliveryJobCommand.COMMAND_WORD:
+ return new DeleteDeliveryJobCommandParser().parse(arguments);
+
+ case CompleteDeliveryJobCommand.COMMAND_WORD_MARK:
+ return new CompleteDeliveryJobCommandParser(true).parse(arguments);
+
+ case CompleteDeliveryJobCommand.COMMAND_WORD_UNMARK:
+ return new CompleteDeliveryJobCommandParser(false).parse(arguments);
+
+ case StatisticsCommand.COMMAND_WORD:
+ return new StatisticsCommand();
+
+ default:
+ throw new ParseException(MESSAGE_UNKNOWN_COMMAND);
+ }
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/parser/Parser.java b/src/main/java/seedu/address/logic/parser/Parser.java
index d6551ad8e3f..ce644a9c6fd 100644
--- a/src/main/java/seedu/address/logic/parser/Parser.java
+++ b/src/main/java/seedu/address/logic/parser/Parser.java
@@ -10,6 +10,7 @@ public interface Parser {
/**
* Parses {@code userInput} into a command and returns it.
+ *
* @throws ParseException if {@code userInput} does not conform the expected format
*/
T parse(String userInput) throws ParseException;
diff --git a/src/main/java/seedu/address/logic/parser/ParserUtil.java b/src/main/java/seedu/address/logic/parser/ParserUtil.java
index b117acb9c55..e766de63701 100644
--- a/src/main/java/seedu/address/logic/parser/ParserUtil.java
+++ b/src/main/java/seedu/address/logic/parser/ParserUtil.java
@@ -2,6 +2,8 @@
import static java.util.Objects.requireNonNull;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
@@ -9,6 +11,7 @@
import seedu.address.commons.core.index.Index;
import seedu.address.commons.util.StringUtil;
import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.jobs.DeliveryDate;
import seedu.address.model.person.Address;
import seedu.address.model.person.Email;
import seedu.address.model.person.Name;
@@ -25,6 +28,7 @@ public class ParserUtil {
/**
* Parses {@code oneBasedIndex} into an {@code Index} and returns it. Leading and trailing whitespaces will be
* trimmed.
+ *
* @throws ParseException if the specified index is invalid (not non-zero unsigned integer).
*/
public static Index parseIndex(String oneBasedIndex) throws ParseException {
@@ -95,6 +99,24 @@ public static Email parseEmail(String email) throws ParseException {
return new Email(trimmedEmail);
}
+ /**
+ * Parses a {@code String date} into an {@code LocalDate date}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given {@code LocalDate} is invalid.
+ */
+ public static LocalDate parseDate(String date) throws ParseException {
+ requireNonNull(date);
+ String trimmedDate = date.trim();
+ if (!DeliveryDate.isValidDate(trimmedDate)) {
+ throw new ParseException(DeliveryDate.MESSAGE_CONSTRAINTS);
+ }
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+ LocalDate jobDate = LocalDate.parse(trimmedDate, formatter);
+ return jobDate;
+ }
+
+
/**
* Parses a {@code String tag} into a {@code Tag}.
* Leading and trailing whitespaces will be trimmed.
diff --git a/src/main/java/seedu/address/logic/parser/jobs/AddDeliveryJobCommandParser.java b/src/main/java/seedu/address/logic/parser/jobs/AddDeliveryJobCommandParser.java
new file mode 100644
index 00000000000..c4c36576c62
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/jobs/AddDeliveryJobCommandParser.java
@@ -0,0 +1,111 @@
+package seedu.address.logic.parser.jobs;
+
+import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DELIVERY_DATE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DELIVERY_SLOT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EARNING;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_RECIPIENT_ID;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SENDER_ID;
+
+import java.util.Optional;
+import java.util.logging.Logger;
+import java.util.stream.Stream;
+
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.logic.commands.jobs.AddDeliveryJobCommand;
+import seedu.address.logic.parser.ArgumentMultimap;
+import seedu.address.logic.parser.ArgumentTokenizer;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.Prefix;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.jobs.DeliveryDate;
+import seedu.address.model.jobs.DeliveryJob;
+import seedu.address.model.jobs.DeliverySlot;
+import seedu.address.model.jobs.Earning;
+
+/**
+ * Parses input arguments and creates a new AddDeliveryJobCommand object
+ */
+public class AddDeliveryJobCommandParser implements Parser {
+ private static final String MESSAGE_SLOT_MISSING = "Delivery slot missing, "
+ + "both date and slot are required for scheduling";
+ private static final String MESSAGE_DATE_MISSING = "Delivery date missing, "
+ + "both date and slot are required for scheduling";
+ private final Logger logger = LogsCenter.getLogger(getClass());
+
+ /**
+ * Returns true if none of the prefixes contains empty {@code Optional} values
+ * in the given
+ * {@code ArgumentMultimap}.
+ */
+ private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) {
+ return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent());
+ }
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the AddCommand
+ * and returns an AddCommand object for execution.
+ *
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public AddDeliveryJobCommand parse(String args) throws ParseException {
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_SENDER_ID, PREFIX_RECIPIENT_ID,
+ PREFIX_DELIVERY_DATE, PREFIX_DELIVERY_SLOT, PREFIX_EARNING);
+
+ if (!arePrefixesPresent(argMultimap, PREFIX_SENDER_ID, PREFIX_RECIPIENT_ID)
+ || !argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddDeliveryJobCommand.MESSAGE_USAGE));
+ }
+
+ String sid = argMultimap.getValue(PREFIX_SENDER_ID).get();
+ String rid = argMultimap.getValue(PREFIX_RECIPIENT_ID).get();
+ Optional date = Optional.empty();
+ Optional slot = Optional.empty();
+ Optional earn;
+
+ if (argMultimap.getValue(PREFIX_DELIVERY_DATE).isPresent()
+ && !argMultimap.getValue(PREFIX_DELIVERY_SLOT).isPresent()) {
+ throw new ParseException(
+ String.format(MESSAGE_SLOT_MISSING));
+ } else if (!argMultimap.getValue(PREFIX_DELIVERY_DATE).isPresent()
+ && argMultimap.getValue(PREFIX_DELIVERY_SLOT).isPresent()) {
+ throw new ParseException(
+ String.format(MESSAGE_DATE_MISSING));
+ } else if (argMultimap.getValue(PREFIX_DELIVERY_DATE).isPresent()
+ && argMultimap.getValue(PREFIX_DELIVERY_SLOT).isPresent()) {
+ try {
+ date = argMultimap.getValue(PREFIX_DELIVERY_DATE).map(x -> new DeliveryDate(x));
+ } catch (Exception e) {
+ throw new ParseException(DeliveryDate.MESSAGE_CONSTRAINTS);
+ }
+
+ try {
+ slot = argMultimap.getValue(PREFIX_DELIVERY_SLOT).map(x -> new DeliverySlot(x));
+ } catch (Exception e) {
+ throw new ParseException(DeliverySlot.MESSAGE_CONSTRAINTS);
+ }
+ }
+
+ if (argMultimap.getValue(PREFIX_EARNING).isPresent()) {
+ try {
+ earn = argMultimap.getValue(PREFIX_EARNING).map(x -> new Earning(x));
+ } catch (Exception e) {
+ throw new ParseException(Earning.MESSAGE_CONSTRAINTS);
+ }
+ } else {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddDeliveryJobCommand.MESSAGE_USAGE));
+ }
+
+ if (sid.equals("")) {
+ throw new ParseException(AddDeliveryJobCommand.MESSAGE_SENDER_CONSTRAINT);
+ }
+
+ if (rid.equals("")) {
+ throw new ParseException(AddDeliveryJobCommand.MESSAGE_RECIPIENT_CONSTRAINT);
+ }
+
+ return new AddDeliveryJobCommand(new DeliveryJob(rid, sid, date, slot, earn, ""));
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/jobs/CompleteDeliveryJobCommandParser.java b/src/main/java/seedu/address/logic/parser/jobs/CompleteDeliveryJobCommandParser.java
new file mode 100644
index 00000000000..63759f13ff9
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/jobs/CompleteDeliveryJobCommandParser.java
@@ -0,0 +1,35 @@
+package seedu.address.logic.parser.jobs;
+
+import seedu.address.logic.commands.jobs.CompleteDeliveryJobCommand;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new CompleteDeliveryJobCommandParser object
+ */
+public class CompleteDeliveryJobCommandParser implements Parser {
+
+ private Boolean setDelivered;
+
+ /**
+ * Constructs a CompleteDeliveryJobCommandParser.
+ *
+ * @param setDelivered
+ */
+ public CompleteDeliveryJobCommandParser(Boolean setDelivered) {
+ this.setDelivered = setDelivered;
+ }
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the
+ * FindCommand
+ * and returns a FindCommand object for execution.
+ *
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public CompleteDeliveryJobCommand parse(String args) throws ParseException {
+ String jobId = args.trim();
+ return new CompleteDeliveryJobCommand(jobId, setDelivered);
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/parser/jobs/DeleteDeliveryJobCommandParser.java b/src/main/java/seedu/address/logic/parser/jobs/DeleteDeliveryJobCommandParser.java
new file mode 100644
index 00000000000..a32ed797e17
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/jobs/DeleteDeliveryJobCommandParser.java
@@ -0,0 +1,27 @@
+package seedu.address.logic.parser.jobs;
+
+import seedu.address.logic.commands.jobs.DeleteDeliveryJobCommand;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new DeleteCommand object
+ */
+public class DeleteDeliveryJobCommandParser implements Parser {
+
+ private static final String EMPTY_CMD = "Job ID cannot be empty!\n";
+ /**
+ * Parses the given {@code String} of arguments in the context of the DeleteCommand
+ * and returns a DeleteCommand object for execution.
+ *
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public DeleteDeliveryJobCommand parse(String args) throws ParseException {
+ String jobId = args.trim();
+ if (jobId.isEmpty()) {
+ throw new ParseException(EMPTY_CMD + DeleteDeliveryJobCommand.MESSAGE_USAGE);
+ }
+ return new DeleteDeliveryJobCommand(jobId);
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/parser/jobs/EditDeliveryJobCommandParser.java b/src/main/java/seedu/address/logic/parser/jobs/EditDeliveryJobCommandParser.java
new file mode 100644
index 00000000000..e61df239479
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/jobs/EditDeliveryJobCommandParser.java
@@ -0,0 +1,157 @@
+package seedu.address.logic.parser.jobs;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.core.Messages.MESSAGE_EITHER_INDEX_OR_ID;
+import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DELIVERY_DATE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DELIVERY_SLOT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EARNING;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_IS_DELIVERED;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_JOB_ID;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_RECIPIENT_ID;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SENDER_ID;
+
+import java.util.Optional;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.jobs.EditDeliveryJobCommand;
+import seedu.address.logic.commands.jobs.EditDeliveryJobCommand.EditDeliveryJobDescriptor;
+import seedu.address.logic.parser.ArgumentMultimap;
+import seedu.address.logic.parser.ArgumentTokenizer;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.ParserUtil;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.jobs.DeliveryDate;
+import seedu.address.model.jobs.DeliverySlot;
+import seedu.address.model.jobs.Earning;
+
+/**
+ * Parses input arguments and creates a new EditCommand object
+ */
+public class EditDeliveryJobCommandParser implements Parser {
+
+ private static final String MESSAGE_DATE_FORMAT = "Invalid date format!\nDate/ | YYYY-MM-DD";
+ private static final String MESSAGE_SLOT_FORMAT = "Invalid slot format!\nSlot/ | 1..5";
+ private static final String MESSAGE_EARN_FORMAT = "Invalid earning format!\nearn/ d.f ";
+ private static final String MESSAGE_EMPTY_SENDER_ID = "Sender id missing!\nsi/ id ";
+ private static final String MESSAGE_EMPTY_JOB_ID = "Job id missing!\nji/ id ";
+ private static final String MESSAGE_EMPTY_RECIPIENT_ID = "Recipient id missing!\nri/ id ";
+ private static final String MESSAGE_EMPTY_STATUS = "Invalid status!\nDone/ t | f ";
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the
+ * EditCommand
+ * and returns an EditCommand object for execution.
+ *
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public EditDeliveryJobCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_JOB_ID, PREFIX_SENDER_ID,
+ PREFIX_RECIPIENT_ID,
+ PREFIX_DELIVERY_DATE, PREFIX_DELIVERY_SLOT, PREFIX_EARNING, PREFIX_IS_DELIVERED);
+
+ Optional index = Optional.empty();
+
+ try {
+ index = Optional.of(ParserUtil.parseIndex(argMultimap.getPreamble()));
+ } catch (ParseException pe) {
+ // try if index exist
+ }
+
+ if (argMultimap.getValue(PREFIX_JOB_ID).isPresent() && index.isPresent()) {
+ throw new ParseException(
+ String.format(MESSAGE_EITHER_INDEX_OR_ID, EditDeliveryJobCommand.MESSAGE_USAGE));
+ }
+
+ if (argMultimap.getValue(PREFIX_JOB_ID).isEmpty() && index.isEmpty()) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditDeliveryJobCommand.MESSAGE_USAGE));
+ }
+
+ EditDeliveryJobDescriptor editDeliveryJobDescriptor = new EditDeliveryJobDescriptor();
+ argMultimap.getValue(PREFIX_JOB_ID).ifPresent(val -> editDeliveryJobDescriptor.setJobId(val));
+
+ if (argMultimap.getValue(PREFIX_SENDER_ID).isPresent()) {
+ String val = argMultimap.getValue(PREFIX_SENDER_ID).get();
+ if (val.isBlank()) {
+ throw new ParseException(String.format(MESSAGE_EMPTY_SENDER_ID));
+ }
+
+ editDeliveryJobDescriptor.setSender(val);
+ }
+
+ if (argMultimap.getValue(PREFIX_RECIPIENT_ID).isPresent()) {
+ String val = argMultimap.getValue(PREFIX_RECIPIENT_ID).get();
+ if (val.isBlank()) {
+ throw new ParseException(String.format(MESSAGE_EMPTY_RECIPIENT_ID));
+ }
+
+ editDeliveryJobDescriptor.setRecipient(val);
+ }
+
+ if (argMultimap.getValue(PREFIX_DELIVERY_DATE).isPresent()) {
+ try {
+ String val = argMultimap.getValue(PREFIX_DELIVERY_DATE).get();
+ DeliveryDate toEditDate;
+ if (val.isBlank()) {
+ toEditDate = DeliveryDate.placeholder();
+ } else {
+ toEditDate = new DeliveryDate(val);
+ }
+ editDeliveryJobDescriptor.setDeliveryDate(toEditDate);
+ } catch (IllegalArgumentException e) {
+ throw new ParseException(
+ String.format(MESSAGE_DATE_FORMAT, e.getMessage()));
+ }
+ }
+
+ if (argMultimap.getValue(PREFIX_DELIVERY_SLOT).isPresent()) {
+ try {
+ String val = argMultimap.getValue(PREFIX_DELIVERY_SLOT).get();
+ DeliverySlot toEditSlot;
+ if (val.isBlank()) {
+ toEditSlot = DeliverySlot.placeholder();
+ } else {
+ toEditSlot = new DeliverySlot(val);
+ }
+ editDeliveryJobDescriptor.setDeliverySlot(toEditSlot);
+ } catch (IllegalArgumentException e) {
+ throw new ParseException(
+ String.format(MESSAGE_SLOT_FORMAT, e.getMessage()));
+ }
+ }
+
+ if (argMultimap.getValue(PREFIX_EARNING).isPresent()) {
+ try {
+ String val = argMultimap.getValue(PREFIX_EARNING).get();
+ editDeliveryJobDescriptor.setEarning(new Earning(val));
+ } catch (IllegalArgumentException e) {
+ throw new ParseException(
+ String.format(MESSAGE_EARN_FORMAT, e.getMessage()));
+ }
+ }
+
+ if (argMultimap.getValue(PREFIX_IS_DELIVERED).isPresent()) {
+ String val = argMultimap.getValue(PREFIX_IS_DELIVERED).get().toLowerCase();
+ boolean isDone = false;
+ if (val.isBlank() || (!val.equals("t") && !val.equals("f"))) {
+ throw new ParseException(String.format(MESSAGE_EMPTY_STATUS));
+ }
+ if (val.startsWith("t")) {
+ isDone = true;
+ }
+ editDeliveryJobDescriptor.setDelivered(isDone);
+ }
+
+ if (!editDeliveryJobDescriptor.isAnyFieldEdited()) {
+ throw new ParseException(EditDeliveryJobCommand.MESSAGE_NOT_EDITED);
+ }
+
+ if (index.isPresent()) {
+ return new EditDeliveryJobCommand(index.get(), editDeliveryJobDescriptor);
+ } else {
+ return new EditDeliveryJobCommand(editDeliveryJobDescriptor);
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/jobs/FindDeliveryJobCommandParser.java b/src/main/java/seedu/address/logic/parser/jobs/FindDeliveryJobCommandParser.java
new file mode 100644
index 00000000000..b4919c93b05
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/jobs/FindDeliveryJobCommandParser.java
@@ -0,0 +1,197 @@
+package seedu.address.logic.parser.jobs;
+
+import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DELIVERY_DATE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DELIVERY_SLOT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EARNING;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_IS_DELIVERED;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_JOB_ID;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_RECIPIENT_ID;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SENDER_ID;
+
+import java.util.function.Predicate;
+import java.util.stream.Stream;
+
+import seedu.address.logic.commands.jobs.FindDeliveryJobCommand;
+import seedu.address.logic.parser.ArgumentMultimap;
+import seedu.address.logic.parser.ArgumentTokenizer;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.Prefix;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.jobs.DeliveryDate;
+import seedu.address.model.jobs.DeliveryJob;
+import seedu.address.model.jobs.DeliverySlot;
+import seedu.address.model.jobs.Earning;
+import seedu.address.model.jobs.predicate.DeliveryJobContainsDeliveryDatePredicate;
+import seedu.address.model.jobs.predicate.DeliveryJobContainsDeliverySlotPredicate;
+import seedu.address.model.jobs.predicate.DeliveryJobContainsEarningPredicate;
+import seedu.address.model.jobs.predicate.DeliveryJobContainsJobIdPredicate;
+import seedu.address.model.jobs.predicate.DeliveryJobContainsRecipientIdPredicate;
+import seedu.address.model.jobs.predicate.DeliveryJobContainsSenderIdPredicate;
+import seedu.address.model.jobs.predicate.DeliveryJobContainsStatusPredicate;
+
+/**
+ * Parses input arguments and creates a new FindCommand object
+ */
+public class FindDeliveryJobCommandParser implements Parser {
+
+ private static final String MESSAGE_DATE_FORMAT = "Invalid date format!\nDate/ | YYYY-MM-DD";
+ private static final String MESSAGE_SLOT_FORMAT = "Invalid slot format!\nSlot/ | 1..5";
+ private static final String MESSAGE_EARN_FORMAT = "Invalid earning format!\nearn/ d.f ";
+ private static final String MESSAGE_EMPTY_SENDER_ID = "Sender id missing!\nsi/ id ";
+ private static final String MESSAGE_EMPTY_JOB_ID = "Job id missing!\nji/ id ";
+ private static final String MESSAGE_EMPTY_RECIPIENT_ID = "Recipient id missing!\nri/ id ";
+ private static final String MESSAGE_EMPTY_STATUS = "Invalid status!\nDone/ t | f ";
+
+ /**
+ * Returns true if none of the prefixes contains empty {@code Optional} values
+ * in the given
+ * {@code ArgumentMultimap}.
+ */
+ private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) {
+ return Stream.of(prefixes).anyMatch(prefix -> argumentMultimap.getValue(prefix).isPresent());
+ }
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the
+ * FindCommand
+ * and returns a FindCommand object for execution.
+ *
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public FindDeliveryJobCommand parse(String args) throws ParseException {
+
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_JOB_ID, PREFIX_SENDER_ID,
+ PREFIX_RECIPIENT_ID,
+ PREFIX_DELIVERY_DATE, PREFIX_DELIVERY_SLOT, PREFIX_EARNING, PREFIX_IS_DELIVERED);
+
+ if (!arePrefixesPresent(argMultimap, PREFIX_JOB_ID, PREFIX_SENDER_ID, PREFIX_RECIPIENT_ID,
+ PREFIX_DELIVERY_DATE, PREFIX_DELIVERY_SLOT, PREFIX_EARNING, PREFIX_IS_DELIVERED)
+ || !argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindDeliveryJobCommand.MESSAGE_USAGE));
+ }
+
+ Predicate predicate = null;
+ String query = "";
+
+ if (argMultimap.getValue(PREFIX_JOB_ID).isPresent()) {
+ String val = argMultimap.getValue(PREFIX_JOB_ID).get();
+ if (val.isBlank()) {
+ throw new ParseException(String.format(MESSAGE_EMPTY_JOB_ID));
+ }
+ query += "job id: " + val + "\n";
+ predicate = new DeliveryJobContainsJobIdPredicate(val.toUpperCase());
+ }
+
+ if (argMultimap.getValue(PREFIX_SENDER_ID).isPresent()) {
+ String val = argMultimap.getValue(PREFIX_SENDER_ID).get();
+ if (val.isBlank()) {
+ throw new ParseException(String.format(MESSAGE_EMPTY_SENDER_ID));
+ }
+
+ if (predicate != null) {
+ predicate = predicate.and(new DeliveryJobContainsSenderIdPredicate(val.toUpperCase()));
+ } else {
+ predicate = new DeliveryJobContainsSenderIdPredicate(val.toUpperCase());
+ }
+ query += "sender id: " + val + "\n";
+ }
+
+ if (argMultimap.getValue(PREFIX_RECIPIENT_ID).isPresent()) {
+ String val = argMultimap.getValue(PREFIX_RECIPIENT_ID).get();
+ if (val.isBlank()) {
+ throw new ParseException(String.format(MESSAGE_EMPTY_RECIPIENT_ID));
+ }
+
+ if (predicate != null) {
+ predicate = predicate.and(new DeliveryJobContainsRecipientIdPredicate(val.toUpperCase()));
+ } else {
+ predicate = new DeliveryJobContainsRecipientIdPredicate(val.toUpperCase());
+ }
+ query += "recipient id: " + val + "\n";
+ }
+
+ if (argMultimap.getValue(PREFIX_DELIVERY_DATE).isPresent()) {
+ try {
+ String val = argMultimap.getValue(PREFIX_DELIVERY_DATE).get();
+ DeliveryDate toFindDate;
+ if (val.isBlank()) {
+ toFindDate = DeliveryDate.placeholder();
+ } else {
+ toFindDate = new DeliveryDate(val);
+ }
+ if (predicate != null) {
+ predicate = predicate.and(new DeliveryJobContainsDeliveryDatePredicate(toFindDate));
+ } else {
+ predicate = new DeliveryJobContainsDeliveryDatePredicate(toFindDate);
+ }
+ query += "delivery date: " + toFindDate.toString() + "\n";
+ } catch (IllegalArgumentException e) {
+ throw new ParseException(
+ String.format(MESSAGE_DATE_FORMAT, e.getMessage()));
+ }
+ }
+
+ if (argMultimap.getValue(PREFIX_DELIVERY_SLOT).isPresent()) {
+ try {
+ String val = argMultimap.getValue(PREFIX_DELIVERY_SLOT).get();
+ DeliverySlot toFindSlot;
+ if (val.isBlank()) {
+ toFindSlot = DeliverySlot.placeholder();
+ } else {
+ toFindSlot = new DeliverySlot(val);
+ }
+ if (predicate != null) {
+ predicate = predicate.and(new DeliveryJobContainsDeliverySlotPredicate(toFindSlot));
+ } else {
+ predicate = new DeliveryJobContainsDeliverySlotPredicate(toFindSlot);
+ }
+ query += "delivery slot: " + toFindSlot.toString() + "\n";
+ } catch (IllegalArgumentException e) {
+ throw new ParseException(
+ String.format(MESSAGE_SLOT_FORMAT, e.getMessage()));
+ }
+ }
+
+ if (argMultimap.getValue(PREFIX_EARNING).isPresent()) {
+ try {
+ String val = argMultimap.getValue(PREFIX_EARNING).get();
+ if (predicate != null) {
+ predicate = predicate.and(new DeliveryJobContainsEarningPredicate(new Earning(val)));
+ } else {
+ predicate = new DeliveryJobContainsEarningPredicate(new Earning(val));
+ }
+ query += "earning: " + val + "\n";
+ } catch (IllegalArgumentException e) {
+ throw new ParseException(
+ String.format(MESSAGE_EARN_FORMAT, e.getMessage()));
+ }
+ }
+
+ if (argMultimap.getValue(PREFIX_IS_DELIVERED).isPresent()) {
+ String val = argMultimap.getValue(PREFIX_IS_DELIVERED).get().toLowerCase();
+ boolean done = false;
+ if (val.isBlank() || (!val.equals("t") && !val.equals("f"))) {
+ throw new ParseException(String.format(MESSAGE_EMPTY_STATUS));
+ }
+ if (val.startsWith("t")) {
+ done = true;
+ }
+ if (predicate != null) {
+ predicate = predicate.and(new DeliveryJobContainsStatusPredicate(done));
+ } else {
+ predicate = new DeliveryJobContainsStatusPredicate(done);
+ }
+ query += "status: " + Boolean.toString(done) + "\n";
+ }
+
+ if (predicate != null) {
+ return new FindDeliveryJobCommand(predicate, query);
+ } else {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindDeliveryJobCommand.MESSAGE_USAGE));
+ }
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/parser/jobs/ImportDeliveryJobCommandParser.java b/src/main/java/seedu/address/logic/parser/jobs/ImportDeliveryJobCommandParser.java
new file mode 100644
index 00000000000..b3f17222af3
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/jobs/ImportDeliveryJobCommandParser.java
@@ -0,0 +1,181 @@
+package seedu.address.logic.parser.jobs;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Scanner;
+import java.util.Set;
+
+import seedu.address.logic.parser.ParserUtil;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.jobs.DeliveryDate;
+import seedu.address.model.jobs.DeliveryJob;
+import seedu.address.model.jobs.DeliverySlot;
+import seedu.address.model.jobs.Earning;
+import seedu.address.model.person.Address;
+import seedu.address.model.person.Email;
+import seedu.address.model.person.Name;
+import seedu.address.model.person.Person;
+import seedu.address.model.person.Phone;
+import seedu.address.model.tag.Tag;
+
+
+/**
+ * Parses CSV file contents into separate jobs and job details
+ */
+public class ImportDeliveryJobCommandParser {
+
+ public static final String MESSAGE_MISSING_ELEMENT_IN_IMPORT = "Missing element in import";
+ public static final String MESSAGE_EMPTY_FILE = "File is empty";
+ public static final String MESSAGE_WRONG_FILE = "File type is unsupported. Please upload a CSV file and check if "
+ + "file extension is named .csv";
+
+ public static final String MESSAGE_WRONG_DELIMITER = "Wrong delimiter used, please use comma as delimiter.";
+ public static final String MESSAGE_MISSING_HEADER = "Missing header in import. "
+ + "Header needs to consist in this order of Recipient ID, Sender ID, Delivery date, Delivery slot, Price, "
+ + "Recipient ID, Recipient Name, Recipient Phone, Recipient Email, Recipient Address, "
+ + "Recipient Tag, Sender ID, Sender Name, Sender Phone, Sender Email, Sender Address,"
+ + " Sender Tag.";
+
+ /**
+ * Parses the given CSV File in the context of the ImportCommand
+ *
+ * @param file CSV file containing delivery jobs
+ * @return List of delivery jobs to be added
+ * @throws ParseException if the user input does not conform to
+ * the expected format
+ */
+ public static List parse(File file, List listOfCustomers)
+ throws ParseException, FileNotFoundException {
+ Scanner sc = new Scanner(file);
+ if (sc.hasNextLine()) {
+ List listOfAddDeliveryJob = new ArrayList<>();
+ String header = sc.nextLine();
+ String[] arrOfHeader = header.split(",");
+ if (arrOfHeader.length == 0) {
+ throw new ParseException(MESSAGE_EMPTY_FILE);
+ } else if (!getFileExtensions(file).equals("csv")) {
+ throw new ParseException(MESSAGE_WRONG_FILE);
+ } else if (arrOfHeader.length == 1) {
+ throw new ParseException(MESSAGE_WRONG_DELIMITER);
+ } else if (!headerValidity(arrOfHeader)) {
+ throw new ParseException(MESSAGE_MISSING_HEADER);
+ }
+ while (sc.hasNextLine()) {
+ String line = sc.nextLine();
+ String[] arrOfStr = line.split(",");
+ if (arrOfStr.length < 17) {
+ throw new ParseException(MESSAGE_MISSING_ELEMENT_IN_IMPORT);
+ }
+ addJobs(arrOfStr, listOfCustomers, listOfAddDeliveryJob);
+ }
+ sc.close();
+ return listOfAddDeliveryJob;
+ } else {
+ throw new ParseException(MESSAGE_EMPTY_FILE);
+ }
+ }
+
+ /**
+ * Parses recipient or sender from jobs in CSV file.
+ *
+ * @param arrOfStr array of person details
+ * @param index index of array
+ * @return Person object using the details
+ * @throws ParseException if the user input does not conform to
+ * the expected format
+ */
+ public static Person recipientOrSender(String[] arrOfStr, int index) throws ParseException {
+ String personID = arrOfStr[index];
+ Name name = new Name(arrOfStr[index + 1]);
+ Phone phone = new Phone(arrOfStr[index + 2]);
+ Email email = new Email(arrOfStr[index + 3]);
+ Address address = new Address(arrOfStr[index + 4]);
+ String[] arrOfTags = arrOfStr[index + 5].split(" ");
+ Set tags = ParserUtil.parseTags(Arrays.asList(arrOfTags));
+ Person person;
+ if (arrOfStr[0].equals("na")) {
+ Set tagSet = new HashSet<>();
+ person = new Person(name, phone, email, address, tags);
+ } else {
+ person = new Person(personID, name, phone, email, address, tags);
+ }
+ return person;
+ }
+
+ /**
+ * Ensures Header elements are all present and in right order.
+ *
+ * @param arrOfHeader array of header elements
+ * @return Boolean whether header elements are correct
+ */
+ public static boolean headerValidity(String[] arrOfHeader) {
+ if (!arrOfHeader[0].equals("Recipient ID") || !arrOfHeader[1].equals("Sender ID")
+ || !arrOfHeader[2].equals("Delivery date") || !arrOfHeader[3].equals("Delivery slot")
+ || !arrOfHeader[4].equals("Price")
+ || !arrOfHeader[5].equals("Recipient ID") || !arrOfHeader[6].equals("Recipient Name")
+ || !arrOfHeader[7].equals("Recipient Phone") || !arrOfHeader[8].equals("Recipient Email")
+ || !arrOfHeader[9].equals("Recipient Address") || !arrOfHeader[10].equals("Recipient Tag")
+ || !arrOfHeader[11].equals("Sender ID")
+ || !arrOfHeader[12].equals("Sender Name") || !arrOfHeader[13].equals("Sender Phone")
+ || !arrOfHeader[14].equals("Sender Email") || !arrOfHeader[15].equals("Sender Address")
+ || !arrOfHeader[16].equals("Sender Tag")) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Gets type of file extension.
+ *
+ * @param file File that extension is checked
+ * @return String file extension name
+ */
+ public static String getFileExtensions(File file) {
+ String fileName = file.getName();
+ String extension = "";
+ int i = fileName.lastIndexOf(".");
+ extension = fileName.substring(i + 1, fileName.length());
+ return extension;
+ }
+
+
+ /**
+ * Adds new delivery job to list of jobs to be imported in.
+ *
+ * @param arrOfStr array of job and its customers details
+ * @param listOfCustomers list of job's customers to be checked if
+ * exist and added into address book.
+ * @param listOfAddDeliveryJob list of delivery jobs to be
+ * imported
+ */
+ public static void addJobs(String[] arrOfStr, List listOfCustomers,
+ List listOfAddDeliveryJob) throws ParseException {
+ String sid = arrOfStr[0];
+ String rid = arrOfStr[1];
+ String ded = arrOfStr[2];
+ String des = arrOfStr[3];
+ String ear = arrOfStr[4];
+ Person recipient = recipientOrSender(arrOfStr, 5);
+ Person sender = recipientOrSender(arrOfStr, 11);
+ listOfCustomers.add(sender);
+ listOfCustomers.add(recipient);
+
+ if (ded.equals("na") && des.equals("na")) {
+ Optional date = Optional.empty();
+ Optional slot = Optional.empty();
+ Optional earn = Optional.of(new Earning(ear));
+ DeliveryJob job = new DeliveryJob(rid, sid, date, slot, earn, "");
+ listOfAddDeliveryJob.add(job);
+ } else {
+ DeliveryJob job = new DeliveryJob(rid, sid, ded, des, ear, "");
+ listOfAddDeliveryJob.add(job);
+ }
+ }
+
+}
+
diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/seedu/address/logic/parser/person/AddCommandParser.java
similarity index 88%
rename from src/main/java/seedu/address/logic/parser/AddCommandParser.java
rename to src/main/java/seedu/address/logic/parser/person/AddCommandParser.java
index 3b8bfa035e8..fd90628d94a 100644
--- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/person/AddCommandParser.java
@@ -1,4 +1,4 @@
-package seedu.address.logic.parser;
+package seedu.address.logic.parser.person;
import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
@@ -10,7 +10,12 @@
import java.util.Set;
import java.util.stream.Stream;
-import seedu.address.logic.commands.AddCommand;
+import seedu.address.logic.commands.person.AddCommand;
+import seedu.address.logic.parser.ArgumentMultimap;
+import seedu.address.logic.parser.ArgumentTokenizer;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.ParserUtil;
+import seedu.address.logic.parser.Prefix;
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.person.Address;
import seedu.address.model.person.Email;
@@ -24,9 +29,18 @@
*/
public class AddCommandParser implements Parser {
+ /**
+ * Returns true if none of the prefixes contains empty {@code Optional} values in the given
+ * {@code ArgumentMultimap}.
+ */
+ private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) {
+ return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent());
+ }
+
/**
* Parses the given {@code String} of arguments in the context of the AddCommand
* and returns an AddCommand object for execution.
+ *
* @throws ParseException if the user input does not conform the expected format
*/
public AddCommand parse(String args) throws ParseException {
@@ -49,12 +63,4 @@ public AddCommand parse(String args) throws ParseException {
return new AddCommand(person);
}
- /**
- * Returns true if none of the prefixes contains empty {@code Optional} values in the given
- * {@code ArgumentMultimap}.
- */
- private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) {
- return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent());
- }
-
}
diff --git a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java b/src/main/java/seedu/address/logic/parser/person/DeleteCommandParser.java
similarity index 83%
rename from src/main/java/seedu/address/logic/parser/DeleteCommandParser.java
rename to src/main/java/seedu/address/logic/parser/person/DeleteCommandParser.java
index 522b93081cc..b9851b66604 100644
--- a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/person/DeleteCommandParser.java
@@ -1,9 +1,11 @@
-package seedu.address.logic.parser;
+package seedu.address.logic.parser.person;
import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import seedu.address.commons.core.index.Index;
-import seedu.address.logic.commands.DeleteCommand;
+import seedu.address.logic.commands.person.DeleteCommand;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.ParserUtil;
import seedu.address.logic.parser.exceptions.ParseException;
/**
@@ -14,6 +16,7 @@ public class DeleteCommandParser implements Parser {
/**
* Parses the given {@code String} of arguments in the context of the DeleteCommand
* and returns a DeleteCommand object for execution.
+ *
* @throws ParseException if the user input does not conform the expected format
*/
public DeleteCommand parse(String args) throws ParseException {
diff --git a/src/main/java/seedu/address/logic/parser/EditCommandParser.java b/src/main/java/seedu/address/logic/parser/person/EditCommandParser.java
similarity index 89%
rename from src/main/java/seedu/address/logic/parser/EditCommandParser.java
rename to src/main/java/seedu/address/logic/parser/person/EditCommandParser.java
index 845644b7dea..6b6ffcc3143 100644
--- a/src/main/java/seedu/address/logic/parser/EditCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/person/EditCommandParser.java
@@ -1,4 +1,4 @@
-package seedu.address.logic.parser;
+package seedu.address.logic.parser.person;
import static java.util.Objects.requireNonNull;
import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
@@ -14,8 +14,12 @@
import java.util.Set;
import seedu.address.commons.core.index.Index;
-import seedu.address.logic.commands.EditCommand;
-import seedu.address.logic.commands.EditCommand.EditPersonDescriptor;
+import seedu.address.logic.commands.person.EditCommand;
+import seedu.address.logic.commands.person.EditCommand.EditPersonDescriptor;
+import seedu.address.logic.parser.ArgumentMultimap;
+import seedu.address.logic.parser.ArgumentTokenizer;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.ParserUtil;
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.tag.Tag;
@@ -27,6 +31,7 @@ public class EditCommandParser implements Parser {
/**
* Parses the given {@code String} of arguments in the context of the EditCommand
* and returns an EditCommand object for execution.
+ *
* @throws ParseException if the user input does not conform the expected format
*/
public EditCommand parse(String args) throws ParseException {
diff --git a/src/main/java/seedu/address/logic/parser/FindCommandParser.java b/src/main/java/seedu/address/logic/parser/person/FindCommandParser.java
similarity index 87%
rename from src/main/java/seedu/address/logic/parser/FindCommandParser.java
rename to src/main/java/seedu/address/logic/parser/person/FindCommandParser.java
index 4fb71f23103..7d2e1a399dc 100644
--- a/src/main/java/seedu/address/logic/parser/FindCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/person/FindCommandParser.java
@@ -1,10 +1,11 @@
-package seedu.address.logic.parser;
+package seedu.address.logic.parser.person;
import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import java.util.Arrays;
-import seedu.address.logic.commands.FindCommand;
+import seedu.address.logic.commands.person.FindCommand;
+import seedu.address.logic.parser.Parser;
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.person.NameContainsKeywordsPredicate;
@@ -16,6 +17,7 @@ public class FindCommandParser implements Parser {
/**
* Parses the given {@code String} of arguments in the context of the FindCommand
* and returns a FindCommand object for execution.
+ *
* @throws ParseException if the user input does not conform the expected format
*/
public FindCommand parse(String args) throws ParseException {
diff --git a/src/main/java/seedu/address/logic/parser/reminder/AddReminderParser.java b/src/main/java/seedu/address/logic/parser/reminder/AddReminderParser.java
new file mode 100644
index 00000000000..b86a731c6cc
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/reminder/AddReminderParser.java
@@ -0,0 +1,68 @@
+package seedu.address.logic.parser.reminder;
+
+import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DESCRIPTION;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_TIME;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeParseException;
+import java.util.stream.Stream;
+
+import seedu.address.commons.util.DateTimeUtil;
+import seedu.address.logic.commands.reminder.AddReminderCommand;
+import seedu.address.logic.parser.ArgumentMultimap;
+import seedu.address.logic.parser.ArgumentTokenizer;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.Prefix;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.jobs.DeliveryDate;
+import seedu.address.model.reminder.Reminder;
+
+/**
+ * Parses input arguments and creates a new AddReminderCommand object
+ */
+public class AddReminderParser implements Parser {
+
+ /**
+ * Returns true if none of the prefixes contains empty {@code Optional} values in the given
+ * {@code ArgumentMultimap}.
+ */
+ private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) {
+ return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent());
+ }
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the AddReminderCommand
+ * and returns an AddReminderCommand object for execution.
+ *
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ @Override
+ public AddReminderCommand parse(String args) throws ParseException {
+ ArgumentMultimap argMultimap =
+ ArgumentTokenizer.tokenize(args, PREFIX_DESCRIPTION, PREFIX_TIME);
+
+ if (!arePrefixesPresent(argMultimap, PREFIX_TIME)
+ || !argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddReminderCommand.MESSAGE_USAGE));
+ }
+
+ String description = argMultimap.getValue(PREFIX_DESCRIPTION).orElse("");
+ System.out.println(description.length());
+ if (description.length() > 50) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddReminderCommand.MESSAGE_USAGE));
+ }
+
+ String dateTimeString = argMultimap.getValue(PREFIX_TIME).orElse("none");
+ LocalDateTime dateTime;
+ try {
+ dateTime = DateTimeUtil.toDateTime(dateTimeString);
+ } catch (DateTimeParseException e) {
+ throw new ParseException(DeliveryDate.MESSAGE_CONSTRAINTS);
+ }
+
+ Reminder reminder = new Reminder(description, dateTime);
+
+ return new AddReminderCommand(reminder);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/reminder/DeleteReminderParser.java b/src/main/java/seedu/address/logic/parser/reminder/DeleteReminderParser.java
new file mode 100644
index 00000000000..88df198222e
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/reminder/DeleteReminderParser.java
@@ -0,0 +1,22 @@
+package seedu.address.logic.parser.reminder;
+
+import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import seedu.address.logic.commands.reminder.DeleteReminderCommand;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new DeleteReminderCommand object
+ */
+public class DeleteReminderParser implements Parser {
+ @Override
+ public DeleteReminderCommand parse(String args) throws ParseException {
+ try {
+ return new DeleteReminderCommand(Integer.parseInt(args.trim()));
+ } catch (NumberFormatException e) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteReminderCommand.MESSAGE_USAGE), e);
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/timetable/TimetableDateCommandParser.java b/src/main/java/seedu/address/logic/parser/timetable/TimetableDateCommandParser.java
new file mode 100644
index 00000000000..90b4b347125
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/timetable/TimetableDateCommandParser.java
@@ -0,0 +1,49 @@
+package seedu.address.logic.parser.timetable;
+
+import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE;
+
+import java.time.LocalDate;
+import java.util.stream.Stream;
+
+import seedu.address.logic.commands.timetable.TimetableDateCommand;
+import seedu.address.logic.parser.ArgumentMultimap;
+import seedu.address.logic.parser.ArgumentTokenizer;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.ParserUtil;
+import seedu.address.logic.parser.Prefix;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new TimetableDateCommand object
+ */
+public class TimetableDateCommandParser implements Parser {
+
+ /**
+ * Returns true if none of the prefixes contains empty {@code Optional} values in the given
+ * {@code ArgumentMultimap}.
+ */
+ private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) {
+ return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent());
+ }
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the TimetableDateCommand
+ * and returns an TimetableDateCommand object for execution.
+ *
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public TimetableDateCommand parse(String args) throws ParseException {
+ ArgumentMultimap argMultimap =
+ ArgumentTokenizer.tokenize(args, PREFIX_DATE);
+
+ if (!arePrefixesPresent(argMultimap, PREFIX_DATE)
+ || !argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, TimetableDateCommand.MESSAGE_USAGE));
+ }
+
+ LocalDate date = ParserUtil.parseDate(argMultimap.getValue(PREFIX_DATE).get());
+ return new TimetableDateCommand(date);
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/timetable/TimetableParser.java
similarity index 60%
rename from src/main/java/seedu/address/logic/parser/AddressBookParser.java
rename to src/main/java/seedu/address/logic/parser/timetable/TimetableParser.java
index 1e466792b46..db8c963a6c9 100644
--- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java
+++ b/src/main/java/seedu/address/logic/parser/timetable/TimetableParser.java
@@ -1,26 +1,23 @@
-package seedu.address.logic.parser;
+package seedu.address.logic.parser.timetable;
import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
-import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND;
+import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_TIMETABLE_COMMAND;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import seedu.address.logic.commands.AddCommand;
-import seedu.address.logic.commands.ClearCommand;
import seedu.address.logic.commands.Command;
-import seedu.address.logic.commands.DeleteCommand;
-import seedu.address.logic.commands.EditCommand;
import seedu.address.logic.commands.ExitCommand;
-import seedu.address.logic.commands.FindCommand;
import seedu.address.logic.commands.HelpCommand;
-import seedu.address.logic.commands.ListCommand;
+import seedu.address.logic.commands.timetable.TimetableCompletedCommand;
+import seedu.address.logic.commands.timetable.TimetableDateCommand;
+import seedu.address.logic.commands.timetable.TimetableUnscheduleCommand;
import seedu.address.logic.parser.exceptions.ParseException;
/**
- * Parses user input.
+ * Represent a parser for timetable command
*/
-public class AddressBookParser {
+public class TimetableParser {
/**
* Used for initial separation of command word and args.
@@ -43,33 +40,23 @@ public Command parseCommand(String userInput) throws ParseException {
final String commandWord = matcher.group("commandWord");
final String arguments = matcher.group("arguments");
switch (commandWord) {
-
- case AddCommand.COMMAND_WORD:
- return new AddCommandParser().parse(arguments);
-
- case EditCommand.COMMAND_WORD:
- return new EditCommandParser().parse(arguments);
-
- case DeleteCommand.COMMAND_WORD:
- return new DeleteCommandParser().parse(arguments);
-
- case ClearCommand.COMMAND_WORD:
- return new ClearCommand();
-
- case FindCommand.COMMAND_WORD:
- return new FindCommandParser().parse(arguments);
-
- case ListCommand.COMMAND_WORD:
- return new ListCommand();
+ case HelpCommand.COMMAND_WORD:
+ return new HelpCommand();
case ExitCommand.COMMAND_WORD:
return new ExitCommand();
- case HelpCommand.COMMAND_WORD:
- return new HelpCommand();
+ case TimetableDateCommand.COMMAND_WORD:
+ return new TimetableDateCommandParser().parse(arguments);
+
+ case TimetableUnscheduleCommand.COMMAND_WORD:
+ return new TimetableUnscheduleCommand();
+
+ case TimetableCompletedCommand.COMMAND_WORD:
+ return new TimetableCompletedCommand();
default:
- throw new ParseException(MESSAGE_UNKNOWN_COMMAND);
+ throw new ParseException(MESSAGE_UNKNOWN_TIMETABLE_COMMAND);
}
}
diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java
index 1a943a0781a..837748d15b1 100644
--- a/src/main/java/seedu/address/model/AddressBook.java
+++ b/src/main/java/seedu/address/model/AddressBook.java
@@ -3,10 +3,14 @@
import static java.util.Objects.requireNonNull;
import java.util.List;
+import java.util.Optional;
import javafx.collections.ObservableList;
+import javafx.collections.transformation.FilteredList;
import seedu.address.model.person.Person;
import seedu.address.model.person.UniquePersonList;
+import seedu.address.model.reminder.Reminder;
+import seedu.address.model.reminder.ReminderList;
/**
* Wraps all data at the address-book level
@@ -15,19 +19,21 @@
public class AddressBook implements ReadOnlyAddressBook {
private final UniquePersonList persons;
-
+ private final ReminderList reminderList;
/*
- * The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication
- * between constructors. See https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html
- *
- * Note that non-static init blocks are not recommended to use. There are other ways to avoid duplication
- * among constructors.
- */
+ * The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication
+ * between constructors. See https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html
+ *
+ * Note that non-static init blocks are not recommended to use. There are other ways to avoid duplication
+ * among constructors.
+ */
{
persons = new UniquePersonList();
+ reminderList = new ReminderList();
}
- public AddressBook() {}
+ public AddressBook() {
+ }
/**
* Creates an AddressBook using the Persons in the {@code toBeCopied}
@@ -54,10 +60,9 @@ public void resetData(ReadOnlyAddressBook newData) {
requireNonNull(newData);
setPersons(newData.getPersonList());
+ setReminderList(newData.getReminderList());
}
- //// person-level operations
-
/**
* Returns true if a person with the same identity as {@code person} exists in the address book.
*/
@@ -66,6 +71,8 @@ public boolean hasPerson(Person person) {
return persons.contains(person);
}
+ //// person-level operations
+
/**
* Adds a person to the address book.
* The person must not already exist in the address book.
@@ -81,7 +88,6 @@ public void addPerson(Person p) {
*/
public void setPerson(Person target, Person editedPerson) {
requireNonNull(editedPerson);
-
persons.setPerson(target, editedPerson);
}
@@ -93,7 +99,26 @@ public void removePerson(Person key) {
persons.remove(key);
}
- //// util methods
+ /**
+ * Adds a reminder to the address book.
+ */
+ public void addReminder(Reminder r) {
+ reminderList.add(r);
+ }
+
+ //// reminder-level operations
+
+ /**
+ * Removes {@code Reminder} from this {@code AddressBook}.
+ * {@code Reminder} must exist in the address book.
+ */
+ public void removeReminder(int i) {
+ reminderList.remove(i);
+ }
+
+ public void sortReminderList() {
+ reminderList.sortByOldest();
+ }
@Override
public String toString() {
@@ -101,11 +126,22 @@ public String toString() {
// TODO: refine later
}
+ //// util methods
+
@Override
public ObservableList getPersonList() {
return persons.asUnmodifiableObservableList();
}
+ @Override
+ public ObservableList getReminderList() {
+ return reminderList.asUnmodifiableObservableList();
+ }
+
+ public void setReminderList(List reminderList) {
+ this.reminderList.setReminderList(reminderList);
+ }
+
@Override
public boolean equals(Object other) {
return other == this // short circuit if same object
@@ -117,4 +153,12 @@ public boolean equals(Object other) {
public int hashCode() {
return persons.hashCode();
}
+
+ public Optional getPersonById(String id) {
+ FilteredList list = persons.asUnmodifiableObservableList().filtered(x -> x.getPersonId().equals(id));
+ if (list.size() == 1) {
+ return Optional.of(list.get(0));
+ }
+ return Optional.empty();
+ }
}
diff --git a/src/main/java/seedu/address/model/DeliveryJobSystem.java b/src/main/java/seedu/address/model/DeliveryJobSystem.java
new file mode 100644
index 00000000000..abb6430d44f
--- /dev/null
+++ b/src/main/java/seedu/address/model/DeliveryJobSystem.java
@@ -0,0 +1,120 @@
+package seedu.address.model;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+
+import javafx.collections.ObservableList;
+import seedu.address.model.jobs.DeliveryJob;
+import seedu.address.model.jobs.UniqueDeliveryList;
+
+/**
+ * DeliveryJobSystem
+ */
+public class DeliveryJobSystem implements ReadOnlyDeliveryJobSystem {
+
+ private final UniqueDeliveryList jobs;
+
+ {
+ jobs = new UniqueDeliveryList();
+ }
+
+ /**
+ * DeliveryJobSystem
+ */
+ public DeliveryJobSystem() {}
+
+ /**
+ * DeliveryJobSystem
+ *
+ * @param deliveryJobSystem
+ */
+ public DeliveryJobSystem(ReadOnlyDeliveryJobSystem deliveryJobSystem) {
+ this();
+ resetData(deliveryJobSystem);
+ }
+
+ private void setDeliveryJobs(List jobs) {
+ this.jobs.setDeliveryJobs(jobs);
+ }
+
+ /**
+ * resetData
+ *
+ * @param newData
+ */
+ public void resetData(ReadOnlyDeliveryJobSystem newData) {
+ requireNonNull(newData);
+
+ setDeliveryJobs(newData.getDeliveryJobList());
+ }
+
+ /**
+ * setDeliveryJob
+ *
+ * @param target
+ * @param editedJob
+ */
+ public void setDeliveryJob(DeliveryJob target, DeliveryJob editedJob) {
+ requireNonNull(editedJob);
+
+ jobs.setDeliveryJob(target, editedJob);
+ }
+
+ /**
+ * DeliveryJobSystem
+ */
+ public ObservableList getDeliveryJobList() {
+ return jobs.asUnmodifiableObservableList();
+ }
+
+ /**
+ * addDeliveryJob
+ *
+ * @param job
+ */
+ public void addDeliveryJob(DeliveryJob job) {
+ jobs.add(job);
+ }
+
+ /**
+ * removeDeliveryJob
+ *
+ * @param key
+ */
+ public void removeDeliveryJob(DeliveryJob key) {
+ jobs.remove(key);
+ }
+
+ /**
+ * Returns true if a delivery job with the same identity as {@code delivery job} exists in the address book.
+ */
+ public boolean hasDeliveryJob(DeliveryJob job) {
+ requireNonNull(job);
+ return jobs.contains(job);
+ }
+
+ /**
+ * @return number of jobs in list
+ */
+ public int size() {
+ return jobs.size();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof DeliveryJobSystem // instanceof handles nulls
+ && jobs.equals(((DeliveryJobSystem) other).jobs));
+ }
+
+ @Override
+ public String toString() {
+ return jobs.asUnmodifiableObservableList().size() + " delivery jobs";
+ }
+
+ @Override
+ public int hashCode() {
+ return jobs.hashCode();
+ }
+}
diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java
index d54df471c1f..ec33ec397a8 100644
--- a/src/main/java/seedu/address/model/Model.java
+++ b/src/main/java/seedu/address/model/Model.java
@@ -1,11 +1,19 @@
package seedu.address.model;
import java.nio.file.Path;
+import java.time.LocalDate;
+import java.util.Comparator;
+import java.util.Map;
+import java.util.Optional;
import java.util.function.Predicate;
import javafx.collections.ObservableList;
import seedu.address.commons.core.GuiSettings;
+import seedu.address.model.jobs.DeliveryJob;
+import seedu.address.model.jobs.DeliveryList;
import seedu.address.model.person.Person;
+import seedu.address.model.reminder.Reminder;
+import seedu.address.model.stats.WeeklyStats;
/**
* The API of the Model component.
@@ -13,16 +21,17 @@
public interface Model {
/** {@code Predicate} that always evaluate to true */
Predicate PREDICATE_SHOW_ALL_PERSONS = unused -> true;
+ Predicate PREDICATE_SHOW_ALL_DELIVERY_JOBS = unused -> true;
/**
- * Replaces user prefs data with the data in {@code userPrefs}.
+ * Returns the user prefs.
*/
- void setUserPrefs(ReadOnlyUserPrefs userPrefs);
+ ReadOnlyUserPrefs getUserPrefs();
/**
- * Returns the user prefs.
+ * Replaces user prefs data with the data in {@code userPrefs}.
*/
- ReadOnlyUserPrefs getUserPrefs();
+ void setUserPrefs(ReadOnlyUserPrefs userPrefs);
/**
* Returns the user prefs' GUI settings.
@@ -44,19 +53,27 @@ public interface Model {
*/
void setAddressBookFilePath(Path addressBookFilePath);
+ /**
+ * Returns the AddressBook
+ */
+ ReadOnlyAddressBook getAddressBook();
+
/**
* Replaces address book data with the data in {@code addressBook}.
*/
void setAddressBook(ReadOnlyAddressBook addressBook);
- /** Returns the AddressBook */
- ReadOnlyAddressBook getAddressBook();
-
/**
* Returns true if a person with the same identity as {@code person} exists in the address book.
*/
boolean hasPerson(Person person);
+ /**
+ * Returns person with specified ID
+ * @param id
+ */
+ Optional getPersonById(String id);
+
/**
* Deletes the given person.
* The person must exist in the address book.
@@ -76,12 +93,208 @@ public interface Model {
*/
void setPerson(Person target, Person editedPerson);
- /** Returns an unmodifiable view of the filtered person list */
+ /**
+ * Returns an unmodifiable view of the filtered person list
+ */
ObservableList getFilteredPersonList();
/**
* Updates the filter of the filtered person list to filter by the given {@code predicate}.
+ *
* @throws NullPointerException if {@code predicate} is null.
*/
void updateFilteredPersonList(Predicate predicate);
+
+ // DELIVERY JOB SYSTEM ===================================
+
+ /**
+ * Returns the user prefs' delivery job system file path.
+ */
+ Path getDeliveryJobSystemFilePath();
+
+ /**
+ * Sets the user prefs' delivery job system file path.
+ */
+ void setDeliveryJobSystemFilePath(Path deliveryJobSystemFilePath);
+
+ /**
+ * Sets delivery job system
+ * @param jobSystem
+ */
+ void setDeliveryJobSystem(ReadOnlyDeliveryJobSystem jobSystem);
+
+ /**
+ * Returns delivery job system
+ */
+ ReadOnlyDeliveryJobSystem getDeliveryJobSystem();
+
+ /**
+ * Returns the filtered delivery job list
+ */
+ ObservableList getFilteredDeliveryJobList();
+
+ /**
+ * Checks if the job list has a certain job
+ * @param job job to find
+ */
+ boolean hasDeliveryJob(DeliveryJob job);
+
+ /**
+ * Deletes delivery job in job list
+ * @param target job to delete
+ */
+ void deleteDeliveryJob(DeliveryJob target);
+
+ /**
+ * Adds delivery job to job list
+ * @param job job to add
+ */
+ void addDeliveryJob(DeliveryJob job);
+
+ /**
+ * Sets/updates delivery job in job list
+ * @param target job to edit/to be replaced
+ * @param editedJob new job to replace
+ */
+ void setDeliveryJob(DeliveryJob target, DeliveryJob editedJob);
+
+ /**
+ * Returns job list
+ */
+ ObservableList getDeliveryJobList();
+
+ /**
+ * Returns job list sorted
+ */
+ ObservableList getSortedDeliveryJobListByComparator();
+
+ /**
+ * Updates filtered delivery job list based on new predicate
+ * @param predicate
+ */
+ void updateFilteredDeliveryJobList(Predicate predicate);
+
+ /**
+ * Updates sorted delivery job list based on new sorter
+ * @param sorter
+ */
+ void updateSortedDeliveryJobList(Comparator sorter);
+
+ /**
+ * Updates sorted delivery job list based on new sorter
+ * @param sorter
+ */
+ void updateSortedDeliveryJobListByComparator(Comparator sorter);
+
+ /**
+ * Updates sorted delivery job list by date and earning
+ */
+ void updateSortedDeliveryJobListByDate();
+
+ /**
+ * Updates delivery job list in week containing given date
+ * @param date date to focus
+ */
+ void updateWeekDeliveryJobList(LocalDate date);
+
+ /**
+ * Updates focus date
+ * @param jobDate
+ */
+ void updateFocusDate(LocalDate jobDate);
+
+ /**
+ * Returns sorted delivery job list
+ */
+ ObservableList getSortedDeliveryJobList();
+
+ /**
+ * Returns sorted delivery job list by date
+ */
+ Map getSortedDeliveryJobListByDate();
+
+ /**
+ * Returns job list in the week
+ */
+ Map getWeekDeliveryJobList();
+
+ /**
+ * Returns job list in a specific day of week
+ * @param dayOfWeek
+ */
+ DeliveryList getDayOfWeekJob(int dayOfWeek);
+
+ /**
+ * Returns list of unscheduled jobs
+ */
+ ObservableList getUnscheduledDeliveryJobList();
+
+ /**
+ * Returns list of completed jobs
+ */
+ ObservableList getCompletedDeliveryJobList();
+
+ /**
+ * Returns focus date
+ */
+ LocalDate getFocusDate();
+
+
+
+ // NOTIFICATION =========================================
+
+ /**
+ * Deletes the given reminder.
+ * The reminder must exist in reminders.
+ */
+ void deleteReminder(int i);
+
+ /**
+ * Adds the given reminder.
+ */
+ void addReminder(Reminder reminder);
+
+ /**
+ * Returns an unmodifiable view of the filtered reminder list
+ */
+ ObservableList getReminderList();
+
+ /**
+ * Sorts reminder list
+ */
+
+ void sortReminderList();
+
+ /**
+ * Indicate that a reminder has already been shown for this app's runtime
+ */
+ void setHasShown(int i, boolean b);
+
+ // STATISTICS =========================================
+
+ /**
+ * Checks if the given job should be place in weekly stats.
+ */
+ boolean sameWeek(DeliveryJob job, WeeklyStats weeklyStats);
+
+ /**
+ * Returns an unmodifiable view of the Delivery Job list with only
+ * jobs that are in the same week as the given date.
+ */
+ ObservableList weekJobsList(ObservableList list, LocalDate date);
+
+ /**
+ * Returns the total earnings of delivery jobs in the list.
+ */
+ double getTotalEarnings(ObservableList list);
+
+ /**
+ * Returns the total number of completed delivery jobs in the list.
+ */
+ int getTotalCompleted(ObservableList list);
+
+ /**
+ * Returns the total number of pending delivery jobs in the list.
+ */
+ int getTotalPending(ObservableList list);
}
diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java
index 86c1df298d7..246e5fad1f0 100644
--- a/src/main/java/seedu/address/model/ModelManager.java
+++ b/src/main/java/seedu/address/model/ModelManager.java
@@ -4,53 +4,96 @@
import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
import java.nio.file.Path;
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
import java.util.function.Predicate;
import java.util.logging.Logger;
+import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
+import javafx.collections.transformation.SortedList;
import seedu.address.commons.core.GuiSettings;
import seedu.address.commons.core.LogsCenter;
+import seedu.address.model.jobs.DeliveryDate;
+import seedu.address.model.jobs.DeliveryJob;
+import seedu.address.model.jobs.DeliveryList;
+import seedu.address.model.jobs.Earning;
+import seedu.address.model.jobs.sorters.SortbyTimeAndEarn;
import seedu.address.model.person.Person;
+import seedu.address.model.reminder.Reminder;
+import seedu.address.model.stats.WeeklyStats;
/**
* Represents the in-memory model of the address book data.
*/
public class ModelManager implements Model {
+ public static final SortbyTimeAndEarn SORTER_BY_DATE = new SortbyTimeAndEarn();
+
private static final Logger logger = LogsCenter.getLogger(ModelManager.class);
private final AddressBook addressBook;
+ private final DeliveryJobSystem deliveryJobSystem;
private final UserPrefs userPrefs;
private final FilteredList filteredPersons;
+ private final FilteredList filteredDeliveryJobs;
+ private final SortedList sortedDeliveryJobsList;
+ private final ObservableList reminderList;
+ private final Map weekJobListGroupedByDate;
+ private SortedList sortedDeliveryJobs;
+ private LocalDate focusDate;
+ private Map jobListGroupedByDate;
+
+
/**
* Initializes a ModelManager with the given addressBook and userPrefs.
+ * @param addressBook
+ * @param deliveryJobSystem
+ * @param userPrefs
*/
- public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs) {
- requireAllNonNull(addressBook, userPrefs);
+ public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyDeliveryJobSystem deliveryJobSystem,
+ ReadOnlyUserPrefs userPrefs) {
+ requireAllNonNull(addressBook, deliveryJobSystem, userPrefs);
logger.fine("Initializing with address book: " + addressBook + " and user prefs " + userPrefs);
this.addressBook = new AddressBook(addressBook);
+ this.deliveryJobSystem = new DeliveryJobSystem(deliveryJobSystem);
this.userPrefs = new UserPrefs(userPrefs);
- filteredPersons = new FilteredList<>(this.addressBook.getPersonList());
+ this.filteredPersons = new FilteredList(this.addressBook.getPersonList());
+ this.filteredDeliveryJobs = new FilteredList(this.deliveryJobSystem.getDeliveryJobList());
+ this.sortedDeliveryJobs = new SortedList(this.deliveryJobSystem.getDeliveryJobList());
+ this.sortedDeliveryJobsList = new SortedList(filteredDeliveryJobs);
+ //updateSortedDeliveryJobListByDate();
+ this.jobListGroupedByDate = new HashMap();
+ this.weekJobListGroupedByDate = new HashMap();
+ this.reminderList = this.addressBook.getReminderList();
+ this.focusDate = LocalDate.now();
}
+ /**
+ * ModelManager.
+ */
public ModelManager() {
- this(new AddressBook(), new UserPrefs());
+ this(new AddressBook(), new DeliveryJobSystem(), new UserPrefs());
}
- //=========== UserPrefs ==================================================================================
+ // UserPrefs ===================================================================
@Override
- public void setUserPrefs(ReadOnlyUserPrefs userPrefs) {
- requireNonNull(userPrefs);
- this.userPrefs.resetData(userPrefs);
+ public ReadOnlyUserPrefs getUserPrefs() {
+ return userPrefs;
}
@Override
- public ReadOnlyUserPrefs getUserPrefs() {
- return userPrefs;
+ public void setUserPrefs(ReadOnlyUserPrefs userPrefs) {
+ requireNonNull(userPrefs);
+ this.userPrefs.resetData(userPrefs);
}
@Override
@@ -75,16 +118,16 @@ public void setAddressBookFilePath(Path addressBookFilePath) {
userPrefs.setAddressBookFilePath(addressBookFilePath);
}
- //=========== AddressBook ================================================================================
+ // AddressBook ===============================================================
@Override
- public void setAddressBook(ReadOnlyAddressBook addressBook) {
- this.addressBook.resetData(addressBook);
+ public ReadOnlyAddressBook getAddressBook() {
+ return addressBook;
}
@Override
- public ReadOnlyAddressBook getAddressBook() {
- return addressBook;
+ public void setAddressBook(ReadOnlyAddressBook addressBook) {
+ this.addressBook.resetData(addressBook);
}
@Override
@@ -93,6 +136,12 @@ public boolean hasPerson(Person person) {
return addressBook.hasPerson(person);
}
+ @Override
+ public Optional getPersonById(String id) {
+ requireNonNull(id);
+ return addressBook.getPersonById(id);
+ }
+
@Override
public void deletePerson(Person target) {
addressBook.removePerson(target);
@@ -111,10 +160,11 @@ public void setPerson(Person target, Person editedPerson) {
addressBook.setPerson(target, editedPerson);
}
- //=========== Filtered Person List Accessors =============================================================
+ // =========== Filtered Person List Accessors ==================
/**
- * Returns an unmodifiable view of the list of {@code Person} backed by the internal list of
+ * Returns an unmodifiable view of the list of {@code Person} backed by the
+ * internal list of
* {@code versionedAddressBook}
*/
@Override
@@ -128,6 +178,260 @@ public void updateFilteredPersonList(Predicate predicate) {
filteredPersons.setPredicate(predicate);
}
+ // DeliveryJob System =====================================================
+
+ @Override
+ public Path getDeliveryJobSystemFilePath() {
+ return userPrefs.getDeliveryJobSystemFilePath();
+ }
+
+ @Override
+ public void setDeliveryJobSystemFilePath(Path deliveryJobSystemFilePath) {
+ requireNonNull(deliveryJobSystemFilePath);
+ userPrefs.setDeliveryJobSystemFilePath(deliveryJobSystemFilePath);
+ }
+
+ @Override
+ public void setDeliveryJobSystem(ReadOnlyDeliveryJobSystem jobSystem) {
+ this.deliveryJobSystem.resetData(jobSystem);
+ }
+
+ @Override
+ public ReadOnlyDeliveryJobSystem getDeliveryJobSystem() {
+ return deliveryJobSystem;
+ }
+
+ @Override
+ public boolean hasDeliveryJob(DeliveryJob job) {
+ requireNonNull(job);
+ return deliveryJobSystem.hasDeliveryJob(job);
+ }
+
+ @Override
+ public void deleteDeliveryJob(DeliveryJob target) {
+ deliveryJobSystem.removeDeliveryJob(target);
+ }
+
+ @Override
+ public void addDeliveryJob(DeliveryJob job) {
+ deliveryJobSystem.addDeliveryJob(job);
+ updateFilteredDeliveryJobList(PREDICATE_SHOW_ALL_DELIVERY_JOBS);
+ }
+
+ @Override
+ public void setDeliveryJob(DeliveryJob target, DeliveryJob editedJob) {
+ requireAllNonNull(target, editedJob);
+
+ deliveryJobSystem.setDeliveryJob(target, editedJob);
+ }
+
+ // =========== Filtered Delivery Job List Accessors ============
+
+ @Override
+ public ObservableList getDeliveryJobList() {
+ updateFilteredDeliveryJobList(PREDICATE_SHOW_ALL_DELIVERY_JOBS);
+ return filteredDeliveryJobs;
+ }
+
+ @Override
+ public ObservableList getFilteredDeliveryJobList() {
+ return filteredDeliveryJobs;
+ }
+
+ @Override
+ public void updateFilteredDeliveryJobList(Predicate predicate) {
+ requireAllNonNull(predicate);
+ filteredDeliveryJobs.setPredicate(predicate);
+ }
+
+ @Override
+ public void updateSortedDeliveryJobList(Comparator sorter) {
+ requireNonNull(sorter);
+ sortedDeliveryJobs = new SortedList(this.deliveryJobSystem.getDeliveryJobList());
+ sortedDeliveryJobs.setComparator(sorter);
+ }
+
+ @Override
+ public void updateSortedDeliveryJobListByComparator(Comparator sorter) {
+ requireNonNull(sorter);
+ sortedDeliveryJobsList.setComparator(sorter);
+ }
+
+ @Override
+ public ObservableList getSortedDeliveryJobListByComparator() {
+ return sortedDeliveryJobsList;
+ }
+
+ @Override
+ public ObservableList getSortedDeliveryJobList() {
+ return FXCollections.observableArrayList(sortedDeliveryJobs);
+ }
+
+ @Override
+ public void updateSortedDeliveryJobListByDate() {
+ updateSortedDeliveryJobList(SORTER_BY_DATE);
+ jobListGroupedByDate.clear();
+ for (int i = 0; i < sortedDeliveryJobs.size(); i++) {
+ if (sortedDeliveryJobs.get(i).isScheduled()
+ && (!sortedDeliveryJobs.get(i).getDeliveredStatus())) {
+ addJobToJobListBasedOnDay(jobListGroupedByDate, sortedDeliveryJobs.get(i));
+ }
+ }
+ }
+
+ /**
+ * Adds job to job list grouped by date according to delivery date
+ * Given that job has delivery date and slot
+ * @param jobListGroupedByDate
+ * @param toAdd
+ */
+ private void addJobToJobListBasedOnDay(Map jobListGroupedByDate, DeliveryJob toAdd) {
+ LocalDate jobDate = toAdd.getDate();
+ int jobSlot = toAdd.getSlot();
+ int slotIndex = (jobSlot) - 1;
+
+ if (jobListGroupedByDate.containsKey(jobDate)) {
+ DeliveryList jobsInCurrentSlot = jobListGroupedByDate.get(jobDate);
+ if (jobsInCurrentSlot.size() == 0) {
+ jobsInCurrentSlot = createEmptyDayJobList();
+ }
+ if (slotIndex > 4) {
+ jobsInCurrentSlot.get(5).add(toAdd);
+ } else {
+ jobsInCurrentSlot.get(slotIndex).add(toAdd);
+ }
+ jobListGroupedByDate.put(jobDate, jobsInCurrentSlot);
+ } else {
+ DeliveryList newDateJobList = createEmptyDayJobList();
+ if (slotIndex > 4) {
+ newDateJobList.get(5).add(toAdd);
+ } else {
+ newDateJobList.get(slotIndex).add(toAdd);
+ }
+ jobListGroupedByDate.put(jobDate, newDateJobList);
+ }
+ }
+
+ /**
+ * Update list of week delivery job list,
+ * period spans from 1 week before given date
+ * to 1 week after given date
+ * @param date given/focused date
+ */
+ @Override
+ public void updateWeekDeliveryJobList(LocalDate date) {
+ requireNonNull(date);
+ weekJobListGroupedByDate.clear();
+ this.focusDate = date;
+
+ int focusDayOfWeek = date.getDayOfWeek().getValue();
+ int dayOfWeekTracker = 1;
+
+ while (dayOfWeekTracker < 8) {
+ int dayToAdd = dayOfWeekTracker - focusDayOfWeek;
+ LocalDate dayInWeek = date.plusDays(dayToAdd);
+ addWeekJobList(dayInWeek);
+ dayOfWeekTracker++;
+ }
+ }
+
+ @Override
+ public void updateFocusDate(LocalDate jobDate) {
+ requireNonNull(jobDate);
+ this.focusDate = jobDate;
+ }
+
+ private void addWeekJobList(LocalDate dayToAdd) {
+ if (jobListGroupedByDate.containsKey(dayToAdd)) {
+ DeliveryList currentJobList = jobListGroupedByDate.get(dayToAdd);
+ weekJobListGroupedByDate.put(dayToAdd, currentJobList);
+ } else {
+ weekJobListGroupedByDate.put(dayToAdd, null);
+ }
+ }
+
+ private DeliveryList createEmptyDayJobList() {
+ ArrayList> newEmptyArr = new ArrayList>();
+ for (int i = 0; i < 6; i++) {
+ newEmptyArr.add(new ArrayList());
+ }
+ return new DeliveryList(newEmptyArr);
+ }
+
+ @Override
+ public Map getSortedDeliveryJobListByDate() {
+ return jobListGroupedByDate;
+ }
+
+ @Override
+ public Map getWeekDeliveryJobList() {
+ return weekJobListGroupedByDate;
+ }
+
+ @Override
+ public DeliveryList getDayOfWeekJob(int dayOfWeek) {
+ int focusDayOfWeek = focusDate.getDayOfWeek().getValue();
+ LocalDate dayToGet = focusDate.plusDays(dayOfWeek - focusDayOfWeek);
+ return weekJobListGroupedByDate.get(dayToGet);
+ }
+
+ @Override
+ public ObservableList getUnscheduledDeliveryJobList() {
+ updateSortedDeliveryJobList(SORTER_BY_DATE);
+ FilteredList unscheduledJobList =
+ new FilteredList<>(FXCollections.observableArrayList(sortedDeliveryJobs));
+ unscheduledJobList.setPredicate(job -> (!job.isValidScheduled()));
+ return FXCollections.observableArrayList(unscheduledJobList);
+ }
+
+ @Override
+ public ObservableList getCompletedDeliveryJobList() {
+ updateSortedDeliveryJobList(SORTER_BY_DATE);
+ FilteredList unscheduledJobList =
+ new FilteredList<>(FXCollections.observableArrayList(sortedDeliveryJobs));
+ unscheduledJobList.setPredicate(job -> (job.getDeliveredStatus()));
+ return FXCollections.observableArrayList(unscheduledJobList);
+ }
+
+ @Override
+ public LocalDate getFocusDate() {
+ return focusDate;
+ }
+
+ //=========== ReminderList Accessors =============================================================
+
+ @Override
+ public void deleteReminder(int i) {
+ addressBook.removeReminder(i);
+ }
+
+ @Override
+ public void addReminder(Reminder reminder) {
+ addressBook.addReminder(reminder);
+ }
+
+ /**
+ * Returns an unmodifiable view of the list of {@code Person} backed by the internal list of
+ * {@code versionedAddressBook}
+ */
+ @Override
+ public ObservableList getReminderList() {
+ return reminderList;
+ }
+
+ /**
+ * Sorts reminder list
+ */
+ @Override
+ public void sortReminderList() {
+ addressBook.sortReminderList();
+ }
+
+ @Override
+ public void setHasShown(int i, boolean b) {
+ reminderList.get(i).setHasShown(b);
+ }
+
@Override
public boolean equals(Object obj) {
// short circuit if same object
@@ -147,4 +451,62 @@ public boolean equals(Object obj) {
&& filteredPersons.equals(other.filteredPersons);
}
+ @Override
+ public boolean sameWeek(DeliveryJob job, WeeklyStats weeklyStats) {
+ Optional deliveryDate = job.getDeliveryDate();
+ if (!deliveryDate.isEmpty()) {
+ DeliveryDate date = deliveryDate.get();
+ LocalDate localDate = date.getDate();
+ return weeklyStats.getDates().contains(localDate);
+ }
+ return false;
+ }
+
+ @Override
+ public ObservableList weekJobsList(ObservableList list, LocalDate date) {
+ ObservableList newList = FXCollections.observableArrayList(list);
+ WeeklyStats weeklyStats = new WeeklyStats(date);
+ for (DeliveryJob job: list) {
+ if (!sameWeek(job, weeklyStats)) {
+ newList.remove(job);
+ }
+ }
+ return newList;
+ }
+
+ @Override
+ public double getTotalEarnings(ObservableList list) {
+ double earnings = 0;
+ for (DeliveryJob job: list) {
+ Optional earning = job.getEarning();
+ Earning earn = earning.get();
+ if (earn != null) {
+ earnings += Double.parseDouble(earn.value);
+ }
+ }
+ return earnings;
+ }
+
+ @Override
+ public int getTotalCompleted(ObservableList list) {
+ int completed = 0;
+ for (DeliveryJob job: list) {
+ if (job.getDeliveredStatus()) {
+ completed += 1;
+ }
+ }
+ return completed;
+ }
+
+ @Override
+ public int getTotalPending(ObservableList list) {
+ int pending = 0;
+ for (DeliveryJob job: list) {
+ if (!job.getDeliveredStatus()) {
+ pending += 1;
+ }
+ }
+ return pending;
+ }
+
}
diff --git a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java
index 6ddc2cd9a29..9f721412f52 100644
--- a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java
+++ b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java
@@ -1,7 +1,10 @@
package seedu.address.model;
+import java.util.Optional;
+
import javafx.collections.ObservableList;
import seedu.address.model.person.Person;
+import seedu.address.model.reminder.Reminder;
/**
* Unmodifiable view of an address book
@@ -14,4 +17,8 @@ public interface ReadOnlyAddressBook {
*/
ObservableList getPersonList();
+ ObservableList getReminderList();
+
+ Optional getPersonById(String id);
+
}
diff --git a/src/main/java/seedu/address/model/ReadOnlyDeliveryJobSystem.java b/src/main/java/seedu/address/model/ReadOnlyDeliveryJobSystem.java
new file mode 100644
index 00000000000..2b4bc9a1e62
--- /dev/null
+++ b/src/main/java/seedu/address/model/ReadOnlyDeliveryJobSystem.java
@@ -0,0 +1,13 @@
+package seedu.address.model;
+
+import javafx.collections.ObservableList;
+import seedu.address.model.jobs.DeliveryJob;
+
+/**
+ * ReadOnlyDeliveryJobSystem
+ */
+public interface ReadOnlyDeliveryJobSystem {
+
+ ObservableList getDeliveryJobList();
+
+}
diff --git a/src/main/java/seedu/address/model/UserPrefs.java b/src/main/java/seedu/address/model/UserPrefs.java
index 25a5fd6eab9..7ab403f58cf 100644
--- a/src/main/java/seedu/address/model/UserPrefs.java
+++ b/src/main/java/seedu/address/model/UserPrefs.java
@@ -15,11 +15,13 @@ public class UserPrefs implements ReadOnlyUserPrefs {
private GuiSettings guiSettings = new GuiSettings();
private Path addressBookFilePath = Paths.get("data" , "addressbook.json");
+ private Path deliveryJobSystemFilePath = Paths.get("data" , "deliveryjobsystem.json");
/**
* Creates a {@code UserPrefs} with default values.
*/
- public UserPrefs() {}
+ public UserPrefs() {
+ }
/**
* Creates a {@code UserPrefs} with the prefs in {@code userPrefs}.
@@ -84,4 +86,13 @@ public String toString() {
return sb.toString();
}
+ public Path getDeliveryJobSystemFilePath() {
+ return deliveryJobSystemFilePath;
+ }
+
+ public void setDeliveryJobSystemFilePath(Path deliveryJobSystemFilePath) {
+ requireNonNull(deliveryJobSystemFilePath);
+ this.deliveryJobSystemFilePath = deliveryJobSystemFilePath;
+ }
+
}
diff --git a/src/main/java/seedu/address/model/jobs/DeliveryDate.java b/src/main/java/seedu/address/model/jobs/DeliveryDate.java
new file mode 100644
index 00000000000..a4cf4d0efcd
--- /dev/null
+++ b/src/main/java/seedu/address/model/jobs/DeliveryDate.java
@@ -0,0 +1,95 @@
+package seedu.address.model.jobs;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.AppUtil.checkArgument;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+
+/**
+ * Represents a job's job date in the address book.
+ * Guarantees: immutable; is valid as declared in {@link #isValidDate(String)}
+ */
+public class DeliveryDate {
+
+ public static final String MESSAGE_CONSTRAINTS = "Dates must be valid date "
+ + "- only contain numeric characters and spaces, "
+ + "and it should not be blank.\n"
+ + "Date should have format like this: YYYY-mm-DD";
+
+ /*
+ * The first character of the address must not be a whitespace,
+ * otherwise " " (a blank string) becomes a valid input.
+ */
+ public static final String VALIDATION_REGEX = "\\d{4}-\\d{2}-\\d{2}";
+ public static final DateTimeFormatter VALID_FORMAT = DateTimeFormatter.ofPattern("YYYY-MM-dd");
+
+ /**
+ * Represents invalid date in storage.
+ */
+ private static final String PLACEHOLDER = "9999-12-31";
+
+ public final String date;
+
+ /**
+ * Constructs a {@code JobDate}.
+ *
+ * @param date A valid date.
+ */
+ public DeliveryDate(String date) {
+ requireNonNull(date);
+ checkArgument(isValidDate(date), MESSAGE_CONSTRAINTS);
+ this.date = date;
+ }
+
+ /**
+ * Constructs a placeholder {@code JobDate}.
+ *
+ * @param date A valid date.
+ */
+ private DeliveryDate() {
+ this.date = PLACEHOLDER;
+ }
+
+ /**
+ * Returns true if a given string is a valid date.
+ */
+ public static boolean isValidDate(String test) {
+ try {
+ LocalDate.parse(test);
+ } catch (DateTimeParseException e) {
+ return false;
+ }
+ return test.matches(VALIDATION_REGEX);
+ }
+
+ /**
+ * Returns date in LocalDate format
+ */
+ public LocalDate getDate() {
+ return LocalDate.parse(this.date);
+ }
+
+ @Override
+ public String toString() {
+ return date;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof DeliveryDate // instanceof handles nulls
+ && date.equals(((DeliveryDate) other).date)); // state check
+ }
+
+ @Override
+ public int hashCode() {
+ return date.hashCode();
+ }
+
+ public static DeliveryDate placeholder() {
+ return new DeliveryDate();
+ }
+
+}
diff --git a/src/main/java/seedu/address/model/jobs/DeliveryJob.java b/src/main/java/seedu/address/model/jobs/DeliveryJob.java
new file mode 100644
index 00000000000..9e36a3f71a9
--- /dev/null
+++ b/src/main/java/seedu/address/model/jobs/DeliveryJob.java
@@ -0,0 +1,476 @@
+package seedu.address.model.jobs;
+
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+
+import java.time.LocalDate;
+import java.util.Optional;
+import java.util.UUID;
+
+/**
+ * Represents delivery jobs entities.
+ */
+public class DeliveryJob {
+ // Identity fields
+ private final String jobId;
+
+ // Delivery informations
+ private final String recipient;
+ private final String sender; // aka customer
+ private final Optional deliveryDate;
+ private final Optional deliverySlot;
+ private final Optional earning;
+ private final Boolean isDelivered;
+ private final String description;
+
+ /**
+ * Constructs a job entity.
+ *
+ * @param recipient
+ * @param sender
+ * @param earning
+ */
+ public DeliveryJob(String recipient, String sender, String earning, String description) {
+ this(genJobId(recipient, sender), recipient, sender, Optional.empty(), Optional.empty(),
+ Optional.of(new Earning(earning)), false, description);
+ }
+
+ /**
+ * Constructs a job entity.
+ *
+ * @param recipient
+ * @param sender
+ * @param deliveryDate
+ * @param deliverySlot
+ * @param earning
+ */
+ public DeliveryJob(String recipient, String sender, Optional deliveryDate,
+ Optional deliverySlot, Optional earning,
+ String description) {
+ this(genJobId(recipient, sender), recipient, sender, deliveryDate,
+ deliverySlot, earning, false, description);
+ }
+
+ /**
+ * Constructs a job entity.
+ *
+ * @param recipient
+ * @param sender
+ * @param deliveryDate
+ * @param deliverySlot
+ * @param earning
+ */
+ public DeliveryJob(String recipient, String sender, String deliveryDate, String deliverySlot, String earning,
+ String description) {
+ this(genJobId(recipient, sender), recipient, sender, Optional.of(new DeliveryDate(deliveryDate)),
+ Optional.of(new DeliverySlot(deliverySlot)), Optional.of(new Earning(earning)), false, description);
+ }
+
+ /**
+ * DeliveryJob
+ *
+ * @param jobId
+ * @param recipient
+ * @param deliverySlot
+ * @param sender
+ * @param earning
+ * @param isDelivered
+ */
+ public DeliveryJob(String jobId, String recipient, String sender, Optional deliveryDate,
+ Optional deliverySlot,
+ Optional earning,
+ Boolean isDelivered, String description) {
+ this(true, jobId, recipient, sender, deliveryDate,
+ deliverySlot, earning, isDelivered,
+ description);
+ }
+
+ /**
+ * A special constructor to bypass null check.
+ *
+ * @param jobId
+ * @param recipient
+ * @param deliverySlot
+ * @param sender
+ * @param earning
+ * @param isDelivered
+ */
+ public DeliveryJob(boolean checkNull, String jobId, String recipient, String sender,
+ Optional deliveryDate,
+ Optional deliverySlot,
+ Optional earning,
+ Boolean isDelivered, String description) {
+ if (checkNull) {
+ requireAllNonNull(jobId, recipient, sender, deliveryDate, deliverySlot, earning, isDelivered, description);
+ }
+ this.jobId = jobId;
+ this.recipient = recipient;
+ this.sender = sender;
+ this.deliveryDate = deliveryDate;
+ this.deliverySlot = deliverySlot;
+ this.earning = earning;
+ this.isDelivered = isDelivered;
+ this.description = description;
+ }
+
+ private static String genJobId(String recipient, String sender) {
+ requireAllNonNull(recipient, sender);
+ return recipient.substring(0, 2)
+ .concat(sender.substring(0, 2))
+ .concat(UUID.randomUUID().toString().substring(0, 6))
+ .toUpperCase();
+ }
+
+ /**
+ * Returns job ID
+ */
+ public String getJobId() {
+ return jobId;
+ }
+
+ /**
+ * Returns recipient ID
+ */
+ public String getRecipientId() {
+ return recipient;
+ }
+
+ /**
+ * Returns sender ID
+ */
+ public String getSenderId() {
+ return sender;
+ }
+
+ /**
+ * Returns delivery date
+ */
+ public Optional getDeliveryDate() {
+ return deliveryDate;
+ }
+
+ /**
+ * Returns delivery slot
+ */
+ public Optional getDeliverySlot() {
+ return deliverySlot;
+ }
+
+ /**
+ * Returns delivery date in LocalDate
+ */
+ public LocalDate getDate() {
+ return deliveryDate.get().getDate();
+ }
+
+ /**
+ * Returns delivery slot in Integer
+ */
+ public int getSlot() {
+ return deliverySlot.get().getSlot();
+ }
+
+ /**
+ * Returns delivery earning
+ */
+ public Optional getEarning() {
+ return earning;
+ }
+
+ /**
+ * Returns delivered status
+ */
+ public Boolean getDeliveredStatus() {
+ return isDelivered;
+ }
+
+ /**
+ * Returns delivery description in proper String format
+ */
+ public String getDescription() {
+ if (description == null) {
+ return "";
+ }
+ return description;
+ }
+
+ /**
+ * Checks if job has delivery date or slot
+ * @return
+ */
+ public boolean hasDateOrSlot() {
+ return hasDate() || hasSlot();
+ }
+
+ /**
+ * Checks if job has delivery date
+ * @return boolean
+ */
+ public boolean hasDate() {
+ return getDeliveryDate().isPresent();
+ }
+
+ /**
+ * Checks if job has delivery slot
+ * @return boolean
+ */
+ public boolean hasSlot() {
+ return getDeliverySlot().isPresent();
+ }
+
+ /**
+ * Checks if job has invalid delivery slot
+ * @return boolean
+ */
+ public boolean hasInvalidSlot() {
+ return getDeliverySlot().isPresent() && (!getDeliverySlot().get().isValidRange());
+ }
+
+ /**
+ * Checks if job has earning
+ * @return boolean
+ */
+ public boolean hasEarning() {
+ return getEarning().isPresent();
+ }
+
+ /**
+ * Checks if job has delivery date and slot.
+ *
+ * @return boolean
+ */
+ public boolean isScheduled() {
+ return getDeliveryDate().isPresent() && getDeliverySlot().isPresent()
+ && getDeliverySlot().get().isPositive();
+ }
+
+ /**
+ * Checks if job has valid delivery date and slot
+ * @return
+ */
+ public boolean isValidScheduled() {
+ return isScheduled() && deliverySlot.get().isValidRange();
+ }
+
+ /**
+ * isSameDeliveryJob.
+ *
+ * @param otherJob
+ * @return
+ */
+ public boolean isSameDeliveryJob(DeliveryJob otherJob) {
+ if (otherJob == this) {
+ return true;
+ }
+
+ return otherJob != null && otherJob.getJobId().equals(getJobId());
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append("[Job detail]\n");
+ if (getJobId() != null) {
+ builder.append("Job Id: " + getJobId() + "\n");
+ }
+
+ if (getSenderId() != null) {
+ builder.append("Sender Id: " + getSenderId() + "\n");
+ }
+
+ if (getRecipientId() != null) {
+ builder.append("Recipient Id: " + getRecipientId() + "\n");
+ }
+
+ if (getDeliveryDate().isPresent()) {
+ builder.append("Delivery Date: " + getDeliveryDate().get() + "\n");
+ }
+
+ if (getDeliverySlot().isPresent()) {
+ builder.append("Delivery Slot: " + getDeliverySlot().get() + "\n");
+ }
+
+ if (getEarning().isPresent()) {
+ builder.append("Earning: " + getEarning().get() + "\n");
+ }
+
+ if (getDeliveredStatus() != null) {
+ builder.append("Status: " + (getDeliveredStatus() ? "Delivered" : "Pending") + "\n");
+ }
+
+ return builder.toString();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof DeliveryJob)) {
+ return false;
+ }
+
+ DeliveryJob otherJob = (DeliveryJob) other;
+ return otherJob.getRecipientId().equals(getRecipientId())
+ && otherJob.getSenderId().equals(getSenderId())
+ && otherJob.getEarning().equals(getEarning());
+ }
+
+ /**
+ * Buider class for building DeliveryJob.
+ */
+ public static class Builder {
+ private String jobId;
+ private String recipient;
+ private String sender; // aka customer
+ private Optional deliveryDate = Optional.empty();
+ private Optional deliverySlot = Optional.empty();
+ private Optional earning = Optional.empty();
+ private Boolean isDelivered;
+ private String description;
+
+ /**
+ * Copys from an existing job.
+ *
+ * @param job
+ * @return
+ */
+ public Builder copy(DeliveryJob job) {
+ this.jobId = job.getJobId();
+ this.recipient = job.getRecipientId();
+ this.sender = job.getSenderId();
+ this.deliveryDate = job.getDeliveryDate();
+ this.deliverySlot = job.getDeliverySlot();
+ this.earning = job.getEarning();
+ this.isDelivered = job.getDeliveredStatus();
+ this.description = job.getDescription();
+ return this;
+ }
+
+ /**
+ * Sets jobid.
+ *
+ * @param id
+ * @return
+ */
+ public Builder setJobId(String id) {
+ this.jobId = id;
+ return this;
+ }
+
+ /**
+ * Sets recipient.
+ *
+ * @param id
+ * @return
+ */
+ public Builder setRecipient(String id) {
+ this.recipient = id;
+ return this;
+ }
+
+ /**
+ * Sets sender.
+ *
+ * @param id
+ * @return
+ */
+ public Builder setSender(String id) {
+ this.sender = id;
+ return this;
+ }
+
+ /**
+ * Sets deliveryDate.
+ *
+ * @param date
+ * @return
+ */
+ public Builder setDeliveryDate(String date) {
+ this.deliveryDate = Optional.of(new DeliveryDate(date));
+ return this;
+ }
+
+ /**
+ * Sets deliverySlot.
+ *
+ * @param slot
+ * @return
+ */
+ public Builder setDeliverySlot(String slot) {
+ this.deliverySlot = Optional.of(new DeliverySlot(slot));
+ return this;
+ }
+
+ /**
+ * Clears deliverySlot.
+ *
+ * @return
+ */
+ public Builder clearDeliverySlot() {
+ this.deliverySlot = Optional.of(DeliverySlot.placeholder());
+ return this;
+ }
+
+ /**
+ * Clears deliveryDate.
+ *
+ * @return
+ */
+ public Builder clearDeliveryDate() {
+ this.deliveryDate = Optional.of(DeliveryDate.placeholder());
+ return this;
+ }
+
+ /**
+ * Sets earning.
+ *
+ * @param earn
+ * @return
+ */
+ public Builder setEarning(String earn) {
+ this.earning = Optional.of(new Earning(earn));
+ return this;
+ }
+
+ /**
+ * Sets isDelivered.
+ *
+ * @param isDelivered
+ * @return
+ */
+ public Builder setDeliveredStatus(boolean isDelivered) {
+ this.isDelivered = isDelivered;
+ return this;
+ }
+
+ /**
+ * Sets description.
+ *
+ * @param description
+ * @return
+ */
+ public Builder setDescription(String description) {
+ this.description = description;
+ return this;
+ }
+
+ /**
+ * Builds DeliveryJob.
+ */
+ public DeliveryJob build() {
+ return new DeliveryJob(jobId, recipient, sender,
+ deliveryDate,
+ deliverySlot, earning, isDelivered, description);
+ }
+
+ /**
+ * Builds DeliveryJob.
+ */
+ public DeliveryJob buildNullable() {
+ return new DeliveryJob(false, jobId, recipient, sender,
+ deliveryDate,
+ deliverySlot, earning, isDelivered, description);
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/model/jobs/DeliveryList.java b/src/main/java/seedu/address/model/jobs/DeliveryList.java
new file mode 100644
index 00000000000..9d13ec6ad1b
--- /dev/null
+++ b/src/main/java/seedu/address/model/jobs/DeliveryList.java
@@ -0,0 +1,47 @@
+package seedu.address.model.jobs;
+
+import java.util.ArrayList;
+
+/**
+ * Represents an arraylist of array list of jobs
+ * Represents list of jobs in a day - classified into different slots
+ */
+public class DeliveryList {
+ private ArrayList> jobList;
+
+ public DeliveryList(ArrayList