diff --git a/.gitignore b/.gitignore
index 5e59b862ba4..92d241ecd42 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,3 +19,9 @@ src/test/data/sandbox/
# MacOS custom attributes files created by Finder
.DS_Store
+
+# Poster Storage
+src/main/resources/images/posters/
+
+# Ui Poster to prevent bugs
+docs/images/Ui.png
diff --git a/README.adoc b/README.adoc
index d34211c9341..96748b3f593 100644
--- a/README.adoc
+++ b/README.adoc
@@ -1,36 +1,57 @@
-= Address Book (Level 3)
+= EzWatchList
ifdef::env-github,env-browser[:relfileprefix: docs/]
-https://travis-ci.org/se-edu/addressbook-level3[image:https://travis-ci.org/se-edu/addressbook-level3.svg?branch=master[Build Status]]
-https://ci.appveyor.com/project/damithc/addressbook-level3[image:https://ci.appveyor.com/api/projects/status/3boko2x2vr5cc3w2?svg=true[Build status]]
-https://coveralls.io/github/se-edu/addressbook-level3?branch=master[image:https://coveralls.io/repos/github/se-edu/addressbook-level3/badge.svg?branch=master[Coverage Status]]
-https://www.codacy.com/app/damith/addressbook-level3?utm_source=github.com&utm_medium=referral&utm_content=se-edu/addressbook-level3&utm_campaign=Badge_Grade[image:https://api.codacy.com/project/badge/Grade/fc0b7775cf7f4fdeaf08776f3d8e364a[Codacy Badge]]
-https://gitter.im/se-edu/Lobby[image:https://badges.gitter.im/se-edu/Lobby.svg[Gitter chat]]
+:imagesDir: /docs/images
+https://travis-ci.org/AY1920S1-CS2103T-F13-4/main[image:https://travis-ci.org/AY1920S1-CS2103T-F13-4/main.svg?branch=master[Build Status]]
+https://coveralls.io/github/AY1920S1-CS2103T-F13-4/main?branch=master[image:https://coveralls.io/repos/github/AY1920S1-CS2103T-F13-4/main/badge.svg?branch=master[Coverage Status]]
-ifdef::env-github[]
-image::docs/images/Ui.png[width="600"]
-endif::[]
+== Introduction
-ifndef::env-github[]
-image::images/Ui.png[width="600"]
-endif::[]
+++++
+
+
+
+
-* This is a desktop Address Book application. It has a GUI but most of the user interactions happen using a CLI (Command Line Interface).
-* It is a Java sample application intended for students learning Software Engineering while using Java as the main programming language.
-* It is *written in OOP fashion*. It provides a *reasonably well-written* code example that is *significantly bigger* (around 6 KLoC)than what students usually write in beginner-level SE modules.
+++++
+
+Tired of using multiple sources on the internet to keep track of all your movies and tv shows that you want to watch? EzWatchlist is a solution to your entertainment problems!
+
+EzWatchlist is a desktop application for cinepliles to manage their favourite movies and tv series with a clean and intuitive interface.
+It was built for users who enjoy using a Command Line Interface (CLI) while still having the benefits of a Graphical User Interface (GUI).
+
+=== Features
+
+Spending too much time looking for shows?
+
+* Get recommendations for shows by using our https://ay1920s1-cs2103t-f13-4.github.io/main/UserGuide.html#movie-and-tv-show-recommendations[recommendations feature]
+
+Forgetting the shows you want to watch?
+
+* Keep track of shows with our inbuilt https://ay1920s1-cs2103t-f13-4.github.io/main/UserGuide.html#watchlist-page[watchlist.]
+
+Curious about your watching habits?
+
+* Check out our https://ay1920s1-cs2103t-f13-4.github.io/main/UserGuide.html#statistics-page[statistics page]
+
+More features
+
+* Integrated with an online database of shows, allowing you to search and get information about shows.
+* Will work and function even without internet.
+* No installation required.
+* Auto-saves.
+
+Excited to use it? Head straight to our <> for a more detailed explanation of our application.
== Site Map
* <>
* <>
-* <>
* <>
* <>
== Acknowledgements
-
-* Some parts of this sample application were inspired by the excellent http://code.makery.ch/library/javafx-8-tutorial/[Java FX tutorial] by
-_Marco Jakob_.
-* Libraries used: https://openjfx.io/[JavaFX], https://github.com/FasterXML/jackson[Jackson], https://github.com/junit-team/junit5[JUnit5]
+* An Adressbook project this application was built upon created by https://se-education.org[SE-EDU initiative]
+* Libraries used: https://openjfx.io/[JavaFX], https://github.com/FasterXML/jackson[Jackson], https://github.com/junit-team/junit5[JUnit5], https://www.themoviedb.org/documentation/api[TMDB api], https://github.com/holgerbrandl/themoviedbapi[Java Wrapper of TMDB]
== Licence : link:LICENSE[MIT]
diff --git a/build.gradle b/build.gradle
index 93029ef8262..69c91452d39 100644
--- a/build.gradle
+++ b/build.gradle
@@ -15,7 +15,7 @@ plugins {
}
// Specifies the entry point of the application
-mainClassName = 'seedu.address.Main'
+mainClassName = 'seedu.ezwatchlist.Main'
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
@@ -23,6 +23,10 @@ targetCompatibility = JavaVersion.VERSION_11
repositories {
mavenCentral()
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
+ maven {
+ url "https://jcenter.bintray.com"
+ }
+ jcenter()
}
checkstyle {
@@ -57,6 +61,9 @@ dependencies {
implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'win'
implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'mac'
implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'linux'
+ implementation group: 'org.openjfx', name: 'javafx-swing', version: javaFxVersion, classifier: 'win'
+ implementation group: 'org.openjfx', name: 'javafx-swing', version: javaFxVersion, classifier: 'mac'
+ implementation group: 'org.openjfx', name: 'javafx-swing', version: javaFxVersion, classifier: 'linux'
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.7.0'
implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.7.4'
@@ -64,10 +71,14 @@ dependencies {
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: jUnitVersion
testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: jUnitVersion
+
+ compile group: 'info.movito', name: 'themoviedbapi', version:'1.10'
+ compile group: 'org.slf4j', name: 'slf4j-log4j12', version: '1.7.28'
+ compile group: 'ch.qos.logback', name:'logback-core', version: '1.0.9'
}
shadowJar {
- archiveName = 'addressbook.jar'
+ archiveName = 'ezwatchlist.jar'
destinationDir = file("${buildDir}/jar/")
}
@@ -133,9 +144,8 @@ asciidoctor {
idprefix: '', // for compatibility with GitHub preview
idseparator: '-',
'site-root': "${sourceDir}", // must be the same as sourceDir, do not modify
- 'site-name': 'AddressBook-Level3',
- 'site-githuburl': 'https://github.com/se-edu/addressbook-level3',
- 'site-seedu': true, // delete this line if your project is not a fork (not a SE-EDU project)
+ 'site-name': 'EzWatchlist',
+ 'site-githuburl': 'https://github.com/AY1920S1-CS2103T-F13-4/main',
]
options['template_dirs'].each {
diff --git a/docs/AboutUs.adoc b/docs/AboutUs.adoc
index 458e6134f45..5971b6c86f8 100644
--- a/docs/AboutUs.adoc
+++ b/docs/AboutUs.adoc
@@ -4,53 +4,51 @@
:imagesDir: images
:stylesDir: stylesheets
-AddressBook - Level 3 was developed by the https://se-edu.github.io/docs/Team.html[se-edu] team. +
-_{The dummy content given below serves as a placeholder to be used by future forks of the project.}_ +
-{empty} +
-We are a team based in the http://www.comp.nus.edu.sg[School of Computing, National University of Singapore].
+Ezwatchlist was developed by a group of students based in the http://www.comp.nus.edu.sg[School of Computing, National University of Singapore].
== Project Team
-=== John Doe
-image::damithc.jpg[width="150", align="left"]
-{empty}[http://www.comp.nus.edu.sg/~damithch[homepage]] [https://github.com/damithc[github]] [<>]
+=== Caleb Goh Ee Gen
+image::heze8.png[width="150", align="left"]
+{empty} [https://github.com/heze8[github]] [<>]
-Role: Project Advisor
+Role: Team Lead +
+Responsibilities: Online database API, Recommendations, Image Retrieval
'''
-=== John Roe
-image::lejolly.jpg[width="150", align="left"]
-{empty}[http://github.com/lejolly[github]] [<>]
+=== Wu Xia
+image::tswuxia.png[width="150", align="left"]
+{empty}[http://github.com/tswuxia[github]] [<>]
-Role: Team Lead +
+Role: Code Quality +
Responsibilities: UI
'''
-=== Johnny Doe
-image::yijinl.jpg[width="150", align="left"]
-{empty}[http://github.com/yijinl[github]] [<>]
+=== Chiang Jiajun Jared
+image::jcjjjared.png[width="150", align="left"]
+{empty}[http://github.com/jcjjjared[github]] [<>]
-Role: Developer +
-Responsibilities: Data
+Role: Integration +
+Responsibilities: Main Logic
'''
-=== Johnny Roe
-image::m133225.jpg[width="150", align="left"]
-{empty}[http://github.com/m133225[github]] [<>]
+=== Wong Chuan Kai
+image::wongchuankai.png[width="150", align="left"]
+{empty}[http://github.com/wongchuankai[github]] [<>]
-Role: Developer +
-Responsibilities: Dev Ops + Threading
+Role: Testing +
+Responsibilities: Component Logic
'''
-=== Benson Meier
-image::yl_coder.jpg[width="150", align="left"]
-{empty}[http://github.com/yl-coder[github]] [<>]
+=== Michelle Yong Kai Wen
+image::michelleykw.png[width="150", align="left"]
+{empty}[http://github.com/michelleykw[github]] [<>]
-Role: Developer +
-Responsibilities: UI
+Role: Documentation +
+Responsibilities: Search and Tag Logic
'''
diff --git a/docs/ContactUs.adoc b/docs/ContactUs.adoc
index 81be279ef6d..f3d357fe933 100644
--- a/docs/ContactUs.adoc
+++ b/docs/ContactUs.adoc
@@ -2,6 +2,6 @@
:site-section: ContactUs
:stylesDir: stylesheets
-* *Bug reports, Suggestions* : Post in our https://github.com/se-edu/addressbook-level3/issues[issue tracker] if you noticed bugs or have suggestions on how to improve.
+* *Bug reports, Suggestions* : Post in our https://github.com/AY1920S1-CS2103T-F13-4/main/issues[issue tracker] if you noticed bugs or have suggestions on how to improve.
* *Contributing* : We welcome pull requests. Follow the process described https://github.com/oss-generic/process[here]
-* *Email us* : You can also reach us at `damith [at] comp.nus.edu.sg`
+* *Email us* : You can also reach us at `ezwatchlist [at] gmail.com`
diff --git a/docs/DevOps.adoc b/docs/DevOps.adoc
index 2aa5a6bc0c1..f2cac163aef 100644
--- a/docs/DevOps.adoc
+++ b/docs/DevOps.adoc
@@ -1,4 +1,4 @@
-= AddressBook Level 3 - Dev Ops
+= EzWatchlist - Dev Ops
:site-section: DeveloperGuide
:toc:
:toc-title:
@@ -12,7 +12,7 @@ ifdef::env-github[]
:note-caption: :information_source:
:warning-caption: :warning:
endif::[]
-:repoURL: https://github.com/se-edu/addressbook-level3/tree/master
+:repoURL: https://github.com/AY1920S1-CS2103T-F13-4/main/tree/master
== Build Automation
@@ -34,14 +34,14 @@ When a pull request has changes to asciidoc files, you can use https://www.netli
Here are the steps to create a new release.
-. Update the version number in link:{repoURL}/src/main/java/seedu/address/MainApp.java[`MainApp.java`].
+. Update the version number in link:{repoURL}/src/main/java/seedu/ezwatchlist/MainApp.java[`MainApp.java`].
. Generate a JAR file <>.
. Tag the repo with the version number. e.g. `v0.1`
. https://help.github.com/articles/creating-releases/[Create a new release using GitHub] and upload the JAR file you created.
== Managing Dependencies
-A project often depends on third-party libraries. For example, Address Book depends on the https://github.com/FasterXML/jackson[Jackson library] for JSON parsing. Managing these _dependencies_ can be automated using Gradle. For example, Gradle can download the dependencies automatically, which is better than these alternatives:
+A project often depends on third-party libraries. For example, EzWatchlist depends on the https://github.com/FasterXML/jackson[Jackson library] for JSON parsing. Managing these _dependencies_ can be automated using Gradle. For example, Gradle can download the dependencies automatically, which is better than these alternatives:
[loweralpha]
. Include those libraries in the repo (this bloats the repo size)
diff --git a/docs/DeveloperGuide.adoc b/docs/DeveloperGuide.adoc
index 3d65905a853..3a758bba1bc 100644
--- a/docs/DeveloperGuide.adoc
+++ b/docs/DeveloperGuide.adoc
@@ -1,4 +1,4 @@
-= AddressBook Level 3 - Developer Guide
+= EzWatchlist - Developer Guide
:site-section: DeveloperGuide
:toc:
:toc-title:
@@ -12,14 +12,17 @@ ifdef::env-github[]
:note-caption: :information_source:
:warning-caption: :warning:
endif::[]
-:repoURL: https://github.com/se-edu/addressbook-level3/tree/master
+:repoURL: https://github.com/AY1920S1-CS2103T-F13-4/main/tree/master
+:icons: font
-By: `Team SE-EDU` Since: `Jun 2016` Licence: `MIT`
+By: `Team CS2103T-F13-4` Since: `Sept 2019` Licence: `MIT`
== Setting up
Refer to the guide <>.
+{sp} +
+
== Design
[[Design-Architecture]]
@@ -34,7 +37,7 @@ The *_Architecture Diagram_* given above explains the high-level design of the A
The `.puml` files used to create diagrams in this document can be found in the link:{repoURL}/docs/diagrams/[diagrams] folder.
Refer to the <> to learn how to create and edit diagrams.
-`Main` has two classes called link:{repoURL}/src/main/java/seedu/address/Main.java[`Main`] and link:{repoURL}/src/main/java/seedu/address/MainApp.java[`MainApp`]. It is responsible for,
+`Main` has two classes called link:{repoURL}/src/main/java/seedu/EzWatchlist/Main.java[`Main`] and link:{repoURL}/src/main/java/seedu/EzWatchlist/MainApp.java[`MainApp`]. It is responsible for,
* At app launch: Initializes the components in the correct sequence, and connects them up with each other.
* At shut down: Shuts down the components and invokes cleanup method where necessary.
@@ -44,14 +47,15 @@ The following class plays an important role at the architecture level:
* `LogsCenter` : Used by many classes to write log messages to the App's log file.
-The rest of the App consists of four components.
+The rest of the App consists of five components.
* <>: The UI of the App.
* <>: The command executor.
* <>: Holds the data of the App in-memory.
* <>: Reads data from, and writes data to, the hard disk.
+* <>: Access data from an online database about Movies and Tv Shows.
-Each of the four components
+Each of the five components
* Defines its _API_ in an `interface` with the same name as the Component.
* Exposes its functionality using a `{Component Name}Manager` class.
@@ -71,15 +75,16 @@ image::ArchitectureSequenceDiagram.png[]
The sections below give more details of each component.
+
[[Design-Ui]]
=== UI component
.Structure of the UI Component
image::UiClassDiagram.png[]
-*API* : link:{repoURL}/src/main/java/seedu/address/ui/Ui.java[`Ui.java`]
+*API* : link:{repoURL}/src/main/java/seedu/EzWatchlist/ui/Ui.java[`Ui.java`]
-The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `PersonListPanel`, `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class.
+The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `ShowListPanel`, `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class.
The `UI` component uses JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files that are in the `src/main/resources/view` folder. For example, the layout of the link:{repoURL}/src/main/java/seedu/address/ui/MainWindow.java[`MainWindow`] is specified in link:{repoURL}/src/main/resources/view/MainWindow.fxml[`MainWindow.fxml`]
@@ -88,6 +93,7 @@ The `UI` component,
* Executes user commands using the `Logic` component.
* Listens for changes to `Model` data so that the UI can be updated with the modified data.
+
[[Design-Logic]]
=== Logic component
@@ -96,11 +102,11 @@ The `UI` component,
image::LogicClassDiagram.png[]
*API* :
-link:{repoURL}/src/main/java/seedu/address/logic/Logic.java[`Logic.java`]
+link:{repoURL}/src/main/java/seedu/EzWatchlist/logic/Logic.java[`Logic.java`]
-. `Logic` uses the `AddressBookParser` class to parse the user command.
+. `Logic` uses the `WatchListParser` class to parse the user command.
. This results in a `Command` object which is executed by the `LogicManager`.
-. The command execution can affect the `Model` (e.g. adding a person).
+. The command execution can affect the `Model` (e.g. adding a show).
. The result of the command execution is encapsulated as a `CommandResult` object which is passed back to the `Ui`.
. In addition, the `CommandResult` object can also instruct the `Ui` to perform certain actions, such as displaying help to the user.
@@ -111,23 +117,24 @@ image::DeleteSequenceDiagram.png[]
NOTE: The lifeline for `DeleteCommandParser` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
+
[[Design-Model]]
=== Model component
.Structure of the Model Component
image::ModelClassDiagram.png[]
-*API* : link:{repoURL}/src/main/java/seedu/address/model/Model.java[`Model.java`]
+*API* : link:{repoURL}/src/main/java/seedu/EzWatchlist/model/Model.java[`Model.java`]
The `Model`,
* stores a `UserPref` object that represents the user's preferences.
-* stores the Address Book data.
-* exposes an unmodifiable `ObservableList` that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change.
+* stores the EzWatchlist data.
+* exposes an unmodifiable `ObservableList` that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change.
* does not depend on any of the other three components.
[NOTE]
-As a more OOP model, we can store a `Tag` list in `Address Book`, which `Person` can reference. This would allow `Address Book` to only require one `Tag` object per unique `Tag`, instead of each `Person` needing their own `Tag` object. An example of how such a model may look like is given below. +
+As a more OOP model, we can store a `Actor` list in `Watch List`, which `Show` can reference. This would allow `Watch List` to only require one `Actor` object per unique `Actor`, instead of each `Show` needing their own `Actor` object. An example of how such a model may look like is given below. +
+
image:BetterModelClassDiagram.png[]
@@ -137,110 +144,153 @@ image:BetterModelClassDiagram.png[]
.Structure of the Storage Component
image::StorageClassDiagram.png[]
-*API* : link:{repoURL}/src/main/java/seedu/address/storage/Storage.java[`Storage.java`]
+*API* : link:{repoURL}/src/main/java/seedu/EzWatchlist/storage/Storage.java[`Storage.java`]
The `Storage` component,
* can save `UserPref` objects in json format and read it back.
-* can save the Address Book data in json format and read it back.
+* can save the Watch list data in json format and read it back.
+
+// tag::apicomponent[]
+[[Design-API]]
+=== API component
+
+[[fig-LogicClassDiagram]]
+.Structure of the Api Component
+image::ApiClass.png[width='600']
+
+*API* :
+link:{repoURL}/src/main/java/seedu/EzWatchlist/api/ApiManager.java[`ApiManager.java`]
+
+In Figure 9, we see the structure of the API component centered around `ApiManager`. Moreover:
+
+. `ApiManager` uses the `ApiUtil` class for static methods for data handling.
+. The `ApiUtil` class creates an `ImageRetrieval` object for retrieving images over the network and a `RecommendationEngine` object to generate recommendations.
+. `ApiManager` object encapsulated by the interface `ApiInterface` can be created at any point in the application to access the online database.
+. If no network connection can be established, an `OnlineConnectionException` is thrown.
+. `ApiManager` will not affect any of the internal logic and model in the application.
+
+// end::apicomponent[]
[[Design-Commons]]
=== Common classes
-Classes used by multiple components are in the `seedu.addressbook.commons` package.
+Classes used by multiple components are in the `seedu.EzWatchlist.commons` package.
+
+{sp} +
== Implementation
This section describes some noteworthy details on how certain features are implemented.
-// tag::undoredo[]
-=== [Proposed] Undo/Redo feature
-==== Proposed Implementation
+// tag::markaswatched[]
+=== [Feature] Mark/Unmark as Watched Feature
-The undo/redo mechanism is facilitated by `VersionedAddressBook`.
-It extends `AddressBook` with an undo/redo history, stored internally as an `addressBookStateList` and `currentStatePointer`.
-Additionally, it implements the following operations:
+The watch feature allows users to mark or unmark shows as watched. It also allows users to keep track of the latest episode
+of a TV series that they have watched.
-* `VersionedAddressBook#commit()` -- Saves the current address book state in its history.
-* `VersionedAddressBook#undo()` -- Restores the previous address book state from its history.
-* `VersionedAddressBook#redo()` -- Restores a previously undone address book state from its history.
+==== Implementation
+The mark/unmark as watched mechanism is facilitated by `WatchCommand` which can be found under the commands package.
+It extends `Command` and uses the `WatchCommandParser` to process the command entered by the user.
-These operations are exposed in the `Model` interface as `Model#commitAddressBook()`, `Model#undoAddressBook()` and `Model#redoAddressBook()` respectively.
+Given below is an example usage scenario and how the mark/unmark as watched mechanism works at each step.
-Given below is an example usage scenario and how the undo/redo mechanism behaves at each step.
+Step 1. The user launches the application, and executes `watch 1 s/2 e/3` command to update the latest watched episode of the first show in the list.
-Step 1. The user launches the application for the first time. The `VersionedAddressBook` will be initialized with the initial address book state, and the `currentStatePointer` pointing to that single address book state.
+Step 2. Entering the command calls `WatchListParser#parseCommand()`, which in turn returns a new `WatchCommandParser` and the `WatchCommandParser#parse()` command is called.
-image::UndoRedoState0.png[]
+Step 3. A new `WatchCommand` is created, with the index of the show being parsed as a field of the `WatchCommand`. A new `WatchShowDescriptor` is also created to relay the episode number and season number to the `WatchCommand` object.
-Step 2. The user executes `delete 5` command to delete the 5th person in the address book. The `delete` command calls `Model#commitAddressBook()`, causing the modified state of the address book after the `delete 5` command executes to be saved in the `addressBookStateList`, and the `currentStatePointer` is shifted to the newly inserted address book state.
+Step 4. The `WatchCommand#execute()` method is called, referencing the current `model`, and the show that is in the current `FilteredShowList` is referenced based off the current `model`.
-image::UndoRedoState1.png[]
+[NOTE]
+If the `index` is out of bounds, a new `CommandException` is thrown.
-Step 3. The user executes `add n/David ...` to add a new person. The `add` command also calls `Model#commitAddressBook()`, causing another modified address book state to be saved into the `addressBookStateList`.
+Step 5. A copy of the show is created through the use of `WatchCommand#createEditedShow()`, with the new total number of seasons and episodes updated if there are any changes.
+A new `isWatched` value of the show is also determined based on the number of episodes that are watched.
-image::UndoRedoState2.png[]
+The following activity diagram below summarizes the calculation of the number of episodes watched:
-[NOTE]
-If a command fails its execution, it will not call `Model#commitAddressBook()`, so the address book state will not be saved into the `addressBookStateList`.
+.WatchActivityDiagram showing how episodes are calculated
+image::WatchActivityDiagram.png[]
-Step 4. The user now decides that adding the person was a mistake, and decides to undo that action by executing the `undo` command. The `undo` command will call `Model#undoAddressBook()`, which will shift the `currentStatePointer` once to the left, pointing it to the previous address book state, and restores the address book to that state.
+Step 6. The show in the current show list is updated to the newly created copy with the updated watched status and latest episode watched, and a `CommandResult` with the new watched status of the show is created.
-image::UndoRedoState3.png[]
+The following sequence diagram shows how the watch operation works:
-[NOTE]
-If the `currentStatePointer` is at index 0, pointing to the initial address book state, then there are no previous address book states to restore. The `undo` command uses `Model#canUndoAddressBook()` to check if this is the case. If so, it will return an error to the user rather than attempting to perform the undo.
+.WatchSequenceDiagram showing flow of the watch command
+image::WatchSequenceDiagram.png[]
-The following sequence diagram shows how the undo operation works:
+==== Design Considerations
-image::UndoSequenceDiagram.png[]
+===== Aspect: Creating a new WatchCommand instead of an altered EditCommand
-NOTE: The lifeline for `UndoCommand` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
+* **Alternative 1 (current choice):** Creating a new `WatchCommand` class for changing the 'watch' status of a show.
+** Pros: Enables for greater cohesion since there is a specific command for editing the 'watch' status of a show.
+** Cons: Requires longer code, and the code is also repetitive since its implementation is similar to that of the `EditCommand`.
+* **Alternative 2:** Use the `WatchCommandParser` to create a new `EditCommand` object that edits the watch status of the show.
+** Pros: Less code repetition and shorter code in general.
+** Cons: This will mean that there is less cohesion of the code and greater dependencies since more classes depend on the `EditCommand` class.
+// end::markaswatched[]
-The `redo` command does the opposite -- it calls `Model#redoAddressBook()`, which shifts the `currentStatePointer` once to the right, pointing to the previously undone state, and restores the address book to that state.
+// tag::statistics[]
+=== Statistics Feature
+==== Proposed Implementation
-[NOTE]
-If the `currentStatePointer` is at index `addressBookStateList.size() - 1`, pointing to the latest address book state, then there are no undone address book states to restore. The `redo` command uses `Model#canRedoAddressBook()` to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo.
+The statistics feature is facilitated by `Statistics` object. It extends `EzWatchlist` with a summary of the users' personal
+preferences and footprint including the most watched genre, shows that you might have forgotten to watch, and the
+recommendations generated for you. It is stored internally as an `Statistics` object containing a `ModelManager`.
+Additionally, it implements the following operations:
-Step 5. The user then decides to execute the command `list`. Commands that do not modify the address book, such as `list`, will usually not call `Model#commitAddressBook()`, `Model#undoAddressBook()` or `Model#redoAddressBook()`. Thus, the `addressBookStateList` remains unchanged.
+* `Statistics#getFavouriteGenre()` -- Gives the genre that appears the most number of times in the list of watched shows.
+* `Statistics#getForgotten()` -- Gives the 3 shows that were added the earliest but have not been watched.
+* `Statistics#getRecommendation()` -- Gives 3 recommendations according to the watched shows.
-image::UndoRedoState4.png[]
+Given below is a usage scenario of a user checking his/her statistics page.
-Step 6. The user executes `clear`, which calls `Model#commitAddressBook()`. Since the `currentStatePointer` is not pointing at the end of the `addressBookStateList`, all address book states after the `currentStatePointer` will be purged. We designed it this way because it no longer makes sense to redo the `add n/David ...` command. This is the behavior that most modern desktop applications follow.
+Step 1. The user launches the application. The Statistics object will be initialized with ModelManager
+containing the current watchlist and watched list. Behind the scenes, the 3 statistics will be calculated and the
+statistics panel will be populated with the result.
-image::UndoRedoState5.png[]
+Step 2. The user clicks on the statistic button and sees the content.
-The following activity diagram summarizes what happens when a user executes a new command:
+This is the sequence diagram of `getFavouriteGenre()`.
-image::CommitActivityDiagram.png[]
+image::GetFavouriteSequenceDiagram.png[]
-==== Design Considerations
+This is the sequence diagram of `getForgotten()`.
-===== Aspect: How undo & redo executes
+image::GetForgottenSequenceDiagram.png[]
-* **Alternative 1 (current choice):** Saves the entire address book.
-** Pros: Easy to implement.
-** Cons: May have performance issues in terms of memory usage.
-* **Alternative 2:** Individual command knows how to undo/redo by itself.
-** Pros: Will use less memory (e.g. for `delete`, just save the person being deleted).
-** Cons: We must ensure that the implementation of each individual command are correct.
+This is the sequence diagram of `getRecommendation()`.
+
+image::GetRecommendationSequenceDiagram.png[]
+
+The following activity diagram summarizes the workflow of Statistics:
-===== Aspect: Data structure to support the undo/redo commands
+image::StatisticsActivityDiagram.png[]
-* **Alternative 1 (current choice):** Use a list to store the history of address book states.
-** Pros: Easy for new Computer Science student undergraduates to understand, who are likely to be the new incoming developers of our project.
-** Cons: Logic is duplicated twice. For example, when a new command is executed, we must remember to update both `HistoryManager` and `VersionedAddressBook`.
-* **Alternative 2:** Use `HistoryManager` for undo/redo
-** Pros: We do not need to maintain a separate list, and just reuse what is already in the codebase.
-** Cons: Requires dealing with commands that have already been undone: We must remember to skip these commands. Violates Single Responsibility Principle and Separation of Concerns as `HistoryManager` now needs to do two different things.
-// end::undoredo[]
+==== Design Considerations
+
+===== Aspect: How Statistics stores the watchlist and watched list
-// tag::dataencryption[]
-=== [Proposed] Data Encryption
+* **Alternative 1 (current choice):** Saves the whole ModelManager as a field.
+** Pros: Easy to implement and make use of.
+** Cons: More dependency on ModelManager.
+* **Alternative 2:** Saves the watchlist and watched list as fields respectively.
+** Pros: Less dependency on ModelManager.
+** Cons: Less potential functionality and the lists might not be up-to-date.
-_{Explain here how the data encryption feature will be implemented}_
+===== Aspect: When the results are calculated
-// end::dataencryption[]
+* **Alternative 1 (current choice):** Calculate when the user navigate to Statistics panel and the user has made changes
+to the watchlist.
+** Pros: Statistics results is more up to date.
+** Cons: Appear less responsive as the API needs to retrieve information during calculation.
+* **Alternative 2 :** Calculate when the application starts and update when there are changes.
+** Pros: It appears more responsive in terms of navigating around the application.
+** Cons: The statistics information will not be updated realtime if the user has changed the watchlist.
+// end::statistics[]
=== Logging
@@ -257,11 +307,397 @@ We are using `java.util.logging` package for logging. The `LogsCenter` class is
* `INFO` : Information showing the noteworthy actions by the App
* `FINE` : Details that is not usually noteworthy but may be useful in debugging e.g. print the actual list instead of just its size
+// tag::onlinedata[]
+=== Online Data
+
+We are using https://www.themoviedb.org/documentation/api[The Movie Database (TMDB)] to retrieve information on movies and tv shows.
+
+==== Implementation
+
+All interactions with the third party library are facilitated by the interface `ApiInterface`. Methods in `ApiInterface` are the main
+way the application retrieves any online information from the database.
+
+*Methods in ApiInterface*
+
+* `getMovieByName` : The method will return a list of movies in the database based on the name of the movie given
+* `getTvShowByName` : The method will return a list of tv shows in the database based on the name of the tv show given
+* `isConnected` : Checks if the application is connected online to the database
+* `getUpcomingMovies` : The method will return a list of upcoming movies from the database.
+* `getMovieByGenre` : The method will return a list of movies from the database based on a set of genres.
+* `getMovieRecommendations` : The method will return a list of movie recommendations specified by the amount required based on the user's movies.
+* `getTvShowRecommendations` : The method will return a list of tv shows recommendations specified by the amount required based on the user's tv shows.
+
+`ApiManager` is an implementation of `ApiInterface` and is dependent on a https://github.com/holgerbrandl/themoviedbapi[java wrapper] for the TMDB api implemented by Holger Brandl.
+Allowing us to retrieve information in the java code.
+
+Given below is an example of how the application might want to retrieve movie information through `ApiInterface`.
+
+Step 1. First an instance of a class implementing `ApiInterface` has to be created for the methods to be called such as `ApiManager`. When `ApiManager` is instantiated the class's internal field
+`Api Key` 's is used to create a *call object* to the TMDB database. All information retrieved must go through this class's api *call object*.
+
+Step 2. The method getMovieByName("Name Of Movie") is called and the *api call object* is passed to the corresponding method in the class `ApiUtil`. The corresponding method then searches the for the movie in the database with the name given.
+A list of wrapped movies is given back, which is then read and used to create new instances of our application's `Show` model with information wrapped in classes such as
+`Name`, `Description`, and `Actor`.
+
+Step 3. To retrieve an image from the database, an image url is downloaded to the computer. The class `ImageRetrieval` does this. An
+`ImageRetrieval` instance is created by the method for each image, and is downloaded to a root folder determined by `ImageRetrieval#defaultDirectory()`.
+
+Step 4. A `poster` class is then created with the image path of the downloaded image. At any point, the `poster` object can be used to load images in the application.
+
+Step 5. With the list of movies returned, the application can then read the internal `Movie` object returned. The `Poster` object in the `Movie` object can be used to load the poster of the movie, and the other fields can be shown to the user as seen in the figure below.
+
+.The movie component shown to the user in `EzWatchlist`.
+image::moviecard.png[width=900]
+
+[NOTE]
+At any point the application might fail due to the application not being connected to the internet. If that occurs a
+`OnlineConnectionException` is thrown for the application to handle.
+
+The relationship between all the classes, and their methods are shown in the following class diagram in the figure below:
+
+.Detailed Api Class Diagram
+image::ApiClassDiagram.png[width=600]
+// tag::apiimpl[]
+
+==== Design Considerations
+
+===== Why this implementation was chosen
+
+* **All API interactions would be in the API package and go through ApiInterface. **
+** Pros:
+- The application wouldn't have to seek access to the database on their own. The interface should provide all the functionality needed.
+- Follows the Single Responsibility Principle (SRP) that a module in the program should be encapsulated and have one repsponsibility.
+
+==== Aspect: Image retrieval implementation
+Images are retrieved through the `ImageRetrieval` class. Which downloads the image into the computer,
+wrapping a `Poster` class with the local path of the image, for the application to access images.
+
+===== Design Considerations
+* ** Alternative 1 (current choice):** Download the image into the computer then access it locally on the computer.
+** Pros:
+*** Easier to keep track of images.
+*** All images are retrieved the same way making it easier to implement showing the images.
+** Cons:
+*** All images shown are currently downloaded without being deleted, hence the amount of images downloaded can get unwieldy quickly, increasing
+amount of memory used.
+* **Alternative 2:** Parse the online url to the application for them to access the image online when needed.
+** Pros:
+*** No need for the images to be downloaded allows less memory to be used by the application.
+** Cons:
+*** Everytime the image is viewed, the application has to retrieve it from online making it more intensive on the network.
+*** If the internet connection fails, the image can no longer be viewed.
+//end::ImageRetrieval[]
+
+==== Aspect: Recommendations
+Recommendations are generated through the `RecommendationEngine` class in the API package. Currently,
+recommendations are retrieved through the `ApiInterface` implemented in the API package.
+
+===== Implementation
+The figure below is a _sequence diagram_ of how the recommendations are generated when called from the `ApiManager`.
+
+.Sequence Diagram of Movie Recommendations retrieval.
+image::movieRecommendationSD.png[width=500]
+
+Step 1. First an instance of `RecommendationEngine` is created by passing in the list of movies the user has and the *api call object* generated by the instance of the `ApiManager` object.
+
+Step 2. The method `getMovieRecommendations(noOfRecommendations)` is called in the `RecommendationEngine` object and it will:
+
+* Check if the list is valid to generate recommendations.
+* Parse the list to get the online entries in the database, and their recommendations from the database.
+* For each recommendation, store it in a _HashMap_ and if there are duplicates increase the value. This counts the amount of occurrences each recommendation occurs.
+* Filter the entries to remove all entries that the user already has.
+
+Step 3. The recommendations are then sorted based on the amount of occurrences in appears in the _HashMap_.
+
+Step 4. The recommendations are then returned in a list in which the length depends on the amount of recommendations requested.
+
+Step 5. With the list of movies returned, the application can then display the movie recommendations back to the user.
+
+[NOTE]
+If no recommendations can be generated, a `NoRecommendationsExceptions` is thrown. Moreover, an
+`OnlineConnectionException` is still thrown when not connected to the internet.
+// end::apiimpl[]
+
+// end::onlinedata[]
[[Implementation-Configuration]]
=== Configuration
Certain properties of the application can be controlled (e.g user prefs file location, logging level) through the configuration file (default: `config.json`).
+// tag::search[]
+[[Implementation]]
+=== [Feature] Search Feature
+The `Search` feature allows users to search for shows from the *online database*, the *internal database*, their *watchlist* or
+*watched-list*.
+It allows users to search for shows based on either "*name*", "*genre*" or "*actors*", or a combination of them. +
+Users can choose to search from the online database or their watchlist and watched-list, and also filter their search
+based on the show type.
+
+The following _activity diagram_ summarises the workflow of the `Search` feature:
+
+.Activity Diagram for the `Search` Feature
+image::SearchActivityDiagram.png[width="450"]
+
+{sp} +
+
+==== Implementation
+The search mechanism is facilitated by `SearchCommand` which can be found under the commands package.
+It extends `Command` and uses the `SearchCommandParser` to process the command entered by the user.
+
+Given below is an example usage scenario and how the search mechanism behaves at each step.
+
+Step 1. The user launches the application, goes to the *Search page* and executes `search n/Avengers o/no`
+command to search for shows named "Avengers" from the watchlist and watched-list.
+
+Step 2. Entering the command calls `LogicManager#execute()`, which in turns calls the `WatchListParser#parseCommand()`.
+
+Step 3. `WatchListParser#parseCommand()` returns a new `SearchCommandParser` and the `SearchCommandParser#parse()` command is called.
+
+[NOTE]
+If the user enters a wrong command, such as incorrect prefixes or keywords, a new `ParseException` is thrown.
+
+Step 4. A new `SearchCommand` is created, with the hash map containing the contents to be searched as a field of
+`SearchCommand`.
+
+Step 5. The `SearchCommand#execute()` method is called, referencing the current model.
+
+[NOTE]
+If the user is not connected online, a new `OnlineConnectionException` is caught and search would be done using the
+*internal database*, *watchlist* and *watched-list* instead of the *online database*.
+
+Step 6. The `SearchCommand#searchByName()` method is called, referencing the current model.
+
+Step 7. The `SearchCommand#addShowFromWatchListIfSameNameAs()` method is called, referencing the current model and name
+of the show to be searched from the list of shows.
+
+Step 8. The `Model#getShowFromWatchlistIfHasName()` method is called, referencing the name of the show to be searched.
+A list of shows with the name "Avengers" is retrieved.
+
+Step 9. The `SearchCommand#addShowToSearchResult()` method is called, referencing the list of the shows found in Step 7.
+Shows are filtered based on the possible filters and added the the `SearchCommand#searchResult`
+
+Step 10. A new `CommandResult` is created, referencing the search message to be shown to user. This `CommandResult` is
+returned to the `LogicManager`.
+
+{sp} +
+The following _sequence diagram_ summarizes how the search operation works based on the example above:
+
+.Sequence Diagram for the `Search` Operation
+image::SearchSequenceDiagram.png[]
+
+{sp} +
+
+==== Design Considerations
+
+===== Aspect: How `SearchCommand` reference the information to be searched for
+* Alternative 1 (current choice): Takes reference to the hash map from `SearchCommandParser`, consisting of all the
+possible methods to search for shows
+** Pros:
+*** Easy to implement
+*** Can be easily made use of by retrieving the list of what the user want to search from the hash map
+** Cons:
+*** Command may be longer and user would be required to be familiarize with the prefix, such as `n/` when searching by name
+*** Certain list might be redundant as the user might not have requested to search by certain fields
+*** Higher run time in checking through all the lists
+
+* Alternative 2: Takes reference to a string from `SearchCommandParser` and by *name*, *genre*, and *actor* using that string
+** Pros:
+*** Easy to parse the information from `SearchCommandParser` to `SearchCommand`
+** Cons:
+*** Does not allow the user to have the freedom to choose what they would like to search by
+*** May return irrelevant results back to the user as the user did not specify what they would like to search based on
+*** Higher run time as there would be a need to search based on all 3 methods
+
+===== Aspect: How `SearchCommand` is executed
+* Current choice: Search by name, followed by genre then actor, when the user chooses to search with any combinations of the 3 methods
+** Pros:
+*** Easy to implement and make use of.
+*** Shows all the shows from the combination of the search results
+** Cons:
+*** The user has to input either a name, genre or actor in order for the search to work
+*** Logic is repetitive when searching based on name, actor and genre
+*** Does not allow the user to search for shows that has a combination of the names, genres and actors +
+e.g. `search n/Avengers g/Comedy` would show a search result with shows that either have the name "Avengers" or the genre "Comedy",
+instead of shows that have both the name "Avengers" and the genre "Comedy"
+
+// end::search[]
+
+{sp} +
+
+// tag::add[]
+=== [Feature] Add feature
+==== Implementation
+The `AddCommand` extends `Command` and uses `AddCommandParser` to process the command entered by the user.
+
+*Scenario 1:* Adding show in the WatchList.
+
+*Scenario 2.* Adding show found from online search.
+
+Given below is an example usage of scenario 1 and how the add mechanism behaves at each step.
+
+Step 1. The user launches the application and executes `add n/Joker...` command to add a show in the WatchList with the name "Joker".
+
+Step 2. Entering the command calls AddCommandParser#parse().
+
+Step 3. A new `AddCommand` is created, with the show to be added in AddCommand.
+
+Step 4. The `AddCommand#execute()` method is called, referencing the current model and add the show given by user to the `filteredShowList` found in model.
+
+
+The following activity diagram summarises the workflow of Add:
+
+image::AddActivitySequenceDiagram.png[width="300"]
+
+_Figure 3: Activity Diagram of `AddCommand`_
+
+In Figure 3, the user first launches the app. After the user input a add command, the program runs and add the show input by user into WatchList.
+
+==== Design Considerations
+
+===== Aspect: How `AddCommand` executes
+** Current choice: Create a show object and add it to a `filteredShowList` found in ModelManager.
+*** Pros: Easy to implement and make use of.
+*** Cons: May have performance issues in terms of memory usage.
+
+
+Given below is an example usage of scenario 2 and how the add mechanism behaves at each step.
+
+Step 1. The user uses the search(Online) command and executes `add INDEX` command to add a show from search result page of INDEX in the WatchList.
+
+Step 2. Entering the command calls AddCommandParser#parse().
+
+Step 3. A new `AddCommand` is created, with the show to be added in AddCommand.
+
+Step 4. The `AddCommand#execute()` method is called, referencing the current model and add the show given by user to the `searchList` of INDEX found in model.
+
+The following sequence diagram shows how the `add` operation works:
+
+image::AddSequenceDiagram.png[width="500"]
+
+The following activity diagram summarises the workflow of Add:
+
+image::AddActivitySequenceDiagram.png[width="300"]
+
+
+_Figure 4: Activity Diagram of `AddCommand`_
+
+In Figure 4, User input search(online) command. User then input `add INDEX` command. Show of INDEX found in search result page is added to WatchList.
+
+==== Design Considerations
+
+===== Aspect: How `AddCommand` executes
+
+** Current choice: Retrieve the show object found in `searchList` of INDEX from ModelManager and add it to `filteredShowList`.
+** Pros: Enables for greater cohesion since there is a specific command for adding information of a show in watchlist.
+** Cons: Requires longer code, and the code is also repetitive since its implementation is similar to that of the add and edit command
+** Cons: May have performance issues in terms of memory usage.
+
+// end::add[]
+
+// tag::add2[]
+=== [Feature] Add feature (Extension)
+==== Implementation
+
+This `add` feature is an extension to the `add` feature found in previous feature.
+It is used after user has searched for a show using `search` feature and the user wants to add a certain show
+into his watchlist.
+
+Given below is an example usage of `add` feature (Extension) and how the add mechanism behaves at each step.
+
+Step 1. The user uses the search(Online) command and executes `add INDEX` command to add a show from search result page of INDEX in the WatchList.
+
+Step 2. Entering the command calls AddCommandParser#parse().
+
+Step 3. A new `AddCommand` is created, with the show to be added in AddCommand.
+
+Step 4. The `AddCommand#execute()` method is called, referencing the current model and add the show given by user to the `searchList` of INDEX found in model.
+
+
+The following sequence diagram shows how the `add` extension operation works:
+
+image::AddSequenceDiagram2.png[width="500"]
+
+The following activity diagram summarises the workflow of Add extension:
+
+image::AddActivitySequenceDiagram2.png[width="350"]
+
+
+_Figure 4: Activity Diagram of `AddCommand`_
+
+In Figure 4, User input search(online) command. User then input `add INDEX` command. Show of INDEX found in search result page is added to WatchList.
+
+==== Design Considerations
+
+===== Aspect: How `AddCommand` executes
+
+** Current choice: Retrieve the show object found in `searchList` of INDEX from ModelManager and add it to `filteredShowList`.
+** Pros: Enables for greater cohesion since there is a specific command for adding information of a show in watchlist.
+** Cons: Requires longer code, and the code is also repetitive since its implementation is similar to that of the add and edit command
+** Cons: May have performance issues in terms of memory usage.
+
+// end::add2[]
+
+
+// tag::sync[]
+=== [Feature] Synchronise user's show data
+
+The synchronise feature allows user to sync a show found in watchlist with online searched show data. It modifies all of the
+parameters/information is user selected show with online searched show data. +
+User may have added their show with their own information. However, user might not know some of the parameters such as actors.
+Thus, user can use the search online command `search n/` to look up information regarding that show. +
+Then, Synchronise command `sync` can be used to update information/modify on that show.
+
+==== Implementation
+
+The Synchronise feature is facilitated by `SyncCommand` object which can be found under the commands package.
+It extends `Command` and uses the `SyncCommandParser` to process the command entered by the user.
+
+Given below is an example usage scenario and how the Synchronise command work as Sync mechanism works at each step.
+
+Pre-Condition: User has already added a certain show into watchlist manually. That show must have at least `name` and `type` parameters.
+Example of Pre-Condition: User has added Titanic movie into watchlist.
+
+Step 1. The user launches the application, go to Search page and execute `search n/titanic`.
+
+Step 2. The user execute `sync 1` command to synchronise index 1 of result page with a show in watchlist with same name (case-insensitive).
+
+Step 3. Entering the command calls `SyncCommandParser#parseCommand()`, which in turn returns a new `SyncCommandParser` and the `SyncCommandParser#parse()` command is called.
+
+Step 4. A new `SyncCommand` is created, with the index of the show being parsed as a field of the `SyncCommand`.
+
+Step 5. The `SyncCommand#execute()` method is called, referencing the current `model`, and the show that is in the current `FilteredShowList` is referenced based off the current `model`.
+
+[NOTE]
+If the `index` is out of bounds, a new `CommandException` is thrown.
+
+Step 6. A list of shows found in search page and watchlist are retrieved from `model`. The show according to the Index of the `searchpagelist` are retrieved as well.
+Then, the list of show in watchlist will be checked through to match the name of the index show.
+
+Step 7. If a show in watchlist matched with the name of the index show, `model.setShow` will be called to replace the show found in watchlist with index show.
+`CommandResult` will be return which contains information regarding the feedback result. Else, `CommandException` is thrown to notify user no similar show name is found in watchlist as index show.
+
+The following sequence diagram shows how the sync operation works:
+
+image::SyncSequenceDiagram.png[width="500"]
+
+
+The following activity diagram summarises the workflow of Sync:
+
+image::SyncActivityDiagram.png[width="500"]
+
+
+==== Design Considerations
+
+===== Aspect: Creating a new Synchronise instead of an altered EditCommand and AddCommand
+
+* **Alternative 1 (current choice):** Creating a new Synchronise class for replace information of a certain show found in search page with one in watchlist.
+** Pros: Enables for greater cohesion since there is a specific command for replacing/modifying information of a show in watchlist.
+** Cons: Requires longer code, and the code is also repetitive since its implementation is similar to that of the add and edit command
+* **Alternative 2:** Use the SyncCommandParser to create a new EditCommand object that edits the information of a certain show found in search page with one in watchlist.
+** Pros: Less code repetition and shorter code in general.
+** Cons: This will mean that there is less cohesion of the code and perhaps greater dependencies since more classes depend on the EditCommand class.
+// end::sync[]
+
== Documentation
Refer to the guide <>.
@@ -279,13 +715,17 @@ Refer to the guide <>.
*Target user profile*:
-* has a need to manage a significant number of contacts
-* prefer desktop apps over other types
-* can type fast
-* prefers typing over mouse input
-* is reasonably comfortable using CLI apps
+* Users who are tidy and organised
+* Forgetful person who has a need to keep track of what movies, tv series they would like to watch
+* Tech-savvy users who prefer desktop apps over other types
+* User can type fast
+* User prefers typing over mouse input
+* User is reasonably comfortable using CLI apps
+* Movie/TV series lover
-*Value proposition*: manage contacts faster than a typical mouse/GUI driven app
+*Value proposition*:
+
+EzWatchlist provides a unique, clean and simple way of organizing and keeping track of your watchlist. All in one solution to your problem.
[appendix]
== User Stories
@@ -295,17 +735,67 @@ Priorities: High (must have) - `* * \*`, Medium (nice to have) - `* \*`, Low (un
[width="59%",cols="22%,<23%,<25%,<30%",options="header",]
|=======================================================================
|Priority |As a ... |I want to ... |So that I can...
-|`* * *` |new user |see usage instructions |refer to instructions when I forget how to use the App
+|`* * *` |forgetful person | keep track of the shows to watch|I won’t forget about them.
+
+|`* * *` |very neat and organised movie lover |can organise the movies I’ve watched in categories/groups | watch them again.
-|`* * *` |user |add a new person |
+|`* * *` |As an organised person| sort the movies and tv shows into genres | easily find a show from a genre that I want to watch.
-|`* * *` |user |delete a person |remove entries that I no longer need
+|`* * *` |As a movie lover |mark movies in the watchlist as “watched” | keep track of what I have watched
-|`* * *` |user |find a person by name |locate details of persons without having to go through the entire list
+|`* * *`| As a fickle person | delete a movie from the watchlist if I don’t feel like watching it anymore| my watchlist will not be cluttered with movies that I have no interest in watching anymore.
+
+|`* * *` |Someone who loves keeping records | keep track of how many movies or tv series I have watched |
+
+|`* * *` |tech-savvy |do any task as quickly as possible| experience is pleasant and quick.
+
+|`* * *` | App user | categorize and search based on distinct things | everything is neat and pleasing.
+
+|`* * *` | App user | track where I am specifically in the series | I can remember where I am for that show.
+
+|`* *`|movie lover who likes to think about the message conveyed in the movie |I can note down some of my thoughts for every movie after watching |
+
+|`* *` | a movie critic| can track my thoughts and criticism of movie easily | it is organised.
+
+|`* *` | a movie/drama lover who also likes music | can note down the name of the soundtrack in the movie in a place specially for that. |
+
+|`* *`| a movie lover | rate a movie that I have watched | I can know what movies I liked to watch
+
+|`* *`| a movie lover | can know about all the movies | I would be able to watch them in the cinemas when they are out.
+
+|`* *`| a movie lover | look up for shows to watch from the recommendations |I will not have to manually search online.
+
+|`* *`| a movie lover | I can search for movies that I have added to the watchlist| I can easily find the movie that I want to watch.
+
+|`* *`| looking for new movies| search for some recommendations| I can watch it when I am free.
+
+|`* *`| a movie lover | search for movies that I have added to the watchlist| easily find the movie that I want to watch.
+
+|`* *`| a movie lover | can keep track of the number of times I have watched a movie |I can find out which movie I liked watching the best.
+
+|`* *` | a movie lover |check the ratings of each movie I have watched |I can recommend them to my friends.
+
+|`* *`|a tv show addict | I can put my favourite shows in | I can enjoy watching it next time.
+
+|`* *`| unorganised person | I can rank the movies that I want to watch into different priorities | I can easily know what are the movies that I desperately want to watch.
+
+|`* *` |unorganised person | can sort movies that I want to watch into the date they were added |I can clear the movies that have been lingering at the back of my mind the longest.
+
+|`* *` | does reviews | keep track of movies and write reviews for the show I have watched |I can have a positive criticism.
+
+|`* *` | someone who forget to watch tv series/movies | I can have an alarm that alert me to watch videos on time.|
+
+|`* *` |As someone who has a deadline to watch movies | I can plan and add deadlines to watch movies.|
+
+|`* *`|As a lover of multiple mediums| I can combine all these different mediums in the same platform | I can keep track of everything in the same place.
+
+|`* *`| As a statistics lover| I can view the statistics of the shows I watched | I can gain insight of my viewing habits.
+
+|`* *`| forgetful person| I can add shows I’ve watched to see a virtual library of things I’ve completed| I can log it like a book in a bookcase.
+
+|`* *`| As an app user| I can see the date I’ve inputted the specific object| I can remember when I completed.
-|`* *` |user |hide <> by default |minimize chance of someone else seeing them by accident
-|`*` |user with many persons in the address book |sort persons by name |locate a person easily
|=======================================================================
_{More to be added}_
@@ -313,70 +803,194 @@ _{More to be added}_
[appendix]
== Use Cases
-(For all use cases below, the *System* is the `AddressBook` and the *Actor* is the `user`, unless specified otherwise)
+(For all use cases below, the *System* is the `EzWatchlist` and the *Actor* is the `user`, unless specified otherwise)
[discrete]
-=== Use case: Delete person
+=== Use case: Add Movie
*MSS*
-1. User requests to list persons
-2. AddressBook shows a list of persons
-3. User requests to delete a specific person in the list
-4. AddressBook deletes the person
+1. User navigates to the main page
+2. User enters 'Add' command
+3. User enters the movie name
+4. EzWatchlist will display a list of movies found
+5. User enters the index given by the list
+6. EzWatchlist will add the movie to their watchlist
+
Use case ends.
*Extensions*
[none]
-* 2a. The list is empty.
+* 3a. No movie is found
+[none]
+** 3a1. User enter movie information himself.
+** 3a2. User saves the movie
+
Use case ends.
-* 3a. The given index is invalid.
+[none]
+* 5a. The given index is invalid.
+[none]
+** 5a1. System shows an error message.
++
+Use case resumes at step 4.
+
+[discrete]
+=== Use case: Mark as watched
+*MSS*
+
+1. User navigates to the main page
+2. User enters 'Watched' command giving the index of the item
+3. EzWatchlist will mark that item as watched
+
+Use case ends.
+
+*Extensions*
+[none]
+* 2a. The given index is invalid.
[none]
-** 3a1. AddressBook shows an error message.
+** 2a1. System shows an error message.
+image::CommitActivityDiagram.png[]
+
-Use case resumes at step 2.
+Use case ends.
+
+[discrete]
+=== Use case: Search locally
+*MSS*
+
+1. User navigates to the main page
+2. User enters 'search' command giving the name of the item
+3. EzWatchlist will bring the user to a search page with items found in a list
+4. User enters View command of the index of the item.
+5. EzWatchlist will bring that item into details page
++
+Use case ends.
+
+*Extensions*
+[none]
+* 2a. The given name is not found.
+[none]
+** 2a1. System shows an error message.
++
+Use case ends.
+
+[none]
+* 4a. The given index is not valid.
+[none]
+** 4a1. System shows an error message.
++
+Use case resumes at step 3.
+
+
+[discrete]
+=== Use case: Edit item
+*MSS*
+
+1. User navigates to the main page.
+2. User enters 'edit' command giving the name of the item.
+3. EzWatchlist will bring the user to a details page of the item entered.
+4. User edits the details of the item.
+5. User saves the edits.
++
+Use case ends.
+
+*Extensions*
+[none]
+* 2a. The given name is not found.
+[none]
+** 2a1. System shows an error message.
++
+Use case ends.
+
+
+[discrete]
+=== Use case: Delete item
+*MSS*
+
+1. User navigates to the page containing the item to be deleted.
+2. User enters 'delete' command giving the name of the item.
+3. EzWatchlist asks for confirmation from the user.
+4. User confirms.
+5. EzWatchlist deletes the item.
++
+Use case ends.
+
+*Extensions*
+[none]
+* 2a. The given name is not found.
+[none]
+** 2a1. System shows an error message.
++
+Use case ends.
+
+[none]
+* 3a. User cancels the deletion.
+[none]
+** Use case ends.
+
+
+[discrete]
+=== Use case: Statistics
+*MSS*
+
+1. User navigates to the statistics page
+2. EzWatchlist displays the information.
++
+Use case ends.
-_{More to be added}_
[appendix]
== Non Functional Requirements
. Should work on any <> as long as it has Java `11` or above installed.
-. Should be able to hold up to 1000 persons without a noticeable sluggishness in performance for typical usage.
+. Should be able to hold up to 1000 movies in the watchlist without a noticeable sluggishness in performance for typical usage.
. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse.
+. The user interface should be intuitive enough for users who are not IT-savvy.
+. Compliance with data, security, copyright laws.
+. The application will be offline-friendly with online data being the only functionality missing.
-_{More to be added}_
+// tag::glossary[]
[appendix]
== Glossary
+[[details-page]] Details page::
+The page that shows the details of shows, which the user may then add it to the watchlist if interested.
-[[mainstream-os]] Mainstream OS::
-Windows, Linux, Unix, OS-X
+[[main-page]] Main Page::
+The default main page with the WatchList tab that contains a list of shows that the user wishes to watch (same as _Watchlist Page_)
-[[private-contact-detail]] Private contact detail::
-A contact detail that is not meant to be shared with others
+[[movies]] Movie::
+A cinema film
-[appendix]
-== Product Survey
+[[search-page]] Search page::
+The page where the user searches for shows to be added or to view their information
+
+[[show-index]] Show index::
+The number reference for the show on the page in EzWatchlist
+
+[[shows]] Shows::
+Movies or TV series
+
+[[statistics-page]] Statistics page::
+The page that shows the statistics of user, such as total number of movies / TV series watched
-*Product Name*
+[[tv-series]] TV Series::
+A television program which consists of possibly several episodes and seasons that are broadcast on regular intervals
-Author: ...
+[[view-command]] View command `view [index]`::
+This command brings the user to the details page of the show with the specified show index
-Pros:
+[[watched-list]] Watched-list::
+The list of shows that the user has watched.
-* ...
-* ...
+[[watched-page]] Watched Page::
+The page where the user can access to view the list of shows indicated as watched
-Cons:
+[[watchlist]] Watchlist::
+The list of shows that the user wishes to watch in the future.
-* ...
-* ...
+// end::glossary[]
[appendix]
== Instructions for Manual Testing
@@ -400,26 +1014,54 @@ These instructions only provide a starting point for testers to work on; testers
.. Re-launch the app by double-clicking the jar file. +
Expected: The most recent window size and location is retained.
-_{ more test cases ... }_
+=== Trying out the Application
+
+. Adding shows
+
+.. Type `add n/Harry Potter and the Chamber of Secrets t/movie`
+.. These commands should add the movie to your list.
+
+. Searching for shows
+
+.. Type `search n/Harry Potter and the Chamber of Secrets`
+.. This should bring you to the search page and display the movie.
+
+. Syncing the show
+
+.. Type `sync 1`
+.. This should sync the show into the watchlist.
+
+. Adding shows from search result
+
+.. Type `search n/stranger things`
+.. Type `add 3`.
+.. This should add the TV series, Stranger Things to the watchlist.
-=== Deleting a person
+. Marking a movie as watched
-. Deleting a person while all persons are listed
+.. Type `watch 1`. This should mark the show as watched and transfer it to your watched list.
+.. Navigate to the watched show by typing "2" and pressing enter.
+.. Alternatively, you may click on the "watched" tab itself.
+.. The `watch` command should only be executable on the watchlist and watched tabs.
-.. Prerequisites: List all persons using the `list` command. Multiple persons in the list.
-.. Test case: `delete 1` +
- Expected: First contact is deleted from the list. Details of the deleted contact shown in the status message. Timestamp in the status bar is updated.
-.. Test case: `delete 0` +
- Expected: No person is deleted. Error details shown in the status message. Status bar remains the same.
-.. Other incorrect delete commands to try: `delete`, `delete x` (where x is larger than the list size) _{give more}_ +
- Expected: Similar to previous.
+. Marking a TV show's seasons and episodes
-_{ more test cases ... }_
+.. Navigate to the watchlist tab by typing "1" and pressing enter or clicking the tab itself.
+.. Type `watch 1 s/2 e/4`
+.. This should update the last watched episode and season of the "Stranger Things".
+.. Try the command with other season and episode numbers.
+.. The `watch` command should only be executable on the watchlist and watched tabs.
-=== Saving data
+. Advanced search features
-. Dealing with missing/corrupted data files
+.. Type `search n/Stranger Things o/yes`
+.. Type `search g/Horror n/Annabelle`
+.. Try other search fields following the user guide's usage.
+.. Also try searching for shows while being disconnected from the internet.
-.. _{explain how to simulate a missing/corrupted file and the expected behavior}_
+. Statistics feature
-_{ more test cases ... }_
+.. Navigate to the statistics page by typing "4" then pressing enter or clicking on the statistics tab.
+.. This should bring you to the statistics tab of EzWatchlist.
+.. Here you can view some shows that have not been watched, your favourite genres and recommendations.
+.. Try accessing this page while being connected and disconnected to the internet.
diff --git a/docs/DeveloperGuide.pdf b/docs/DeveloperGuide.pdf
new file mode 100644
index 00000000000..6fd94b8cc59
Binary files /dev/null and b/docs/DeveloperGuide.pdf differ
diff --git a/docs/Documentation.adoc b/docs/Documentation.adoc
index ad90ac87bda..8ef595d8688 100644
--- a/docs/Documentation.adoc
+++ b/docs/Documentation.adoc
@@ -1,4 +1,4 @@
-= AddressBook Level 3 - Documentation
+= EzWatchlist - Documentation
:site-section: DeveloperGuide
:toc:
:toc-title:
diff --git a/docs/SettingUp.adoc b/docs/SettingUp.adoc
index c0659782fab..165fdf5609f 100644
--- a/docs/SettingUp.adoc
+++ b/docs/SettingUp.adoc
@@ -1,4 +1,4 @@
-= AddressBook Level 3 - Setting Up
+= EzWatchlist - Setting Up
:site-section: DeveloperGuide
:toc:
:toc-title:
@@ -12,7 +12,7 @@ ifdef::env-github[]
:note-caption: :information_source:
:warning-caption: :warning:
endif::[]
-:repoURL: https://github.com/se-edu/addressbook-level3/tree/master
+:repoURL: https://github.com/AY1920S1-CS2103T-F13-4/main/tree/master
== Prerequisites
@@ -37,7 +37,7 @@ Do not disable them. If you have disabled them, go to `File` > `Settings` > `Plu
== Verifying the setup
-. Run the `seedu.address.Main` and try a few commands
+. Run the `seedu.ezwatchlist.Main` and try a few commands
. <> to ensure they all pass.
== Configurations to do before writing code
diff --git a/docs/Testing.adoc b/docs/Testing.adoc
index 5767b92912c..d73393a1a73 100644
--- a/docs/Testing.adoc
+++ b/docs/Testing.adoc
@@ -1,4 +1,4 @@
-= AddressBook Level 3 - Testing
+= EzWatchlist - Testing
:site-section: DeveloperGuide
:toc:
:toc-title:
@@ -35,11 +35,11 @@ See <> for more info on how to run tests using G
We have three types of tests:
. _Unit tests_ targeting the lowest level methods/classes. +
-e.g. `seedu.address.commons.StringUtilTest`
+e.g. `seedu.ezwatchlist.commons.StringUtilTest`
. _Integration tests_ that are checking the integration of multiple code units (those code units are assumed to be working). +
-e.g. `seedu.address.storage.StorageManagerTest`
+e.g. `seedu.ezwatchlist.storage.StorageManagerTest`
. Hybrids of unit and integration tests. These test are checking multiple code units as well as how the are connected together. +
-e.g. `seedu.address.logic.LogicManagerTest`
+e.g. `seedu.ezwatchlist.logic.LogicManagerTest`
== Troubleshooting Testing
diff --git a/docs/UserGuide.adoc b/docs/UserGuide.adoc
index 4e5d297a19f..e3dde74be3e 100644
--- a/docs/UserGuide.adoc
+++ b/docs/UserGuide.adoc
@@ -1,6 +1,7 @@
-= AddressBook Level 3 - User Guide
+= EzWatchlist - User Guide
:site-section: UserGuide
:toc:
+:toclevels: 3
:toc-title:
:toc-placement: preamble
:sectnums:
@@ -12,166 +13,636 @@ ifdef::env-github[]
:tip-caption: :bulb:
:note-caption: :information_source:
endif::[]
-:repoURL: https://github.com/se-edu/addressbook-level3
+:repoURL: https://github.com/AY1920S1-CS2103T-F13-4/main.git
+:icons: font
-By: `Team SE-EDU` Since: `Jun 2016` Licence: `MIT`
+By: `Team CS2103T-F13-4` Since: `Sept 2019` Licence: `MIT`
+// tag::intro[]
== Introduction
+Tired of using multiple sources on the internet to keep track of all your movies and tv shows that you want to watch? EzWatchlist is a solution to your entertainment problems!
-AddressBook Level 3 (AB3) is for those who *prefer to use a desktop app for managing contacts*. More importantly, AB3 is *optimized for those who prefer to work with a Command Line Interface* (CLI) while still having the benefits of a Graphical User Interface (GUI). If you can type fast, AB3 can get your contact management tasks done faster than traditional GUI apps. Interested? Jump to the <> to get started. Enjoy!
+`EzWatchList` is an application is for cinephiles who *prefer to use a desktop for managing movies and tv shows*. More importantly, `EzWatchList` is *optimized for those who prefer to work with a Command Line Interface (CLI)* while still having the benefits of a Graphical User Interface (GUI).
+
+. If you can type fast, `EzWatchList` works faster than traditional GUI apps.
+. Keep track of movies and tv shows that you plan to watch or have watched.
+. Access quick information about your favourite shows through our online database.
+. Discover your personal movie habits, with our statistics page.
+. Can't find any good shows to watch? We will give you recommendations on what to watch based on your `EzWatchlist` usage.
+
+====
+Already interested? Jump to <> to quickly get started. Enjoy!
+====
+
+=== What else does EzWatchlist offer you?
+
+* Integration with an online database of shows, allowing you to search and get information about shows.
+
+* Functions and works without internet.
+
+* No installations required.
+
+* Auto-saves all data.
+
+* Dual-purpose interface, command line interface for those who prefer typing and graphical for those who prefer using a mouse.
+
+=== How do I use EzWatchlist?
+
+EzWatchlist was built with ease in mind hence our interface reflects this design philosophy.
+
+.EzWatchlist's graphical interface
+image::Ui.png[width="900"]
+
+==== Navigation
+
+EzWatchlist is split into four different pages:
+
+. <>, where shows you want to watch are added.
+. <>, where shows you have watched are located.
+. <>, where you search for shows both online and offline.
+. <>, where information about your viewing habits are shown.
+
+This pages are represented by the <> shown visibly in Figure 1. Navigate between pages by using keyboard shortcuts kbd:[1], kbd:[2], kbd:[3], kbd:[4] respectively or
+through a button press on the graphical interface.
+
+==== Interaction
+.EzWatchlist's command line interface.
+image::commandline.png[width="900"]
+
+
+EzWatchlist uses <> entered through the command line interface in figure 2 to interact with the application.
+Typing a command into the interface and entering it (by pressing kbd:[Enter] or clicking `_Go!_`) is the main way of interaction in the application.
+The following is an example of adding a movie into your watch list:
+// end::intro[]
+
+===== Steps to quickly add a movie
+
+* **`search`** `n/ip man` : Searched for movie named `ip man` in the offline and online database which will bring you to the search page automatically.
+
+.Search page for "ip man".
+image::searchIpMan.png[width='500']
+
+* **`add`** `2` : Adds "Ip Man" (2008) shown in entry 2 in Figure 3 into your watchlist.
+
+.Watchlist page after adding the entry.
+image::watchlistIpMan.png[width='500']
+
+
+And that's it. Our <> will guide you to set up our application. If you like to learn more head straight into our <> for a run down on our major features, or
+go to our <> to learn more on our commands. Otherwise, if you're a developer and are interested to
+understand how each of our features were implemented, our <> breaks down each of our major components.
+
+
+What are you waiting for? Start using `EzWatchlist` now!
== Quick Start
. Ensure you have Java `11` or above installed in your Computer.
-. Download the latest `addressbook.jar` link:{repoURL}/releases[here].
-. Copy the file to the folder you want to use as the home folder for your Address Book.
+. Download the latest `ezwatchlist.jar` link:https://github.com/AY1920S1-CS2103T-F13-4/main/releases[here].
+. Copy the file to the folder you want to use as the home folder for your watchlist.
. Double-click the file to start the app. The GUI should appear in a few seconds.
-+
-image::Ui.png[width="790"]
-+
-. Type the command in the command box and press kbd:[Enter] to execute it. +
+. Interact with `EzWatchlist` with <> in the command box and press kbd:[Enter] to execute it. +
e.g. typing *`help`* and pressing kbd:[Enter] will open the help window.
. Some example commands you can try:
-* *`list`* : lists all contacts
-* **`add`**`n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01` : adds a contact named `John Doe` to the Address Book.
-* **`delete`**`3` : deletes the 3rd contact shown in the current list
+* **`add`**`n/Titanic t/movie` : adds the `movie` named `Titanic` to the Watchlist.
+* **`delete`**`3` : deletes the 3rd movie shown in either watch or watched page.
* *`exit`* : exits the app
-. Refer to <> for details of each command.
+. Refer to <> for details of each command.
+
-[[Features]]
== Features
+// tag::UI[]
+=== User-Interface
+
+The UI consists of three parts: sidebar, command bar and various pages that could appear in the main panel.
+The following sections introduce these UI parts in details.
+
+==== Sidebar
+
+EzWatchlist gives users the ability to navigate easily through the **Watchlist, Watchedlist, Search page and Statistics pages** through the use of the side bar at the left of the main page of the graphical user interface. Moreover, keyboard shortcuts kbd:[1], kbd:[2], kbd:[3], kbd:[4] bring you to each page respectively.
+====
+Note: If user has selected the command box, type kbd:[1], kbd:[2], kbd:[3], kbd:[4] and enter to bring you to each page respectively.
+====
+
+==== Command bar
+
+Users input commands in the command bar and any feedback will be displayed below the text area of command bar.
+
+==== Watchlist Page
+
+The watchlist page is the default page that is opened upon start-up of `EzWatchlist`.
+It shows the user’s unwatched movies and television series.
+The user can navigate to this list by clicking the “Watchlist” tab on the side bar or by using the keyboard short-cut `1`.
+
+**Characteristics of the watchlist page:**
+
+* Movie/TV show poster
+* Name of show
+* Genres
+* Actors
+* Description of the show
+* Running Time
+* Last watched episode (For TV series that have been updated using the `watch` command)
+* Checkbox to indicate if the show has been watched
+
+.Main page of the UI: watchlist page
+image::Ui.png[width="790"]
+
+
+==== Watched List Page
+
+The user has the option to view the list of shows that have been marked as watched in the watched list. The user can navigate to this list by clicking the “Watched” tab on the side bar or by using the keyboard short-cut `2`.
+
+==== Search Page
+
+The user can navigate to the search page to find a specific movie or television by searching for its name. The user can navigate to this list by clicking the “Search” tab on the side bar or by using the keyboard short-cut `3`.
+[NOTE]
+The search function may require some time to load the data from the API.
+
+.Main page of the UI: search page
+image::search_page.png[width="790"]
+
+
+==== Statistics Page
+
+The user can navigate to the statistics page to find a summary of his watching habits,
+preferences and history according to what is present in his lists.
+Examples include:
+
+* Movies added but might have forgotten to watch
+* Up to 3 most favourite genres
+* Recommendations
+
+The user can navigate to this list by clicking the “Statistics” tab on the side bar or by using the keyboard short-cut `4`.
+[NOTE]
+The statistics function may require some time to load the data from the API the first time you start the application,
+or after you have made changes to your watchlist.
+
+
+
+.Main page of the UI: statistics page
+image::statistics_page.png[width="790"]
+
+
+==== Details Page (Coming in v2.0)
+
+Upon clicking a movie or tv show title, a pop-up window will emerge, with a detailed view of the movie or tv show. This page provides the user with more information about the show, like the year, cast and ratings. The user also has the ability to edit the details here.
+
+// end::UI[]
+
+// tag::advancedfeatures[]
+=== Advanced Features
+
+==== Parsing of Movie and Tv Show Information
+In EzWatchlist, an online database is accessed so that all the information about your favourite Movies and
+Television Shows is updated, informative, and concise. What this means for you is that:
+
+* Posters of your EzWatchlist shows,
+can be displayed in EzWatchlist's sleek dark interface.
+
+* Moreover, your offline shows can be synced with this database allowing the application to fill in
+the minor details for you.
+
+* Unsure of what actors played in your favourite movie? EzWatchlist will find that information for you.
+
+****
+You don't need to do anything other than be connected to the internet. It is that easy!
+****
+
+==== Movie and Tv Show Recommendations
+EzWatchlist has the ability to recommend Movies and TvShows to you based on
+your EzWatchlist usage. EzWatchlist will create your own personalized recommendations for you.
+
+===== How to get your recommendations
+1. First, make sure that you have added at least one (offline or online) into `EzWatchlist` so that we can use that to generate recommendations for you.
+
+2. Then head to the <> by clicking kbd:[4] or pressing the `Statistics.` button on the
+in the interface.
+
+3. Now your recommendations as seen in Figure 5 will be shown prominently on the bottom of the Statistics Page, with movie recommendations on the left, and Tv Series recommendations on the right.
+
+.Recommendations shown in the Statistics Page.
+image::recommendations.png[width="900]
+
+****
+Excited? Enter your statistics page to get started on your recommendations. You might
+just find your next favourite!
+****
+
+==== Auto-Correct (Coming in v2.0)
+EzWatchlist aims to provide a smooth experience for our typing users. All typing in EzWatchlist will be automatically
+corrected for you when typing in your commands.
+
+****
+Say goodbye to typing errors!
+****
+
+// end::advancedfeatures[]
+[[UserCommands]]
+=== User Commands
====
*Command Format*
-* Words in `UPPER_CASE` are the parameters to be supplied by the user e.g. in `add n/NAME`, `NAME` is a parameter which can be used as `add n/John Doe`.
-* Items in square brackets are optional e.g `n/NAME [t/TAG]` can be used as `n/John Doe t/friend` or as `n/John Doe`.
-* Items with `…` after them can be used multiple times including zero times e.g. `[t/TAG]...` can be used as `{nbsp}` (i.e. 0 times), `t/friend`, `t/friend t/family` etc.
-* Parameters can be in any order e.g. if the command specifies `n/NAME p/PHONE_NUMBER`, `p/PHONE_NUMBER n/NAME` is also acceptable.
+* Words in `UPPER_CASE` are the parameters to be supplied by the user e.g. in `add n/SHOW_NAME t/SHOW_TYPE`, `SHOW_NAME` and `SHOW_TYPE` are parameters which can be used as `add n/The Angry Birds Movie 2 t/movie`.
+* Items in square brackets are optional e.g `n/SHOW_NAME t/SHOW_TYPE [s/DESCRIPTION]` can be used as `n/The Angry Birds Movie t/movie 2 d/Oscar-worthy` or as `n/The Angry Birds Movie t/movie`.
+* Items with `…` after them can be used multiple times including zero times e.g. `[a/ACTOR]...` can be used as `{nbsp}` (i.e. 0 times), `a/Leonardo Di Carpio`, `a/Leonardo Di Carpio a/Christian Bale` etc.
+* Parameters can be in any order e.g. if the command specifies `n/SHOW_NAME t/SHOW_TYPE`, `t/SHOW_TYPE n/SHOW_NAME` is also acceptable.
====
-=== Viewing help : `help`
+==== View help : `help`
Format: `help`
-=== Adding a person: `add`
+// tag::addfirst[]
+==== Add a show: `add`
-Adds a person to the address book +
-Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]...`
+User can add a show they are interested to watch in the watchlist by using +
+`add` command.
[TIP]
-A person can have any number of tags (including 0)
+*`add` can only work in **Watchlist page** and **Watched page**.*
+
+Format: `add n/SHOW_NAME t/SHOW_TYPE [d/DATE_OF_RELEASE] [w/WATCHED] [r/RUNNING_TIME] [s/DESCRIPTION] [a/ACTOR_NAME]...`
+
+* Only `SHOW_NAME`, `SHOW_TYPE` are compulsory fields. Rest is optional.
+* `SHOW_TYPE` can only be 'movie' or 'tv'
+* `WATCHED` can only be 'true' or 'false'
+* `SHOW_NAME`, `DESCRIPTION` and `ACTOR_NAME` can be any words
+* `DATE_OF_RELEASE` must be in the format dd/MM/yyyy
+* `RUNNING_TIME` can be any number above 0
+* `ACTOR_NAME` can be more than one.
+
Examples:
-* `add n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01`
-* `add n/Betsy Crowe t/friend e/betsycrowe@example.com a/Newgate Prison p/1234567 t/criminal`
+* `add n/Titanic t/movie`
+* `add n/Friends t/tv`
+* `add n/John Wick t/movie d/24/10/014 w/false r/101 s/An ex-hit-man comes out of retirement to track down the gangsters that killed his dog and took everything from him. a/Keanu Reeves`
+* `add n/Joker t/movie d/3/10/2019 w/false r/122 s/In Gotham City, mentally-troubled comedian Arthur Fleck is disregarded and mistreated by society. He then embarks on a downward spiral of revolution and bloody crime. This path brings him face-to-face with his alter-ego: "The Joker". a/Joaquin Phoenix`
+
+// end::addfirst[]
+
+// tag::addsecond[]
+==== Adding a show from search result page: `add`
+
+This is an extension to the `add` feature.
+After user has searched from the show, user can add a show found in the search result page into their watchlist.
+
+Format: `add INDEX`
+
+INDEX is a positive integer and is limited to the number of shows found in search result page.
-=== Listing all persons : `list`
+[TIP]
+This add command can only be used if user is currently at `search page`, and has already searched for show using the search online command.
+
+Example Usage:
+
+Pre-condition: User has already searched for a show using search online command.
+
+Step 1. User click (or use keyboard `3` key) on search page. User then input `add 1` on command box.
+
+image::add2.png[width="500"]
+
+Step 2. User click (or press keyboard `1` key) on watch list page. User will see `Joker`
+movie added to watch list.
-Shows a list of all persons in the address book. +
-Format: `list`
+image::add3.png[width="500"]
-=== Editing a person : `edit`
+// end::addsecond[]
-Edits an existing person in the address book. +
-Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]...`
+==== Clear the WatchList: `clear`
+
+Clear the WatchList +
+Format: `clear`
+
+// tag::watch[]
+==== Mark/Unmark as watched : `watch`
+
+To mark an unwatched show in the watchlist as watched, use the command format listed below. +
+Format: `watch INDEX [e/EPISODE_NUMBER] [s/SEASON_NUMBER]`
+
+[TIP]
+Before marking TV show's seasons and episodes, you may want to use the <>
+command to ensure that the TV show's season and episode details are up to date.
+
+*Example Usage:*
+
+1. You want to mark "The Office" in the watchlist page as watched. Navigate to the watchlist by clicking on the watchlist tab or hitting the keyboard shortcut `1`.
+
+.Viewing an unmarked show
+image::WatchTheOffice.png[width="790"]
+
+[start=2]
+2. Enter `watch 1` into the command box in the watchlist tab.
+
+.Entering the watch command
+image::Watch.png[width="790"]
+
+[start=3]
+3. You may now view the show under the watched tab by clicking the watched tab or hitting the keyboard shortcut kbd:[2].
+
+.Viewing the newly marked show
+image::WatchedTheOffice.png[width="790"]
+
+Alternatively, you may click on the watched checkbox to toggle between whether a show is watched as indicated by the red arrow in the image below.
+
+.Marking a show using the checkbox
+image::TheOfficeWatchCommand.png[width="790"]
****
-* Edits the person at the specified `INDEX`. The index refers to the index number shown in the displayed person list. The index *must be a positive integer* 1, 2, 3, ...
-* At least one of the optional fields must be provided.
-* Existing values will be updated to the input values.
-* When editing tags, the existing tags of the person will be removed i.e adding of tags is not cumulative.
-* You can remove all the person's tags by typing `t/` without specifying any tags after it.
+* The `index` refers to the index number shown in the displayed watchlist. The index *must be a positive integer* 1, 2, 3, ...
+* Any number of the optional fields may be provided.
+* Having only the index of the show will mark/unmark the show as watched.
+* Having the index and the episode number of the show will update the cumulative number of episodes of the show that are watched.
+* Having the index and the season number of the show will update the cumulative number of seasons of the show that are watched.
+* Having the index, season number and the episode number of the show will update the last watched episode to be the indicated episode of the indicated season of the show.
****
+[TIP]
+Using the `watch` command on an already watched show will un-mark the show as watched.
+[TIP]
+`watch` would only work in **Watchlist page** and **Watched page**.
Examples:
-* `edit 1 p/91234567 e/johndoe@example.com` +
-Edits the phone number and email address of the 1st person to be `91234567` and `johndoe@example.com` respectively.
-* `edit 2 n/Betsy Crower t/` +
-Edits the name of the 2nd person to be `Betsy Crower` and clears all existing tags.
+* `watch 1` +
+Marks/un-marks the first show of the list as watched.
+* `watch 2 e/20` +
+Marks the first 20 episodes of the second show of the list as watched.
+* `watch 2 s/5` +
+Marks all episodes of the first 5 seasons of the second show as watched.
+* `watch 3 s/5 e/2` +
+Marks all episodes up to and including the second episode of the fifth season of the third show in the list as watched.
-=== Locating persons by name: `find`
+// end::watch[]
+==== Edit a show's details : `edit`
-Finds persons whose names contain any of the given keywords. +
-Format: `find KEYWORD [MORE_KEYWORDS]`
+Edits an existing show in the list +
+Format: `edit INDEX [n/SHOW_NAME] [d/DATE_OF_RELEASE] [w/WATCHED] [r/RUNNING_TIME] [s/DESCRIPTION] [a/ACTOR]...`
****
-* The search is case insensitive. e.g `hans` will match `Hans`
-* The order of the keywords does not matter. e.g. `Hans Bo` will match `Bo Hans`
-* Only the name is searched.
-* Only full words will be matched e.g. `Han` will not match `Hans`
-* Persons matching at least one keyword will be returned (i.e. `OR` search). e.g. `Hans Bo` will return `Hans Gruber`, `Bo Yang`
+* Edits the show at the specified `INDEX`. The index refers to the index number shown in the displayed watchlist. The index *must be a positive integer* 1, 2, 3, ...
+* At least one of the optional fields must be provided.
+* Existing values will be updated to the input values.
+* When editing actors, the existing actors of the show will be removed i.e adding of actors is not cumulative.
+* You can remove all the show's actors by typing `a/` without specifying any actors after it.
****
+[TIP]
+`edit` would only work in **Watchlist page** and **Watched page**.
+
Examples:
-* `find John` +
-Returns `john` and `John Doe`
-* `find Betsy Tim John` +
-Returns any person having names `Betsy`, `Tim`, or `John`
+* `edit 1 n/Joker a/Joaquin Phoenix` +
+Edits the name and actor name of the 1st show in the list to be `Joker` and `Joaquin Phoenix` respectively.
+* `edit 2 a/` +
+Clears all existing actors of the 2nd show in the list.
+
+//tag::search[]
+
+==== Search for a show: `search`
+
+Searches for shows whose names contain any of the given keywords from the online database, unless specified to be from
+the watchlist or watched list. +
+
+Format:
+
+* by name: +
+`search n/SHOW_NAME... [g/GENRE]... [a/ACTOR_NAME]... [o/FROM_ONLINE] [t/SHOW_TYPE] [w/HAS_WATCHED]`
+
+* by genre: +
+`search g/GENRE... [n/SHOW_NAME]... [a/ACTOR_NAME]... [o/FROM_ONLINE] [t/SHOW_TYPE] [w/HAS_WATCHED]`
+
+* by actor (from watchlist only): +
+`search a/ACTOR_NAME... [n/SHOW_NAME]... [g/GENRE]... [o/FROM_ONLINE] [t/SHOW_TYPE] [w/HAS_WATCHED]`
+
+** `SHOW_NAME`, `GENRE`, `ACTOR_NAME` can be any words, as long as it does not contain `/`.
+** `FROM_ONLINE` and `HAS_WATCHED` can only be `yes`, `true`, `no` or `false`.
+** `SHOW_TYPE` can only be `movie` or `tv`.
+
+====
+[TIP]
+Special commands to take note of: +
+`o/no`: to search from watchlist or watched list +
+`o/no w/no`: to search from watchlist only +
+`o/no w/yes`: to search from watched list only
+====
+====
+[NOTE]
+When searching based on genre online, only movies will be searched. +
+{sp} +
+For `[o/FROM_ONLINE] [t/SHOW_TYPE] [w/HAS_WATCHED]`, if multiple entries are entered, only the last would be considered. e.g. "search n/Avengers t/movie t/tv" will be interpreted as "search n/Avengers t/tv". +
+{sp} +
+Space between prefix and slash is not acceptable. e.g. "n /Avengers" will throw an error. +
+Space after prefix is acceptable. e.g. "n/ Avengers" will be interpreted as "n/Avengers".
+====
+
+====
+[NOTE]
+The search is case insensitive. e.g "avengers" will match "Avengers". +
+The order of the keywords matter. e.g. "Chris Evans" will not match "Evans Chris". +
+Not only full words will be matched. e.g. "Joke" will also match with "Joker".
+====
+{sp} +
+*Example Usage:* +
+You may want to search for movies named "Avengers" and also movies with an actor named "Tom".
+As shown below, assume that your watchlist only has a movie "Avengers: Endgame" that you have watched.
+
+.Current *Watched Page* with a movie watched "Avengers: Endgame"
+image::watchedPageWithAvengerEndgame.png[width="790"]
+{sp} +
+[start=1]
+1. Navigate to the *Search page* by one of the following ways: +
+* clicking on the *search* tab
+* hitting the keyboard shortcut kbd:[3]
+* typing `3` in the command box and pressing kbd:[enter]
+* typing `search` in the command box and pressing kbd:[enter] as shown in the figure below
+
+.Entering of the `search` command
+image::searchCommand.png[width="790"]
+{sp} +
+After you have entered the command, you will be led to the *Search page* as shown below.
+
+.Graphical Interface of the *Search Page*
+image::emptySearchPage.png[width="790"]
+
+[TIP]
+You may skip Step 1 as you will be automatically be navigated to the *Search Page* when you key in any valid `search`
+command, such as the one in Step 2 below.
+
+{sp} +
+[start=2]
+1. Enters `search n/Avengers a/Tom t/movie` into the command box in the *Search Page* as shown below.
+
+.Entering of the `search` command to search for shows
+image::searchAvengerUserInput.png[width="790"]
+
+{sp} +
+Press kbd:[enter] after entering the information shown above and wait for the information to load.
+
+.Loading Screen of the *Search page*
+image::searchPageLoadingScreen.png[width="790"]
+
+The loading page as shown above will appear while EzWatchlist searches for your shows.
+
+[TIP]
+You can go to the other pages in the mean time. Do allow some time for the search to load.
+
+{sp} +
+[start=3]
+1. Search page shows the list of shows based on `search n/Avengers a/Tom t/movie`.
+
+{sp} +
+*Scenario A: You are offline* +
+Only movies from the watchlist or offline database with the movie name "Avengers" or actor name "Tom" will be shown,
+as seen in the figure below.
+
+.*Search Page* showing the search results when offline
+image::searchPageSearchedForAvengersWhenOffline.png[width="790"]
+
+{sp} +
+*Scenario B: You are online* +
+Only movies from the online database with the movie name "Avengers" or actor name "Tom" will be shown, as seen in the
+figure below.
+
+.*Search Page* showing the search results when online
+image::searchPageSearchedForAvengersWhenOnline.png[width="790"]
+{sp} +
+Examples:
+
+* By name:
+** `search n/Joker o/no` +
+Returns shows with the name "Joker" within the watchlist
+** `search n/Avengers g/Science Fiction t/movie n/Spiderman` +
+(If online) Returns movies from the online database with the name "Avenger" or "Spiderman" and movies with the genre "Science Fiction" +
+(If offline) Returns movies from the internal database, watchlist and watched-list with the name "Avenger" or "Spiderman" and movies with the genre "Science Fiction"
+* By genre:
+** `search g/Action t/movie` +
+(If online) Returns movies from the online database with the genre "Action" +
+(If offline) Returns movies from the internal database, watchlist and watched-list with the genre "Action"
+* By actor: (from watchlist only)
+** `search a/Tom o/no w/no` +
+Returns shows within the watchlist with actor named "Tom"
+
+// end::search[]
+
+// tag::sync[]
+==== Synchronise user\'s show data: `sync`
+
+If user has lack of information about a certain show in their watch list, User can use `sync` command.
+Synchronise, `sync`, command will transfer all the information about a certain show (for example: show A) found in
+search result page with a show (for example: show A') that has the same name as Show A found in watch list.
+
+====
+Note:
+
+1. The show in the watch list must have at least a name and type.
+2. Names are not-case sensitive.
+3. `Sync` will *WRITE OVER* all the information of show with same name found in watchlist.
+====
+
+Format: `sync INDEX`
+
+INDEX is a positive integer and is limited to the number of shows found in search result page.
+
+*Example Usage*
+
+Scenario 1: User has already input 'Titanic' show into watchlist manually.
+
+image::Sync1.png[width="350"]
+
+Step 1. User searches for `Titanic` show in search page.
+
+Step 2. `Titanic` result page will be displayed. User input `sync 2` to synchronise movie at index 2 of the list with a movie
+of same name found in watchlist.
+
+image::Sync5.png[width="500"]
+
+Step 3. Go to watchlist. New information of `Titanic` in watchlist will be displayed.
+
+image::Sync6.png[width="500"]
+
+Scenario 2. User has not input Titanic show into watchlist manually.
+
+image::Sync21.png[width="350"]
+
+Step 1. Similar to scenario 1, user searches for `Titanic` show in search page.
+
+image::Sync4.png[width="500"]
+
+Step 2. `Titanic` result page will be displayed. If user were to sync any index, error message will be displayed
+because there is no show of similar name found in watch list.
+
+image::Sync22.png[width="500"]
+
+Step 3. User can choose to use `add 1` command to add show of index 1 found in search result page.
+
+image::Sync23.png[width="500"]
+
+// end::sync[]
// tag::delete[]
-=== Deleting a person : `delete`
+==== Deleting a show : `delete`
-Deletes the specified person from the address book. +
+Deletes the specified show from the watchlist. +
Format: `delete INDEX`
****
-* Deletes the person at the specified `INDEX`.
-* The index refers to the index number shown in the displayed person list.
+* Deletes the show at the specified `INDEX`.
+* The index refers to the index number shown in the displayed watchlist.
* The index *must be a positive integer* 1, 2, 3, ...
****
+[NOTE]
+`delete` would only work in **Watchlist page** and **Watched page**.
+
Examples:
-* `list` +
-`delete 2` +
-Deletes the 2nd person in the address book.
-* `find Betsy` +
+* `delete 2` +
+Deletes the 2nd show in the watchlist.
+* `search Angry` +
`delete 1` +
-Deletes the 1st person in the results of the `find` command.
+Deletes the 1st show in the results of the `search` command.
// end::delete[]
-=== Clearing all entries : `clear`
-
-Clears all entries from the address book. +
-Format: `clear`
-
-=== Exiting the program : `exit`
+==== Exiting the program : `exit`
Exits the program. +
Format: `exit`
-=== Saving the data
+==== Saving the data
-Address book data are saved in the hard disk automatically after any command that changes the data. +
+EzWatchlist's data are saved in the hard disk automatically after any command that changes the data. +
There is no need to save manually.
-// tag::dataencryption[]
-=== Encrypting data files `[coming in v2.0]`
-
-_{explain how the user can enable/disable data encryption}_
-// end::dataencryption[]
-
== FAQ
*Q*: How do I transfer my data to another Computer? +
-*A*: Install the app in the other computer and overwrite the empty data file it creates with the file that contains the data of your previous Address Book folder.
+*A*: Install the app in the other computer and overwrite the empty data file it creates with the file that contains the data of your previous EzWatchlist folder.
== Command Summary
-* *Add* `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]...` +
-e.g. `add n/James Ho p/22224444 e/jamesho@example.com a/123, Clementi Rd, 1234665 t/friend t/colleague`
+* *Add* : `add n/SHOW_NAME t/SHOW_TYPE [d/DATE_OF_RELEASE] [w/WATCHED] [r/RUNNING_TIME] [s/DESCRIPTION] [a/ACTOR_NAME]` +
+e.g. `add n/John Wick t/movie d/24 OCTOBER 2014 w/false r/101 s/An ex-hit-man comes out of retirement to track down the gangsters that killed his dog and took everything from him. a/Keanu Reeves`
+* *Add from search result page* : `add INDEX` +
+e.g. `add 1`
+* *Sync* : `sync INDEX` +
+e.g. `sync 2`
* *Clear* : `clear`
+* *Watch* : `watch INDEX [e/EPISODE_NUMBER] [s/SEASON_NUMBER]`
* *Delete* : `delete INDEX` +
e.g. `delete 3`
-* *Edit* : `edit INDEX [n/NAME] [p/PHONE_NUMBER] [e/EMAIL] [a/ADDRESS] [t/TAG]...` +
-e.g. `edit 2 n/James Lee e/jameslee@example.com`
-* *Find* : `find KEYWORD [MORE_KEYWORDS]` +
-e.g. `find James Jake`
-* *List* : `list`
+* *Edit* : `edit INDEX [n/SHOW_NAME] [d/DATE_OF_RELEASE] [w/WATCHED] [r/RUNNING_TIME] [s/DESCRIPTION] [a/ACTOR]...` +
+e.g. `edit 2 n/John Wick a/Johnny Depp`
+* *Search*
+** *by name* : `search n/SHOW_NAME... [g/GENRE]... [a/ACTOR_NAME]... [o/FROM_ONLINE] [t/TYPE] [w/IS_WATCH]` +
+e.g. `search n/Joker o/no` searches for shows with the name 'Joker' within the watchlist
+** *by genre* : `search g/GENRE... [n/SHOW_NAME]... [a/ACTOR_NAME]... [o/FROM_ONLINE] [t/TYPE] [w/IS_WATCH]` +
+e.g. `search g/Action t/movie` searches for movies from the online database with the genre 'Action'
+** *by actor from watchlist* : `search a/ACTOR_NAME... [n/SHOW_NAME]... [g/GENRE]... [o/FROM_ONLINE] [t/TYPE] [w/IS_WATCH]` +
+e.g. `search a/Tom` searches from shows within the watchlist with actor named 'Tom'
+
+* *Exit* : `exit`
* *Help* : `help`
diff --git a/docs/UserGuide.pdf b/docs/UserGuide.pdf
new file mode 100644
index 00000000000..9461528edc5
Binary files /dev/null and b/docs/UserGuide.pdf differ
diff --git a/docs/UsingAppVeyor.adoc b/docs/UsingAppVeyor.adoc
index 12a7a89ac68..8adeeafe2d1 100644
--- a/docs/UsingAppVeyor.adoc
+++ b/docs/UsingAppVeyor.adoc
@@ -6,12 +6,6 @@ ifdef::env-github[]
:note-caption: :information_source:
endif::[]
-[NOTE]
-====
-This document was originally written for _AddressBook Level 4_ and hence its screenshots refer to `addressbook-level4`.
-For use with _AddressBook Level 3_, wherever `addressbook-level4` is used in the screenshots, you should use *`addressbook-level3`*.
-====
-
https://www.appveyor.com/[AppVeyor] is a _Continuous Integration_ platform for GitHub projects. It runs its builds on Windows virtual machines.
AppVeyor can run the project's tests automatically whenever new code is pushed to the repo. This ensures that existing functionality and features have not been broken on Windows by the changes.
diff --git a/docs/UsingCheckstyle.adoc b/docs/UsingCheckstyle.adoc
index a12ab09cc9c..5e2f02bd279 100644
--- a/docs/UsingCheckstyle.adoc
+++ b/docs/UsingCheckstyle.adoc
@@ -8,12 +8,6 @@ ifdef::env-github[]
:note-caption: :information_source:
endif::[]
-[NOTE]
-====
-This document was originally written for _AddressBook Level 4_ and hence its screenshots refer to `addressbook-level4`.
-For use with _AddressBook Level 3_, wherever `addressbook-level4` is used in the screenshots, you should use *`addressbook-level3`*.
-====
-
== Configuring Checkstyle-IDEA
. Install the Checkstyle-IDEA plugin by going to `File` > `Settings` (Windows/Linux), or `IntelliJ IDEA` > `Preferences...` (macOS). +
diff --git a/docs/diagram.uml b/docs/diagram.uml
new file mode 100644
index 00000000000..a5e9969b0f9
--- /dev/null
+++ b/docs/diagram.uml
@@ -0,0 +1,18 @@
+
+
+ JAVA
+
+
+ seedu.ezwatchlist.api.model.ApiMain
+
+
+
+
+
+ seedu.ezwatchlist.api.model.ApiMain
+
+
+ All
+ private
+
+
diff --git a/docs/diagrams/DeleteSequenceDiagram.puml b/docs/diagrams/DeleteSequenceDiagram.puml
index 1dc2311b245..b9618cc84f5 100644
--- a/docs/diagrams/DeleteSequenceDiagram.puml
+++ b/docs/diagrams/DeleteSequenceDiagram.puml
@@ -3,7 +3,7 @@
box Logic LOGIC_COLOR_T1
participant ":LogicManager" as LogicManager LOGIC_COLOR
-participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR
+participant ":WatchListParser" as WatchListParser LOGIC_COLOR
participant ":DeleteCommandParser" as DeleteCommandParser LOGIC_COLOR
participant "d:DeleteCommand" as DeleteCommand LOGIC_COLOR
participant ":CommandResult" as CommandResult LOGIC_COLOR
@@ -16,17 +16,17 @@ end box
[-> LogicManager : execute("delete 1")
activate LogicManager
-LogicManager -> AddressBookParser : parseCommand("delete 1")
-activate AddressBookParser
+LogicManager -> WatchListParser : parseCommand("delete 1")
+activate WatchListParser
create DeleteCommandParser
-AddressBookParser -> DeleteCommandParser
+WatchListParser -> DeleteCommandParser
activate DeleteCommandParser
-DeleteCommandParser --> AddressBookParser
+DeleteCommandParser --> WatchListParser
deactivate DeleteCommandParser
-AddressBookParser -> DeleteCommandParser : parse("1")
+WatchListParser -> DeleteCommandParser : parse("1")
activate DeleteCommandParser
create DeleteCommand
@@ -36,14 +36,14 @@ activate DeleteCommand
DeleteCommand --> DeleteCommandParser : d
deactivate DeleteCommand
-DeleteCommandParser --> AddressBookParser : d
+DeleteCommandParser --> WatchListParser : d
deactivate DeleteCommandParser
'Hidden arrow to position the destroy marker below the end of the activation bar.
-DeleteCommandParser -[hidden]-> AddressBookParser
+DeleteCommandParser -[hidden]-> WatchListParser
destroy DeleteCommandParser
-AddressBookParser --> LogicManager : d
-deactivate AddressBookParser
+WatchListParser --> LogicManager : d
+deactivate WatchListParser
LogicManager -> DeleteCommand : execute()
activate DeleteCommand
diff --git a/docs/diagrams/GetFavouriteSequenceDiagram.puml b/docs/diagrams/GetFavouriteSequenceDiagram.puml
new file mode 100644
index 00000000000..39a1af93c47
--- /dev/null
+++ b/docs/diagrams/GetFavouriteSequenceDiagram.puml
@@ -0,0 +1,25 @@
+@startuml
+!include style.puml
+
+box Statistics LOGIC_COLOR_T1
+participant ":Statistics" as Statistics LOGIC_COLOR
+end box
+
+
+box Model MODEL_COLOR_T1
+participant ":ModelManager" as ModelManager LOGIC_COLOR
+end box
+
+[-> Statistics : getFavouriteGenre()
+activate Statistics
+
+Statistics -> ModelManager: getWatchedShowList()
+activate ModelManager
+
+Statistics <-- ModelManager: watchedlist
+deactivate ModelManager
+
+[<--Statistics: favouriteGenres
+deactivate Statistics
+
+@enduml
diff --git a/docs/diagrams/GetForgottenSequenceDiagram.puml b/docs/diagrams/GetForgottenSequenceDiagram.puml
new file mode 100644
index 00000000000..a50f1031481
--- /dev/null
+++ b/docs/diagrams/GetForgottenSequenceDiagram.puml
@@ -0,0 +1,25 @@
+@startuml
+!include style.puml
+
+box Statistics LOGIC_COLOR_T1
+participant ":Statistics" as Statistics LOGIC_COLOR
+end box
+
+
+box Model MODEL_COLOR_T1
+participant ":ModelManager" as ModelManager LOGIC_COLOR
+end box
+
+[-> Statistics : getForgotten()
+activate Statistics
+
+Statistics -> ModelManager: getWatchlist()
+activate ModelManager
+
+Statistics <-- ModelManager: watchlist
+deactivate ModelManager
+
+[<--Statistics: forgotten
+deactivate Statistics
+
+@enduml
diff --git a/docs/diagrams/GetRecommendationSequenceDiagram.puml b/docs/diagrams/GetRecommendationSequenceDiagram.puml
new file mode 100644
index 00000000000..c0972fa7ee0
--- /dev/null
+++ b/docs/diagrams/GetRecommendationSequenceDiagram.puml
@@ -0,0 +1,25 @@
+@startuml
+!include style.puml
+
+box Statistics LOGIC_COLOR_T1
+participant ":Statistics" as Statistics LOGIC_COLOR
+end box
+
+
+box Model MODEL_COLOR_T1
+participant ":ModelManager" as ModelManager LOGIC_COLOR
+end box
+
+[-> Statistics : getRecommendation()
+activate Statistics
+
+Statistics -> ModelManager: getWatchedShowList()
+activate ModelManager
+
+Statistics <-- ModelManager: watchlist
+deactivate ModelManager
+
+[<--Statistics: recommendations
+deactivate Statistics
+
+@enduml
diff --git a/docs/diagrams/SearchActivityDiagram.puml b/docs/diagrams/SearchActivityDiagram.puml
new file mode 100644
index 00000000000..4d52d23b342
--- /dev/null
+++ b/docs/diagrams/SearchActivityDiagram.puml
@@ -0,0 +1,51 @@
+@startuml
+skinparam backgroundColor white
+
+skinparam activity {
+ StartColor RoyalBlue
+ EndColor RoyalBlue
+ BackgroundColor RoyalBlue
+ arrowColor Blue
+ activityDiamondBackgroundColor Blue
+ activityBarColor blue
+ BorderColor blue
+ ControlBackgroundColor white
+ FontColor Snow
+ FontName Calibri
+ FontSize 16
+}
+
+skinparam activityDiamond {
+ BackgroundColor Snow
+ BorderColor blue
+
+ FontSize 24
+}
+
+start
+
+:User launches EzWatchlist;
+:User goes to the **Search Page**;
+:User executes Search command;
+
+while () is ([Incorrect command format])
+ : EzWatchlist prompts user about the correct command format;
+endwhile ([Correct command format])
+
+: **Loading page** shown;
+
+if () then ([Online])
+ :EzWatchlist searches from
+ the online database;
+else ([Offline])
+ : EzWatchlist searches from
+ the offline database, user's
+ watchlist and watched-list;
+endif
+
+
+:Search result shown on the **Search Page**;
+
+stop
+
+@enduml
diff --git a/docs/diagrams/SearchSequenceDiagram.puml b/docs/diagrams/SearchSequenceDiagram.puml
new file mode 100644
index 00000000000..7f9a04bd1b8
--- /dev/null
+++ b/docs/diagrams/SearchSequenceDiagram.puml
@@ -0,0 +1,75 @@
+@startuml
+!include style.puml
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":WatchListParser" as WatchListParser LOGIC_COLOR
+participant ":SearchCommandParser" as SearchCommandParser LOGIC_COLOR
+participant "s:SearchCommand" as SearchCommand LOGIC_COLOR
+participant ":CommandResult" as CommandResult LOGIC_COLOR
+end box
+
+box Model MODEL_COLOR_T1
+participant ":Model" as Model MODEL_COLOR
+end box
+
+[-> LogicManager : execute\n("search n/Avengers o/no",\n mainWindow, "search-list")
+activate LogicManager
+
+LogicManager -> WatchListParser : parseCommand\n("search n/Avengers o/no",\n "search-list")
+activate WatchListParser
+
+create SearchCommandParser
+WatchListParser -> SearchCommandParser
+activate SearchCommandParser
+
+SearchCommandParser --> WatchListParser
+deactivate SearchCommandParser
+
+WatchListParser -> SearchCommandParser : parse\n("n/Avengers o/no",\n "search-list")
+activate SearchCommandParser
+
+create SearchCommand
+SearchCommandParser -> SearchCommand
+activate SearchCommand
+
+SearchCommand --> SearchCommandParser : s
+deactivate SearchCommand
+
+SearchCommandParser --> WatchListParser : s
+deactivate SearchCommandParser
+'Hidden arrow to position the destroy marker below the end of the activation bar.
+SearchCommandParser -[hidden]-> WatchListParser
+destroy SearchCommandParser
+
+WatchListParser --> LogicManager : s
+deactivate WatchListParser
+
+LogicManager -> SearchCommand : execute(model)
+activate SearchCommand
+
+
+SearchCommand -> SearchCommand : searchByName(model)
+activate SearchCommand
+
+SearchCommand -> Model : getShowFromWatchlistIfHasName\n("Avengers", model)
+activate Model
+return
+
+deactivate SearchCommand
+
+deactivate Model
+
+create CommandResult
+SearchCommand -> CommandResult
+activate CommandResult
+
+CommandResult --> SearchCommand
+deactivate CommandResult
+
+SearchCommand --> LogicManager : result
+deactivate SearchCommand
+
+[<--LogicManager
+deactivate LogicManager
+@enduml
diff --git a/docs/diagrams/StatisticsActivityDiagram.puml b/docs/diagrams/StatisticsActivityDiagram.puml
new file mode 100644
index 00000000000..48f1448837f
--- /dev/null
+++ b/docs/diagrams/StatisticsActivityDiagram.puml
@@ -0,0 +1,13 @@
+@startuml
+start
+:User launches app;
+
+'Since the beta syntax does not support placing the condition outside the
+'diamond we place it as the true branch instead.
+
+:Statistics calculates
+the 3 results and
+populate the panel;
+
+stop
+@enduml
diff --git a/docs/diagrams/StorageClassDiagram.puml b/docs/diagrams/StorageClassDiagram.puml
index 6adb2e156bf..e157adc2f22 100644
--- a/docs/diagrams/StorageClassDiagram.puml
+++ b/docs/diagrams/StorageClassDiagram.puml
@@ -6,19 +6,26 @@ skinparam classBackgroundColor STORAGE_COLOR
Interface Storage <>
Interface UserPrefsStorage <>
-Interface AddressBookStorage <>
+Interface WatchListStorage <>
Class StorageManager
Class JsonUserPrefsStorage
-Class JsonAddressBookStorage
+Class JsonWatchListStorage
StorageManager .left.|> Storage
StorageManager o-right-> UserPrefsStorage
-StorageManager o--> AddressBookStorage
+StorageManager o--> WatchListStorage
JsonUserPrefsStorage .left.|> UserPrefsStorage
-JsonAddressBookStorage .left.|> AddressBookStorage
-JsonAddressBookStorage .down.> JsonSerializableAddressBookStorage
-JsonSerializableAddressBookStorage .right.> JsonSerializablePerson
-JsonSerializablePerson .right.> JsonAdaptedTag
+JsonWatchListStorage .left.|> WatchListStorage
+JsonWatchListStorage .down.> JsonSerializableWatchList
+JsonSerializableWatchList .right.> JsonAdaptedShows
+JsonAdaptedShows .right.> JsonAdaptedMovie
+JsonAdaptedShows .down.> JsonAdaptedTvShow
+JsonAdaptedMovie .down.> JsonAdaptedGenre
+JsonAdaptedTvShow .down.> JsonAdaptedGenre
+JsonAdaptedMovie .down.> JsonAdaptedActor
+JsonAdaptedTvShow .down.> JsonAdaptedActor
+JsonAdaptedTvShow .down.> JsonAdaptedTvSeason
+JsonAdaptedTvSeason .down.> JsonAdaptedEpisode
@enduml
diff --git a/docs/diagrams/UiClassDiagram.puml b/docs/diagrams/UiClassDiagram.puml
index 92746f9fcf7..1c7438173c2 100644
--- a/docs/diagrams/UiClassDiagram.puml
+++ b/docs/diagrams/UiClassDiagram.puml
@@ -11,8 +11,12 @@ Class UiManager
Class MainWindow
Class HelpWindow
Class ResultDisplay
-Class PersonListPanel
-Class PersonCard
+Class ShowListPanel
+class WatchedPanel
+class SearchPanel
+class StatisticsPanel
+class LoadingPanel
+Class ShowCard
Class StatusBarFooter
Class CommandBox
}
@@ -33,25 +37,33 @@ UiManager -down-> MainWindow
MainWindow --> HelpWindow
MainWindow *-down-> CommandBox
MainWindow *-down-> ResultDisplay
-MainWindow *-down-> PersonListPanel
+MainWindow *-down-> ShowListPanel
+MainWindow *-down-> WatchedPanel
+MainWindow *-down-> SearchPanel
+MainWindow *-down-> StatisticsPanel
+MainWindow *-down-> LoadingPanel
MainWindow *-down-> StatusBarFooter
-PersonListPanel -down-> PersonCard
+ShowListPanel -down-> ShowCard
+WatchedPanel -down-> ShowCard
+SearchPanel -down-> ShowCard
+StatisticsPanel -down-> ShowCard
MainWindow -left-|> UiPart
ResultDisplay --|> UiPart
CommandBox --|> UiPart
-PersonListPanel --|> UiPart
-PersonCard --|> UiPart
+ShowListPanel --|> UiPart
+ShowCard --|> UiPart
StatusBarFooter --|> UiPart
+LoadingPanel -down-|> UiPart
HelpWindow -down-|> UiPart
-PersonCard ..> Model
+ShowCard ..> Model
UiManager -right-> Logic
MainWindow -left-> Logic
-PersonListPanel -[hidden]left- HelpWindow
+ShowListPanel -[hidden]left- HelpWindow
HelpWindow -[hidden]left- CommandBox
CommandBox -[hidden]left- ResultDisplay
ResultDisplay -[hidden]left- StatusBarFooter
diff --git a/docs/diagrams/WatchActivityDiagram.puml b/docs/diagrams/WatchActivityDiagram.puml
new file mode 100644
index 00000000000..dbfd965b73e
--- /dev/null
+++ b/docs/diagrams/WatchActivityDiagram.puml
@@ -0,0 +1,54 @@
+@startuml
+skinparam backgroundColor white
+skinparam activity {
+ StartColor RoyalBlue
+ EndColor RoyalBlue
+ BackgroundColor RoyalBlue
+ arrowColor Blue
+ activityDiamondBackgroundColor Blue
+ activityBarColor blue
+ BorderColor blue
+ ControlBackgroundColor white
+ FontColor Snow
+ FontName Calibri
+ FontSize 16
+}
+
+skinparam activityDiamond {
+ BackgroundColor Snow
+ BorderColor blue
+ FontSize 24
+}
+
+start
+:User executes Watch command;
+
+'Since the beta syntax does not support placing the condition outside the
+'diamond we place it as the true branch instead.
+
+if () then ([Fields specified])
+if () then ( [Seasons and
+episodes specified])
+ :Set last watched episode
+ to episode number
+ of season number;
+else ([else])
+ if () then ([Seasons specified])
+ : Set last watched
+ episode to last episode
+ of season number;
+ else ([Episodes specified])
+ : Set last watched
+ episode to specified
+ episode number;
+ endif
+endif
+else ([else])
+ :Toggle the watched
+ status of the show;
+endif
+:Show information is updated;
+
+:Appropriate result is shown to the user;
+stop
+@enduml
diff --git a/docs/diagrams/WatchSequenceDiagram.puml b/docs/diagrams/WatchSequenceDiagram.puml
new file mode 100644
index 00000000000..b3899a9aa41
--- /dev/null
+++ b/docs/diagrams/WatchSequenceDiagram.puml
@@ -0,0 +1,80 @@
+@startuml
+!include style.puml
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":WatchListParser" as WatchListParser LOGIC_COLOR
+participant ":WatchCommandParser" as WatchCommandParser LOGIC_COLOR
+participant "w:WatchCommand" as WatchCommand LOGIC_COLOR
+participant ":CommandResult" as CommandResult LOGIC_COLOR
+end box
+
+box Model MODEL_COLOR_T1
+participant ":Model" as Model MODEL_COLOR
+end box
+
+[-> LogicManager : execute("watch 1")
+activate LogicManager
+
+LogicManager -> WatchListParser : parseCommand("watch 1")
+activate WatchListParser
+
+create WatchCommandParser
+WatchListParser -> WatchCommandParser
+activate WatchCommandParser
+
+WatchCommandParser --> WatchListParser
+deactivate WatchCommandParser
+
+WatchListParser -> WatchCommandParser : parse("1")
+activate WatchCommandParser
+
+create WatchCommand
+WatchCommandParser -> WatchCommand
+activate WatchCommand
+
+WatchCommand --> WatchCommandParser : w
+deactivate WatchCommand
+
+WatchCommandParser --> WatchListParser : w
+deactivate WatchCommandParser
+'Hidden arrow to position the destroy marker below the end of the activation bar.
+WatchCommandParser -[hidden]-> WatchListParser
+destroy WatchCommandParser
+
+WatchListParser --> LogicManager : w
+deactivate WatchListParser
+
+LogicManager -> WatchCommand : execute()
+activate WatchCommand
+
+WatchCommand -> Model : getFilteredShowList()
+activate Model
+
+Model --> WatchCommand
+deactivate Model
+
+WatchCommand -> WatchCommand : createEditedShow(showToEdit)
+activate WatchCommand
+WatchCommand --> WatchCommand
+deactivate WatchCommand
+
+WatchCommand -> Model : setShow(showToEdit, EditedShow)
+activate Model
+
+Model --> WatchCommand
+deactivate Model
+
+create CommandResult
+WatchCommand -> CommandResult
+activate CommandResult
+
+CommandResult --> WatchCommand
+deactivate CommandResult
+
+WatchCommand --> LogicManager : result
+deactivate WatchCommand
+
+[<--LogicManager
+deactivate LogicManager
+@enduml
diff --git a/docs/diagrams/plantuml/AddActivitySequenceDiagram.puml b/docs/diagrams/plantuml/AddActivitySequenceDiagram.puml
new file mode 100644
index 00000000000..5c55143d0a8
--- /dev/null
+++ b/docs/diagrams/plantuml/AddActivitySequenceDiagram.puml
@@ -0,0 +1,37 @@
+@startuml
+skinparam backgroundColor white
+skinparam activity {
+ StartColor RoyalBlue
+ EndColor RoyalBlue
+ BackgroundColor RoyalBlue
+ arrowColor Blue
+ activityDiamondBackgroundColor Blue
+ activityBarColor blue
+ BorderColor blue
+ ControlBackgroundColor white
+ FontColor Snow
+ FontName Calibri
+ FontSize 16
+}
+
+skinparam activityDiamond {
+ BackgroundColor Snow
+ BorderColor blue
+ FontSize 24
+}
+start
+:User executes Add command
+ "add n/Joker t/movie";
+
+'Since the beta syntax does not support placing the condition outside the
+'diamond we place it as the true branch instead.
+
+if () then ([No existing show found in watchlist])
+ : 'Joker' show of type 'movie' is
+ added to watch list;
+ stop
+else([Existing show found in watchlist])
+ : Error result message will be
+ displayed to the user;
+stop
+@enduml
diff --git a/docs/diagrams/plantuml/AddActivitySequenceDiagram2.puml b/docs/diagrams/plantuml/AddActivitySequenceDiagram2.puml
new file mode 100644
index 00000000000..e02dc46ba0c
--- /dev/null
+++ b/docs/diagrams/plantuml/AddActivitySequenceDiagram2.puml
@@ -0,0 +1,37 @@
+@startuml
+skinparam backgroundColor white
+skinparam activity {
+ StartColor RoyalBlue
+ EndColor RoyalBlue
+ BackgroundColor RoyalBlue
+ arrowColor Blue
+ activityDiamondBackgroundColor Blue
+ activityBarColor blue
+ BorderColor blue
+ ControlBackgroundColor white
+ FontColor Snow
+ FontName Calibri
+ FontSize 16
+}
+
+skinparam activityDiamond {
+ BackgroundColor Snow
+ BorderColor blue
+ FontSize 24
+}
+start
+:User executes Add command (Extension)
+ Add INDEX;
+
+'Since the beta syntax does not support placing the condition outside the
+'diamond we place it as the true branch instead.
+
+if () then ([INDEX IS VALID])
+ : Show of INDEX found in search
+ result page is added to watchlist;
+ stop
+else([INDEX IS INVALID])
+ : Error result message will be displayed
+ to the user;
+stop
+@enduml
diff --git a/docs/diagrams/plantuml/AddSequenceDiagram.puml b/docs/diagrams/plantuml/AddSequenceDiagram.puml
new file mode 100644
index 00000000000..410aab4e412
--- /dev/null
+++ b/docs/diagrams/plantuml/AddSequenceDiagram.puml
@@ -0,0 +1,53 @@
+@startuml
+!include style.puml
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR
+participant "u:UndoCommand" as UndoCommand LOGIC_COLOR
+end box
+
+box Model MODEL_COLOR_T1
+participant ":Model" as Model MODEL_COLOR
+participant ":VersionedAddressBook" as VersionedAddressBook MODEL_COLOR
+end box
+[-> LogicManager : execute(undo)
+activate LogicManager
+
+LogicManager -> AddressBookParser : parseCommand(undo)
+activate AddressBookParser
+
+create UndoCommand
+AddressBookParser -> UndoCommand
+activate UndoCommand
+
+UndoCommand --> AddressBookParser
+deactivate UndoCommand
+
+AddressBookParser --> LogicManager : u
+deactivate AddressBookParser
+
+LogicManager -> UndoCommand : execute()
+activate UndoCommand
+
+UndoCommand -> Model : undoAddressBook()
+activate Model
+
+Model -> VersionedAddressBook : undo()
+activate VersionedAddressBook
+
+VersionedAddressBook -> VersionedAddressBook :resetData(ReadOnlyAddressBook)
+VersionedAddressBook --> Model :
+deactivate VersionedAddressBook
+
+Model --> UndoCommand
+deactivate Model
+
+UndoCommand --> LogicManager : result
+deactivate UndoCommand
+UndoCommand -[hidden]-> LogicManager : result
+destroy UndoCommand
+
+[<--LogicManager
+deactivate LogicManager
+@enduml
diff --git a/docs/diagrams/plantuml/SyncActivityDiagram.puml b/docs/diagrams/plantuml/SyncActivityDiagram.puml
new file mode 100644
index 00000000000..ebe1e9b2714
--- /dev/null
+++ b/docs/diagrams/plantuml/SyncActivityDiagram.puml
@@ -0,0 +1,47 @@
+@startuml
+skinparam backgroundColor white
+skinparam activity {
+ StartColor RoyalBlue
+ EndColor RoyalBlue
+ BackgroundColor RoyalBlue
+ arrowColor Blue
+ activityDiamondBackgroundColor Blue
+ activityBarColor blue
+ BorderColor blue
+ ControlBackgroundColor white
+ FontColor Snow
+ FontName Calibri
+ FontSize 16
+}
+
+skinparam activityDiamond {
+ BackgroundColor Snow
+ BorderColor blue
+ FontSize 24
+}
+
+start
+:User has searched for show in search page;
+:User executes Sync command
+ "Sync INDEX";
+
+'Since the beta syntax does not support placing the condition outside the
+'diamond we place it as the true branch instead.
+
+if () then ([INDEX IS VALID])
+if () then ([Show of same name found in watchlist])
+ : Show of INDEX found in search
+ result page is synchronised with
+ the same show in watchlist.;
+ stop
+else ([ELSE])
+ : Error result message displayed:
+ "Show cannot be found in watchlist
+ to be sync-ed";
+ stop
+ endif
+else([INDEX IS INVALID])
+ : Error result message will be displayed
+ to the user;
+stop
+@enduml
diff --git a/docs/images/105-1054061_check-mark-comments-check-icon.png b/docs/images/105-1054061_check-mark-comments-check-icon.png
new file mode 100644
index 00000000000..cbbd7194575
Binary files /dev/null and b/docs/images/105-1054061_check-mark-comments-check-icon.png differ
diff --git a/docs/images/117912.png b/docs/images/117912.png
new file mode 100644
index 00000000000..c329db256d5
Binary files /dev/null and b/docs/images/117912.png differ
diff --git a/docs/images/55369.png b/docs/images/55369.png
new file mode 100644
index 00000000000..ff649a6e026
Binary files /dev/null and b/docs/images/55369.png differ
diff --git a/docs/images/61-512.png b/docs/images/61-512.png
new file mode 100644
index 00000000000..57ac51592cb
Binary files /dev/null and b/docs/images/61-512.png differ
diff --git a/docs/images/AddActivityDiagram2.png b/docs/images/AddActivityDiagram2.png
new file mode 100644
index 00000000000..3b3e643541b
Binary files /dev/null and b/docs/images/AddActivityDiagram2.png differ
diff --git a/docs/images/AddActivitySequenceDiagram.png b/docs/images/AddActivitySequenceDiagram.png
new file mode 100644
index 00000000000..c31635a7c25
Binary files /dev/null and b/docs/images/AddActivitySequenceDiagram.png differ
diff --git a/docs/images/AddActivitySequenceDiagram2.png b/docs/images/AddActivitySequenceDiagram2.png
new file mode 100644
index 00000000000..32a0f6fbed5
Binary files /dev/null and b/docs/images/AddActivitySequenceDiagram2.png differ
diff --git a/docs/images/AddSequenceDiagram.png b/docs/images/AddSequenceDiagram.png
new file mode 100644
index 00000000000..4618f843b27
Binary files /dev/null and b/docs/images/AddSequenceDiagram.png differ
diff --git a/docs/images/AddSequenceDiagram2.png b/docs/images/AddSequenceDiagram2.png
new file mode 100644
index 00000000000..de3a8c153b5
Binary files /dev/null and b/docs/images/AddSequenceDiagram2.png differ
diff --git a/docs/images/ApiClass.png b/docs/images/ApiClass.png
new file mode 100644
index 00000000000..0ba3f2dc9a8
Binary files /dev/null and b/docs/images/ApiClass.png differ
diff --git a/docs/images/ApiClassDiagram.png b/docs/images/ApiClassDiagram.png
new file mode 100644
index 00000000000..588eef6f16c
Binary files /dev/null and b/docs/images/ApiClassDiagram.png differ
diff --git a/docs/images/ArchitectureSequenceDiagram.png b/docs/images/ArchitectureSequenceDiagram.png
index aa198138f8f..115d085ceb4 100644
Binary files a/docs/images/ArchitectureSequenceDiagram.png and b/docs/images/ArchitectureSequenceDiagram.png differ
diff --git a/docs/images/BetterModelClassDiagram.png b/docs/images/BetterModelClassDiagram.png
index bc7ed18ae29..ab6fa6f16e3 100644
Binary files a/docs/images/BetterModelClassDiagram.png and b/docs/images/BetterModelClassDiagram.png differ
diff --git a/docs/images/CK_PPP.png b/docs/images/CK_PPP.png
new file mode 100644
index 00000000000..366d44308b6
Binary files /dev/null and b/docs/images/CK_PPP.png differ
diff --git a/docs/images/CommitActivityDiagram.png b/docs/images/CommitActivityDiagram.png
index 4de4fa4bf2b..800464a0a0d 100644
Binary files a/docs/images/CommitActivityDiagram.png and b/docs/images/CommitActivityDiagram.png differ
diff --git a/docs/images/DeleteSequenceDiagram.png b/docs/images/DeleteSequenceDiagram.png
index fa327b39618..908c41a53ce 100644
Binary files a/docs/images/DeleteSequenceDiagram.png and b/docs/images/DeleteSequenceDiagram.png differ
diff --git a/docs/images/GetFavouriteSequenceDiagram.png b/docs/images/GetFavouriteSequenceDiagram.png
new file mode 100644
index 00000000000..d4ac71e4886
Binary files /dev/null and b/docs/images/GetFavouriteSequenceDiagram.png differ
diff --git a/docs/images/GetForgottenSequenceDiagram.png b/docs/images/GetForgottenSequenceDiagram.png
new file mode 100644
index 00000000000..212b40000ef
Binary files /dev/null and b/docs/images/GetForgottenSequenceDiagram.png differ
diff --git a/docs/images/GetRecommendationSequenceDiagram.png b/docs/images/GetRecommendationSequenceDiagram.png
new file mode 100644
index 00000000000..0b5d89eefb8
Binary files /dev/null and b/docs/images/GetRecommendationSequenceDiagram.png differ
diff --git a/docs/images/LogicClassDiagram.png b/docs/images/LogicClassDiagram.png
index b9e853cef12..58fe159c7ac 100644
Binary files a/docs/images/LogicClassDiagram.png and b/docs/images/LogicClassDiagram.png differ
diff --git a/docs/images/ModelClassDiagram.png b/docs/images/ModelClassDiagram.png
index 280064118cf..5db713cd74a 100644
Binary files a/docs/images/ModelClassDiagram.png and b/docs/images/ModelClassDiagram.png differ
diff --git a/docs/images/SeEduLogo.png b/docs/images/SeEduLogo.png
deleted file mode 100644
index 31ad50b6f88..00000000000
Binary files a/docs/images/SeEduLogo.png and /dev/null differ
diff --git a/docs/images/SearchActivityDiagram.png b/docs/images/SearchActivityDiagram.png
new file mode 100644
index 00000000000..4fe939b0bb9
Binary files /dev/null and b/docs/images/SearchActivityDiagram.png differ
diff --git a/docs/images/SearchSequenceDiagram.png b/docs/images/SearchSequenceDiagram.png
new file mode 100644
index 00000000000..1204ab10de2
Binary files /dev/null and b/docs/images/SearchSequenceDiagram.png differ
diff --git a/docs/images/StatisticsActivityDiagram.png b/docs/images/StatisticsActivityDiagram.png
new file mode 100644
index 00000000000..5cea7c8ddc9
Binary files /dev/null and b/docs/images/StatisticsActivityDiagram.png differ
diff --git a/docs/images/StorageClassDiagram.png b/docs/images/StorageClassDiagram.png
index d87c1216820..14aad71517c 100644
Binary files a/docs/images/StorageClassDiagram.png and b/docs/images/StorageClassDiagram.png differ
diff --git a/docs/images/Sync1.png b/docs/images/Sync1.png
new file mode 100644
index 00000000000..66750df2004
Binary files /dev/null and b/docs/images/Sync1.png differ
diff --git a/docs/images/Sync2.png b/docs/images/Sync2.png
new file mode 100644
index 00000000000..fe138ac46fa
Binary files /dev/null and b/docs/images/Sync2.png differ
diff --git a/docs/images/Sync21.png b/docs/images/Sync21.png
new file mode 100644
index 00000000000..8620c4c32ce
Binary files /dev/null and b/docs/images/Sync21.png differ
diff --git a/docs/images/Sync22.png b/docs/images/Sync22.png
new file mode 100644
index 00000000000..3a762ff847c
Binary files /dev/null and b/docs/images/Sync22.png differ
diff --git a/docs/images/Sync23.png b/docs/images/Sync23.png
new file mode 100644
index 00000000000..3478f00397a
Binary files /dev/null and b/docs/images/Sync23.png differ
diff --git a/docs/images/Sync3.png b/docs/images/Sync3.png
new file mode 100644
index 00000000000..296f4e5ffae
Binary files /dev/null and b/docs/images/Sync3.png differ
diff --git a/docs/images/Sync4.png b/docs/images/Sync4.png
new file mode 100644
index 00000000000..227fcf7b36c
Binary files /dev/null and b/docs/images/Sync4.png differ
diff --git a/docs/images/Sync5.png b/docs/images/Sync5.png
new file mode 100644
index 00000000000..6461cdaef3f
Binary files /dev/null and b/docs/images/Sync5.png differ
diff --git a/docs/images/Sync6.png b/docs/images/Sync6.png
new file mode 100644
index 00000000000..378558bdb8d
Binary files /dev/null and b/docs/images/Sync6.png differ
diff --git a/docs/images/SyncActivityDiagram.png b/docs/images/SyncActivityDiagram.png
new file mode 100644
index 00000000000..c5b83448bd3
Binary files /dev/null and b/docs/images/SyncActivityDiagram.png differ
diff --git a/docs/images/SyncSequenceDiagram.png b/docs/images/SyncSequenceDiagram.png
new file mode 100644
index 00000000000..8f9e78bdfe8
Binary files /dev/null and b/docs/images/SyncSequenceDiagram.png differ
diff --git a/docs/images/TheOfficeWatchCommand.png b/docs/images/TheOfficeWatchCommand.png
new file mode 100644
index 00000000000..ad910d76aac
Binary files /dev/null and b/docs/images/TheOfficeWatchCommand.png differ
diff --git a/docs/images/Ui.png b/docs/images/Ui.png
index 5bd77847aa2..f32d5c7c779 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 7b4b3dbea45..c6cfadd9eea 100644
Binary files a/docs/images/UiClassDiagram.png and b/docs/images/UiClassDiagram.png differ
diff --git a/docs/images/UndoRedoState0.png b/docs/images/UndoRedoState0.png
index 8f7538cd884..1fec2a09a7f 100644
Binary files a/docs/images/UndoRedoState0.png and b/docs/images/UndoRedoState0.png differ
diff --git a/docs/images/UndoRedoState1.png b/docs/images/UndoRedoState1.png
index df9908d0948..2e286616fe1 100644
Binary files a/docs/images/UndoRedoState1.png and b/docs/images/UndoRedoState1.png differ
diff --git a/docs/images/UndoRedoState2.png b/docs/images/UndoRedoState2.png
index 36519c1015b..a4ef14d12df 100644
Binary files a/docs/images/UndoRedoState2.png and b/docs/images/UndoRedoState2.png differ
diff --git a/docs/images/UndoRedoState3.png b/docs/images/UndoRedoState3.png
index 19959d01712..6303c9363b1 100644
Binary files a/docs/images/UndoRedoState3.png and b/docs/images/UndoRedoState3.png differ
diff --git a/docs/images/UndoRedoState4.png b/docs/images/UndoRedoState4.png
index 4c623e4f2c5..c085a9347af 100644
Binary files a/docs/images/UndoRedoState4.png and b/docs/images/UndoRedoState4.png differ
diff --git a/docs/images/UndoRedoState5.png b/docs/images/UndoRedoState5.png
index 84ad2afa6bd..3fd8a453629 100644
Binary files a/docs/images/UndoRedoState5.png and b/docs/images/UndoRedoState5.png differ
diff --git a/docs/images/UndoSequenceDiagram.png b/docs/images/UndoSequenceDiagram.png
index 6addcd3a8d9..30e89b766fb 100644
Binary files a/docs/images/UndoSequenceDiagram.png and b/docs/images/UndoSequenceDiagram.png differ
diff --git a/docs/images/Watch.png b/docs/images/Watch.png
new file mode 100644
index 00000000000..0811e921be0
Binary files /dev/null and b/docs/images/Watch.png differ
diff --git a/docs/images/WatchActivityDiagram.png b/docs/images/WatchActivityDiagram.png
new file mode 100644
index 00000000000..7922c5552c0
Binary files /dev/null and b/docs/images/WatchActivityDiagram.png differ
diff --git a/docs/images/WatchSequenceDiagram.png b/docs/images/WatchSequenceDiagram.png
new file mode 100644
index 00000000000..868a20ac222
Binary files /dev/null and b/docs/images/WatchSequenceDiagram.png differ
diff --git a/docs/images/WatchTheOffice.png b/docs/images/WatchTheOffice.png
new file mode 100644
index 00000000000..f487af89b08
Binary files /dev/null and b/docs/images/WatchTheOffice.png differ
diff --git a/docs/images/WatchedTheOffice.png b/docs/images/WatchedTheOffice.png
new file mode 100644
index 00000000000..4b72a55d032
Binary files /dev/null and b/docs/images/WatchedTheOffice.png differ
diff --git a/docs/images/activityDiagram.png b/docs/images/activityDiagram.png
new file mode 100644
index 00000000000..24954e5c4ec
Binary files /dev/null and b/docs/images/activityDiagram.png differ
diff --git a/docs/images/add1.png b/docs/images/add1.png
new file mode 100644
index 00000000000..082de5baf9b
Binary files /dev/null and b/docs/images/add1.png differ
diff --git a/docs/images/add2.png b/docs/images/add2.png
new file mode 100644
index 00000000000..48a30be8354
Binary files /dev/null and b/docs/images/add2.png differ
diff --git a/docs/images/add3.png b/docs/images/add3.png
new file mode 100644
index 00000000000..a02c3a92151
Binary files /dev/null and b/docs/images/add3.png differ
diff --git a/docs/images/add_entry_page.png b/docs/images/add_entry_page.png
new file mode 100644
index 00000000000..eb310583dfa
Binary files /dev/null and b/docs/images/add_entry_page.png differ
diff --git a/docs/images/applogo.png b/docs/images/applogo.png
new file mode 100644
index 00000000000..d18b70719ec
Binary files /dev/null and b/docs/images/applogo.png differ
diff --git a/docs/images/commandline.png b/docs/images/commandline.png
new file mode 100644
index 00000000000..2bb9beb1a89
Binary files /dev/null and b/docs/images/commandline.png differ
diff --git a/docs/images/damithc.jpg b/docs/images/damithc.jpg
deleted file mode 100644
index 12754388389..00000000000
Binary files a/docs/images/damithc.jpg and /dev/null differ
diff --git a/docs/images/details_page.png b/docs/images/details_page.png
new file mode 100644
index 00000000000..9cf2614a663
Binary files /dev/null and b/docs/images/details_page.png differ
diff --git a/docs/images/emptySearchPage.png b/docs/images/emptySearchPage.png
new file mode 100644
index 00000000000..bd7efe5a203
Binary files /dev/null and b/docs/images/emptySearchPage.png differ
diff --git a/docs/images/getFavouriteGenre.png b/docs/images/getFavouriteGenre.png
new file mode 100644
index 00000000000..3cb5bda4003
Binary files /dev/null and b/docs/images/getFavouriteGenre.png differ
diff --git a/docs/images/getForgotten.png b/docs/images/getForgotten.png
new file mode 100644
index 00000000000..5b281aedb8f
Binary files /dev/null and b/docs/images/getForgotten.png differ
diff --git a/docs/images/getMovieRecommendationsSequenceDiagram.png b/docs/images/getMovieRecommendationsSequenceDiagram.png
new file mode 100644
index 00000000000..9f9d54994ae
Binary files /dev/null and b/docs/images/getMovieRecommendationsSequenceDiagram.png differ
diff --git a/docs/images/getRecommendation.png b/docs/images/getRecommendation.png
new file mode 100644
index 00000000000..b4ff188908b
Binary files /dev/null and b/docs/images/getRecommendation.png differ
diff --git a/docs/images/heze8.png b/docs/images/heze8.png
new file mode 100644
index 00000000000..93fe05e6109
Binary files /dev/null and b/docs/images/heze8.png differ
diff --git a/docs/images/jcjjjared.png b/docs/images/jcjjjared.png
new file mode 100644
index 00000000000..b2ebd8cc525
Binary files /dev/null and b/docs/images/jcjjjared.png differ
diff --git a/docs/images/keyboard.png b/docs/images/keyboard.png
new file mode 100644
index 00000000000..9ef4301af31
Binary files /dev/null and b/docs/images/keyboard.png differ
diff --git a/docs/images/lejolly.jpg b/docs/images/lejolly.jpg
deleted file mode 100644
index 2d1d94e0cf5..00000000000
Binary files a/docs/images/lejolly.jpg and /dev/null differ
diff --git a/docs/images/m133225.jpg b/docs/images/m133225.jpg
deleted file mode 100644
index fd14fb94593..00000000000
Binary files a/docs/images/m133225.jpg and /dev/null differ
diff --git a/docs/images/michelleykw.png b/docs/images/michelleykw.png
new file mode 100644
index 00000000000..820fb469256
Binary files /dev/null and b/docs/images/michelleykw.png differ
diff --git a/docs/images/movieRecommendationSD.png b/docs/images/movieRecommendationSD.png
new file mode 100644
index 00000000000..41a2fe8105b
Binary files /dev/null and b/docs/images/movieRecommendationSD.png differ
diff --git a/docs/images/moviecard.png b/docs/images/moviecard.png
new file mode 100644
index 00000000000..a51b0ae5d24
Binary files /dev/null and b/docs/images/moviecard.png differ
diff --git a/docs/images/recommendations.png b/docs/images/recommendations.png
new file mode 100644
index 00000000000..313d57cfd47
Binary files /dev/null and b/docs/images/recommendations.png differ
diff --git a/docs/images/searchAvengerUserInput.png b/docs/images/searchAvengerUserInput.png
new file mode 100644
index 00000000000..838a2f38ec6
Binary files /dev/null and b/docs/images/searchAvengerUserInput.png differ
diff --git a/docs/images/searchCommand.png b/docs/images/searchCommand.png
new file mode 100644
index 00000000000..ce7bf638783
Binary files /dev/null and b/docs/images/searchCommand.png differ
diff --git a/docs/images/searchIpMan.png b/docs/images/searchIpMan.png
new file mode 100644
index 00000000000..5d41af2f82a
Binary files /dev/null and b/docs/images/searchIpMan.png differ
diff --git a/docs/images/searchPageLoadingScreen.png b/docs/images/searchPageLoadingScreen.png
new file mode 100644
index 00000000000..1f5add4cf6d
Binary files /dev/null and b/docs/images/searchPageLoadingScreen.png differ
diff --git a/docs/images/searchPageSearchedForAvengersWhenOffline.png b/docs/images/searchPageSearchedForAvengersWhenOffline.png
new file mode 100644
index 00000000000..050cb736cc0
Binary files /dev/null and b/docs/images/searchPageSearchedForAvengersWhenOffline.png differ
diff --git a/docs/images/searchPageSearchedForAvengersWhenOnline.png b/docs/images/searchPageSearchedForAvengersWhenOnline.png
new file mode 100644
index 00000000000..0fb19a7be57
Binary files /dev/null and b/docs/images/searchPageSearchedForAvengersWhenOnline.png differ
diff --git a/docs/images/search_page.png b/docs/images/search_page.png
new file mode 100644
index 00000000000..79cee9194d5
Binary files /dev/null and b/docs/images/search_page.png differ
diff --git a/docs/images/statisticsButton.png b/docs/images/statisticsButton.png
new file mode 100644
index 00000000000..1c68675ef7f
Binary files /dev/null and b/docs/images/statisticsButton.png differ
diff --git a/docs/images/statistics_page.png b/docs/images/statistics_page.png
new file mode 100644
index 00000000000..fa893da3a29
Binary files /dev/null and b/docs/images/statistics_page.png differ
diff --git a/docs/images/tswuxia.png b/docs/images/tswuxia.png
new file mode 100644
index 00000000000..114b14a4ddd
Binary files /dev/null and b/docs/images/tswuxia.png differ
diff --git a/docs/images/watchedPageWithAvengerEndgame.png b/docs/images/watchedPageWithAvengerEndgame.png
new file mode 100644
index 00000000000..b4cbfab7499
Binary files /dev/null and b/docs/images/watchedPageWithAvengerEndgame.png differ
diff --git a/docs/images/watchlistIpMan.png b/docs/images/watchlistIpMan.png
new file mode 100644
index 00000000000..82c3821fa8b
Binary files /dev/null and b/docs/images/watchlistIpMan.png differ
diff --git a/docs/images/watchlist_page.png b/docs/images/watchlist_page.png
new file mode 100644
index 00000000000..21bfea8efc2
Binary files /dev/null and b/docs/images/watchlist_page.png differ
diff --git a/docs/images/wongchuankai.png b/docs/images/wongchuankai.png
new file mode 100644
index 00000000000..b831dc02cd9
Binary files /dev/null and b/docs/images/wongchuankai.png differ
diff --git a/docs/images/yijinl.jpg b/docs/images/yijinl.jpg
deleted file mode 100644
index adbf62ad940..00000000000
Binary files a/docs/images/yijinl.jpg and /dev/null differ
diff --git a/docs/images/yl_coder.jpg b/docs/images/yl_coder.jpg
deleted file mode 100644
index 17b48a73227..00000000000
Binary files a/docs/images/yl_coder.jpg and /dev/null differ
diff --git a/docs/team/heze8.adoc b/docs/team/heze8.adoc
new file mode 100644
index 00000000000..19c35739b4b
--- /dev/null
+++ b/docs/team/heze8.adoc
@@ -0,0 +1,179 @@
+= Caleb Goh - Project Portfolio
+:site-section: AboutUs
+:imagesDir: ../images
+:stylesDir: ../stylesheets
+
+== PROJECT: `EzWatchlist`
+
+== 1. Introduction
+The following Project Portfolio documents my contributions to the software project, `EzWatchlist`.
+Which was a project my team of software engineering students and I decided to pursue for our Software Engineering project.
+
+Our team were initially tasked with enhancing a basic command line interface (CLI) desktop application that functioned as an Adressbook.
+What this means is that the application focused on typing as the main mode of use, and had an existing rudimentary base of code already functioning.
+
+Ultimately, we made the decision to modify it into a management application for Movies and Tv Shows named `EzWatchlist`.
+
+=== 1.1 Overview
+
+image::Ui.png[width=600]
+Figure 1. The graphical interface for `EzWatchlist`.
+
+`EzWatchlist` is an application that saves movies and television shows the user adds into a watchlist.
+The user interacts with `EzWatchlist` through typing commands or the graphical interface seen in Figure 1.
+This enhanced application boasts the following features:
+
+. Keeps track of the movies and television shows that they planed to watch.
+. Keeps track of the movies and television shows that they have watched.
+. Allows them to update the episodes they are at each television show.
+. Gets information about their watching habits.
+. Gets recommendations for movies and television shows based on their personal taste.
+
+
+[NOTE]
+The following symbols, abbreviations and formatting used in this document:
+
+|===
+| *API - Application Programming Interface* | An interface as a intermediary to use other programs or software.
+| *`Highlighted words`* | Our application related components such as commands, classes, methods, e.t.c.
+| image:keyboard.png[] | Keyboard characters on the desktop.
+| https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests[#number] | Pull request links based on their serial number.
+|===
+
+The following sections illustrate these enhancements in more detail, as well as the relevant documentation
+I have added to the user and developer guides in relation to these enhancements.
+
+== 2. Summary of contributions
+This section shows a summary of my role in the project, my technical ability to code, document, and other beneficial contributions to the
+team project.
+
+=== Role
+
+My main technical role was to design and write the codes for the integration with the application with an online database of movies and tv shows.
+This was highly integral due to the nature and vision of the application where information about movies and tv shows
+should be readily available to the user.
+
+Moreover, I took the supervising role of team lead in this project.
+I made sure that the team was well coordinated and that we were all clear on the overall vision of the application at every point in the development.
+Ensuring clear communication throughout the team, so that we all knew what to do. And that issues that arrived were handled smoothly and cleanly.
+
+=== *Major enhancement*:
+Added the ability for the application to integrate with an *online database of movies and tv shows*.
+
+*What it does*:
+
+. Allows the user to see images in the application.
+. Retrieve information about movies and tv shows.
+. Search for movies and tv shows from a large up-to-date database.
+. Access reviews, ratings, recommendations, collections of movies and tv shows, genre information, and much more.
+
+*Justification*:
+
+This feature improves the product significantly because a user should be encouraged to use the application by providing
+appealing images, comprehensive information and up-to-date data.
+
+*Highlights*:
+
+This feature required a robust knowledge on how to set up third party libraries in a Java application.
+Json conversion (Online data retrieval) is also another key element when considering the API used.
+
+Moreover, I had to integrate this external data with our own internal systems. Meaning my teammates had to use my implementations easily. Hence, data handling and conversion had to be designed in an understandable manner.
+
+*Credits*:
+
+We are using The Movie Database (TMDB) API to retrieve information on movies and tv shows. As well as
+using a java wrapper for the TMDB api implemented by Holger Brandl.
+
+
+=== *Minor enhancement*:
+
+Added ability to *store and retrieve images* in the application.
+
+** Justification: This feature improves the visual interface, and provides movies and tv shows a more potent identity compared to just words.
+** Highlights: A lot of consideration had to
+be placed in the retrieval and presentation of images. I had to integrate the image portrayal with the `JavaFX`-based interface,
+consider how to store images and retrieve them when needed.
+
+=== *Minor enhancement*:
+
+Added ability to get *recommendations* based on the user's personal movies and tv shows.
+
+** Justification: This feature improves the product because a user can gain the added benefit of getting more suggestions for Movies and Tv Shows, which
+makes the application more robust. Moreover, it improves the value of the application encouraging usage of the application.
+** Highlights: This design for this enhancement had considerable thought into the efficiency of this feature due to the large amount of processing required from parsing the user's data. There is also the subjective manner of knowing what to recommend from a large list.
+** Credits: The Movie Database (TMDB) API
+
+
+*Code contributed*:
+
+All of my contributions to the application can be found in the following link: [https://nus-cs2103-ay1920s1.github.io/tp-dashboard/#search=heze8&sort=groupTitle&sortWithin=title&since=2019-09-06&timeframe=commit&mergegroup=false&groupSelect=groupByRepos&breakdown=false[My Code Contribution]]
+
+
+*Other contributions*:
+
+Project management:
+
+*** I am the Project lead, thus my job consisted of making sure the team coordinated, the overall vision of the application was reached, and handling all the issues that arose in the development.
+
+*** Managed releases `v1.1` - `v1.4` (3 releases) on GitHub
+
+*** Managed and assigned the issues, milestones and project board on Github.
+
+Enhancements to existing features:
+
+*** Updated the application's model to integrate seamlessly with the external library's data. This meant that each internal model class had to be error-friendly due to the unpredictable manner of the online database.
+*** Added more internal classes such as `Poster` and `Genre` to be used in the application. (Commits https://github.com/AY1920S1-CS2103T-F13-4/main/commit/f9e7b69e36d8404e79107bc94d04670321d71aa3[Poster] and https://github.com/AY1920S1-CS2103T-F13-4/main/commit/e07c06d1fe2420db5163956c63d92cbf9cfbad5c[Genre])
+*** Wrote additional tests to increase coverage.
+*** Helped to integrate the API code with the rest of the application.
+
+Documentation:
+
+*** Did cosmetic tweaks to our website. https://github.com/AY1920S1-CS2103T-F13-4/main/pull/224[Pull request #224]
+
+Community:
+
+*** Reviewed Pull Request : https://github.com/AY1920S1-CS2103T-F13-4/main/pull/107[#107], https://github.com/AY1920S1-CS2103T-F13-4/main/pull/30[#30], https://github.com/AY1920S1-CS2103T-F13-4/main/pull/73[#73], https://github.com/AY1920S1-CS2103T-F13-4/main/pull/51[#51], https://github.com/AY1920S1-CS2103T-F13-4/main/pull/78[#78]
+*** Reported bugs and suggestions for other teams in the class: https://github.com/AY1920S1-CS2103T-T11-3/main/issues[T11-3 team], https://github.com/nus-cs2103-AY1920S1/addressbook-level3/pull/20[F12-4 team]
+
+Tools:
+
+*** Integrated a third party library (TMDB) to the project (https://www.themoviedb.org/documentation/api[TMDB Api])
+*** Integrated a new Github plugin (Java wrapper) to the team repo.
+*** Added a successfully merged pull request to the Java Wrapper we are using in our application to fix their issue of not supporting recommendations. (https://github.com/holgerbrandl/themoviedbapi/pull/111[Pull request merged])
+
+
+== 3. Contributions to the User Guide
+Our `EzWatchlist` user guide consists of instructions to the user on how to use our application.
+It displays my ability to document in an engaging and readable format.
+
+I wrote the introduction to ease the user into the main features of the application, breaking it down to navigation and interaction with our program.
+
+|===
+|_The following is an excerpt from our `EzWatchlist` User Guide, showing additions that I have
+made for the Introduction. The full introduction can be found https://ay1920s1-cs2103t-f13-4.github.io/main/UserGuide.html#introduction[here]:_
+
+|===
+include::../UserGuide.adoc[tag=intro]
+
+___
+
+|===
+|_Moreover I wrote the Advanced Features portion of the user guide, available https://ay1920s1-cs2103t-f13-4.github.io/main/UserGuide.html#advanced-features[here]._
+|===
+
+== Contributions to the Developer Guide
+This sections showcases my contribution to the `EzWatchlist` Developer Guide, which serve to document the implementation of our features to technical users. It demonstrates my capability to use diagrams
+and illustrate technical details.
+
+|===
+|_The following are sections for the Api Component I wrote to introduce the design architecture in the application._
+|===
+
+include::../DeveloperGuide.adoc[tag=apicomponent]
+
+|===
+|_Moreover, I also contributed the description of my implementation of the Api Model classes, Image Retrieval, and generation of Recommendations. The following is only an excerpt of my design considerations. The full portion can be found https://ay1920s1-cs2103t-f13-4.github.io/main/DeveloperGuide.html#online-data[here]_
+|===
+
+include::../DeveloperGuide.adoc[tag=apiimpl]
+
diff --git a/docs/team/heze8.pdf b/docs/team/heze8.pdf
new file mode 100644
index 00000000000..97ab3143c33
Binary files /dev/null and b/docs/team/heze8.pdf differ
diff --git a/docs/team/jcjjjared.adoc b/docs/team/jcjjjared.adoc
new file mode 100644
index 00000000000..332f5e4e019
--- /dev/null
+++ b/docs/team/jcjjjared.adoc
@@ -0,0 +1,126 @@
+= Jared Chiang: Project Portfolio Page for EzWatchlist
+:site-section: AboutUs
+:imagesDir: ../images
+:stylesDir: ../stylesheets
+:icons: font
+
+This document serves to show the various contributions that I have made in the development of link:https://ay1920s1-cs2103t-f13-4.github.io/main/index.html[EzWatchlist]
+in a concise and understandable manner.
+
+== More About The Project
+EzWatchlist is a desktop application that allows forgetful users to keep track of movies and tv series that they want to watch or have watched.
+It was developed by a group of 5 students from the National University of Singapore taking the Software Engineering module including
+myself under the premise that we had to either optimize or morph an existing link:https://nus-cs2103-ay1920s1.github.io/addressbook-level3/index.html[AddressBook] application. We chose the latter. Another
+constraint was ensuring that the application has a command-line interface, which means that all commands should be executable through typing.
+The picture below shows the various components of EzWatchlist.
+
+.Graphical User Interface of EzWacthlist
+image::Ui.png[width="790"]
+
+=== Main Features
+* Adding, editing and deleting shows
+* Keeping track of shows and episodes that are watched
+* Online search and syncing of shows
+* Statistics about user's watchlist
+
+=== Role
+I was tasked with ensuring integration of the features of EzWatchlist. This means that I had to ensure that the commands
+work with one another well and perform their expected functions in the respective pages. This was a challenge
+since our application uses 4 different tabs, each with their unique functions and list of shows, so the implementation
+of the commands would vary from tab to tab.
+
+=== Interpreting Symbols
+The table below shows a summary of the symbols used and their respective meanings.
+
+[width="59%",cols="^22%,^50%",options="header",align="centre"]
+|===================================
+|Symbol |Meaning
+
+|`Command` `Component`|Commands and Components of the Project
+
+|icon:lightbulb-o[role="icon-tip", size="2x"]|Tips for the user
+
+|icon:info-circle[role="icon-note", size="2x"] |Additional information
+|===================================
+
+== Summary of Contributions
+
+This section provides an overview of the contributions that I have made to the development of EzWatchlist. It also
+showcases some of my coding and documentation abilities, and the challenges I faced during the development of EzWatchlist.
+
+=== Major Contributions
+
+==== Implemented the `watch` feature
+
+*What it does*
+
+The `watch` feature allows users to update their watch list if a show has been watched.
+It also helps users to keep track of where they are in a TV series in terms
+of seasons and episodes.
+
+*Justification*
+
+The `watch` feature enables users to keep track of shows that they have and have not watched so that they can plan
+out what they want to watch in the future.
+
+*Highlights*
+
+This enhancement resulted in a need to change the storage structure of the shows to include the episodes and tv seasons.
+Its implementation was a challenge since it required structural change of the show objects, splitting them into movies and tv shows.
+The addition of the clickable checkbox to mark or unmark shows also increased the difficulty of the implementation.
+
+=== Minor Contributions
+
+* Separated the the watchlist into different sections (not watched and watched).
+* Changed the storage structure of the shows to save them as either movies or tv shows. This feature was
+implemented with the intention of possibly retrieving season and episode details future versions.
+
+=== Code Contributions
+View my code contributions link:https://nus-cs2103-ay1920s1.github.io/tp-dashboard/#search=jcjjjared&sort=groupTitle&sortWithin=title&since=2019-09-06&timeframe=commit&mergegroup=false&groupSelect=groupByRepos&breakdown=false[here].
+
+=== Other Contributions
+
+[NOTE]
+*#Numbers* represent pull requests to the mentioned contributions.
+These links show the code that has been contributed to the team project
+
+*Project Management*
+
+* Provided the team with information about the tasks that need to be completed
+* Assigned tasks link:https://github.com/AY1920S1-CS2103T-F13-4/main/issues/25[1], link:https://github.com/AY1920S1-CS2103T-F13-4/main/issues/42[2]
+
+*Enhancements to Features:*
+
+* Improved `add` command by reducing amount of information needed to be entered link:https://github.com/AY1920S1-CS2103T-F13-4/main/pull/138[#138]
+* Changed storage structure to save shows as movies or tv shows link:https://github.com/AY1920S1-CS2103T-F13-4/main/pull/117[#117]
+* Writing of various tests for EzWatchlist to reduce the possibility of bugs when using EzWatchlist link:https://github.com/AY1920S1-CS2103T-F13-4/main/pull/132[#231], link:https://github.com/AY1920S1-CS2103T-F13-4/main/pull/237[#237]
+
+*Documentation:*
+
+* Improved the structure of the user guide link:https://github.com/AY1920S1-CS2103T-F13-4/main/pull/15[#15], link:https://github.com/AY1920S1-CS2103T-F13-4/main/pull/214[#214]
+* Updated the developer guide link:https://github.com/AY1920S1-CS2103T-F13-4/main/pull/241[#241]
+
+*Community:*
+
+* Reviewed team pull requests link:https://github.com/AY1920S1-CS2103T-F13-4/main/pull/77[#74], link:https://github.com/AY1920S1-CS2103T-F13-4/main/pull/81[#81], https://github.com/AY1920S1-CS2103T-F13-4/main/pull/84[#84]
+* Reported bugs and suggestions for other teams in the class (examples: link:https://github.com/jcjjjared/ped/issues/1[1], link:https://github.com/jcjjjared/ped/issues/2[2], link:https://github.com/jcjjjared/ped/issues/4[3])
+
+*Tools:*
+
+* Set up Travis Continuous Integration
+
+== Contributions to User Guide
+
+This section serves to showcase my contributions to the User Guide, as well as my ability to write
+documentation for users in a concise and understandable manner. It also shows my ability to use asciidoc and
+markup language for formatting.
+
+include::../UserGuide.adoc[tag=watch]
+
+== Contributions to Developer Guide
+
+This sections showcases my contribution to the EzWatchlist Developer Guide. It exhibits my ability to use diagrams
+and technical terms to inform other developers of how the features were implemented and the possibility
+for enhancements.
+
+include::../DeveloperGuide.adoc[tag=markaswatched]
diff --git a/docs/team/jcjjjared.pdf b/docs/team/jcjjjared.pdf
new file mode 100644
index 00000000000..df71ecae8a6
Binary files /dev/null and b/docs/team/jcjjjared.pdf differ
diff --git a/docs/team/michelleykw.adoc b/docs/team/michelleykw.adoc
new file mode 100644
index 00000000000..8ddbd16b34b
--- /dev/null
+++ b/docs/team/michelleykw.adoc
@@ -0,0 +1,94 @@
+= Michelle Yong Kai Wen: Project Portfolio for EzWatchList
+:site-section: AboutUs
+:imagesDir: ../images
+:stylesDir: ../stylesheets
+:icons: font
+
+:xrefstyle: full
+:experimental:
+ifdef::env-github[]
+:tip-caption: :bulb:
+:note-caption: :information_source:
+endif::[]
+
+== 1. Introduction
+
+The purpose of this portfolio is to document my roles and contributions to the project, in terms of the code, the User
+Guide and the Developer Guide.
+
+In a team of 5 software engineering students, we were tasked to enhance an existing desktop application,
+link:https://nus-cs2103-ay1920s1.github.io/addressbook-level3/index.html[AddressBook], for our software
+engineering project. We chose to morph it into a watchlist for movie and television series called
+link:https://ay1920s1-cs2103t-f13-4.github.io/main/index.html[EzWatchlist].
+
+=== 1.1. About EzWatchlist
+
+EzWatchList is a Command Line Interface desktop application, which means that it is designed for those who types fast
+and prefer to execute commands through typing. It helps users, especially those who are forgetful, to keep track of the
+shows they had watched or wish to watch, reducing the hassle of having to search for shows frequently.
+
+.Graphical User Interface of EzWatchlist
+image::Ui.png[width="500"]
+
+==== 1.2.1. Main Features
+* Add, edit and delete shows
+* Search online and offline
+* Statistics
+
+=== 1.3. Interpreting Symbols
+The table below shows a summary of the symbols used and their respective meanings
+[width="80%",cols="^22%,^78%",options="header",align="centre"]
+|===================================
+|Symbol |Meaning
+|`Commands`|Commands input to the command line of EzWatchList
+|icon:lightbulb-o[role="icon-tip", size="2x"]|Tips for the user
+|icon:info-circle[role="icon-note", size="2x"] |Additional information
+|kbd:[3] |Keyboard input to EzWatchList
+|===================================
+
+=== 1.4. Role
+
+My role was to implement the `search` feature and ensure proper documentation of the code. I had to implement the
+`search` feature such that users would be able to search when online and offline. The
+code had to be refactored and the `search` command had to be modified to allow users to search for different
+categories such as searching by show name, actor or genre.
+
+== 2. Summary of Contributions
+This section shows a summary of my coding, documentation, and other helpful contributions to the team project. +
+
+=== 2.1. Major Contributions
+*Enhancement added: Implemented the `Search` command* +
+
+* *What it does*: The `search` feature allows users to search for shows, from the online database, offline database, their watchlist or watched-list.
+Users can search for shows based on the show name, genres or actors and filter based on the type and where the show is found at.
+* *Justification*:The `search` feature enables users to find new shows online easily or check if they had watched a certain show.
+* *Highlights*: This enhancement requires a change on how `SearchCommand` process the search method and additional integration with the API.
+* *Code contribution*: View my code contributions link:https://nus-cs2103-ay1920s1.github.io/tp-dashboard/#search=michelleykw&sort=groupTitle&sortWithin=title&since=2019-09-06&timeframe=commit&mergegroup=false&groupSelect=groupByRepos&breakdown=false[here].
+
+==== 2.2. Other contributions:
+* *Project Management*: Assigned issues
+link:https://github.com/AY1920S1-CS2103T-F13-4/main/issues/39[#39]
+link:https://github.com/AY1920S1-CS2103T-F13-4/main/issues/59[#59]
+link:https://github.com/AY1920S1-CS2103T-F13-4/main/issues/171[#171]
+* *Enhancements to existing features*: Implemented an internal storage
+link:https://github.com/AY1920S1-CS2103T-F13-4/main/pull/246[#246]
+* *Documentation*: Updated the User Guide and Developer Guide
+link:https://github.com/AY1920S1-CS2103T-F13-4/main/pull/118[#118]
+* *Community*: Reviewed team pull requests
+link:https://github.com/AY1920S1-CS2103T-F13-4/main/pull/30[#30]
+
+== 3. Contributions to the User Guide
+This section showcase my contributions to the EzWatchlist User Guide and my ability to write documentations to teach
+users on how use EzWatchlist.
+
+include::../UserGuide.adoc[tag=search]
+
+{sp} +
+{sp} +
+{sp} +
+
+== 4. Contributions to the Developer Guide
+This section showcase my contributions to the EzWatchlist Developer Guide and my ability to use diagrams and technical
+terms to inform other developers on the features and implementation of EzWatchlist.
+
+include::../DeveloperGuide.adoc[tag=search]
diff --git a/docs/team/tswuxia.adoc b/docs/team/tswuxia.adoc
new file mode 100644
index 00000000000..0c04468c271
--- /dev/null
+++ b/docs/team/tswuxia.adoc
@@ -0,0 +1,80 @@
+= Wu Xia - Project Portfolio
+:site-section: AboutUs
+:imagesDir: ../images
+:stylesDir: ../stylesheets
+
+== PROJECT: `EzWatchlist`
+
+---
+
+This document is to document my contributions to the desktop application `EzWatchlist`.
+
+== Overview
+
+EzWatchlist is a desktop application where the user can systematically record down what movie/TV series he/she wants to
+watch or has watched. It is developed by a group of 5 NUS Computer Science students for the software engineering course
+based on the Addressbook level 3 codebase provided. One specification is that the user prefers command line interface
+(CLI), hence the CLI in the application. However, we also implemented a graphical user interface (GUI) created with
+JavaFX. The project is written in Java.
+
+== Summary of contributions
+
+=== *Major enhancement*:
+
+Changed the *GUI* to better suit EzWatchlist
+
+* *What it does:*
+** Structures the features into 4 tabs and links them together with the main logic which allows the user to easily
+navigate around the tabs and execute commands.
+** Ensures that each visible component is displayed with an appropriate size, appearance, space and position.
+** Enhances user experience through user-friendly layout and placeholder text and loading signs to keep the user informed.
+** Enhances user experience through a creative theme.
+
+* *Justification:*
+** This feature improves the product significantly because a user would like to use an application that is user friendly
+and has an appealing appearance.
+
+* *Highlights:*
+** This enhancement requires deep understanding of both the implementation of the application itself and also how to use
+javafx flexibly in order to effectively communicate between the user and the application and present the message in a
+desirable manner. It also requires repetitive adjustments and testing of the appearance of the elements to produce a
+final result.
+
+
+=== *Minor enhancement*:
+
+Added a `Statistics` feature that reflects the user's personal preferences.
+
+=== *Code contributed*:
+Code contributed can be found here: [https://nus-cs2103-ay1920s1.github.io/tp-dashboard/#search=tswuxia&sort=groupTitle&sortWithin=title&since=2019-09-06&timeframe=commit&mergegroup=false&groupSelect=groupByRepos&breakdown=false&tabOpen=true&tabType=authorship&tabAuthor=tswuxia&tabRepo=AY1920S1-CS2103T-F13-4%2Fmain%5Bmaster%5D[My Code Contribution]]
+
+=== *Other contributions*:
+
+** Enhancements to existing features:
+*** Updated the GUI color scheme (Pull request https://github.com/AY1920S1-CS2103T-F13-4/main/pull/123[#123])
+*** Reported bugs and suggestions for other teams in the class (examples: https://github.com/tswuxia/ped/issues/7[1],
+https://github.com/tswuxia/ped/issues/6[2], https://github.com/tswuxia/ped/issues/5[3], https://github.com/tswuxia/ped/issues/4[4],
+https://github.com/tswuxia/ped/issues/3[5], https://github.com/tswuxia/ped/issues/2[6], https://github.com/tswuxia/ped/issues/1[7])
+
+== Contributions to the User Guide
+|===
+|_Given below are sections I contributed to the User Guide. They showcase my ability to write user documentation targeting normal
+application users in a clear and understandable manner._
+|===
+
+include::../UserGuide.adoc[tag=UI]
+
+== Contributions to the Developer Guide
+
+|===
+|_Given below are sections I contributed to the Developer Guide. They showcase my ability to write technical documentation
+and the technical depth of my contributions to the project._
+|===
+
+include::../DeveloperGuide.adoc[tag=statistics]
+
+== PROJECT: PowerPointLabs
+
+---
+
+_{Optionally, you may include other projects in your portfolio.}_
diff --git a/docs/team/tswuxia.pdf b/docs/team/tswuxia.pdf
new file mode 100644
index 00000000000..8eeae5223c7
Binary files /dev/null and b/docs/team/tswuxia.pdf differ
diff --git a/docs/team/wongchuankai.adoc b/docs/team/wongchuankai.adoc
new file mode 100644
index 00000000000..e27e2b8634d
--- /dev/null
+++ b/docs/team/wongchuankai.adoc
@@ -0,0 +1,200 @@
+= Wong Chuan Kai - Project Portfolio
+:site-section: AboutUs
+:imagesDir: ../images
+:stylesDir: ../stylesheets
+:icons: font
+
+== PROJECT: `EzWatchList`
+
+
+== 1. Introduction
+
+The purpose of the Project Portfolio is to document and showcase my contributions to the software project, link:https://ay1920s1-cs2103t-f13-4.github.io/main/index.html[EzWatchList]
+
+Our team was initially tasked with enhancing a basic command line interface(CLI) desktop application
+for our Software Engineering project. We are also allowed to morph it to other application that uses command line interface.
+Thus, we chose to morph it into a movie records management system called EzWatchList. This enhanced
+version provides a unique, clean and simple way of organizing and keeping track of movie or TV show watch list.
+
+=== 1.1 Overview
+
+image::CK_PPP.png[width="500"]
+_Figure 1. The graphical interface for Ezwatchlist._
+
+`EzWatchList` helps users to keep track and organise movie or TV show in a watch list. User can simply
+interact with the application by inputting commands into the interface.
+
+Main features of EzWatchList:
+
+- Keep track of shows that users plan to watch
+- Allows users to edit and mark down shows that they have watched
+- Allows users to search for show online using `search` online function or search within user's watch list.
+- Gives users statistics about their watching habits and recommend shows to them.
+
+The following sections illustrate these enhancements in more detail, as well as the relevant documentation
+I have added to the user and developer guides in relation to these enhancements.
+
+=== Interpreting Symbols
+The table below shows a summary of the symbols used and their respective meanings.
+
+[width="59%",cols="^22%,^50%",options="header",align="centre"]
+|===================================
+|Symbol |Meaning
+
+|`Command` `Component`|Commands and Components of the Project
+
+|icon:lightbulb-o[role="icon-tip", size="2x"]|Tips for the user
+
+|icon:info-circle[role="icon-note", size="2x"] |Additional information
+|===================================
+
+== 2. Summary of contributions
+|===
+|_This section shows a summary of my challenges, coding, documentation, and other helpful contributions to the team project._
+|===
+
+My role was to design and implement the `Add` and `Sync` features. These commands are essential to the application as they are the
+building block of the application. Before I can design these features, I have to understand
+the application requirement first such as functional and non-function requirements. I also have to know who my target audience
+is and study the use-cases properly to plan out and design these features.
+
+=== 2.1 Major enhancement:
+
+==== *Added `add` and synchronise, `sync`, commands.*
+
+*1. Modifying and extension of `add` command*
+
+*What it does*:
+
+There are two separate functions for `Add` command.
+First, it allows the user to add movies or TV shows into the watch list.
+User has to input several information of the show such as name, description, and
+name of actors.
+
+Beside this core function, the add command also allows user to add a movie found in search
+result page after user has used the `search` online feature. Search page displays the search result of the movies the user
+is interested to watch. The `search` feature is implemented by my team mate, Michelle.
+
+*Justification*:
+
+The purpose of this application is to allow user to track movie list. Thus, adding shows into watch list is a
+fundamental and core function that runs the application. If the user wants to know more about a certain movie, he can
+simply use the `search` function to search for it. The `search` function is able to gather information about movies online
+and user can choose to ‘add’ a certain movie from the search list into his own watch list.
+
+*Highlights*:
+
+The addition of `add` command requires some understanding of both the implementation of the application and how to retrieve
+the data from 3rd party API(TMDB).
+
+*Credits*:
+
+Since we are retrieving information on shows online, we have decided to use The Movie Database (TMDB) api.
+
+
+*2. Synchronise, Sync, command*
+
+*What it does*:
+
+User may not have updated information about the show they want to add. They can use the `search` function to check the
+details of the show they are interested to watch. Then, they are able to use the `sync` to synchronise a show found in
+search page to their show which has already been added in watch list.
+
+*Justification*:
+
+User will definitely not know most of the information about a show. Hence, by implementing this `sync` function, users
+will then be able to get updated information about the show.
+
+*Highlights*:
+
+This enhancement requires some understanding of both the implementation of the application and how to retrieve
+the data from 3rd party API(TMDB).
+
+
+*Credits*:
+
+Since we are retrieving information on shows online, we decided to use The Movie Database (TMDB) api.
+
+=== *2.2 Minor enhancement:*
+
+==== Added shortcut keys to move about pages easily.
+
+*What it does*:
+
+Users are able to press a single shortcut key button to move about the panels such as ‘Watchlist’, ‘Watched’, ‘Search’
+and ‘Statistics’ without moving and clicking mouse.
+
+*Justification*:
+
+Our target user is one who prefers to type fast and complete tasks quickly. One of the non-functional requirement is to
+reduce the usage of the mouse as this is mainly a command line application. Adding shortcut keys will reduce the
+usage of the mouse.
+
+*Highlights*:
+
+This addition make it a lot easier for user to navigate through the different pages.
+
+//==== Added multi threading when `search` function is used.
+
+*Code contributed*:
+
+My contributions to EzWatchList can be found in the following link:[https://nus-cs2103-ay1920s1.github.io/tp-dashboard/#search=wongchuankai&sort=groupTitle&sortWithin=title&since=2019-09-06&timeframe=commit&mergegroup=false&groupSelect=groupByRepos&breakdown=false[My Code Contribution]]
+[https://github.com[Test code]]
+
+=== *2.3 Other contributions:*
+
+*Project management*:
+
+*** I am in charged of testing, so my responsibility is to plan, understand and write test codes for this project.
+
+*** My job is to understand the component logic as well so I am analyse the problems faced better.
+
+*Enhancements to existing features* :
+
+*** I have added multi-threading when the user uses `search` command. Since, it might take some time to load the data,
+multi-threading allows the application to process these data in the background. Users will then be able to do other task
+in the application like using `add` command as well.
+
+*** I have added shortcut keys so users are able to navigate the pages easily.
+
+*** Since I am in charged of the unit testing, I have added test cases to improve test coverage.
+
+*Documentation*:
+
+* Improved the structure of the user guide link:https://github.com/AY1920S1-CS2103T-F13-4/main/pull/258[#258], link:https://github.com/AY1920S1-CS2103T-F13-4/main/pull/279[#279]
+* Updated the developer guide link:https://github.com/AY1920S1-CS2103T-F13-4/main/pull/11[#11], link:https://github.com/AY1920S1-CS2103T-F13-4/main/pull/258[#258]
+
+
+*Community*:
+
+* Reported bugs and suggestions for other teams in the class (examples: link:https://github.com/wongchuankai/ped/blob/master/files/fceb38f2-23e3-4f5e-a7a7-42afce9258a8.png[1], link:https://github.com/wongchuankai/ped/blob/master/files/f0a21388-54e5-49b2-9cfd-efb47c514667.png[2], link:https://github.com/wongchuankai/ped/blob/master/files/bf312b96-02eb-47e5-a170-7946d1f7f100.png[3])
+
+*Tools*:
+
+*** Integrated a third party library (TMDB) to the project (https://www.themoviedb.org/documentation/api[TMDB Api])
+
+
+== 3. Contributions to the User Guide
+|===
+|_This section is an excerpt from our EzWatchList User Guide, showing additions that I have made for the sync
+features. They showcase my ability to write documentation targeting end-users._
+|===
+
+include::../UserGuide.adoc[tag=sync]
+
+
+== Contributions to the Developer Guide
+
+|===
+|_Given below are sections I contributed to the Developer Guide, showing additions that I have made for the add and sync
+features. They showcase my ability to write technical documentation
+and the technical depth of my software engineering skills to the project._
+|===
+
+include::../DeveloperGuide.adoc[tag=add2]
+include::../DeveloperGuide.adoc[tag=sync]
+
+
+
+---
+
diff --git a/docs/team/wongchuankai.pdf b/docs/team/wongchuankai.pdf
new file mode 100644
index 00000000000..981edcce3fd
Binary files /dev/null and b/docs/team/wongchuankai.pdf differ
diff --git a/docs/templates/_header.html.slim b/docs/templates/_header.html.slim
index 3c2d5aed43c..8d840758c99 100644
--- a/docs/templates/_header.html.slim
+++ b/docs/templates/_header.html.slim
@@ -1,26 +1,4 @@
/ NOTE: You must restart the gradle daemon after modifying any template file for the changes to take effect.
-- if !(attr? 'no-site-header') && (attr? 'site-seedu')
- #seedu-header
- nav.navbar.navbar-lg.navbar-light.bg-lighter
- .container
- a.navbar-brand href='https://se-edu.github.io/'
- img src=(site_url 'images/SeEduLogo.png') alt='SE-EDU'
- ul.navbar-nav
- li.nav-item
- a.nav-link href='https://se-edu.github.io/addressbook-level1' AB-1
- li.nav-item
- a.nav-link href='https://se-edu.github.io/addressbook-level2' AB-2
- li.nav-item
- a.nav-link.active href=(site_url 'index.html') AB-3
- li.nav-item
- a.nav-link href='https://se-edu.github.io/addressbook-level4' AB-4
- li.nav-item
- a.nav-link href='https://se-edu.github.io/collate' Collate
- li.nav-item
- a.nav-link href='https://se-edu.github.io/se-book' Book
- li.nav-item
- a.nav-link href='https://se-edu.github.io/learningresources' Resources
-
- if !(attr? 'no-site-header')
#site-header
nav.navbar.navbar-light.bg-light
diff --git a/src/main/java/seedu/address/commons/core/Messages.java b/src/main/java/seedu/address/commons/core/Messages.java
deleted file mode 100644
index 1deb3a1e469..00000000000
--- a/src/main/java/seedu/address/commons/core/Messages.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package seedu.address.commons.core;
-
-/**
- * Container for user visible messages.
- */
-public class Messages {
-
- public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command";
- public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s";
- public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "The person index provided is invalid";
- public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!";
-
-}
diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/address/logic/Logic.java
deleted file mode 100644
index 92cd8fa605a..00000000000
--- a/src/main/java/seedu/address/logic/Logic.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package seedu.address.logic;
-
-import java.nio.file.Path;
-
-import javafx.collections.ObservableList;
-import seedu.address.commons.core.GuiSettings;
-import seedu.address.logic.commands.CommandResult;
-import seedu.address.logic.commands.exceptions.CommandException;
-import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.ReadOnlyAddressBook;
-import seedu.address.model.person.Person;
-
-/**
- * API of the Logic component
- */
-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;
-
- /**
- * Returns the AddressBook.
- *
- * @see seedu.address.model.Model#getAddressBook()
- */
- ReadOnlyAddressBook getAddressBook();
-
- /** Returns an unmodifiable view of the filtered list of persons */
- ObservableList getFilteredPersonList();
-
- /**
- * Returns the user prefs' address book file path.
- */
- Path getAddressBookFilePath();
-
- /**
- * Returns the user prefs' GUI settings.
- */
- GuiSettings getGuiSettings();
-
- /**
- * Set 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
deleted file mode 100644
index d47ce874b1a..00000000000
--- a/src/main/java/seedu/address/logic/LogicManager.java
+++ /dev/null
@@ -1,78 +0,0 @@
-package seedu.address.logic;
-
-import java.io.IOException;
-import java.nio.file.Path;
-import java.util.logging.Logger;
-
-import javafx.collections.ObservableList;
-import seedu.address.commons.core.GuiSettings;
-import seedu.address.commons.core.LogsCenter;
-import seedu.address.logic.commands.Command;
-import seedu.address.logic.commands.CommandResult;
-import seedu.address.logic.commands.exceptions.CommandException;
-import seedu.address.logic.parser.AddressBookParser;
-import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.Model;
-import seedu.address.model.ReadOnlyAddressBook;
-import seedu.address.model.person.Person;
-import seedu.address.storage.Storage;
-
-/**
- * The main LogicManager of the app.
- */
-public class LogicManager implements Logic {
- public static final String FILE_OPS_ERROR_MESSAGE = "Could not save data to file: ";
- private final Logger logger = LogsCenter.getLogger(LogicManager.class);
-
- private final Model model;
- private final Storage storage;
- private final AddressBookParser addressBookParser;
-
- public LogicManager(Model model, Storage storage) {
- this.model = model;
- this.storage = storage;
- addressBookParser = new AddressBookParser();
- }
-
- @Override
- public CommandResult execute(String commandText) throws CommandException, ParseException {
- logger.info("----------------[USER COMMAND][" + commandText + "]");
-
- CommandResult commandResult;
- Command command = addressBookParser.parseCommand(commandText);
- commandResult = command.execute(model);
-
- try {
- storage.saveAddressBook(model.getAddressBook());
- } catch (IOException ioe) {
- throw new CommandException(FILE_OPS_ERROR_MESSAGE + ioe, ioe);
- }
-
- return commandResult;
- }
-
- @Override
- public ReadOnlyAddressBook getAddressBook() {
- return model.getAddressBook();
- }
-
- @Override
- public ObservableList getFilteredPersonList() {
- return model.getFilteredPersonList();
- }
-
- @Override
- public Path getAddressBookFilePath() {
- return model.getAddressBookFilePath();
- }
-
- @Override
- public GuiSettings getGuiSettings() {
- return model.getGuiSettings();
- }
-
- @Override
- public void setGuiSettings(GuiSettings guiSettings) {
- model.setGuiSettings(guiSettings);
- }
-}
diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/address/logic/commands/AddCommand.java
deleted file mode 100644
index 71656d7c5c8..00000000000
--- a/src/main/java/seedu/address/logic/commands/AddCommand.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package seedu.address.logic.commands;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
-
-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 static final String COMMAND_WORD = "add";
-
- public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a person to the address book. "
- + "Parameters: "
- + PREFIX_NAME + "NAME "
- + PREFIX_PHONE + "PHONE "
- + PREFIX_EMAIL + "EMAIL "
- + PREFIX_ADDRESS + "ADDRESS "
- + "[" + PREFIX_TAG + "TAG]...\n"
- + "Example: " + COMMAND_WORD + " "
- + PREFIX_NAME + "John Doe "
- + PREFIX_PHONE + "98765432 "
- + PREFIX_EMAIL + "johnd@example.com "
- + PREFIX_ADDRESS + "311, Clementi Ave 2, #02-25 "
- + PREFIX_TAG + "friends "
- + PREFIX_TAG + "owesMoney";
-
- public static final String MESSAGE_SUCCESS = "New person added: %1$s";
- public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book";
-
- private final Person toAdd;
-
- /**
- * Creates an AddCommand to add the specified {@code Person}
- */
- public AddCommand(Person person) {
- requireNonNull(person);
- toAdd = person;
- }
-
- @Override
- public CommandResult execute(Model model) throws CommandException {
- requireNonNull(model);
-
- if (model.hasPerson(toAdd)) {
- throw new CommandException(MESSAGE_DUPLICATE_PERSON);
- }
-
- model.addPerson(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 AddCommand // instanceof handles nulls
- && toAdd.equals(((AddCommand) other).toAdd));
- }
-}
diff --git a/src/main/java/seedu/address/logic/commands/ClearCommand.java b/src/main/java/seedu/address/logic/commands/ClearCommand.java
deleted file mode 100644
index 9c86b1fa6e4..00000000000
--- a/src/main/java/seedu/address/logic/commands/ClearCommand.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package seedu.address.logic.commands;
-
-import static java.util.Objects.requireNonNull;
-
-import seedu.address.model.AddressBook;
-import seedu.address.model.Model;
-
-/**
- * Clears the address book.
- */
-public class ClearCommand extends Command {
-
- public static final String COMMAND_WORD = "clear";
- public static final String MESSAGE_SUCCESS = "Address book has been cleared!";
-
-
- @Override
- public CommandResult execute(Model model) {
- requireNonNull(model);
- model.setAddressBook(new AddressBook());
- return new CommandResult(MESSAGE_SUCCESS);
- }
-}
diff --git a/src/main/java/seedu/address/logic/commands/EditCommand.java b/src/main/java/seedu/address/logic/commands/EditCommand.java
deleted file mode 100644
index 7e36114902f..00000000000
--- a/src/main/java/seedu/address/logic/commands/EditCommand.java
+++ /dev/null
@@ -1,226 +0,0 @@
-package seedu.address.logic.commands;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
-import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Optional;
-import java.util.Set;
-
-import seedu.address.commons.core.Messages;
-import seedu.address.commons.core.index.Index;
-import seedu.address.commons.util.CollectionUtil;
-import seedu.address.logic.commands.exceptions.CommandException;
-import seedu.address.model.Model;
-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;
-
-/**
- * Edits the details of an existing person in the address book.
- */
-public class EditCommand extends Command {
-
- public static final String COMMAND_WORD = "edit";
-
- public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the person identified "
- + "by the index number used in the displayed person list. "
- + "Existing values will be overwritten by the input values.\n"
- + "Parameters: INDEX (must be a positive integer) "
- + "[" + PREFIX_NAME + "NAME] "
- + "[" + PREFIX_PHONE + "PHONE] "
- + "[" + PREFIX_EMAIL + "EMAIL] "
- + "[" + PREFIX_ADDRESS + "ADDRESS] "
- + "[" + PREFIX_TAG + "TAG]...\n"
- + "Example: " + COMMAND_WORD + " 1 "
- + PREFIX_PHONE + "91234567 "
- + PREFIX_EMAIL + "johndoe@example.com";
-
- public static final String MESSAGE_EDIT_PERSON_SUCCESS = "Edited Person: %1$s";
- public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided.";
- public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book.";
-
- private final Index index;
- private final EditPersonDescriptor editPersonDescriptor;
-
- /**
- * @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) {
- requireNonNull(index);
- requireNonNull(editPersonDescriptor);
-
- this.index = index;
- this.editPersonDescriptor = new EditPersonDescriptor(editPersonDescriptor);
- }
-
- @Override
- public CommandResult execute(Model model) throws CommandException {
- requireNonNull(model);
- List lastShownList = model.getFilteredPersonList();
-
- if (index.getZeroBased() >= lastShownList.size()) {
- throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
- }
-
- Person personToEdit = lastShownList.get(index.getZeroBased());
- Person editedPerson = createEditedPerson(personToEdit, editPersonDescriptor);
-
- if (!personToEdit.isSamePerson(editedPerson) && model.hasPerson(editedPerson)) {
- throw new CommandException(MESSAGE_DUPLICATE_PERSON);
- }
-
- model.setPerson(personToEdit, editedPerson);
- model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
- 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
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof EditCommand)) {
- return false;
- }
-
- // state check
- EditCommand e = (EditCommand) other;
- return index.equals(e.index)
- && editPersonDescriptor.equals(e.editPersonDescriptor);
- }
-
- /**
- * Stores the details to edit the person with. Each non-empty field value will replace the
- * corresponding field value of the person.
- */
- public static class EditPersonDescriptor {
- private Name name;
- private Phone phone;
- private Email email;
- private Address address;
- private Set tags;
-
- public EditPersonDescriptor() {}
-
- /**
- * Copy constructor.
- * A defensive copy of {@code tags} is used internally.
- */
- public EditPersonDescriptor(EditPersonDescriptor toCopy) {
- setName(toCopy.name);
- setPhone(toCopy.phone);
- setEmail(toCopy.email);
- setAddress(toCopy.address);
- setTags(toCopy.tags);
- }
-
- /**
- * Returns true if at least one field is edited.
- */
- public boolean isAnyFieldEdited() {
- return CollectionUtil.isAnyNonNull(name, phone, email, address, tags);
- }
-
- public void setName(Name name) {
- this.name = name;
- }
-
- public Optional getName() {
- return Optional.ofNullable(name);
- }
-
- public void setPhone(Phone phone) {
- this.phone = phone;
- }
-
- public Optional getPhone() {
- return Optional.ofNullable(phone);
- }
-
- public void setEmail(Email email) {
- this.email = email;
- }
-
- public Optional getEmail() {
- return Optional.ofNullable(email);
- }
-
- public void setAddress(Address address) {
- this.address = address;
- }
-
- 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;
- }
-
- /**
- * Returns an unmodifiable tag set, which throws {@code UnsupportedOperationException}
- * if modification is attempted.
- * Returns {@code Optional#empty()} if {@code tags} is null.
- */
- public Optional> getTags() {
- return (tags != null) ? Optional.of(Collections.unmodifiableSet(tags)) : Optional.empty();
- }
-
- @Override
- public boolean equals(Object other) {
- // short circuit if same object
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof EditPersonDescriptor)) {
- return false;
- }
-
- // state check
- EditPersonDescriptor e = (EditPersonDescriptor) other;
-
- return getName().equals(e.getName())
- && getPhone().equals(e.getPhone())
- && getEmail().equals(e.getEmail())
- && getAddress().equals(e.getAddress())
- && getTags().equals(e.getTags());
- }
- }
-}
diff --git a/src/main/java/seedu/address/logic/commands/FindCommand.java b/src/main/java/seedu/address/logic/commands/FindCommand.java
deleted file mode 100644
index d6b19b0a0de..00000000000
--- a/src/main/java/seedu/address/logic/commands/FindCommand.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package seedu.address.logic.commands;
-
-import static java.util.Objects.requireNonNull;
-
-import seedu.address.commons.core.Messages;
-import seedu.address.model.Model;
-import seedu.address.model.person.NameContainsKeywordsPredicate;
-
-/**
- * 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 static final String COMMAND_WORD = "find";
-
- public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all persons whose names contain any of "
- + "the specified keywords (case-insensitive) and displays them as a list with index numbers.\n"
- + "Parameters: KEYWORD [MORE_KEYWORDS]...\n"
- + "Example: " + COMMAND_WORD + " alice bob charlie";
-
- private final NameContainsKeywordsPredicate predicate;
-
- public FindCommand(NameContainsKeywordsPredicate predicate) {
- this.predicate = predicate;
- }
-
- @Override
- public CommandResult execute(Model model) {
- requireNonNull(model);
- model.updateFilteredPersonList(predicate);
- return new CommandResult(
- String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredPersonList().size()));
- }
-
- @Override
- public boolean equals(Object other) {
- return other == this // short circuit if same object
- || (other instanceof FindCommand // instanceof handles nulls
- && predicate.equals(((FindCommand) other).predicate)); // state check
- }
-}
diff --git a/src/main/java/seedu/address/logic/commands/ListCommand.java b/src/main/java/seedu/address/logic/commands/ListCommand.java
deleted file mode 100644
index 84be6ad2596..00000000000
--- a/src/main/java/seedu/address/logic/commands/ListCommand.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package seedu.address.logic.commands;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
-
-import seedu.address.model.Model;
-
-/**
- * Lists all persons in the address book to the user.
- */
-public class ListCommand extends Command {
-
- public static final String COMMAND_WORD = "list";
-
- public static final String MESSAGE_SUCCESS = "Listed all persons";
-
-
- @Override
- public CommandResult execute(Model model) {
- requireNonNull(model);
- model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
- return new CommandResult(MESSAGE_SUCCESS);
- }
-}
diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/seedu/address/logic/parser/AddCommandParser.java
deleted file mode 100644
index 3b8bfa035e8..00000000000
--- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package seedu.address.logic.parser;
-
-import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
-
-import java.util.Set;
-import java.util.stream.Stream;
-
-import seedu.address.logic.commands.AddCommand;
-import seedu.address.logic.parser.exceptions.ParseException;
-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 input arguments and creates a new AddCommand object
- */
-public class AddCommandParser implements Parser {
-
- /**
- * 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 {
- ArgumentMultimap argMultimap =
- ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG);
-
- if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL)
- || !argMultimap.getPreamble().isEmpty()) {
- throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE));
- }
-
- Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get());
- Phone phone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get());
- Email email = ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get());
- Address address = ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get());
- Set tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG));
-
- Person person = new Person(name, phone, email, address, tagList);
-
- 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/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java
deleted file mode 100644
index 1e466792b46..00000000000
--- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java
+++ /dev/null
@@ -1,76 +0,0 @@
-package seedu.address.logic.parser;
-
-import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
-import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_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.parser.exceptions.ParseException;
-
-/**
- * Parses user input.
- */
-public class AddressBookParser {
-
- /**
- * Used for initial separation of command word and args.
- */
- private static final Pattern BASIC_COMMAND_FORMAT = Pattern.compile("(?\\S+)(?.*)");
-
- /**
- * 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) {
-
- 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();
-
- default:
- throw new ParseException(MESSAGE_UNKNOWN_COMMAND);
- }
- }
-
-}
diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java
deleted file mode 100644
index 75b1a9bf119..00000000000
--- a/src/main/java/seedu/address/logic/parser/CliSyntax.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package seedu.address.logic.parser;
-
-/**
- * Contains Command Line Interface (CLI) syntax definitions common to multiple commands
- */
-public class CliSyntax {
-
- /* Prefix definitions */
- public static final Prefix PREFIX_NAME = new Prefix("n/");
- public static final Prefix PREFIX_PHONE = new Prefix("p/");
- public static final Prefix PREFIX_EMAIL = new Prefix("e/");
- public static final Prefix PREFIX_ADDRESS = new Prefix("a/");
- public static final Prefix PREFIX_TAG = new Prefix("t/");
-
-}
diff --git a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java
deleted file mode 100644
index 522b93081cc..00000000000
--- a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package seedu.address.logic.parser;
-
-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.parser.exceptions.ParseException;
-
-/**
- * Parses input arguments and creates a new DeleteCommand object
- */
-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 {
- try {
- Index index = ParserUtil.parseIndex(args);
- return new DeleteCommand(index);
- } catch (ParseException pe) {
- throw new ParseException(
- String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE), pe);
- }
- }
-
-}
diff --git a/src/main/java/seedu/address/logic/parser/EditCommandParser.java b/src/main/java/seedu/address/logic/parser/EditCommandParser.java
deleted file mode 100644
index 845644b7dea..00000000000
--- a/src/main/java/seedu/address/logic/parser/EditCommandParser.java
+++ /dev/null
@@ -1,82 +0,0 @@
-package seedu.address.logic.parser;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Optional;
-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.parser.exceptions.ParseException;
-import seedu.address.model.tag.Tag;
-
-/**
- * Parses input arguments and creates a new EditCommand object
- */
-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 {
- requireNonNull(args);
- ArgumentMultimap argMultimap =
- ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG);
-
- Index index;
-
- try {
- index = ParserUtil.parseIndex(argMultimap.getPreamble());
- } catch (ParseException pe) {
- throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE), pe);
- }
-
- EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor();
- if (argMultimap.getValue(PREFIX_NAME).isPresent()) {
- editPersonDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()));
- }
- if (argMultimap.getValue(PREFIX_PHONE).isPresent()) {
- editPersonDescriptor.setPhone(ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get()));
- }
- if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) {
- editPersonDescriptor.setEmail(ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get()));
- }
- if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) {
- editPersonDescriptor.setAddress(ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get()));
- }
- parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editPersonDescriptor::setTags);
-
- if (!editPersonDescriptor.isAnyFieldEdited()) {
- throw new ParseException(EditCommand.MESSAGE_NOT_EDITED);
- }
-
- return new EditCommand(index, editPersonDescriptor);
- }
-
- /**
- * Parses {@code Collection tags} into a {@code Set} if {@code tags} is non-empty.
- * If {@code tags} contain only one element which is an empty string, it will be parsed into a
- * {@code Set} containing zero tags.
- */
- private Optional> parseTagsForEdit(Collection tags) throws ParseException {
- assert tags != null;
-
- if (tags.isEmpty()) {
- return Optional.empty();
- }
- Collection tagSet = tags.size() == 1 && tags.contains("") ? Collections.emptySet() : tags;
- return Optional.of(ParserUtil.parseTags(tagSet));
- }
-
-}
diff --git a/src/main/java/seedu/address/logic/parser/FindCommandParser.java b/src/main/java/seedu/address/logic/parser/FindCommandParser.java
deleted file mode 100644
index 4fb71f23103..00000000000
--- a/src/main/java/seedu/address/logic/parser/FindCommandParser.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package seedu.address.logic.parser;
-
-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.parser.exceptions.ParseException;
-import seedu.address.model.person.NameContainsKeywordsPredicate;
-
-/**
- * Parses input arguments and creates a new FindCommand object
- */
-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 {
- String trimmedArgs = args.trim();
- if (trimmedArgs.isEmpty()) {
- throw new ParseException(
- String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE));
- }
-
- String[] nameKeywords = trimmedArgs.split("\\s+");
-
- return new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList(nameKeywords)));
- }
-
-}
diff --git a/src/main/java/seedu/address/logic/parser/ParserUtil.java b/src/main/java/seedu/address/logic/parser/ParserUtil.java
deleted file mode 100644
index b117acb9c55..00000000000
--- a/src/main/java/seedu/address/logic/parser/ParserUtil.java
+++ /dev/null
@@ -1,124 +0,0 @@
-package seedu.address.logic.parser;
-
-import static java.util.Objects.requireNonNull;
-
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
-
-import seedu.address.commons.core.index.Index;
-import seedu.address.commons.util.StringUtil;
-import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.person.Address;
-import seedu.address.model.person.Email;
-import seedu.address.model.person.Name;
-import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
-
-/**
- * Contains utility methods used for parsing strings in the various *Parser classes.
- */
-public class ParserUtil {
-
- public static final String MESSAGE_INVALID_INDEX = "Index is not a non-zero unsigned integer.";
-
- /**
- * 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 {
- String trimmedIndex = oneBasedIndex.trim();
- if (!StringUtil.isNonZeroUnsignedInteger(trimmedIndex)) {
- throw new ParseException(MESSAGE_INVALID_INDEX);
- }
- return Index.fromOneBased(Integer.parseInt(trimmedIndex));
- }
-
- /**
- * Parses a {@code String name} into a {@code Name}.
- * Leading and trailing whitespaces will be trimmed.
- *
- * @throws ParseException if the given {@code name} is invalid.
- */
- public static Name parseName(String name) throws ParseException {
- requireNonNull(name);
- String trimmedName = name.trim();
- if (!Name.isValidName(trimmedName)) {
- throw new ParseException(Name.MESSAGE_CONSTRAINTS);
- }
- return new Name(trimmedName);
- }
-
- /**
- * Parses a {@code String phone} into a {@code Phone}.
- * Leading and trailing whitespaces will be trimmed.
- *
- * @throws ParseException if the given {@code phone} is invalid.
- */
- public static Phone parsePhone(String phone) throws ParseException {
- requireNonNull(phone);
- String trimmedPhone = phone.trim();
- if (!Phone.isValidPhone(trimmedPhone)) {
- throw new ParseException(Phone.MESSAGE_CONSTRAINTS);
- }
- return new Phone(trimmedPhone);
- }
-
- /**
- * Parses a {@code String address} into an {@code Address}.
- * Leading and trailing whitespaces will be trimmed.
- *
- * @throws ParseException if the given {@code address} is invalid.
- */
- public static Address parseAddress(String address) throws ParseException {
- requireNonNull(address);
- String trimmedAddress = address.trim();
- if (!Address.isValidAddress(trimmedAddress)) {
- throw new ParseException(Address.MESSAGE_CONSTRAINTS);
- }
- return new Address(trimmedAddress);
- }
-
- /**
- * Parses a {@code String email} into an {@code Email}.
- * Leading and trailing whitespaces will be trimmed.
- *
- * @throws ParseException if the given {@code email} is invalid.
- */
- public static Email parseEmail(String email) throws ParseException {
- requireNonNull(email);
- String trimmedEmail = email.trim();
- if (!Email.isValidEmail(trimmedEmail)) {
- throw new ParseException(Email.MESSAGE_CONSTRAINTS);
- }
- return new Email(trimmedEmail);
- }
-
- /**
- * Parses a {@code String tag} into a {@code Tag}.
- * Leading and trailing whitespaces will be trimmed.
- *
- * @throws ParseException if the given {@code tag} is invalid.
- */
- public static Tag parseTag(String tag) throws ParseException {
- requireNonNull(tag);
- String trimmedTag = tag.trim();
- if (!Tag.isValidTagName(trimmedTag)) {
- throw new ParseException(Tag.MESSAGE_CONSTRAINTS);
- }
- return new Tag(trimmedTag);
- }
-
- /**
- * Parses {@code Collection tags} into a {@code Set}.
- */
- public static Set parseTags(Collection tags) throws ParseException {
- requireNonNull(tags);
- final Set tagSet = new HashSet<>();
- for (String tagName : tags) {
- tagSet.add(parseTag(tagName));
- }
- return tagSet;
- }
-}
diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java
deleted file mode 100644
index 1a943a0781a..00000000000
--- a/src/main/java/seedu/address/model/AddressBook.java
+++ /dev/null
@@ -1,120 +0,0 @@
-package seedu.address.model;
-
-import static java.util.Objects.requireNonNull;
-
-import java.util.List;
-
-import javafx.collections.ObservableList;
-import seedu.address.model.person.Person;
-import seedu.address.model.person.UniquePersonList;
-
-/**
- * Wraps all data at the address-book level
- * Duplicates are not allowed (by .isSamePerson comparison)
- */
-public class AddressBook implements ReadOnlyAddressBook {
-
- private final UniquePersonList persons;
-
- /*
- * 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();
- }
-
- public AddressBook() {}
-
- /**
- * Creates an AddressBook using the Persons in the {@code toBeCopied}
- */
- public AddressBook(ReadOnlyAddressBook toBeCopied) {
- this();
- resetData(toBeCopied);
- }
-
- //// list overwrite operations
-
- /**
- * Replaces the contents of the person list with {@code persons}.
- * {@code persons} must not contain duplicate persons.
- */
- public void setPersons(List persons) {
- this.persons.setPersons(persons);
- }
-
- /**
- * Resets the existing data of this {@code AddressBook} with {@code newData}.
- */
- public void resetData(ReadOnlyAddressBook newData) {
- requireNonNull(newData);
-
- setPersons(newData.getPersonList());
- }
-
- //// person-level operations
-
- /**
- * Returns true if a person with the same identity as {@code person} exists in the address book.
- */
- public boolean hasPerson(Person person) {
- requireNonNull(person);
- return persons.contains(person);
- }
-
- /**
- * Adds a person to the address book.
- * The person must not already exist in the address book.
- */
- public void addPerson(Person p) {
- persons.add(p);
- }
-
- /**
- * Replaces the given person {@code target} in the list with {@code editedPerson}.
- * {@code target} must exist in the address book.
- * The person identity of {@code editedPerson} must not be the same as another existing person in the address book.
- */
- public void setPerson(Person target, Person editedPerson) {
- requireNonNull(editedPerson);
-
- persons.setPerson(target, editedPerson);
- }
-
- /**
- * Removes {@code key} from this {@code AddressBook}.
- * {@code key} must exist in the address book.
- */
- public void removePerson(Person key) {
- persons.remove(key);
- }
-
- //// util methods
-
- @Override
- public String toString() {
- return persons.asUnmodifiableObservableList().size() + " persons";
- // TODO: refine later
- }
-
- @Override
- public ObservableList getPersonList() {
- return persons.asUnmodifiableObservableList();
- }
-
- @Override
- public boolean equals(Object other) {
- return other == this // short circuit if same object
- || (other instanceof AddressBook // instanceof handles nulls
- && persons.equals(((AddressBook) other).persons));
- }
-
- @Override
- public int hashCode() {
- return persons.hashCode();
- }
-}
diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java
deleted file mode 100644
index d54df471c1f..00000000000
--- a/src/main/java/seedu/address/model/Model.java
+++ /dev/null
@@ -1,87 +0,0 @@
-package seedu.address.model;
-
-import java.nio.file.Path;
-import java.util.function.Predicate;
-
-import javafx.collections.ObservableList;
-import seedu.address.commons.core.GuiSettings;
-import seedu.address.model.person.Person;
-
-/**
- * The API of the Model component.
- */
-public interface Model {
- /** {@code Predicate} that always evaluate to true */
- Predicate PREDICATE_SHOW_ALL_PERSONS = unused -> true;
-
- /**
- * Replaces user prefs data with the data in {@code userPrefs}.
- */
- void setUserPrefs(ReadOnlyUserPrefs userPrefs);
-
- /**
- * Returns the user prefs.
- */
- ReadOnlyUserPrefs getUserPrefs();
-
- /**
- * Returns the user prefs' GUI settings.
- */
- GuiSettings getGuiSettings();
-
- /**
- * Sets the user prefs' GUI settings.
- */
- void setGuiSettings(GuiSettings guiSettings);
-
- /**
- * Returns the user prefs' address book file path.
- */
- Path getAddressBookFilePath();
-
- /**
- * Sets the user prefs' address book file path.
- */
- void setAddressBookFilePath(Path addressBookFilePath);
-
- /**
- * 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);
-
- /**
- * Deletes the given person.
- * The person must exist in the address book.
- */
- void deletePerson(Person target);
-
- /**
- * Adds the given person.
- * {@code person} must not already exist in the address book.
- */
- void addPerson(Person person);
-
- /**
- * Replaces the given person {@code target} with {@code editedPerson}.
- * {@code target} must exist in the address book.
- * The person identity of {@code editedPerson} must not be the same as another existing person in the address book.
- */
- void setPerson(Person target, Person editedPerson);
-
- /** 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);
-}
diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java
deleted file mode 100644
index 0650c954f5c..00000000000
--- a/src/main/java/seedu/address/model/ModelManager.java
+++ /dev/null
@@ -1,151 +0,0 @@
-package seedu.address.model;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
-
-import java.nio.file.Path;
-import java.util.function.Predicate;
-import java.util.logging.Logger;
-
-import javafx.collections.ObservableList;
-import javafx.collections.transformation.FilteredList;
-import seedu.address.commons.core.GuiSettings;
-import seedu.address.commons.core.LogsCenter;
-import seedu.address.model.person.Person;
-
-/**
- * Represents the in-memory model of the address book data.
- */
-public class ModelManager implements Model {
- private static final Logger logger = LogsCenter.getLogger(ModelManager.class);
-
- private final AddressBook addressBook;
- private final UserPrefs userPrefs;
- private final FilteredList filteredPersons;
-
- /**
- * Initializes a ModelManager with the given addressBook and userPrefs.
- */
- public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs) {
- super();
- requireAllNonNull(addressBook, userPrefs);
-
- logger.fine("Initializing with address book: " + addressBook + " and user prefs " + userPrefs);
-
- this.addressBook = new AddressBook(addressBook);
- this.userPrefs = new UserPrefs(userPrefs);
- filteredPersons = new FilteredList<>(this.addressBook.getPersonList());
- }
-
- public ModelManager() {
- this(new AddressBook(), new UserPrefs());
- }
-
- //=========== UserPrefs ==================================================================================
-
- @Override
- public void setUserPrefs(ReadOnlyUserPrefs userPrefs) {
- requireNonNull(userPrefs);
- this.userPrefs.resetData(userPrefs);
- }
-
- @Override
- public ReadOnlyUserPrefs getUserPrefs() {
- return userPrefs;
- }
-
- @Override
- public GuiSettings getGuiSettings() {
- return userPrefs.getGuiSettings();
- }
-
- @Override
- public void setGuiSettings(GuiSettings guiSettings) {
- requireNonNull(guiSettings);
- userPrefs.setGuiSettings(guiSettings);
- }
-
- @Override
- public Path getAddressBookFilePath() {
- return userPrefs.getAddressBookFilePath();
- }
-
- @Override
- public void setAddressBookFilePath(Path addressBookFilePath) {
- requireNonNull(addressBookFilePath);
- userPrefs.setAddressBookFilePath(addressBookFilePath);
- }
-
- //=========== AddressBook ================================================================================
-
- @Override
- public void setAddressBook(ReadOnlyAddressBook addressBook) {
- this.addressBook.resetData(addressBook);
- }
-
- @Override
- public ReadOnlyAddressBook getAddressBook() {
- return addressBook;
- }
-
- @Override
- public boolean hasPerson(Person person) {
- requireNonNull(person);
- return addressBook.hasPerson(person);
- }
-
- @Override
- public void deletePerson(Person target) {
- addressBook.removePerson(target);
- }
-
- @Override
- public void addPerson(Person person) {
- addressBook.addPerson(person);
- updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
- }
-
- @Override
- public void setPerson(Person target, Person editedPerson) {
- requireAllNonNull(target, editedPerson);
-
- addressBook.setPerson(target, editedPerson);
- }
-
- //=========== Filtered Person List Accessors =============================================================
-
- /**
- * Returns an unmodifiable view of the list of {@code Person} backed by the internal list of
- * {@code versionedAddressBook}
- */
- @Override
- public ObservableList getFilteredPersonList() {
- return filteredPersons;
- }
-
- @Override
- public void updateFilteredPersonList(Predicate predicate) {
- requireNonNull(predicate);
- filteredPersons.setPredicate(predicate);
- }
-
- @Override
- public boolean equals(Object obj) {
- // short circuit if same object
- if (obj == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(obj instanceof ModelManager)) {
- return false;
- }
-
- // state check
- ModelManager other = (ModelManager) obj;
- return addressBook.equals(other.addressBook)
- && userPrefs.equals(other.userPrefs)
- && filteredPersons.equals(other.filteredPersons);
- }
-
-}
diff --git a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java
deleted file mode 100644
index 6ddc2cd9a29..00000000000
--- a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package seedu.address.model;
-
-import javafx.collections.ObservableList;
-import seedu.address.model.person.Person;
-
-/**
- * Unmodifiable view of an address book
- */
-public interface ReadOnlyAddressBook {
-
- /**
- * Returns an unmodifiable view of the persons list.
- * This list will not contain any duplicate persons.
- */
- ObservableList getPersonList();
-
-}
diff --git a/src/main/java/seedu/address/model/person/Address.java b/src/main/java/seedu/address/model/person/Address.java
deleted file mode 100644
index 60472ca22a0..00000000000
--- a/src/main/java/seedu/address/model/person/Address.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package seedu.address.model.person;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.commons.util.AppUtil.checkArgument;
-
-/**
- * Represents a Person's address in the address book.
- * Guarantees: immutable; is valid as declared in {@link #isValidAddress(String)}
- */
-public class Address {
-
- public static final String MESSAGE_CONSTRAINTS = "Addresses can take any values, and it should not be blank";
-
- /*
- * 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 = "[^\\s].*";
-
- public final String value;
-
- /**
- * Constructs an {@code Address}.
- *
- * @param address A valid address.
- */
- public Address(String address) {
- requireNonNull(address);
- checkArgument(isValidAddress(address), MESSAGE_CONSTRAINTS);
- value = address;
- }
-
- /**
- * Returns true if a given string is a valid email.
- */
- public static boolean isValidAddress(String test) {
- return test.matches(VALIDATION_REGEX);
- }
-
- @Override
- public String toString() {
- return value;
- }
-
- @Override
- public boolean equals(Object other) {
- return other == this // short circuit if same object
- || (other instanceof Address // instanceof handles nulls
- && value.equals(((Address) other).value)); // state check
- }
-
- @Override
- public int hashCode() {
- return value.hashCode();
- }
-
-}
diff --git a/src/main/java/seedu/address/model/person/Email.java b/src/main/java/seedu/address/model/person/Email.java
deleted file mode 100644
index a5bbe0b6a5f..00000000000
--- a/src/main/java/seedu/address/model/person/Email.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package seedu.address.model.person;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.commons.util.AppUtil.checkArgument;
-
-/**
- * Represents a Person's email in the address book.
- * Guarantees: immutable; is valid as declared in {@link #isValidEmail(String)}
- */
-public class Email {
-
- private static final String SPECIAL_CHARACTERS = "!#$%&'*+/=?`{|}~^.-";
- public static final String MESSAGE_CONSTRAINTS = "Emails should be of the format local-part@domain "
- + "and adhere to the following constraints:\n"
- + "1. The local-part should only contain alphanumeric characters and these special characters, excluding "
- + "the parentheses, (" + SPECIAL_CHARACTERS + ") .\n"
- + "2. This is followed by a '@' and then a domain name. "
- + "The domain name must:\n"
- + " - be at least 2 characters long\n"
- + " - start and end with alphanumeric characters\n"
- + " - consist of alphanumeric characters, a period or a hyphen for the characters in between, if any.";
- // alphanumeric and special characters
- private static final String LOCAL_PART_REGEX = "^[\\w" + SPECIAL_CHARACTERS + "]+";
- private static final String DOMAIN_FIRST_CHARACTER_REGEX = "[^\\W_]"; // alphanumeric characters except underscore
- private static final String DOMAIN_MIDDLE_REGEX = "[a-zA-Z0-9.-]*"; // alphanumeric, period and hyphen
- private static final String DOMAIN_LAST_CHARACTER_REGEX = "[^\\W_]$";
- public static final String VALIDATION_REGEX = LOCAL_PART_REGEX + "@"
- + DOMAIN_FIRST_CHARACTER_REGEX + DOMAIN_MIDDLE_REGEX + DOMAIN_LAST_CHARACTER_REGEX;
-
- public final String value;
-
- /**
- * Constructs an {@code Email}.
- *
- * @param email A valid email address.
- */
- public Email(String email) {
- requireNonNull(email);
- checkArgument(isValidEmail(email), MESSAGE_CONSTRAINTS);
- value = email;
- }
-
- /**
- * Returns if a given string is a valid email.
- */
- public static boolean isValidEmail(String test) {
- return test.matches(VALIDATION_REGEX);
- }
-
- @Override
- public String toString() {
- return value;
- }
-
- @Override
- public boolean equals(Object other) {
- return other == this // short circuit if same object
- || (other instanceof Email // instanceof handles nulls
- && value.equals(((Email) other).value)); // state check
- }
-
- @Override
- public int hashCode() {
- return value.hashCode();
- }
-
-}
diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/address/model/person/Person.java
deleted file mode 100644
index 557a7a60cd5..00000000000
--- a/src/main/java/seedu/address/model/person/Person.java
+++ /dev/null
@@ -1,120 +0,0 @@
-package seedu.address.model.person;
-
-import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Objects;
-import java.util.Set;
-
-import seedu.address.model.tag.Tag;
-
-/**
- * Represents a Person in the address book.
- * Guarantees: details are present and not null, field values are validated, immutable.
- */
-public class Person {
-
- // Identity fields
- private final Name name;
- private final Phone phone;
- private final Email email;
-
- // Data fields
- private final Address address;
- private final Set tags = new HashSet<>();
-
- /**
- * Every field must be present and not null.
- */
- public Person(Name name, Phone phone, Email email, Address address, Set tags) {
- requireAllNonNull(name, phone, email, address, tags);
- this.name = name;
- this.phone = phone;
- this.email = email;
- this.address = address;
- this.tags.addAll(tags);
- }
-
- public Name getName() {
- return name;
- }
-
- public Phone getPhone() {
- return phone;
- }
-
- public Email getEmail() {
- return email;
- }
-
- public Address getAddress() {
- return address;
- }
-
- /**
- * Returns an immutable tag set, which throws {@code UnsupportedOperationException}
- * if modification is attempted.
- */
- public Set getTags() {
- return Collections.unmodifiableSet(tags);
- }
-
- /**
- * Returns true if both persons of the same name have at least one other identity field that is the same.
- * This defines a weaker notion of equality between two persons.
- */
- public boolean isSamePerson(Person otherPerson) {
- if (otherPerson == this) {
- return true;
- }
-
- return otherPerson != null
- && otherPerson.getName().equals(getName())
- && (otherPerson.getPhone().equals(getPhone()) || otherPerson.getEmail().equals(getEmail()));
- }
-
- /**
- * Returns true if both persons have the same identity and data fields.
- * This defines a stronger notion of equality between two persons.
- */
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- if (!(other instanceof Person)) {
- return false;
- }
-
- Person otherPerson = (Person) other;
- return otherPerson.getName().equals(getName())
- && otherPerson.getPhone().equals(getPhone())
- && otherPerson.getEmail().equals(getEmail())
- && otherPerson.getAddress().equals(getAddress())
- && otherPerson.getTags().equals(getTags());
- }
-
- @Override
- public int hashCode() {
- // use this method for custom fields hashing instead of implementing your own
- return Objects.hash(name, phone, email, address, tags);
- }
-
- @Override
- public String toString() {
- final StringBuilder builder = new StringBuilder();
- builder.append(getName())
- .append(" Phone: ")
- .append(getPhone())
- .append(" Email: ")
- .append(getEmail())
- .append(" Address: ")
- .append(getAddress())
- .append(" Tags: ");
- getTags().forEach(builder::append);
- return builder.toString();
- }
-
-}
diff --git a/src/main/java/seedu/address/model/person/Phone.java b/src/main/java/seedu/address/model/person/Phone.java
deleted file mode 100644
index 872c76b382f..00000000000
--- a/src/main/java/seedu/address/model/person/Phone.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package seedu.address.model.person;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.commons.util.AppUtil.checkArgument;
-
-/**
- * Represents a Person's phone number in the address book.
- * Guarantees: immutable; is valid as declared in {@link #isValidPhone(String)}
- */
-public class Phone {
-
-
- public static final String MESSAGE_CONSTRAINTS =
- "Phone numbers should only contain numbers, and it should be at least 3 digits long";
- public static final String VALIDATION_REGEX = "\\d{3,}";
- public final String value;
-
- /**
- * Constructs a {@code Phone}.
- *
- * @param phone A valid phone number.
- */
- public Phone(String phone) {
- requireNonNull(phone);
- checkArgument(isValidPhone(phone), MESSAGE_CONSTRAINTS);
- value = phone;
- }
-
- /**
- * Returns true if a given string is a valid phone number.
- */
- public static boolean isValidPhone(String test) {
- return test.matches(VALIDATION_REGEX);
- }
-
- @Override
- public String toString() {
- return value;
- }
-
- @Override
- public boolean equals(Object other) {
- return other == this // short circuit if same object
- || (other instanceof Phone // instanceof handles nulls
- && value.equals(((Phone) other).value)); // state check
- }
-
- @Override
- public int hashCode() {
- return value.hashCode();
- }
-
-}
diff --git a/src/main/java/seedu/address/model/person/UniquePersonList.java b/src/main/java/seedu/address/model/person/UniquePersonList.java
deleted file mode 100644
index 0fee4fe57e6..00000000000
--- a/src/main/java/seedu/address/model/person/UniquePersonList.java
+++ /dev/null
@@ -1,137 +0,0 @@
-package seedu.address.model.person;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
-
-import java.util.Iterator;
-import java.util.List;
-
-import javafx.collections.FXCollections;
-import javafx.collections.ObservableList;
-import seedu.address.model.person.exceptions.DuplicatePersonException;
-import seedu.address.model.person.exceptions.PersonNotFoundException;
-
-/**
- * A list of persons that enforces uniqueness between its elements and does not allow nulls.
- * A person is considered unique by comparing using {@code Person#isSamePerson(Person)}. As such, adding and updating of
- * persons uses Person#isSamePerson(Person) for equality so as to ensure that the person being added or updated is
- * unique in terms of identity in the UniquePersonList. However, the removal of a person uses Person#equals(Object) so
- * as to ensure that the person with exactly the same fields will be removed.
- *
- * Supports a minimal set of list operations.
- *
- * @see Person#isSamePerson(Person)
- */
-public class UniquePersonList implements Iterable {
-
- private final ObservableList internalList = FXCollections.observableArrayList();
- private final ObservableList internalUnmodifiableList =
- FXCollections.unmodifiableObservableList(internalList);
-
- /**
- * Returns true if the list contains an equivalent person as the given argument.
- */
- public boolean contains(Person toCheck) {
- requireNonNull(toCheck);
- return internalList.stream().anyMatch(toCheck::isSamePerson);
- }
-
- /**
- * Adds a person to the list.
- * The person must not already exist in the list.
- */
- public void add(Person toAdd) {
- requireNonNull(toAdd);
- if (contains(toAdd)) {
- throw new DuplicatePersonException();
- }
- internalList.add(toAdd);
- }
-
- /**
- * Replaces the person {@code target} in the list with {@code editedPerson}.
- * {@code target} must exist in the list.
- * The person identity of {@code editedPerson} must not be the same as another existing person in the list.
- */
- public void setPerson(Person target, Person editedPerson) {
- requireAllNonNull(target, editedPerson);
-
- int index = internalList.indexOf(target);
- if (index == -1) {
- throw new PersonNotFoundException();
- }
-
- if (!target.isSamePerson(editedPerson) && contains(editedPerson)) {
- throw new DuplicatePersonException();
- }
-
- internalList.set(index, editedPerson);
- }
-
- /**
- * Removes the equivalent person from the list.
- * The person must exist in the list.
- */
- public void remove(Person toRemove) {
- requireNonNull(toRemove);
- if (!internalList.remove(toRemove)) {
- throw new PersonNotFoundException();
- }
- }
-
- public void setPersons(UniquePersonList replacement) {
- requireNonNull(replacement);
- internalList.setAll(replacement.internalList);
- }
-
- /**
- * Replaces the contents of this list with {@code persons}.
- * {@code persons} must not contain duplicate persons.
- */
- public void setPersons(List persons) {
- requireAllNonNull(persons);
- if (!personsAreUnique(persons)) {
- throw new DuplicatePersonException();
- }
-
- internalList.setAll(persons);
- }
-
- /**
- * Returns the backing list as an unmodifiable {@code ObservableList}.
- */
- public ObservableList asUnmodifiableObservableList() {
- return internalUnmodifiableList;
- }
-
- @Override
- public Iterator iterator() {
- return internalList.iterator();
- }
-
- @Override
- public boolean equals(Object other) {
- return other == this // short circuit if same object
- || (other instanceof UniquePersonList // instanceof handles nulls
- && internalList.equals(((UniquePersonList) other).internalList));
- }
-
- @Override
- public int hashCode() {
- return internalList.hashCode();
- }
-
- /**
- * Returns true if {@code persons} contains only unique persons.
- */
- private boolean personsAreUnique(List persons) {
- for (int i = 0; i < persons.size() - 1; i++) {
- for (int j = i + 1; j < persons.size(); j++) {
- if (persons.get(i).isSamePerson(persons.get(j))) {
- return false;
- }
- }
- }
- return true;
- }
-}
diff --git a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java b/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java
deleted file mode 100644
index d7290f59442..00000000000
--- a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package seedu.address.model.person.exceptions;
-
-/**
- * Signals that the operation will result in duplicate Persons (Persons are considered duplicates if they have the same
- * identity).
- */
-public class DuplicatePersonException extends RuntimeException {
- public DuplicatePersonException() {
- super("Operation would result in duplicate persons");
- }
-}
diff --git a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java b/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java
deleted file mode 100644
index fa764426ca7..00000000000
--- a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package seedu.address.model.person.exceptions;
-
-/**
- * Signals that the operation is unable to find the specified person.
- */
-public class PersonNotFoundException extends RuntimeException {}
diff --git a/src/main/java/seedu/address/model/util/SampleDataUtil.java b/src/main/java/seedu/address/model/util/SampleDataUtil.java
deleted file mode 100644
index 1806da4facf..00000000000
--- a/src/main/java/seedu/address/model/util/SampleDataUtil.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package seedu.address.model.util;
-
-import java.util.Arrays;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import seedu.address.model.AddressBook;
-import seedu.address.model.ReadOnlyAddressBook;
-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;
-
-/**
- * Contains utility methods for populating {@code AddressBook} with sample data.
- */
-public class SampleDataUtil {
- public static Person[] getSamplePersons() {
- return new Person[] {
- new Person(new Name("Alex Yeoh"), new Phone("87438807"), new Email("alexyeoh@example.com"),
- new Address("Blk 30 Geylang Street 29, #06-40"),
- getTagSet("friends")),
- new Person(new Name("Bernice Yu"), new Phone("99272758"), new Email("berniceyu@example.com"),
- new Address("Blk 30 Lorong 3 Serangoon Gardens, #07-18"),
- getTagSet("colleagues", "friends")),
- new Person(new Name("Charlotte Oliveiro"), new Phone("93210283"), new Email("charlotte@example.com"),
- new Address("Blk 11 Ang Mo Kio Street 74, #11-04"),
- getTagSet("neighbours")),
- new Person(new Name("David Li"), new Phone("91031282"), new Email("lidavid@example.com"),
- new Address("Blk 436 Serangoon Gardens Street 26, #16-43"),
- getTagSet("family")),
- new Person(new Name("Irfan Ibrahim"), new Phone("92492021"), new Email("irfan@example.com"),
- new Address("Blk 47 Tampines Street 20, #17-35"),
- getTagSet("classmates")),
- new Person(new Name("Roy Balakrishnan"), new Phone("92624417"), new Email("royb@example.com"),
- new Address("Blk 45 Aljunied Street 85, #11-31"),
- getTagSet("colleagues"))
- };
- }
-
- public static ReadOnlyAddressBook getSampleAddressBook() {
- AddressBook sampleAb = new AddressBook();
- for (Person samplePerson : getSamplePersons()) {
- sampleAb.addPerson(samplePerson);
- }
- return sampleAb;
- }
-
- /**
- * Returns a tag set containing the list of strings given.
- */
- public static Set getTagSet(String... strings) {
- return Arrays.stream(strings)
- .map(Tag::new)
- .collect(Collectors.toSet());
- }
-
-}
diff --git a/src/main/java/seedu/address/storage/AddressBookStorage.java b/src/main/java/seedu/address/storage/AddressBookStorage.java
deleted file mode 100644
index 4599182b3f9..00000000000
--- a/src/main/java/seedu/address/storage/AddressBookStorage.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package seedu.address.storage;
-
-import java.io.IOException;
-import java.nio.file.Path;
-import java.util.Optional;
-
-import seedu.address.commons.exceptions.DataConversionException;
-import seedu.address.model.ReadOnlyAddressBook;
-
-/**
- * Represents a storage for {@link seedu.address.model.AddressBook}.
- */
-public interface AddressBookStorage {
-
- /**
- * Returns the file path of the data file.
- */
- Path getAddressBookFilePath();
-
- /**
- * Returns AddressBook data as a {@link ReadOnlyAddressBook}.
- * Returns {@code Optional.empty()} if storage file is not found.
- * @throws DataConversionException if the data in storage is not in the expected format.
- * @throws IOException if there was any problem when reading from the storage.
- */
- Optional readAddressBook() throws DataConversionException, IOException;
-
- /**
- * @see #getAddressBookFilePath()
- */
- Optional readAddressBook(Path filePath) throws DataConversionException, IOException;
-
- /**
- * Saves the given {@link ReadOnlyAddressBook} to the storage.
- * @param addressBook cannot be null.
- * @throws IOException if there was any problem writing to the file.
- */
- void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException;
-
- /**
- * @see #saveAddressBook(ReadOnlyAddressBook)
- */
- void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException;
-
-}
diff --git a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java b/src/main/java/seedu/address/storage/JsonAdaptedPerson.java
deleted file mode 100644
index a6321cec2ea..00000000000
--- a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java
+++ /dev/null
@@ -1,109 +0,0 @@
-package seedu.address.storage;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-import seedu.address.commons.exceptions.IllegalValueException;
-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;
-
-/**
- * Jackson-friendly version of {@link Person}.
- */
-class JsonAdaptedPerson {
-
- public static final String MISSING_FIELD_MESSAGE_FORMAT = "Person's %s field is missing!";
-
- private final String name;
- private final String phone;
- private final String email;
- private final String address;
- private final List tagged = new ArrayList<>();
-
- /**
- * Constructs a {@code JsonAdaptedPerson} with the given person details.
- */
- @JsonCreator
- public JsonAdaptedPerson(@JsonProperty("name") String name, @JsonProperty("phone") String phone,
- @JsonProperty("email") String email, @JsonProperty("address") String address,
- @JsonProperty("tagged") List tagged) {
- this.name = name;
- this.phone = phone;
- this.email = email;
- this.address = address;
- if (tagged != null) {
- this.tagged.addAll(tagged);
- }
- }
-
- /**
- * Converts a given {@code Person} into this class for Jackson use.
- */
- public JsonAdaptedPerson(Person source) {
- name = source.getName().fullName;
- phone = source.getPhone().value;
- email = source.getEmail().value;
- address = source.getAddress().value;
- tagged.addAll(source.getTags().stream()
- .map(JsonAdaptedTag::new)
- .collect(Collectors.toList()));
- }
-
- /**
- * Converts this Jackson-friendly adapted person object into the model's {@code Person} object.
- *
- * @throws IllegalValueException if there were any data constraints violated in the adapted person.
- */
- public Person toModelType() throws IllegalValueException {
- final List personTags = new ArrayList<>();
- for (JsonAdaptedTag tag : tagged) {
- personTags.add(tag.toModelType());
- }
-
- if (name == null) {
- throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName()));
- }
- if (!Name.isValidName(name)) {
- throw new IllegalValueException(Name.MESSAGE_CONSTRAINTS);
- }
- final Name modelName = new Name(name);
-
- if (phone == null) {
- throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName()));
- }
- if (!Phone.isValidPhone(phone)) {
- throw new IllegalValueException(Phone.MESSAGE_CONSTRAINTS);
- }
- final Phone modelPhone = new Phone(phone);
-
- if (email == null) {
- throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.class.getSimpleName()));
- }
- if (!Email.isValidEmail(email)) {
- throw new IllegalValueException(Email.MESSAGE_CONSTRAINTS);
- }
- final Email modelEmail = new Email(email);
-
- if (address == null) {
- throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName()));
- }
- if (!Address.isValidAddress(address)) {
- throw new IllegalValueException(Address.MESSAGE_CONSTRAINTS);
- }
- final Address modelAddress = new Address(address);
-
- final Set modelTags = new HashSet<>(personTags);
- return new Person(modelName, modelPhone, modelEmail, modelAddress, modelTags);
- }
-
-}
diff --git a/src/main/java/seedu/address/storage/JsonAdaptedTag.java b/src/main/java/seedu/address/storage/JsonAdaptedTag.java
deleted file mode 100644
index 0df22bdb754..00000000000
--- a/src/main/java/seedu/address/storage/JsonAdaptedTag.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package seedu.address.storage;
-
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonValue;
-
-import seedu.address.commons.exceptions.IllegalValueException;
-import seedu.address.model.tag.Tag;
-
-/**
- * Jackson-friendly version of {@link Tag}.
- */
-class JsonAdaptedTag {
-
- private final String tagName;
-
- /**
- * Constructs a {@code JsonAdaptedTag} with the given {@code tagName}.
- */
- @JsonCreator
- public JsonAdaptedTag(String tagName) {
- this.tagName = tagName;
- }
-
- /**
- * Converts a given {@code Tag} into this class for Jackson use.
- */
- public JsonAdaptedTag(Tag source) {
- tagName = source.tagName;
- }
-
- @JsonValue
- public String getTagName() {
- return tagName;
- }
-
- /**
- * Converts this Jackson-friendly adapted tag object into the model's {@code Tag} object.
- *
- * @throws IllegalValueException if there were any data constraints violated in the adapted tag.
- */
- public Tag toModelType() throws IllegalValueException {
- if (!Tag.isValidTagName(tagName)) {
- throw new IllegalValueException(Tag.MESSAGE_CONSTRAINTS);
- }
- return new Tag(tagName);
- }
-
-}
diff --git a/src/main/java/seedu/address/storage/JsonAddressBookStorage.java b/src/main/java/seedu/address/storage/JsonAddressBookStorage.java
deleted file mode 100644
index dfab9daaa0d..00000000000
--- a/src/main/java/seedu/address/storage/JsonAddressBookStorage.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package seedu.address.storage;
-
-import static java.util.Objects.requireNonNull;
-
-import java.io.IOException;
-import java.nio.file.Path;
-import java.util.Optional;
-import java.util.logging.Logger;
-
-import seedu.address.commons.core.LogsCenter;
-import seedu.address.commons.exceptions.DataConversionException;
-import seedu.address.commons.exceptions.IllegalValueException;
-import seedu.address.commons.util.FileUtil;
-import seedu.address.commons.util.JsonUtil;
-import seedu.address.model.ReadOnlyAddressBook;
-
-/**
- * A class to access AddressBook data stored as a json file on the hard disk.
- */
-public class JsonAddressBookStorage implements AddressBookStorage {
-
- private static final Logger logger = LogsCenter.getLogger(JsonAddressBookStorage.class);
-
- private Path filePath;
-
- public JsonAddressBookStorage(Path filePath) {
- this.filePath = filePath;
- }
-
- public Path getAddressBookFilePath() {
- return filePath;
- }
-
- @Override
- public Optional readAddressBook() throws DataConversionException {
- return readAddressBook(filePath);
- }
-
- /**
- * Similar to {@link #readAddressBook()}.
- *
- * @param filePath location of the data. Cannot be null.
- * @throws DataConversionException if the file is not in the correct format.
- */
- public Optional readAddressBook(Path filePath) throws DataConversionException {
- requireNonNull(filePath);
-
- Optional jsonAddressBook = JsonUtil.readJsonFile(
- filePath, JsonSerializableAddressBook.class);
- if (!jsonAddressBook.isPresent()) {
- return Optional.empty();
- }
-
- try {
- return Optional.of(jsonAddressBook.get().toModelType());
- } catch (IllegalValueException ive) {
- logger.info("Illegal values found in " + filePath + ": " + ive.getMessage());
- throw new DataConversionException(ive);
- }
- }
-
- @Override
- public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException {
- saveAddressBook(addressBook, filePath);
- }
-
- /**
- * Similar to {@link #saveAddressBook(ReadOnlyAddressBook)}.
- *
- * @param filePath location of the data. Cannot be null.
- */
- public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException {
- requireNonNull(addressBook);
- requireNonNull(filePath);
-
- FileUtil.createIfMissing(filePath);
- JsonUtil.saveJsonFile(new JsonSerializableAddressBook(addressBook), filePath);
- }
-
-}
diff --git a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java b/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java
deleted file mode 100644
index 5efd834091d..00000000000
--- a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package seedu.address.storage;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.stream.Collectors;
-
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.annotation.JsonRootName;
-
-import seedu.address.commons.exceptions.IllegalValueException;
-import seedu.address.model.AddressBook;
-import seedu.address.model.ReadOnlyAddressBook;
-import seedu.address.model.person.Person;
-
-/**
- * An Immutable AddressBook that is serializable to JSON format.
- */
-@JsonRootName(value = "addressbook")
-class JsonSerializableAddressBook {
-
- public static final String MESSAGE_DUPLICATE_PERSON = "Persons list contains duplicate person(s).";
-
- private final List persons = new ArrayList<>();
-
- /**
- * Constructs a {@code JsonSerializableAddressBook} with the given persons.
- */
- @JsonCreator
- public JsonSerializableAddressBook(@JsonProperty("persons") List persons) {
- this.persons.addAll(persons);
- }
-
- /**
- * Converts a given {@code ReadOnlyAddressBook} into this class for Jackson use.
- *
- * @param source future changes to this will not affect the created {@code JsonSerializableAddressBook}.
- */
- public JsonSerializableAddressBook(ReadOnlyAddressBook source) {
- persons.addAll(source.getPersonList().stream().map(JsonAdaptedPerson::new).collect(Collectors.toList()));
- }
-
- /**
- * Converts this address book into the model's {@code AddressBook} object.
- *
- * @throws IllegalValueException if there were any data constraints violated.
- */
- public AddressBook toModelType() throws IllegalValueException {
- AddressBook addressBook = new AddressBook();
- for (JsonAdaptedPerson jsonAdaptedPerson : persons) {
- Person person = jsonAdaptedPerson.toModelType();
- if (addressBook.hasPerson(person)) {
- throw new IllegalValueException(MESSAGE_DUPLICATE_PERSON);
- }
- addressBook.addPerson(person);
- }
- return addressBook;
- }
-
-}
diff --git a/src/main/java/seedu/address/storage/Storage.java b/src/main/java/seedu/address/storage/Storage.java
deleted file mode 100644
index beda8bd9f11..00000000000
--- a/src/main/java/seedu/address/storage/Storage.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package seedu.address.storage;
-
-import java.io.IOException;
-import java.nio.file.Path;
-import java.util.Optional;
-
-import seedu.address.commons.exceptions.DataConversionException;
-import seedu.address.model.ReadOnlyAddressBook;
-import seedu.address.model.ReadOnlyUserPrefs;
-import seedu.address.model.UserPrefs;
-
-/**
- * API of the Storage component
- */
-public interface Storage extends AddressBookStorage, UserPrefsStorage {
-
- @Override
- Optional readUserPrefs() throws DataConversionException, IOException;
-
- @Override
- void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException;
-
- @Override
- Path getAddressBookFilePath();
-
- @Override
- Optional readAddressBook() throws DataConversionException, IOException;
-
- @Override
- void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException;
-
-}
diff --git a/src/main/java/seedu/address/storage/StorageManager.java b/src/main/java/seedu/address/storage/StorageManager.java
deleted file mode 100644
index e4f452b6cbf..00000000000
--- a/src/main/java/seedu/address/storage/StorageManager.java
+++ /dev/null
@@ -1,77 +0,0 @@
-package seedu.address.storage;
-
-import java.io.IOException;
-import java.nio.file.Path;
-import java.util.Optional;
-import java.util.logging.Logger;
-
-import seedu.address.commons.core.LogsCenter;
-import seedu.address.commons.exceptions.DataConversionException;
-import seedu.address.model.ReadOnlyAddressBook;
-import seedu.address.model.ReadOnlyUserPrefs;
-import seedu.address.model.UserPrefs;
-
-/**
- * Manages storage of AddressBook data in local storage.
- */
-public class StorageManager implements Storage {
-
- private static final Logger logger = LogsCenter.getLogger(StorageManager.class);
- private AddressBookStorage addressBookStorage;
- private UserPrefsStorage userPrefsStorage;
-
-
- public StorageManager(AddressBookStorage addressBookStorage, UserPrefsStorage userPrefsStorage) {
- super();
- this.addressBookStorage = addressBookStorage;
- this.userPrefsStorage = userPrefsStorage;
- }
-
- // ================ UserPrefs methods ==============================
-
- @Override
- public Path getUserPrefsFilePath() {
- return userPrefsStorage.getUserPrefsFilePath();
- }
-
- @Override
- public Optional readUserPrefs() throws DataConversionException, IOException {
- return userPrefsStorage.readUserPrefs();
- }
-
- @Override
- public void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException {
- userPrefsStorage.saveUserPrefs(userPrefs);
- }
-
-
- // ================ AddressBook methods ==============================
-
- @Override
- public Path getAddressBookFilePath() {
- return addressBookStorage.getAddressBookFilePath();
- }
-
- @Override
- public Optional readAddressBook() throws DataConversionException, IOException {
- return readAddressBook(addressBookStorage.getAddressBookFilePath());
- }
-
- @Override
- public Optional readAddressBook(Path filePath) throws DataConversionException, IOException {
- logger.fine("Attempting to read data from file: " + filePath);
- return addressBookStorage.readAddressBook(filePath);
- }
-
- @Override
- public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException {
- saveAddressBook(addressBook, addressBookStorage.getAddressBookFilePath());
- }
-
- @Override
- public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException {
- logger.fine("Attempting to write to data file: " + filePath);
- addressBookStorage.saveAddressBook(addressBook, filePath);
- }
-
-}
diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/address/ui/MainWindow.java
deleted file mode 100644
index 90bbf11de97..00000000000
--- a/src/main/java/seedu/address/ui/MainWindow.java
+++ /dev/null
@@ -1,193 +0,0 @@
-package seedu.address.ui;
-
-import java.util.logging.Logger;
-
-import javafx.event.ActionEvent;
-import javafx.fxml.FXML;
-import javafx.scene.control.MenuItem;
-import javafx.scene.control.TextInputControl;
-import javafx.scene.input.KeyCombination;
-import javafx.scene.input.KeyEvent;
-import javafx.scene.layout.StackPane;
-import javafx.stage.Stage;
-import seedu.address.commons.core.GuiSettings;
-import seedu.address.commons.core.LogsCenter;
-import seedu.address.logic.Logic;
-import seedu.address.logic.commands.CommandResult;
-import seedu.address.logic.commands.exceptions.CommandException;
-import seedu.address.logic.parser.exceptions.ParseException;
-
-/**
- * The Main Window. Provides the basic application layout containing
- * a menu bar and space where other JavaFX elements can be placed.
- */
-public class MainWindow extends UiPart {
-
- private static final String FXML = "MainWindow.fxml";
-
- private final Logger logger = LogsCenter.getLogger(getClass());
-
- private Stage primaryStage;
- private Logic logic;
-
- // Independent Ui parts residing in this Ui container
- private PersonListPanel personListPanel;
- private ResultDisplay resultDisplay;
- private HelpWindow helpWindow;
-
- @FXML
- private StackPane commandBoxPlaceholder;
-
- @FXML
- private MenuItem helpMenuItem;
-
- @FXML
- private StackPane personListPanelPlaceholder;
-
- @FXML
- private StackPane resultDisplayPlaceholder;
-
- @FXML
- private StackPane statusbarPlaceholder;
-
- public MainWindow(Stage primaryStage, Logic logic) {
- super(FXML, primaryStage);
-
- // Set dependencies
- this.primaryStage = primaryStage;
- this.logic = logic;
-
- // Configure the UI
- setWindowDefaultSize(logic.getGuiSettings());
-
- setAccelerators();
-
- helpWindow = new HelpWindow();
- }
-
- public Stage getPrimaryStage() {
- return primaryStage;
- }
-
- private void setAccelerators() {
- setAccelerator(helpMenuItem, KeyCombination.valueOf("F1"));
- }
-
- /**
- * Sets the accelerator of a MenuItem.
- * @param keyCombination the KeyCombination value of the accelerator
- */
- private void setAccelerator(MenuItem menuItem, KeyCombination keyCombination) {
- menuItem.setAccelerator(keyCombination);
-
- /*
- * TODO: the code below can be removed once the bug reported here
- * https://bugs.openjdk.java.net/browse/JDK-8131666
- * is fixed in later version of SDK.
- *
- * According to the bug report, TextInputControl (TextField, TextArea) will
- * consume function-key events. Because CommandBox contains a TextField, and
- * ResultDisplay contains a TextArea, thus some accelerators (e.g F1) will
- * not work when the focus is in them because the key event is consumed by
- * the TextInputControl(s).
- *
- * For now, we add following event filter to capture such key events and open
- * help window purposely so to support accelerators even when focus is
- * in CommandBox or ResultDisplay.
- */
- getRoot().addEventFilter(KeyEvent.KEY_PRESSED, event -> {
- if (event.getTarget() instanceof TextInputControl && keyCombination.match(event)) {
- menuItem.getOnAction().handle(new ActionEvent());
- event.consume();
- }
- });
- }
-
- /**
- * Fills up all the placeholders of this window.
- */
- void fillInnerParts() {
- personListPanel = new PersonListPanel(logic.getFilteredPersonList());
- personListPanelPlaceholder.getChildren().add(personListPanel.getRoot());
-
- resultDisplay = new ResultDisplay();
- resultDisplayPlaceholder.getChildren().add(resultDisplay.getRoot());
-
- StatusBarFooter statusBarFooter = new StatusBarFooter(logic.getAddressBookFilePath());
- statusbarPlaceholder.getChildren().add(statusBarFooter.getRoot());
-
- CommandBox commandBox = new CommandBox(this::executeCommand);
- commandBoxPlaceholder.getChildren().add(commandBox.getRoot());
- }
-
- /**
- * Sets the default size based on {@code guiSettings}.
- */
- private void setWindowDefaultSize(GuiSettings guiSettings) {
- primaryStage.setHeight(guiSettings.getWindowHeight());
- primaryStage.setWidth(guiSettings.getWindowWidth());
- if (guiSettings.getWindowCoordinates() != null) {
- primaryStage.setX(guiSettings.getWindowCoordinates().getX());
- primaryStage.setY(guiSettings.getWindowCoordinates().getY());
- }
- }
-
- /**
- * Opens the help window or focuses on it if it's already opened.
- */
- @FXML
- public void handleHelp() {
- if (!helpWindow.isShowing()) {
- helpWindow.show();
- } else {
- helpWindow.focus();
- }
- }
-
- void show() {
- primaryStage.show();
- }
-
- /**
- * Closes the application.
- */
- @FXML
- private void handleExit() {
- GuiSettings guiSettings = new GuiSettings(primaryStage.getWidth(), primaryStage.getHeight(),
- (int) primaryStage.getX(), (int) primaryStage.getY());
- logic.setGuiSettings(guiSettings);
- helpWindow.hide();
- primaryStage.hide();
- }
-
- public PersonListPanel getPersonListPanel() {
- return personListPanel;
- }
-
- /**
- * Executes the command and returns the result.
- *
- * @see seedu.address.logic.Logic#execute(String)
- */
- private CommandResult executeCommand(String commandText) throws CommandException, ParseException {
- try {
- CommandResult commandResult = logic.execute(commandText);
- logger.info("Result: " + commandResult.getFeedbackToUser());
- resultDisplay.setFeedbackToUser(commandResult.getFeedbackToUser());
-
- if (commandResult.isShowHelp()) {
- handleHelp();
- }
-
- if (commandResult.isExit()) {
- handleExit();
- }
-
- return commandResult;
- } catch (CommandException | ParseException e) {
- logger.info("Invalid command: " + commandText);
- resultDisplay.setFeedbackToUser(e.getMessage());
- throw e;
- }
- }
-}
diff --git a/src/main/java/seedu/address/ui/PersonCard.java b/src/main/java/seedu/address/ui/PersonCard.java
deleted file mode 100644
index 0684b088868..00000000000
--- a/src/main/java/seedu/address/ui/PersonCard.java
+++ /dev/null
@@ -1,74 +0,0 @@
-package seedu.address.ui;
-
-import java.util.Comparator;
-
-import javafx.fxml.FXML;
-import javafx.scene.control.Label;
-import javafx.scene.layout.FlowPane;
-import javafx.scene.layout.HBox;
-import javafx.scene.layout.Region;
-import seedu.address.model.person.Person;
-
-/**
- * An UI component that displays information of a {@code Person}.
- */
-public class PersonCard extends UiPart {
-
- private static final String FXML = "PersonListCard.fxml";
-
- /**
- * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX.
- * As a consequence, UI elements' variable names cannot be set to such keywords
- * or an exception will be thrown by JavaFX during runtime.
- *
- * @see The issue on AddressBook level 4
- */
-
- public final Person person;
-
- @FXML
- private HBox cardPane;
- @FXML
- private Label name;
- @FXML
- private Label id;
- @FXML
- private Label phone;
- @FXML
- private Label address;
- @FXML
- private Label email;
- @FXML
- private FlowPane tags;
-
- public PersonCard(Person person, int displayedIndex) {
- super(FXML);
- this.person = person;
- id.setText(displayedIndex + ". ");
- name.setText(person.getName().fullName);
- phone.setText(person.getPhone().value);
- address.setText(person.getAddress().value);
- email.setText(person.getEmail().value);
- person.getTags().stream()
- .sorted(Comparator.comparing(tag -> tag.tagName))
- .forEach(tag -> tags.getChildren().add(new Label(tag.tagName)));
- }
-
- @Override
- public boolean equals(Object other) {
- // short circuit if same object
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof PersonCard)) {
- return false;
- }
-
- // state check
- PersonCard card = (PersonCard) other;
- return id.getText().equals(card.id.getText())
- && person.equals(card.person);
- }
-}
diff --git a/src/main/java/seedu/address/ui/PersonListPanel.java b/src/main/java/seedu/address/ui/PersonListPanel.java
deleted file mode 100644
index 1328917096e..00000000000
--- a/src/main/java/seedu/address/ui/PersonListPanel.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package seedu.address.ui;
-
-import java.util.logging.Logger;
-
-import javafx.collections.ObservableList;
-import javafx.fxml.FXML;
-import javafx.scene.control.ListCell;
-import javafx.scene.control.ListView;
-import javafx.scene.layout.Region;
-import seedu.address.commons.core.LogsCenter;
-import seedu.address.model.person.Person;
-
-/**
- * Panel containing the list of persons.
- */
-public class PersonListPanel extends UiPart {
- private static final String FXML = "PersonListPanel.fxml";
- private final Logger logger = LogsCenter.getLogger(PersonListPanel.class);
-
- @FXML
- private ListView personListView;
-
- public PersonListPanel(ObservableList personList) {
- super(FXML);
- personListView.setItems(personList);
- personListView.setCellFactory(listView -> new PersonListViewCell());
- }
-
- /**
- * Custom {@code ListCell} that displays the graphics of a {@code Person} using a {@code PersonCard}.
- */
- class PersonListViewCell extends ListCell {
- @Override
- protected void updateItem(Person person, boolean empty) {
- super.updateItem(person, empty);
-
- if (empty || person == null) {
- setGraphic(null);
- setText(null);
- } else {
- setGraphic(new PersonCard(person, getIndex() + 1).getRoot());
- }
- }
- }
-
-}
diff --git a/src/main/java/seedu/address/AppParameters.java b/src/main/java/seedu/ezwatchlist/AppParameters.java
similarity index 93%
rename from src/main/java/seedu/address/AppParameters.java
rename to src/main/java/seedu/ezwatchlist/AppParameters.java
index ab552c398f3..6d363fe116a 100644
--- a/src/main/java/seedu/address/AppParameters.java
+++ b/src/main/java/seedu/ezwatchlist/AppParameters.java
@@ -1,4 +1,4 @@
-package seedu.address;
+package seedu.ezwatchlist;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -7,8 +7,8 @@
import java.util.logging.Logger;
import javafx.application.Application;
-import seedu.address.commons.core.LogsCenter;
-import seedu.address.commons.util.FileUtil;
+import seedu.ezwatchlist.commons.core.LogsCenter;
+import seedu.ezwatchlist.commons.util.FileUtil;
/**
* Represents the parsed command-line parameters given to the application.
diff --git a/src/main/java/seedu/address/Main.java b/src/main/java/seedu/ezwatchlist/Main.java
similarity index 96%
rename from src/main/java/seedu/address/Main.java
rename to src/main/java/seedu/ezwatchlist/Main.java
index 052a5068631..5aad393a1d2 100644
--- a/src/main/java/seedu/address/Main.java
+++ b/src/main/java/seedu/ezwatchlist/Main.java
@@ -1,4 +1,4 @@
-package seedu.address;
+package seedu.ezwatchlist;
import javafx.application.Application;
diff --git a/src/main/java/seedu/address/MainApp.java b/src/main/java/seedu/ezwatchlist/MainApp.java
similarity index 56%
rename from src/main/java/seedu/address/MainApp.java
rename to src/main/java/seedu/ezwatchlist/MainApp.java
index e5cfb161b73..c2b49b30655 100644
--- a/src/main/java/seedu/address/MainApp.java
+++ b/src/main/java/seedu/ezwatchlist/MainApp.java
@@ -1,4 +1,4 @@
-package seedu.address;
+package seedu.ezwatchlist;
import java.io.IOException;
import java.nio.file.Path;
@@ -7,36 +7,40 @@
import javafx.application.Application;
import javafx.stage.Stage;
-import seedu.address.commons.core.Config;
-import seedu.address.commons.core.LogsCenter;
-import seedu.address.commons.core.Version;
-import seedu.address.commons.exceptions.DataConversionException;
-import seedu.address.commons.util.ConfigUtil;
-import seedu.address.commons.util.StringUtil;
-import seedu.address.logic.Logic;
-import seedu.address.logic.LogicManager;
-import seedu.address.model.AddressBook;
-import seedu.address.model.Model;
-import seedu.address.model.ModelManager;
-import seedu.address.model.ReadOnlyAddressBook;
-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.Storage;
-import seedu.address.storage.StorageManager;
-import seedu.address.storage.UserPrefsStorage;
-import seedu.address.ui.Ui;
-import seedu.address.ui.UiManager;
+import seedu.ezwatchlist.commons.core.Config;
+import seedu.ezwatchlist.commons.core.LogsCenter;
+import seedu.ezwatchlist.commons.core.Version;
+import seedu.ezwatchlist.commons.exceptions.DataConversionException;
+import seedu.ezwatchlist.commons.util.ConfigUtil;
+import seedu.ezwatchlist.commons.util.StringUtil;
+import seedu.ezwatchlist.logic.Logic;
+import seedu.ezwatchlist.logic.LogicManager;
+import seedu.ezwatchlist.model.Model;
+import seedu.ezwatchlist.model.ModelManager;
+import seedu.ezwatchlist.model.ReadOnlyUserPrefs;
+import seedu.ezwatchlist.model.ReadOnlyWatchList;
+import seedu.ezwatchlist.model.UserPrefs;
+import seedu.ezwatchlist.model.WatchList;
+import seedu.ezwatchlist.model.util.DataBaseUtil;
+import seedu.ezwatchlist.model.util.SampleDataUtil;
+import seedu.ezwatchlist.statistics.Statistics;
+import seedu.ezwatchlist.storage.DatabaseStorage;
+import seedu.ezwatchlist.storage.JsonDatabaseStorage;
+import seedu.ezwatchlist.storage.JsonUserPrefsStorage;
+import seedu.ezwatchlist.storage.JsonWatchListStorage;
+import seedu.ezwatchlist.storage.Storage;
+import seedu.ezwatchlist.storage.StorageManager;
+import seedu.ezwatchlist.storage.UserPrefsStorage;
+import seedu.ezwatchlist.storage.WatchListStorage;
+import seedu.ezwatchlist.ui.Ui;
+import seedu.ezwatchlist.ui.UiManager;
/**
* Runs the application.
*/
public class MainApp extends Application {
- public static final Version VERSION = new Version(0, 6, 0, true);
+ public static final Version VERSION = new Version(1, 3, 0, true);
private static final Logger logger = LogsCenter.getLogger(MainApp.class);
@@ -45,10 +49,11 @@ public class MainApp extends Application {
protected Storage storage;
protected Model model;
protected Config config;
+ protected Statistics statistics;
@Override
public void init() throws Exception {
- logger.info("=============================[ Initializing AddressBook ]===========================");
+ logger.info("=============================[ Initializing WatchList ]===========================");
super.init();
AppParameters appParameters = AppParameters.parse(getParameters());
@@ -56,41 +61,60 @@ 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);
+ WatchListStorage watchListStorage = new JsonWatchListStorage(userPrefs.getWatchListFilePath());
+ DatabaseStorage databaseStorage = new JsonDatabaseStorage(userPrefs.getDatabaseFilePath());
+ storage = new StorageManager(watchListStorage, databaseStorage, userPrefsStorage);
initLogging(config);
model = initModelManager(storage, userPrefs);
+ statistics = new Statistics(model);
+
logic = new LogicManager(model, storage);
- ui = new UiManager(logic);
+ ui = new UiManager(logic, statistics);
}
/**
- * 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 watchlist and {@code userPrefs}.
+ * The data from the sample watchlist will be used instead if {@code storage}'s watchlist is not found,
+ * or an empty watchlist will be used instead if errors occur when reading {@code storage}'s watchlist.
*/
private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) {
- Optional addressBookOptional;
- ReadOnlyAddressBook initialData;
+ Optional watchListOptional;
+ Optional databaseOptional;
+ ReadOnlyWatchList initialData;
+ ReadOnlyWatchList database;
+ try {
+ watchListOptional = storage.readWatchList();
+ if (!watchListOptional.isPresent()) {
+ logger.info("Data file not found. Will be starting with a sample WatchList");
+ }
+ initialData = watchListOptional.orElseGet(SampleDataUtil::getSampleWatchList);
+ } catch (DataConversionException e) {
+ logger.warning("Data file not in the correct format. Will be starting with an empty WatchList");
+ initialData = new WatchList();
+ } catch (IOException e) {
+ logger.warning("Problem while reading from the file. Will be starting with an empty WatchList");
+ initialData = new WatchList();
+ }
+
try {
- addressBookOptional = storage.readAddressBook();
- if (!addressBookOptional.isPresent()) {
- logger.info("Data file not found. Will be starting with a sample AddressBook");
+ databaseOptional = storage.readDatabase();
+ if (!databaseOptional.isPresent()) {
+ logger.info("Database not found. Will be starting with a new database");
}
- initialData = addressBookOptional.orElseGet(SampleDataUtil::getSampleAddressBook);
+ database = databaseOptional.orElseGet(DataBaseUtil::getShowDatabaseList);
} catch (DataConversionException e) {
- logger.warning("Data file not in the correct format. Will be starting with an empty AddressBook");
- initialData = new AddressBook();
+ logger.warning("Data file not in the correct format. Will be starting with an empty database");
+ database = new WatchList();
} catch (IOException e) {
- logger.warning("Problem while reading from the file. Will be starting with an empty AddressBook");
- initialData = new AddressBook();
+ logger.warning("Problem while reading from the file. Will be starting with an empty database");
+ database = new WatchList();
}
- return new ModelManager(initialData, userPrefs);
+ return new ModelManager(initialData, database, userPrefs);
}
private void initLogging(Config config) {
@@ -151,7 +175,7 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) {
+ "Using default user prefs");
initializedPrefs = new UserPrefs();
} catch (IOException e) {
- logger.warning("Problem while reading from the file. Will be starting with an empty AddressBook");
+ logger.warning("Problem while reading from the file. Will be starting with an empty WatchList");
initializedPrefs = new UserPrefs();
}
@@ -167,13 +191,13 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) {
@Override
public void start(Stage primaryStage) {
- logger.info("Starting AddressBook " + MainApp.VERSION);
+ logger.info("Starting WatchList " + MainApp.VERSION);
ui.start(primaryStage);
}
@Override
public void stop() {
- logger.info("============================ [ Stopping Address Book ] =============================");
+ logger.info("============================ [ Stopping WatchList ] =============================");
try {
storage.saveUserPrefs(model.getUserPrefs());
} catch (IOException e) {
diff --git a/src/main/java/seedu/ezwatchlist/api/exceptions/NoRecommendationsException.java b/src/main/java/seedu/ezwatchlist/api/exceptions/NoRecommendationsException.java
new file mode 100644
index 00000000000..8daef7ce8a7
--- /dev/null
+++ b/src/main/java/seedu/ezwatchlist/api/exceptions/NoRecommendationsException.java
@@ -0,0 +1,17 @@
+package seedu.ezwatchlist.api.exceptions;
+
+/**
+ * Represents an error when RecommendationEngine fails to return a recommendation.
+ */
+public class NoRecommendationsException extends Exception {
+ private String message;
+
+ public NoRecommendationsException(String message) {
+ super(message);
+ this.message = message;
+ }
+
+ public String getMessage() {
+ return this.message;
+ }
+}
diff --git a/src/main/java/seedu/ezwatchlist/api/exceptions/OnlineConnectionException.java b/src/main/java/seedu/ezwatchlist/api/exceptions/OnlineConnectionException.java
new file mode 100644
index 00000000000..a4f459c4c6b
--- /dev/null
+++ b/src/main/java/seedu/ezwatchlist/api/exceptions/OnlineConnectionException.java
@@ -0,0 +1,18 @@
+package seedu.ezwatchlist.api.exceptions;
+
+/**
+ * Represents an error when application fails to connect online to the API
+ */
+public class OnlineConnectionException extends Exception {
+ private String message;
+
+ public OnlineConnectionException(String message) {
+ super(message);
+ this.message = message;
+ }
+
+ public String getMessage() {
+ return this.message;
+ }
+}
+
diff --git a/src/main/java/seedu/ezwatchlist/api/model/ApiInterface.java b/src/main/java/seedu/ezwatchlist/api/model/ApiInterface.java
new file mode 100644
index 00000000000..3454e81d6c0
--- /dev/null
+++ b/src/main/java/seedu/ezwatchlist/api/model/ApiInterface.java
@@ -0,0 +1,88 @@
+package seedu.ezwatchlist.api.model;
+
+import java.util.List;
+import java.util.Set;
+
+import seedu.ezwatchlist.api.exceptions.NoRecommendationsException;
+import seedu.ezwatchlist.api.exceptions.OnlineConnectionException;
+import seedu.ezwatchlist.model.show.Genre;
+import seedu.ezwatchlist.model.show.Movie;
+import seedu.ezwatchlist.model.show.TvShow;
+
+/**
+ * Interface to retrieve information from online API.
+ * Methods used here will return the information for Movies and Tv Shows.
+ */
+public interface ApiInterface {
+
+ /**
+ * Returns a list of Movies from the API search method.
+ *
+ * @param name the name of the Movie that the user wants to search.
+ * @exception OnlineConnectionException when the user is not connected to the internet.
+ */
+ List getMovieByName(String name) throws OnlineConnectionException;
+
+ /**
+ * Returns a list of Tv Shows from the API search method.
+ *
+ * @param name the name of the Tv Show that the user wants to search.
+ * @exception OnlineConnectionException when the user is not connected to the internet.
+ */
+ List getTvShowByName(String name) throws OnlineConnectionException;
+
+ /**
+ * Retrieves a list of upcoming Movies in the API.
+ *
+ * @return a list of Movies that are upcoming from the API.
+ * @throws OnlineConnectionException when not connected to the internet.
+ */
+ List getUpcomingMovies() throws OnlineConnectionException;
+
+ /**
+ * Retrieves a list of recommended Movies based on the list of Movies the user has.
+ * @param userMovies list of Movies that belongs to the user.
+ * @param noOfRecommendations Number of recommendations returned. The method will attempt to reach that number,
+ * it will act as an upper limit to the amount of Movies returned.
+ * @return list of Movies that are recommended to the user.
+ * @throws OnlineConnectionException when not connected to the internet.
+ * @throws NoRecommendationsException when no recommendations can be generated.
+ */
+ List getMovieRecommendations(List userMovies, int noOfRecommendations)
+ throws OnlineConnectionException, NoRecommendationsException;
+
+ /**
+ * Retrieves a list of recommended Tv Shows based on the list of Tv Shows the user has.
+ * @param userTvShows list of Tv Shows that belongs to the user.
+ * @param noOfRecommendations Number of recommendations returned. The method will attempt to reach that number,
+ * it will act as an upper limit to the amount of Tv Shows returned.
+ * @return list of Tv Shows that are recommended to the user.
+ * @throws OnlineConnectionException when not connected to the internet.
+ * @throws NoRecommendationsException when no recommendations can be generated.
+ */
+ List getTvShowRecommendations(List userTvShows, int noOfRecommendations)
+ throws OnlineConnectionException, NoRecommendationsException;
+
+ /**
+ * Returns a list of Tv Shows from the API search method.
+ *
+ * @param genreSet the set of genres that the user wants to search.
+ * @throws OnlineConnectionException when not connected to the internet.
+ */
+ //List getTvShowByGenre(Set genreSet) throws OnlineConnectionException;
+
+ /**
+ * Returns a list of movies from the API search method.
+ *
+ * @param genreSet the set of genres that the user wants to search.
+ * @throws OnlineConnectionException when not connected to the internet.
+ */
+ List getMovieByGenre(Set genreSet) throws OnlineConnectionException;
+
+ /**
+ * Checks if the api is connected to the internet.
+ */
+ static boolean isConnected() {
+ return false;
+ }
+}
diff --git a/src/main/java/seedu/ezwatchlist/api/model/ApiManager.java b/src/main/java/seedu/ezwatchlist/api/model/ApiManager.java
new file mode 100644
index 00000000000..e27123709a2
--- /dev/null
+++ b/src/main/java/seedu/ezwatchlist/api/model/ApiManager.java
@@ -0,0 +1,220 @@
+package seedu.ezwatchlist.api.model;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import info.movito.themoviedbapi.TmdbApi;
+import info.movito.themoviedbapi.TvResultsPage;
+import info.movito.themoviedbapi.model.core.MovieResultsPage;
+import info.movito.themoviedbapi.tools.MovieDbException;
+import seedu.ezwatchlist.api.exceptions.NoRecommendationsException;
+import seedu.ezwatchlist.api.exceptions.OnlineConnectionException;
+import seedu.ezwatchlist.api.util.ApiUtil;
+import seedu.ezwatchlist.model.show.Genre;
+import seedu.ezwatchlist.model.show.Movie;
+import seedu.ezwatchlist.model.show.TvShow;
+
+/**
+ * Main class for the API to connect to the server
+ */
+public class ApiManager extends ApiUtil implements ApiInterface {
+ //API key is to connect with the TMDB server.
+ private static final String API_KEY = "44ed1d7975d7c699743229199b1fc26e";
+ private static final String CONNECTION_ERROR_MESSAGE = "Looks like you're not connected to the internet";
+ private TmdbApi apiCall;
+
+ /**
+ * Constructor for ApiMain object used to interact with the API.
+ *
+ * @throws OnlineConnectionException when not connected to the internet.
+ */
+ public ApiManager() throws OnlineConnectionException {
+ try {
+ apiCall = new TmdbApi(API_KEY);
+ } catch (MovieDbException e) {
+ //when not connected to the internet
+ notConnected();
+ }
+ }
+
+ /**
+ * Checks if the API is connected to the internet.
+ *
+ * @return true if connected to the API.
+ */
+ public static boolean isConnected() {
+ try {
+ new TmdbApi(API_KEY);
+ } catch (MovieDbException e) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Helper function to call when not connected to the API.
+ *
+ * @throws OnlineConnectionException when the method is called with an error message.
+ */
+ public static void notConnected() throws OnlineConnectionException {
+ throw new OnlineConnectionException(CONNECTION_ERROR_MESSAGE);
+ }
+
+ /**
+ * Retrieves a list of upcoming movies in the API.
+ *
+ * @return a list of movies that are upcoming from the API.
+ * @throws OnlineConnectionException when not connected to the internet.
+ */
+ public List getUpcomingMovies() throws OnlineConnectionException {
+ ArrayList movies = new ArrayList<>();
+ try {
+ MovieResultsPage upcoming = apiCall.getMovies().getUpcoming(null, null, null);
+ ApiUtil.extractMovies(movies, upcoming, apiCall);
+
+ return movies;
+ } catch (MovieDbException e) {
+ notConnected();
+ return movies;
+ }
+ }
+
+ /**
+ * Retrieves the movies from the API by the string given.
+ *
+ * @param name the name of the movie that the user wants to search.
+ * @return a list of movies that are returned from the API search call.
+ * @throws OnlineConnectionException when not connected to the internet.
+ */
+ public List getMovieByName(String name) throws OnlineConnectionException {
+ ArrayList movies = new ArrayList<>();
+ try {
+ MovieResultsPage page = apiCall.getSearch().searchMovie(name,
+ null, null, false, 1);
+
+
+ ApiUtil.extractMovies(movies, page, apiCall);
+
+ return movies;
+ } catch (MovieDbException e) {
+ notConnected();
+ return movies;
+ }
+ }
+
+ /**
+ * Retrieves a list of recommended Movies based on the list of Movies the user has.
+ * @param userMovies list of Movies that belongs to the user.
+ * @param noOfRecommendations Number of recommendations returned. The method will attempt to reach that number,
+ * it will act as an upper limit to the amount of Movies returned.
+ * @return list of Movies that are recommended to the user.
+ * @throws OnlineConnectionException when not connected to the internet.
+ * @throws NoRecommendationsException when no recommendations can be generated.
+ */
+ public List getMovieRecommendations(List userMovies, int noOfRecommendations)
+ throws OnlineConnectionException, NoRecommendationsException {
+ try {
+ RecommendationEngine recommendation = new RecommendationEngine(userMovies, null, apiCall);
+ return recommendation.getMovieRecommendations(noOfRecommendations);
+ } catch (MovieDbException e) {
+ notConnected();
+ return null;
+ }
+ }
+
+ /**
+ * Retrieves a list of recommended Tv Shows based on the list of Tv Shows the user has.
+ * @param userTvShows list of Tv Shows that belongs to the user.
+ * @param noOfRecommendations Number of recommendations returned. The method will attempt to reach that number,
+ * it will act as an upper limit to the amount of Tv Shows returned.
+ * @return list of Tv Shows that are recommended to the user.
+ * @throws OnlineConnectionException when not connected to the internet.
+ * @throws NoRecommendationsException when no recommendations can be generated.
+ */
+ public List getTvShowRecommendations(List userTvShows, int noOfRecommendations)
+ throws OnlineConnectionException, NoRecommendationsException {
+ try {
+ RecommendationEngine recommendation = new RecommendationEngine(null, userTvShows, apiCall);
+ return recommendation.getTvShowRecommendations(noOfRecommendations);
+ } catch (MovieDbException e) {
+ notConnected();
+ return null;
+ }
+ }
+
+ /**
+ * Retrieves the tv shows from the API by the string given.
+ *
+ * @param name the name of the tv show that the user wants to search.
+ * @return a list of tv shows that are returned from the API search call.
+ * @throws OnlineConnectionException when not connected to the internet.
+ */
+ public List getTvShowByName(String name) throws OnlineConnectionException {
+ ArrayList tvShows = new ArrayList<>();
+
+ try {
+ TvResultsPage page = apiCall.getSearch().searchTv(name, null, 1);
+
+ ApiUtil.extractTvShows(tvShows, page, apiCall);
+ return tvShows;
+ } catch (MovieDbException e) {
+ notConnected();
+ return tvShows;
+ }
+ }
+
+ /*public List getTvShowByGenre(Set genreSet) throws OnlineConnectionException {
+ ArrayList tvShows = new ArrayList<>();
+ try{
+ List genreList = apiCall.getGenre().getGenreList(null);
+ for (Genre genreSearched : genreSet) {
+ for (info.movito.themoviedbapi.model.Genre genreApi : genreList) {
+ if (genreApi.getName().toLowerCase().contains(genreSearched.getGenreName().toLowerCase())) {
+ int genreID = genreApi.getId();
+ apiCall.getGenre().getGenreMovies()
+ Discover discover = new Discover();
+ discover.includeAdult(false).withGenres(genreID);
+ MovieResultsPage tvPage = apiCall.getDiscover().getDiscover(discover);
+ ApiUtil.extractTvShows(tvShows, tvPage, apiCall);
+ }
+ }
+ }
+ return tvShows;
+ } catch (MovieDbException e) {
+ notConnected();
+ return tvShows;
+ }
+ }
+
+ */
+
+ /**
+ * Returns a list of movies from the API search method.
+ *
+ * @param genreSet the set of genres that the user wants to search.
+ * @throws OnlineConnectionException when not connected to the internet.
+ */
+ public List getMovieByGenre(Set genreSet) throws OnlineConnectionException {
+ ArrayList movies = new ArrayList<>();
+ try {
+
+ List genreList = apiCall.getGenre().getGenreList(null);
+ for (Genre genreSearched : genreSet) {
+ for (info.movito.themoviedbapi.model.Genre genreApi : genreList) {
+ if (genreApi.getName().toLowerCase().contains(genreSearched.getGenreName().toLowerCase())) {
+ int genreId = genreApi.getId();
+ MovieResultsPage moviePage = apiCall.getGenre().getGenreMovies(genreId, null, 1,
+ true);
+ ApiUtil.extractMovies(movies, moviePage, apiCall);
+ }
+ }
+ }
+
+ return movies;
+ } catch (MovieDbException e) {
+ notConnected();
+ return movies;
+ }
+ }
+}
diff --git a/src/main/java/seedu/ezwatchlist/api/model/ImageRetrieval.java b/src/main/java/seedu/ezwatchlist/api/model/ImageRetrieval.java
new file mode 100644
index 00000000000..b9fe88ad162
--- /dev/null
+++ b/src/main/java/seedu/ezwatchlist/api/model/ImageRetrieval.java
@@ -0,0 +1,128 @@
+package seedu.ezwatchlist.api.model;
+
+import static java.util.Objects.isNull;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.nio.file.FileAlreadyExistsException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.logging.Logger;
+
+import info.movito.themoviedbapi.TmdbApi;
+import info.movito.themoviedbapi.model.config.TmdbConfiguration;
+import info.movito.themoviedbapi.tools.MovieDbException;
+import seedu.ezwatchlist.api.exceptions.OnlineConnectionException;
+import seedu.ezwatchlist.commons.core.LogsCenter;
+
+/**
+ * A class to retrieve images from the internet to store in the user's cache.
+ */
+public class ImageRetrieval {
+ private static final Logger logger = LogsCenter.getLogger(ImageRetrieval.class);
+ private static final String DEFAULT_FILE_SIZE = "w300";
+ private static final String ROOT = defaultDirectory();
+ public static final String IMAGE_CACHE_LOCATION = ROOT + File.separator
+ + "Ezwatchlist" + File.separator + "posters";
+ private String imageUrl;
+ private String formattedFileName;
+
+ /**
+ * Creates an instance of a image retrieval used to download images online
+ *
+ * @param tmdbApi the tmdbApi object used to get the url
+ * @param filePath the url online to the image
+ * @param fileName the name of the show
+ * @throws OnlineConnectionException when not connected to the internet
+ */
+ public ImageRetrieval(TmdbApi tmdbApi, String filePath, String fileName)
+ throws OnlineConnectionException, IllegalArgumentException {
+ try {
+ if (!isNull(filePath)) {
+ TmdbConfiguration configuration = tmdbApi.getConfiguration();
+ imageUrl = configuration.getBaseUrl() + DEFAULT_FILE_SIZE + filePath;
+ String filterString = fileName.replaceAll("[^A-Za-z0-9\\[\\]]", "_");
+ formattedFileName = filterString + filePath.hashCode();
+ } else {
+ throw new IllegalArgumentException("Missing filepath");
+ }
+ } catch (MovieDbException e) {
+ throw new OnlineConnectionException("Internet Connection failed at Image Retrieval");
+ }
+ }
+
+ /**
+ * Retrieves the default directory of the platform
+ *
+ * @return the string path to the root folder
+ */
+ //adapted from
+ //https://stackoverflow.com/questions/6561172/find-directory-for-application-data-on-linux-and-macintosh
+ static String defaultDirectory() {
+ String os = System.getProperty("os.name").toLowerCase();
+ if (os.contains("win")) {
+ return System.getenv("APPDATA");
+ } else if (os.contains("mac")) {
+ return System.getProperty("user.home") + "/Library/Application Support";
+ } else if (os.contains("nux")) {
+ return System.getProperty("user.home");
+ } else {
+ return System.getProperty("user.dir");
+ }
+ }
+
+ /**
+ * Retrieves the image online by downloading it into the save folder
+ *
+ * @return the string path of the file
+ * @throws OnlineConnectionException when not connected to the internet
+ */
+ public String retrieveImage() throws OnlineConnectionException {
+ try {
+ downloadImage();
+ } finally {
+ return formattedFileName + ".png";
+ }
+ }
+
+ /**
+ * Returns the online url from this object instance.
+ *
+ * @return the online url
+ */
+ public String getImageUrl() {
+ return imageUrl;
+ }
+
+ /**
+ * Downloads the image from the online url. Is a helper method.
+ * @throws OnlineConnectionException when not connected online.
+ */
+ private void downloadImage() throws OnlineConnectionException {
+ try (InputStream in = new URL(imageUrl).openStream()) {
+ File parent = new File(IMAGE_CACHE_LOCATION);
+
+ if (!parent.exists()) {
+ parent.mkdirs();
+ }
+
+ Path filepath = Paths.get(IMAGE_CACHE_LOCATION + File.separator
+ + formattedFileName + ".png");
+
+ if (filepath.toFile().exists()) {
+ throw new FileAlreadyExistsException("Duplicate image");
+ }
+
+ Files.copy(in, filepath);
+
+ } catch (FileAlreadyExistsException f) {
+ logger.info(f.getMessage());
+ } catch (IOException e) {
+ logger.info(e.getMessage());
+ throw new OnlineConnectionException("No internet connection at downloading image");
+ }
+ }
+}
diff --git a/src/main/java/seedu/ezwatchlist/api/model/RecommendationEngine.java b/src/main/java/seedu/ezwatchlist/api/model/RecommendationEngine.java
new file mode 100644
index 00000000000..595d5062775
--- /dev/null
+++ b/src/main/java/seedu/ezwatchlist/api/model/RecommendationEngine.java
@@ -0,0 +1,258 @@
+package seedu.ezwatchlist.api.model;
+
+import static java.util.Map.Entry.comparingByValue;
+import static java.util.Objects.isNull;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+import info.movito.themoviedbapi.TmdbApi;
+import info.movito.themoviedbapi.TmdbMovies;
+import info.movito.themoviedbapi.TmdbTV;
+import info.movito.themoviedbapi.TvResultsPage;
+import info.movito.themoviedbapi.model.MovieDb;
+import info.movito.themoviedbapi.model.core.MovieResultsPage;
+import info.movito.themoviedbapi.model.core.ResultsPage;
+import info.movito.themoviedbapi.model.tv.TvSeries;
+import info.movito.themoviedbapi.tools.MovieDbException;
+
+import seedu.ezwatchlist.api.exceptions.NoRecommendationsException;
+import seedu.ezwatchlist.api.exceptions.OnlineConnectionException;
+import seedu.ezwatchlist.api.util.ApiUtil;
+import seedu.ezwatchlist.model.show.Movie;
+import seedu.ezwatchlist.model.show.Name;
+import seedu.ezwatchlist.model.show.TvShow;
+
+/**
+ * Class used to generate recommendations based on list that the user has. The recommendations are sorted based on the
+ * likelihood that the recommendations are more suited to the user, with the best recommendation being the first entry
+ * in the list.
+ */
+public class RecommendationEngine extends ApiUtil {
+ private final List userMovies;
+ private final List userTvShows;
+ private List userMoviesId;
+ private List userTvShowsId;
+ private final TmdbApi tmdbApi;
+ private HashMap movieRecommendationOccurrences;
+ private HashMap tvRecommendationOccurrences;
+ private List movieRecommendations;
+ private List tvShowRecommendations;
+
+ /**
+ * Creates an instance of RecommendationEngine used to generate recommendations for the user.
+ * @param movies @nullable the list of Movies the user has.
+ * @param tvShows @nullable the list of Tv Shows the user has.
+ * @param tmdbApi the Api call to retrieve online information.
+ */
+ public RecommendationEngine(@Nullable List movies, @Nullable List tvShows, TmdbApi tmdbApi)
+ throws IllegalArgumentException {
+ if (isNull(tmdbApi)) {
+ throw new IllegalArgumentException();
+ }
+
+ userMovies = movies;
+ userTvShows = tvShows;
+ userMoviesId = new LinkedList<>();
+ userTvShowsId = new LinkedList<>();
+ this.tmdbApi = tmdbApi;
+ movieRecommendationOccurrences = new HashMap<>();
+ tvRecommendationOccurrences = new HashMap<>();
+ movieRecommendations = new LinkedList<>();
+ tvShowRecommendations = new LinkedList<>();
+ }
+
+ /**
+ * Generates Movie recommendations, returning a sorted list of recommendations.
+ * @param noOfRecommendations the upper limit of recommendations returned.
+ * @return Sorted list of recommendations, with the best recommendation in the first entry.
+ * @throws NoRecommendationsException thrown when unable to generate recommendations.
+ * @throws OnlineConnectionException thrown when not connected to the internet.
+ */
+ public List getMovieRecommendations(int noOfRecommendations)
+ throws NoRecommendationsException, OnlineConnectionException {
+ if (movieRecommendations.size() != noOfRecommendations) {
+ validForRecommendations(isNull(userMovies), "Movies from the user is null, unable to");
+ validForRecommendations(userMovies.isEmpty(), "No movies from the user to");
+
+ parseUserMovies();
+ filterRecommendations(true);
+ sortMovieRecommendations(noOfRecommendations);
+
+ validForRecommendations(movieRecommendationOccurrences.isEmpty(), "Unable to");
+ }
+
+ return movieRecommendations;
+ }
+
+ /**
+ * Filters the recommendations by removing all of the user's movies and tv shows if present in the list.
+ * This is to prevent recommending something the user already has.
+ * @param isMovie to filter the movie list or tv show list.
+ */
+ private void filterRecommendations(boolean isMovie) {
+ List idList = isMovie ? userMoviesId : userTvShowsId;
+ HashMap recommendationOccurrences = isMovie ? movieRecommendationOccurrences
+ : tvRecommendationOccurrences;
+
+ for (Integer id : idList) {
+ recommendationOccurrences.remove(id);
+ }
+ }
+
+ /**
+ * Helper method to check if recommendations are valid.
+ * @param check the boolean check.
+ * @param prefix the prefix for the error message.
+ * @throws NoRecommendationsException thrown if invalid.
+ */
+ private void validForRecommendations(boolean check, String prefix) throws NoRecommendationsException {
+ if (check) {
+ throw new NoRecommendationsException(prefix
+ + " generate recommendations");
+ }
+ }
+
+ /**
+ * Generates TV Shows recommendations, returning a sorted list of recommendations.
+ * @param noOfRecommendations the upper limit of recommendations returned.
+ * @return Sorted list of recommendations, with the best recommendation in the first entry.
+ * @throws NoRecommendationsException thrown when unable to generate recommendations.
+ * @throws OnlineConnectionException thrown when not connected to the internet.
+ */
+ public List getTvShowRecommendations(int noOfRecommendations)
+ throws NoRecommendationsException, OnlineConnectionException {
+ if (tvShowRecommendations.size() != noOfRecommendations) {
+ validForRecommendations(isNull(userTvShows), "TvShows from the user is null, unable to");
+ validForRecommendations(userTvShows.isEmpty(), "No TvShows from the user to");
+
+ parseUserTvShows();
+ filterRecommendations(false);
+ sortTvShowRecommendations(noOfRecommendations);
+
+ validForRecommendations(tvRecommendationOccurrences.isEmpty(), "Unable to");
+ }
+
+ return tvShowRecommendations;
+ }
+
+ /**
+ * Sorts and adds the recommendations to the list of recommendations.
+ * @param noOfRecommendations the upper limit of recommendations.
+ */
+ private void sortMovieRecommendations(int noOfRecommendations) {
+ movieRecommendationOccurrences.entrySet().stream()
+ .sorted(comparingByValue())
+ .limit(noOfRecommendations)
+ .forEachOrdered(entry -> movieRecommendations.add(ApiUtil.getMovie(tmdbApi, entry.getKey())));
+ }
+
+ /**
+ * Sorts and adds the recommendations to the list of recommendations.
+ * @param noOfRecommendations the upper limit of recommendations.
+ */
+ private void sortTvShowRecommendations(int noOfRecommendations) {
+ tvRecommendationOccurrences.entrySet().stream()
+ .sorted(comparingByValue())
+ .limit(noOfRecommendations)
+ .forEachOrdered(entry -> tvShowRecommendations.add(ApiUtil.getTvShow(tmdbApi, entry.getKey())));
+ }
+
+ /**
+ * Parses the user's Tv Shows to get it's respective entry in the online database. A recommendation is then
+ * retrieved and added to the map of recommendations based on the number of occurrences.
+ * @throws OnlineConnectionException when not connected to the internet.
+ */
+ private void parseUserTvShows() throws OnlineConnectionException {
+ try {
+ for (TvShow tvShow : userTvShows) {
+ String tvShowName = tvShow.getName().showName;
+
+ if (isInvalidName(tvShowName)) {
+ continue;
+ }
+
+ TvResultsPage tvDbs = tmdbApi.getSearch().searchTv(tvShowName, null, 1);
+ List tvResults = tvDbs.getResults();
+
+ if (tvResults.isEmpty()) {
+ continue;
+ }
+
+ int tvId = tvResults.get(0).getId(); //retrieves the first Tv Show that matches the name.
+ userTvShowsId.add(tvId); //adds the tv show id to a list so that a final filter can take place
+
+ TvSeries series = tmdbApi.getTvSeries().getSeries(tvId, null, TmdbTV.TvMethod.recommendations);
+ ResultsPage recommendations = series.getRecommendations();
+ List results = recommendations.getResults();
+ results.forEach((tvSeries) -> addToRecommendations(tvSeries.getId(), false));
+ }
+ } catch (MovieDbException e) {
+ ApiManager.notConnected();
+ }
+ }
+
+ /**
+ * Parses the user's Movies to get it's respective entry in the online database. A recommendation is then
+ * retrieved and added to the map of recommendations based on the number of occurrences.
+ * @throws OnlineConnectionException when not connected to the internet.
+ */
+ private void parseUserMovies() throws OnlineConnectionException {
+ try {
+ for (Movie m : userMovies) {
+ String movieName = m.getName().showName;
+
+ if (isInvalidName(movieName)) {
+ continue;
+ }
+
+ MovieResultsPage movieDbs = tmdbApi.getSearch()
+ .searchMovie(movieName, null, null, true, 1);
+ List movieDbsResults = movieDbs.getResults();
+
+ if (movieDbsResults.isEmpty()) {
+ continue;
+ }
+
+ int movieId = movieDbsResults.get(0).getId(); //retrieves the first Movie that matches the name.
+ userMoviesId.add(movieId); //adds the movie id to a list so that a final filter can take place
+
+ MovieDb movieDb = tmdbApi.getMovies().getMovie(movieId, null, TmdbMovies.MovieMethod.recommendations);
+ List similarMovies = movieDb.getRecommendations();
+ if (!isNull(similarMovies)) {
+ similarMovies.forEach((movie) -> addToRecommendations(movie.getId(), true));
+ }
+ }
+ } catch (MovieDbException e) {
+ ApiManager.notConnected();
+ }
+ }
+
+ /**
+ * Checks if the name is invalid.
+ * @param showName the Name to be checked.
+ * @return true if invalid.
+ */
+ private boolean isInvalidName(String showName) {
+ return showName.equals(Name.DEFAULT_NAME);
+ }
+
+ /**
+ * Adds an entry to the respective map. Increasing the occurrences if there is a duplicate.
+ * @param id the id of the Show.
+ * @param isMovie if the id belongs to a Movie.
+ */
+ private void addToRecommendations(int id, boolean isMovie) {
+ HashMap showOccurrences =
+ isMovie ? movieRecommendationOccurrences : tvRecommendationOccurrences;
+ if (showOccurrences.containsKey(id)) {
+ Integer numberOfOccurrences = showOccurrences.remove(id);
+ showOccurrences.put(id, numberOfOccurrences + 1);
+ } else {
+ showOccurrences.put(id, 1);
+ }
+ }
+}
diff --git a/src/main/java/seedu/ezwatchlist/api/util/ApiUtil.java b/src/main/java/seedu/ezwatchlist/api/util/ApiUtil.java
new file mode 100644
index 00000000000..ef05f9ade0f
--- /dev/null
+++ b/src/main/java/seedu/ezwatchlist/api/util/ApiUtil.java
@@ -0,0 +1,333 @@
+package seedu.ezwatchlist.api.util;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import info.movito.themoviedbapi.TmdbApi;
+import info.movito.themoviedbapi.TmdbMovies;
+import info.movito.themoviedbapi.TmdbTV;
+import info.movito.themoviedbapi.TmdbTvSeasons;
+import info.movito.themoviedbapi.TvResultsPage;
+import info.movito.themoviedbapi.model.Credits;
+import info.movito.themoviedbapi.model.Genre;
+import info.movito.themoviedbapi.model.MovieDb;
+import info.movito.themoviedbapi.model.core.MovieResultsPage;
+import info.movito.themoviedbapi.model.people.PersonCast;
+import info.movito.themoviedbapi.model.tv.TvEpisode;
+import info.movito.themoviedbapi.model.tv.TvSeason;
+import info.movito.themoviedbapi.model.tv.TvSeries;
+
+import seedu.ezwatchlist.api.exceptions.OnlineConnectionException;
+import seedu.ezwatchlist.api.model.ImageRetrieval;
+import seedu.ezwatchlist.model.actor.Actor;
+import seedu.ezwatchlist.model.show.Date;
+import seedu.ezwatchlist.model.show.Description;
+import seedu.ezwatchlist.model.show.Episode;
+import seedu.ezwatchlist.model.show.IsWatched;
+import seedu.ezwatchlist.model.show.Movie;
+import seedu.ezwatchlist.model.show.Name;
+import seedu.ezwatchlist.model.show.Poster;
+import seedu.ezwatchlist.model.show.RunningTime;
+import seedu.ezwatchlist.model.show.Show;
+import seedu.ezwatchlist.model.show.TvShow;
+
+/**
+ * Contains utility methods for extracting information from movies and tv shows.
+ */
+public class ApiUtil {
+
+ /**
+ * Passes the movies from the movies page into the movies list with the new Movie model used in the application.
+ *
+ * @param movies list of movies to be added into.
+ * @param page results from the API database.
+ * @param apiCall API call to retrieve more information.
+ * @throws OnlineConnectionException when not connected to the internet.
+ */
+ public static void extractMovies(ArrayList movies, MovieResultsPage page,
+ TmdbApi apiCall) throws OnlineConnectionException {
+ for (MovieDb m : page.getResults()) {
+ Movie toAdd = extractMovie(apiCall, m);
+ movies.add(toAdd);
+ }
+ }
+
+ /**
+ * Morphs the MovieDb model into Movie model.
+ * @param apiCall API call to retrieve more information.
+ * @param movieDb model used by the API.
+ * @return Movie used by the application.
+ * @throws OnlineConnectionException when not connected to the internet/
+ */
+ public static Movie extractMovie(TmdbApi apiCall, MovieDb movieDb) throws OnlineConnectionException {
+ Name movieName = new Name(movieDb.getTitle());
+ final int movieId = movieDb.getId(); //movie id to retrieve instance
+ TmdbMovies apiMovie = apiCall.getMovies();
+ //gets the instance of the movie in the database
+ MovieDb movie = apiMovie.getMovie(movieId, null, TmdbMovies.MovieMethod.credits);
+
+ //movie fields
+ RunningTime runtime = new RunningTime(movie.getRuntime());
+ Description overview = new Description(movie.getOverview());
+ Date releaseDate = new Date(movie.getReleaseDate());
+ IsWatched isWatched = new IsWatched("false");
+
+ //actors
+ Set actors = getActors(movie.getCast());
+
+ Movie toAdd = new Movie(movieName, overview,
+ isWatched, releaseDate, runtime, actors);
+
+ //retrieve image
+ addImage(movieDb.getPosterPath(), movieName, toAdd, apiCall);
+
+ //genres
+ setGenres(movie.getGenres(), toAdd);
+ return toAdd;
+ }
+
+ /**
+ * Uses the ImageRetrieval class to set an image to the show.
+ *
+ * @param posterPath path of the image online
+ * @param name name of the show
+ * @param toAdd the show to be modified
+ * @param apiCall the API call used
+ * @throws OnlineConnectionException when not connected to the internet.
+ */
+ private static void addImage(String posterPath, Name name, Show toAdd, TmdbApi apiCall)
+ throws OnlineConnectionException {
+ try {
+ ImageRetrieval instance = new ImageRetrieval(apiCall, posterPath, name.showName);
+ String imagePath = instance.retrieveImage();
+ toAdd.setPoster(new Poster(imagePath));
+ } catch (IllegalArgumentException e) {
+ toAdd.setPoster(new Poster());
+ }
+ }
+
+ /**
+ * Passes the Tv Shows from the Tv Show page into the tvShow list with the new TvShow model used in the application.
+ *
+ * @param tvShows list of tvShows to be added into.
+ * @param page results from the API database.
+ * @param apiCall API call to retrieve more information.
+ * @throws OnlineConnectionException when not connected to the internet.
+ */
+ public static void extractTvShows(ArrayList tvShows, TvResultsPage page,
+ TmdbApi apiCall) throws OnlineConnectionException {
+ for (TvSeries tv : page.getResults()) {
+ TvShow tvShowToAdd = extractTvShow(apiCall, tv);
+
+ tvShows.add(tvShowToAdd);
+ }
+ }
+
+ /**
+ * Morphs the TvSeries model into TvShow model.
+ * @param apiCall API call to retrieve more information.
+ * @param tv model used by the API.
+ * @return TvShow model used by the application.
+ * @throws OnlineConnectionException when not connected to the internet.
+ */
+ public static TvShow extractTvShow(TmdbApi apiCall, TvSeries tv) throws OnlineConnectionException {
+ final int tvId = tv.getId();
+ TmdbTV apiCallTvSeries = apiCall.getTvSeries();
+ TvSeries series = apiCallTvSeries.getSeries(tvId, null);
+ TmdbTvSeasons tvSeasons = apiCall.getTvSeasons();
+ final int numberOfSeasons = series.getNumberOfSeasons();
+
+ //seasons
+ ArrayList seasonsList = new ArrayList<>();
+ extractSeasons(tvId, tvSeasons, numberOfSeasons, seasonsList);
+
+ //runtime
+ List episodeRuntime = series.getEpisodeRuntime();
+ RunningTime runTime = new RunningTime(episodeRuntime.isEmpty() ? 0 : getAverageRuntime(episodeRuntime));
+
+ //tv show fields.
+ Name tvName = new Name(tv.getName()); //name
+ Date date = new Date(series.getFirstAirDate()); //date of release
+ int totalNumOfEpisodes = getTotalNumOfEpisodes(seasonsList);
+ Description description = new Description(tv.getOverview()); //description
+ IsWatched isWatched = new IsWatched("false");
+
+ //actors
+ Credits credits = apiCallTvSeries.getCredits(tvId, null);
+ Set actors = getActors(credits.getCast());
+
+ TvShow tvShowToAdd = new TvShow(tvName, description,
+ isWatched, date, runTime,
+ actors, 0, totalNumOfEpisodes, seasonsList);
+
+ //image
+ addImage(tv.getPosterPath(), tvName, tvShowToAdd, apiCall);
+
+ //genres
+ setGenres(series.getGenres(), tvShowToAdd);
+ return tvShowToAdd;
+ }
+
+ /**
+ * Extracts the seasons into the season list.
+ *
+ * @param tvId id of the TV Show in the database.
+ * @param tvSeasons the api TvSeason database.
+ * @param numberOfSeasons number of seasons of the show.
+ * @param seasonsList list to be added into.
+ */
+ private static void extractSeasons(int tvId, TmdbTvSeasons tvSeasons, int numberOfSeasons,
+ ArrayList seasonsList) {
+ for (int seasonNo = 1; seasonNo <= numberOfSeasons; seasonNo++) {
+ TvSeason tvSeason = tvSeasons.getSeason(tvId, seasonNo,
+ null);
+
+ seedu.ezwatchlist.model.show.TvSeason season = extractEpisodes(tvSeason);
+
+ seasonsList.add(season);
+ }
+ }
+
+ /**
+ * Extracts the episodes of each season.
+ *
+ * @param tvSeason the data from the API of the season.
+ * @return a season list with the episodes added in.
+ */
+ private static seedu.ezwatchlist.model.show.TvSeason extractEpisodes(TvSeason tvSeason) {
+ List episodes = tvSeason.getEpisodes();
+ ArrayList episodeList = new ArrayList<>();
+
+ for (TvEpisode episode : episodes) {
+ episodeList.add(new Episode(
+ episode.getName(), episode.getEpisodeNumber()));
+ }
+
+ return new seedu.ezwatchlist.model.show.TvSeason(tvSeason.getSeasonNumber(), episodes.size(),
+ episodeList);
+ }
+
+ /**
+ * Sets the genres taken from the API.
+ *
+ * @param genres the genre list to be added into
+ * @param tvShowToAdd the show to be modified.
+ */
+ private static void setGenres(List genres, Show tvShowToAdd) {
+ ArrayList genreList = new ArrayList<>();
+ genres.forEach(genre -> genreList.add(new seedu.ezwatchlist.model.show.Genre(genre.getName())));
+ Set genreSet = new HashSet<>(genreList);
+ tvShowToAdd.addGenres(genreSet);
+ }
+
+ /**
+ * Calculates the average runtime.
+ *
+ * @param episodesRuntime list of runtimes
+ * @return the average of the runtime.
+ */
+ private static int getAverageRuntime(List episodesRuntime) {
+ float totalRuntime = 0;
+ int noOfEpisodes = episodesRuntime.size();
+
+ for (int i = 0; i < noOfEpisodes; i++) {
+ int individualRuntime = episodesRuntime.get(i);
+ totalRuntime += individualRuntime;
+ }
+
+ int averageRunTime = Math.round(totalRuntime / noOfEpisodes);
+ return averageRunTime;
+ }
+
+ /**
+ * Retrieves the actors from the online database.
+ *
+ * @param cast the list of cast taken online.
+ * @return a set of actors.
+ */
+ private static Set getActors(List cast) {
+ Set actors = new HashSet<>();
+ for (PersonCast personCast : cast) {
+ Actor actor = new Actor(personCast.getName());
+ actors.add(actor);
+ }
+ return actors;
+ }
+
+ /**
+ * Returns the total number of episodes
+ *
+ * @param tvSeasons the online tv season database
+ * @return total number of episodes
+ */
+ private static int getTotalNumOfEpisodes(List tvSeasons) {
+ int totalEpisodes = 0;
+ for (seedu.ezwatchlist.model.show.TvSeason season : tvSeasons) {
+ totalEpisodes += season.getEpisodes().size();
+ }
+ return totalEpisodes;
+ }
+
+ /**
+ * Filters the list of Shows to a list of Movies.
+ * @param shows the list of Shows.
+ * @return a list of movies from the Shows.
+ */
+ public static List filterToMovieFromShow(List shows) {
+ List movies = new LinkedList<>();
+ for (Show show: shows) {
+ if (show.getType().equals("Movie")) {
+ movies.add((Movie) show);
+ }
+ }
+ return movies;
+ }
+
+ /**
+ * Filters the list of Shows to a list of Tv Shows.
+ * @param shows the list of Shows.
+ * @return a list of Tv Shows from the Shows.
+ */
+ public static List filterToTvShowsFromShow(List shows) {
+ List tvShows = new LinkedList<>();
+ for (Show show: shows) {
+ if (show.getType().equals("Tv Show")) {
+ tvShows.add((TvShow) show);
+ }
+ }
+ return tvShows;
+ }
+
+ /**
+ * Retrieves a Tv Show from it's ID
+ * @param tmdbApi
+ * @param tvId the ID of the Tv Show
+ * @return TvShow
+ */
+ public static TvShow getTvShow(TmdbApi tmdbApi, Integer tvId) {
+ try {
+ TvSeries tvSeries = tmdbApi.getTvSeries().getSeries(tvId, null, TmdbTV.TvMethod.values());
+ return extractTvShow(tmdbApi, tvSeries);
+ } catch (OnlineConnectionException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Retrieves a Movie from it's ID.
+ * @param tmdbApi
+ * @param movieId the ID of the Movie.
+ * @return Movie
+ */
+ public static Movie getMovie(TmdbApi tmdbApi, Integer movieId) {
+ try {
+ MovieDb movie = tmdbApi.getMovies().getMovie(movieId, null, TmdbMovies.MovieMethod.values());
+ return extractMovie(tmdbApi, movie);
+ } catch (OnlineConnectionException e) {
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/commons/core/Config.java b/src/main/java/seedu/ezwatchlist/commons/core/Config.java
similarity index 97%
rename from src/main/java/seedu/address/commons/core/Config.java
rename to src/main/java/seedu/ezwatchlist/commons/core/Config.java
index 91145745521..d7b5e691dd3 100644
--- a/src/main/java/seedu/address/commons/core/Config.java
+++ b/src/main/java/seedu/ezwatchlist/commons/core/Config.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.core;
+package seedu.ezwatchlist.commons.core;
import java.nio.file.Path;
import java.nio.file.Paths;
diff --git a/src/main/java/seedu/address/commons/core/GuiSettings.java b/src/main/java/seedu/ezwatchlist/commons/core/GuiSettings.java
similarity index 98%
rename from src/main/java/seedu/address/commons/core/GuiSettings.java
rename to src/main/java/seedu/ezwatchlist/commons/core/GuiSettings.java
index 5ace559ad15..adac4c223b7 100644
--- a/src/main/java/seedu/address/commons/core/GuiSettings.java
+++ b/src/main/java/seedu/ezwatchlist/commons/core/GuiSettings.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.core;
+package seedu.ezwatchlist.commons.core;
import java.awt.Point;
import java.io.Serializable;
diff --git a/src/main/java/seedu/address/commons/core/LogsCenter.java b/src/main/java/seedu/ezwatchlist/commons/core/LogsCenter.java
similarity index 95%
rename from src/main/java/seedu/address/commons/core/LogsCenter.java
rename to src/main/java/seedu/ezwatchlist/commons/core/LogsCenter.java
index 431e7185e76..59e8eb50dd5 100644
--- a/src/main/java/seedu/address/commons/core/LogsCenter.java
+++ b/src/main/java/seedu/ezwatchlist/commons/core/LogsCenter.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.core;
+package seedu.ezwatchlist.commons.core;
import java.io.IOException;
import java.util.Arrays;
@@ -18,7 +18,7 @@
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 final String LOG_FILE = "watchlist.log";
private static Level currentLogLevel = Level.INFO;
private static final Logger logger = LogsCenter.getLogger(LogsCenter.class);
private static FileHandler fileHandler;
@@ -109,4 +109,8 @@ private static ConsoleHandler createConsoleHandler() {
consoleHandler.setLevel(currentLogLevel);
return consoleHandler;
}
+
+ public static Level getCurrentLogLevel() {
+ return currentLogLevel;
+ }
}
diff --git a/src/main/java/seedu/address/commons/core/Version.java b/src/main/java/seedu/ezwatchlist/commons/core/Version.java
similarity index 98%
rename from src/main/java/seedu/address/commons/core/Version.java
rename to src/main/java/seedu/ezwatchlist/commons/core/Version.java
index e117f91b3b2..0bea159f355 100644
--- a/src/main/java/seedu/address/commons/core/Version.java
+++ b/src/main/java/seedu/ezwatchlist/commons/core/Version.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.core;
+package seedu.ezwatchlist.commons.core;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
diff --git a/src/main/java/seedu/address/commons/core/index/Index.java b/src/main/java/seedu/ezwatchlist/commons/core/index/Index.java
similarity index 97%
rename from src/main/java/seedu/address/commons/core/index/Index.java
rename to src/main/java/seedu/ezwatchlist/commons/core/index/Index.java
index 19536439c09..315be3dc11a 100644
--- a/src/main/java/seedu/address/commons/core/index/Index.java
+++ b/src/main/java/seedu/ezwatchlist/commons/core/index/Index.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.core.index;
+package seedu.ezwatchlist.commons.core.index;
/**
* Represents a zero-based or one-based index.
diff --git a/src/main/java/seedu/ezwatchlist/commons/core/messages/Messages.java b/src/main/java/seedu/ezwatchlist/commons/core/messages/Messages.java
new file mode 100644
index 00000000000..786ab6abbad
--- /dev/null
+++ b/src/main/java/seedu/ezwatchlist/commons/core/messages/Messages.java
@@ -0,0 +1,16 @@
+package seedu.ezwatchlist.commons.core.messages;
+
+/**
+ * Container for user visible messages.
+ */
+public class Messages {
+
+ public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command";
+ public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s";
+ public static final String MESSAGE_INVALID_SHOW_DISPLAYED_INDEX = "The show index provided is invalid";
+ public static final String MESSAGE_SYNC_INVALID_INDEX = "The INDEX only refers to the results found in"
+ + " search result page. Please"
+ + " ensure the index is valid.";
+ public static final String MESSAGE_INVALID_COMMAND = "Can't execute that command in this tab. Please switch over "
+ + "to the watchlist or watched tab and enter the command";
+}
diff --git a/src/main/java/seedu/ezwatchlist/commons/core/messages/SearchMessages.java b/src/main/java/seedu/ezwatchlist/commons/core/messages/SearchMessages.java
new file mode 100644
index 00000000000..5fb1f6562cc
--- /dev/null
+++ b/src/main/java/seedu/ezwatchlist/commons/core/messages/SearchMessages.java
@@ -0,0 +1,38 @@
+package seedu.ezwatchlist.commons.core.messages;
+
+/**
+ * Container for user visible messages when executing the search command.
+ */
+public class SearchMessages {
+ public static final String MESSAGE_USAGE =
+ "Search : Searches for shows whose names contain any of the given keywords from the watchlist, "
+ + "watched list and internal/online database.\n"
+ + "- by name: search n/SHOW_NAME… [g/GENRE]… [a/ACTOR_NAME]… [o/FROM_ONLINE] [t/SHOW_TYPE] "
+ + "[w/HAS_WATCHED]\n"
+ + "- by genre: search g/GENRE… [n/SHOW_NAME]… [a/ACTOR_NAME]… [o/FROM_ONLINE] [t/SHOW_TYPE] "
+ + "[w/HAS_WATCHED]\n"
+ + "- by actor: search a/ACTOR_NAME… [n/SHOW_NAME]… [g/GENRE]… [o/FROM_ONLINE] [t/SHOW_TYPE] "
+ + "[w/HAS_WATCHED]\n"
+ + "Enter 'help' command for more detailed examples.";
+
+ public static final String MESSAGE_SHOWS_FOUND_OVERVIEW = "%1$d shows found!";
+
+ public static final String MESSAGE_INTERNAL_SHOW_LISTED_OVERVIEW = "You are offline. "
+ + "If required, shows would be searched from the internal database instead of the online database.\n"
+ + MESSAGE_SHOWS_FOUND_OVERVIEW;
+
+ public static final String MESSAGE_INVALID_FROM_ONLINE_COMMAND =
+ "Invalid input. For o/[OPTION], OPTION can only be 'true', 'yes', 'false' or 'no'.";
+
+ public static final String MESSAGE_INVALID_IS_WATCHED_COMMAND =
+ "Invalid input. For w/[IS_WATCHED], IS_WATCHED can only be 'true', 'yes', 'false' or 'no'.";
+
+ public static final String MESSAGE_UNABLE_TO_SEARCH_FROM_ONLINE_WHEN_SEARCHING_BY_ACTOR =
+ "Invalid input. When searching by actor, it is not possible to search from online.\n" + MESSAGE_USAGE;
+
+ public static final String MESSAGE_INVALID_TYPE_COMMAND =
+ "Invalid type. t/[TYPE] where TYPE can only be 'movie' or 'tv'";
+
+ public static final String MESSAGE_INVALID_GENRE_COMMAND = "Invalid input. Ensure that genre is not empty.\n"
+ + "search g/GENRE… [n/SHOW_NAME]… [a/ACTOR_NAME]… [o/FROM_ONLINE] [t/TYPE] [w/IS_WATCH]";
+}
diff --git a/src/main/java/seedu/address/commons/exceptions/DataConversionException.java b/src/main/java/seedu/ezwatchlist/commons/exceptions/DataConversionException.java
similarity index 83%
rename from src/main/java/seedu/address/commons/exceptions/DataConversionException.java
rename to src/main/java/seedu/ezwatchlist/commons/exceptions/DataConversionException.java
index 1f689bd8e3f..c5fa20e52f5 100644
--- a/src/main/java/seedu/address/commons/exceptions/DataConversionException.java
+++ b/src/main/java/seedu/ezwatchlist/commons/exceptions/DataConversionException.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.exceptions;
+package seedu.ezwatchlist.commons.exceptions;
/**
* Represents an error during conversion of data from one format to another
diff --git a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java b/src/main/java/seedu/ezwatchlist/commons/exceptions/IllegalValueException.java
similarity index 92%
rename from src/main/java/seedu/address/commons/exceptions/IllegalValueException.java
rename to src/main/java/seedu/ezwatchlist/commons/exceptions/IllegalValueException.java
index 19124db485c..df7313bcf76 100644
--- a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java
+++ b/src/main/java/seedu/ezwatchlist/commons/exceptions/IllegalValueException.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.exceptions;
+package seedu.ezwatchlist.commons.exceptions;
/**
* Signals that some given data does not fulfill some constraints.
diff --git a/src/main/java/seedu/address/commons/util/AppUtil.java b/src/main/java/seedu/ezwatchlist/commons/util/AppUtil.java
similarity index 93%
rename from src/main/java/seedu/address/commons/util/AppUtil.java
rename to src/main/java/seedu/ezwatchlist/commons/util/AppUtil.java
index da90201dfd6..d945a9a0297 100644
--- a/src/main/java/seedu/address/commons/util/AppUtil.java
+++ b/src/main/java/seedu/ezwatchlist/commons/util/AppUtil.java
@@ -1,9 +1,9 @@
-package seedu.address.commons.util;
+package seedu.ezwatchlist.commons.util;
import static java.util.Objects.requireNonNull;
import javafx.scene.image.Image;
-import seedu.address.MainApp;
+import seedu.ezwatchlist.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/ezwatchlist/commons/util/CollectionUtil.java
similarity index 95%
rename from src/main/java/seedu/address/commons/util/CollectionUtil.java
rename to src/main/java/seedu/ezwatchlist/commons/util/CollectionUtil.java
index eafe4dfd681..8dd21413319 100644
--- a/src/main/java/seedu/address/commons/util/CollectionUtil.java
+++ b/src/main/java/seedu/ezwatchlist/commons/util/CollectionUtil.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.util;
+package seedu.ezwatchlist.commons.util;
import static java.util.Objects.requireNonNull;
diff --git a/src/main/java/seedu/address/commons/util/ConfigUtil.java b/src/main/java/seedu/ezwatchlist/commons/util/ConfigUtil.java
similarity index 76%
rename from src/main/java/seedu/address/commons/util/ConfigUtil.java
rename to src/main/java/seedu/ezwatchlist/commons/util/ConfigUtil.java
index f7f8a2bd44c..837db5f369a 100644
--- a/src/main/java/seedu/address/commons/util/ConfigUtil.java
+++ b/src/main/java/seedu/ezwatchlist/commons/util/ConfigUtil.java
@@ -1,11 +1,11 @@
-package seedu.address.commons.util;
+package seedu.ezwatchlist.commons.util;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Optional;
-import seedu.address.commons.core.Config;
-import seedu.address.commons.exceptions.DataConversionException;
+import seedu.ezwatchlist.commons.core.Config;
+import seedu.ezwatchlist.commons.exceptions.DataConversionException;
/**
* A class for accessing the Config File.
diff --git a/src/main/java/seedu/address/commons/util/FileUtil.java b/src/main/java/seedu/ezwatchlist/commons/util/FileUtil.java
similarity index 98%
rename from src/main/java/seedu/address/commons/util/FileUtil.java
rename to src/main/java/seedu/ezwatchlist/commons/util/FileUtil.java
index b1e2767cdd9..c2fa803e8bb 100644
--- a/src/main/java/seedu/address/commons/util/FileUtil.java
+++ b/src/main/java/seedu/ezwatchlist/commons/util/FileUtil.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.util;
+package seedu.ezwatchlist.commons.util;
import java.io.IOException;
import java.nio.file.Files;
diff --git a/src/main/java/seedu/address/commons/util/JsonUtil.java b/src/main/java/seedu/ezwatchlist/commons/util/JsonUtil.java
similarity index 97%
rename from src/main/java/seedu/address/commons/util/JsonUtil.java
rename to src/main/java/seedu/ezwatchlist/commons/util/JsonUtil.java
index 8ef609f055d..03b1e076d13 100644
--- a/src/main/java/seedu/address/commons/util/JsonUtil.java
+++ b/src/main/java/seedu/ezwatchlist/commons/util/JsonUtil.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.util;
+package seedu.ezwatchlist.commons.util;
import static java.util.Objects.requireNonNull;
@@ -20,8 +20,8 @@
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
-import seedu.address.commons.core.LogsCenter;
-import seedu.address.commons.exceptions.DataConversionException;
+import seedu.ezwatchlist.commons.core.LogsCenter;
+import seedu.ezwatchlist.commons.exceptions.DataConversionException;
/**
* Converts a Java object instance to JSON and vice versa
diff --git a/src/main/java/seedu/address/commons/util/StringUtil.java b/src/main/java/seedu/ezwatchlist/commons/util/StringUtil.java
similarity index 95%
rename from src/main/java/seedu/address/commons/util/StringUtil.java
rename to src/main/java/seedu/ezwatchlist/commons/util/StringUtil.java
index 61cc8c9a1cb..fe5e688b9a3 100644
--- a/src/main/java/seedu/address/commons/util/StringUtil.java
+++ b/src/main/java/seedu/ezwatchlist/commons/util/StringUtil.java
@@ -1,7 +1,7 @@
-package seedu.address.commons.util;
+package seedu.ezwatchlist.commons.util;
import static java.util.Objects.requireNonNull;
-import static seedu.address.commons.util.AppUtil.checkArgument;
+import static seedu.ezwatchlist.commons.util.AppUtil.checkArgument;
import java.io.PrintWriter;
import java.io.StringWriter;
diff --git a/src/main/java/seedu/ezwatchlist/logic/Logic.java b/src/main/java/seedu/ezwatchlist/logic/Logic.java
new file mode 100644
index 00000000000..deb1b732af2
--- /dev/null
+++ b/src/main/java/seedu/ezwatchlist/logic/Logic.java
@@ -0,0 +1,87 @@
+package seedu.ezwatchlist.logic;
+
+import java.nio.file.Path;
+import java.util.function.Predicate;
+
+import javafx.collections.ObservableList;
+import seedu.ezwatchlist.api.exceptions.OnlineConnectionException;
+import seedu.ezwatchlist.commons.core.GuiSettings;
+import seedu.ezwatchlist.logic.commands.CommandResult;
+import seedu.ezwatchlist.logic.commands.exceptions.CommandException;
+import seedu.ezwatchlist.logic.parser.exceptions.ParseException;
+import seedu.ezwatchlist.model.Model;
+import seedu.ezwatchlist.model.ReadOnlyWatchList;
+import seedu.ezwatchlist.model.show.Show;
+import seedu.ezwatchlist.ui.MainWindow;
+
+/**
+ * API of the Logic component
+ */
+public interface Logic {
+ /**
+ * Executes the command and returns the result.
+ * @param commandText The command as entered by the user.
+ * @param mainWindow
+ * @param currentTab
+ * @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, MainWindow mainWindow, String currentTab)
+ throws CommandException, ParseException, OnlineConnectionException, InterruptedException;
+
+ /**
+ * Returns the model.
+ * @return the model
+ */
+ Model getModel();
+
+ /**
+ * Returns the WatchList.
+ *
+ * @see Model#getWatchList()
+ */
+ ReadOnlyWatchList getWatchList();
+
+ /**
+ * Returns the database of shows.
+ *
+ * @see Model#getDatabase()
+ */
+ ReadOnlyWatchList getDatabase();
+
+ /** Returns an unmodifiable view of the filtered shows that have not been watched */
+ ObservableList getUnWatchedList();
+
+ /** Returns an unmodifiable view of the filtered watched list of shows */
+ ObservableList getWatchedList();
+
+ /** Returns an unmodifiable view of the filtered list of shows */
+ ObservableList getFilteredShowList();
+
+ /** Updates the filter of the filtered show list by the given {@code predicate}. */
+ void updateFilteredShowList(Predicate predicate);
+
+ /** Returns an unmodifiable view of the search results of shows */
+ ObservableList getSearchResultList();
+
+ /**
+ * Returns the user prefs' watchlist file path.
+ */
+ Path getWatchListFilePath();
+
+ /**
+ * Returns the user prefs' database file path.
+ */
+ Path getDatabaseFilePath();
+
+ /**
+ * Returns the user prefs' GUI settings.
+ */
+ GuiSettings getGuiSettings();
+
+ /**
+ * Set the user prefs' GUI settings.
+ */
+ void setGuiSettings(GuiSettings guiSettings);
+}
diff --git a/src/main/java/seedu/ezwatchlist/logic/LogicManager.java b/src/main/java/seedu/ezwatchlist/logic/LogicManager.java
new file mode 100644
index 00000000000..2edc88fd4d6
--- /dev/null
+++ b/src/main/java/seedu/ezwatchlist/logic/LogicManager.java
@@ -0,0 +1,138 @@
+package seedu.ezwatchlist.logic;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.function.Predicate;
+import java.util.logging.Logger;
+
+import javafx.collections.ObservableList;
+import javafx.concurrent.Task;
+import seedu.ezwatchlist.api.exceptions.OnlineConnectionException;
+import seedu.ezwatchlist.commons.core.GuiSettings;
+import seedu.ezwatchlist.commons.core.LogsCenter;
+import seedu.ezwatchlist.logic.commands.Command;
+import seedu.ezwatchlist.logic.commands.CommandResult;
+import seedu.ezwatchlist.logic.commands.SearchCommand;
+import seedu.ezwatchlist.logic.commands.exceptions.CommandException;
+import seedu.ezwatchlist.logic.parser.WatchListParser;
+import seedu.ezwatchlist.logic.parser.exceptions.ParseException;
+import seedu.ezwatchlist.model.Model;
+import seedu.ezwatchlist.model.ReadOnlyWatchList;
+import seedu.ezwatchlist.model.show.Show;
+import seedu.ezwatchlist.storage.Storage;
+import seedu.ezwatchlist.ui.MainWindow;
+
+/**
+ * The main LogicManager of the app.
+ */
+public class LogicManager implements Logic {
+ public static final String FILE_OPS_ERROR_MESSAGE = "Could not save data to file: ";
+ private final Logger logger = LogsCenter.getLogger(LogicManager.class);
+
+ private final Model model;
+ private final Storage storage;
+ private final WatchListParser watchListParser;
+
+ public LogicManager(Model model, Storage storage) {
+ this.model = model;
+ this.storage = storage;
+ watchListParser = new WatchListParser();
+ }
+
+ @Override
+ public CommandResult execute(String commandText, MainWindow mainWindow, String currentTab)
+ throws CommandException, ParseException, OnlineConnectionException {
+ logger.info("----------------[USER COMMAND][" + commandText + "]");
+
+ final CommandResult[] commandResult = new CommandResult[1];
+ Command command = watchListParser.parseCommand(commandText, currentTab);
+
+ if (command instanceof SearchCommand) {
+ mainWindow.setIsSearchLoading();
+ mainWindow.goToSearch();
+ mainWindow.getResultDisplay().setFeedbackToUser("Loading...");
+ Task task = new Task() {
+ @Override
+ protected CommandResult call() throws Exception {
+ return command.execute(model);
+ }
+ };
+ task.setOnSucceeded(event -> {
+ mainWindow.setIsSearchLoading();
+ mainWindow.goToSearch();
+ commandResult[0] = task.getValue();
+ mainWindow.searchResultLogger(commandResult[0]);
+ });
+ new Thread(task).start();
+ return null;
+ } else {
+ commandResult[0] = command.execute(model);
+ }
+ try {
+ storage.saveWatchList(model.getWatchList());
+ } catch (IOException ioe) {
+ throw new CommandException(FILE_OPS_ERROR_MESSAGE + ioe, ioe);
+ }
+ return commandResult[0];
+ }
+
+ @Override
+ public Model getModel() {
+ return model;
+ }
+
+ @Override
+ public ReadOnlyWatchList getWatchList() {
+ return model.getWatchList();
+ }
+
+ @Override
+ public ReadOnlyWatchList getDatabase() {
+ return model.getDatabase();
+ }
+
+ @Override
+ public ObservableList getUnWatchedList() {
+ return model.getUnWatchedShowList();
+ }
+
+ @Override
+ public ObservableList getWatchedList() {
+ return model.getWatchedShowList();
+ }
+
+ @Override
+ public ObservableList getFilteredShowList() {
+ return model.getFilteredShowList();
+ }
+
+ @Override
+ public void updateFilteredShowList(Predicate predicate) {
+ model.updateFilteredShowList(predicate);
+ }
+
+ @Override
+ public ObservableList getSearchResultList() {
+ return model.getSearchResultList();
+ }
+
+ @Override
+ public Path getWatchListFilePath() {
+ return model.getWatchListFilePath();
+ }
+
+ @Override
+ public Path getDatabaseFilePath() {
+ return model.getDatabaseFilePath();
+ }
+
+ @Override
+ public GuiSettings getGuiSettings() {
+ return model.getGuiSettings();
+ }
+
+ @Override
+ public void setGuiSettings(GuiSettings guiSettings) {
+ model.setGuiSettings(guiSettings);
+ }
+}
diff --git a/src/main/java/seedu/ezwatchlist/logic/commands/AddCommand.java b/src/main/java/seedu/ezwatchlist/logic/commands/AddCommand.java
new file mode 100644
index 00000000000..1b29bd551c7
--- /dev/null
+++ b/src/main/java/seedu/ezwatchlist/logic/commands/AddCommand.java
@@ -0,0 +1,131 @@
+package seedu.ezwatchlist.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.ezwatchlist.logic.parser.CliSyntax.PREFIX_ACTOR;
+import static seedu.ezwatchlist.logic.parser.CliSyntax.PREFIX_DATE_OF_RELEASE;
+import static seedu.ezwatchlist.logic.parser.CliSyntax.PREFIX_DESCRIPTION;
+import static seedu.ezwatchlist.logic.parser.CliSyntax.PREFIX_IS_WATCHED;
+import static seedu.ezwatchlist.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.ezwatchlist.logic.parser.CliSyntax.PREFIX_RUNNING_TIME;
+import static seedu.ezwatchlist.logic.parser.CliSyntax.PREFIX_TYPE;
+
+import java.util.List;
+
+import seedu.ezwatchlist.logic.commands.exceptions.CommandException;
+import seedu.ezwatchlist.model.Model;
+import seedu.ezwatchlist.model.show.Show;
+
+/** @@author wongchuankai
+ * Adds a show to the watchlist.
+ */
+public class AddCommand extends Command {
+
+ public static final String COMMAND_WORD = "add";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a show to the watchlist. "
+ + "Parameters: "
+ + PREFIX_NAME + "NAME "
+ + PREFIX_TYPE + "TYPE ('movie' or 'tv') "
+ + "[" + PREFIX_DATE_OF_RELEASE + "DATE OF RELEASE] "
+ + "[" + PREFIX_IS_WATCHED + "WATCHED ('true' or 'false')] "
+ + "[" + PREFIX_RUNNING_TIME + "RUNNING TIME] "
+ + "[" + PREFIX_DESCRIPTION + "DESCRIPTION] "
+ + "[" + PREFIX_ACTOR + "ACTOR]...\n"
+ + "Example: " + COMMAND_WORD + " "
+ + PREFIX_NAME + "Joker "
+ + PREFIX_TYPE + "movie "
+ + PREFIX_DATE_OF_RELEASE + "4 October 2019 "
+ + PREFIX_IS_WATCHED + "true "
+ + PREFIX_RUNNING_TIME + "122 "
+ + PREFIX_DESCRIPTION + "Joker is funny "
+ + PREFIX_ACTOR + "Joaquin Phoenix "
+ + PREFIX_ACTOR + "Robert De Niro";
+
+ public static final String MESSAGE_USAGE2 = COMMAND_WORD + ": Sync a show found online to the watchlist. "
+ + "Parameters: INDEX (must be a positive integer)\n"
+ + "Example: " + COMMAND_WORD + " 1";
+
+ public static final String MESSAGE_SUCCESS = "New show added: %1$s";
+ public static final String MESSAGE_DUPLICATE_SHOW = "This show already exists in the watchlist";
+ public static final String MESSAGE_SUCCESS2 = "Add movie: %1$s";
+
+ public static final String UNSUCCESSFUL_INDEX = "Search Result Page is currently empty.";
+ public static final String UNSUCCESSFUL_LARGER = "The index is larger than the total number"
+ + " of shows in search page.";
+ public static final String NOT_AT_SEARCH_LIST_PAGE = "'Add Index' command is only available in Search Panel";
+
+ private final Show toAdd;
+
+
+
+ private final int index;
+ private final boolean isFromSearch;
+
+ /**
+ * Creates an AddCommand to add the specified {@code Show}
+ */
+ public AddCommand(Show show) {
+ requireNonNull(show);
+ toAdd = show;
+ index = -1;
+ isFromSearch = false;
+ }
+
+ public AddCommand(int index) {
+ requireNonNull(index);
+ this.index = index;
+ toAdd = null;
+ isFromSearch = true;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+
+ if (isFromSearch) {
+ return fromSearch(model);
+ }
+ if (model.hasShow(toAdd)) {
+ throw new CommandException(MESSAGE_DUPLICATE_SHOW);
+ }
+
+ model.addShow(toAdd);
+ return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd), true);
+ }
+
+ /**
+ * Retrieve movies from searchlist found in model.
+ * @param model
+ * @return
+ * @throws CommandException
+ */
+
+ public CommandResult fromSearch(Model model) throws CommandException {
+ List searchResultList = model.getSearchResultList();
+ if (searchResultList.isEmpty()) {
+ throw new CommandException(UNSUCCESSFUL_INDEX);
+ }
+ if (index > searchResultList.size()) {
+ throw new CommandException(UNSUCCESSFUL_LARGER);
+ }
+ Show fromImdb = searchResultList.get(index - 1);
+ if (model.hasShow(fromImdb)) {
+ throw new CommandException(MESSAGE_DUPLICATE_SHOW);
+ }
+
+ model.addShow(fromImdb);
+ return new CommandResult(String.format(MESSAGE_SUCCESS2, fromImdb), true);
+ }
+ public boolean isFromSearch() {
+ return isFromSearch;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof AddCommand // instanceof handles nulls
+ && toAdd.equals(((AddCommand) other).toAdd)
+ && index == (((AddCommand) other).index)
+ && isFromSearch == ((AddCommand) other).isFromSearch);
+ }
+}
diff --git a/src/main/java/seedu/ezwatchlist/logic/commands/ClearCommand.java b/src/main/java/seedu/ezwatchlist/logic/commands/ClearCommand.java
new file mode 100644
index 00000000000..856dc3e3d13
--- /dev/null
+++ b/src/main/java/seedu/ezwatchlist/logic/commands/ClearCommand.java
@@ -0,0 +1,23 @@
+package seedu.ezwatchlist.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import seedu.ezwatchlist.model.Model;
+import seedu.ezwatchlist.model.WatchList;
+
+/**
+ * Clears the watchlist.
+ */
+public class ClearCommand extends Command {
+
+ public static final String COMMAND_WORD = "clear";
+ public static final String MESSAGE_SUCCESS = "Watchlist has been cleared!";
+
+
+ @Override
+ public CommandResult execute(Model model) {
+ requireNonNull(model);
+ model.setWatchList(new WatchList());
+ return new CommandResult(MESSAGE_SUCCESS, true);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/Command.java b/src/main/java/seedu/ezwatchlist/logic/commands/Command.java
similarity index 65%
rename from src/main/java/seedu/address/logic/commands/Command.java
rename to src/main/java/seedu/ezwatchlist/logic/commands/Command.java
index 64f18992160..58ee97b8676 100644
--- a/src/main/java/seedu/address/logic/commands/Command.java
+++ b/src/main/java/seedu/ezwatchlist/logic/commands/Command.java
@@ -1,7 +1,8 @@
-package seedu.address.logic.commands;
+package seedu.ezwatchlist.logic.commands;
-import seedu.address.logic.commands.exceptions.CommandException;
-import seedu.address.model.Model;
+import seedu.ezwatchlist.api.exceptions.OnlineConnectionException;
+import seedu.ezwatchlist.logic.commands.exceptions.CommandException;
+import seedu.ezwatchlist.model.Model;
/**
* Represents a command with hidden internal logic and the ability to be executed.
@@ -15,6 +16,6 @@ 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, OnlineConnectionException;
}
diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/seedu/ezwatchlist/logic/commands/CommandResult.java
similarity index 73%
rename from src/main/java/seedu/address/logic/commands/CommandResult.java
rename to src/main/java/seedu/ezwatchlist/logic/commands/CommandResult.java
index 92f900b7916..0b33faa6422 100644
--- a/src/main/java/seedu/address/logic/commands/CommandResult.java
+++ b/src/main/java/seedu/ezwatchlist/logic/commands/CommandResult.java
@@ -1,4 +1,4 @@
-package seedu.address.logic.commands;
+package seedu.ezwatchlist.logic.commands;
import static java.util.Objects.requireNonNull;
@@ -17,21 +17,28 @@ public class CommandResult {
/** The application should exit. */
private final boolean exit;
+ private final boolean shortCutKey;
+
+ private final boolean isChangedList;
+
/**
* Constructs a {@code CommandResult} with the specified fields.
*/
- public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) {
+ public CommandResult(String feedbackToUser, boolean showHelp, boolean exit,
+ boolean shortCutKey, boolean isChangedList) {
this.feedbackToUser = requireNonNull(feedbackToUser);
this.showHelp = showHelp;
this.exit = exit;
+ this.shortCutKey = shortCutKey;
+ this.isChangedList = isChangedList;
}
/**
* Constructs a {@code CommandResult} with the specified {@code feedbackToUser},
* and other fields set to their default value.
*/
- public CommandResult(String feedbackToUser) {
- this(feedbackToUser, false, false);
+ public CommandResult(String feedbackToUser, boolean isChangedList) {
+ this(feedbackToUser, false, false, false, isChangedList);
}
public String getFeedbackToUser() {
@@ -46,6 +53,14 @@ public boolean isExit() {
return exit;
}
+ public boolean isShortCutKey() {
+ return shortCutKey;
+ }
+
+ public boolean isChangedList() {
+ return isChangedList;
+ }
+
@Override
public boolean equals(Object other) {
if (other == this) {
@@ -58,6 +73,7 @@ public boolean equals(Object other) {
}
CommandResult otherCommandResult = (CommandResult) other;
+
return feedbackToUser.equals(otherCommandResult.feedbackToUser)
&& showHelp == otherCommandResult.showHelp
&& exit == otherCommandResult.exit;
diff --git a/src/main/java/seedu/address/logic/commands/DeleteCommand.java b/src/main/java/seedu/ezwatchlist/logic/commands/DeleteCommand.java
similarity index 55%
rename from src/main/java/seedu/address/logic/commands/DeleteCommand.java
rename to src/main/java/seedu/ezwatchlist/logic/commands/DeleteCommand.java
index 02fd256acba..1b3428afdba 100644
--- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java
+++ b/src/main/java/seedu/ezwatchlist/logic/commands/DeleteCommand.java
@@ -1,28 +1,28 @@
-package seedu.address.logic.commands;
+package seedu.ezwatchlist.logic.commands;
import static java.util.Objects.requireNonNull;
import java.util.List;
-import seedu.address.commons.core.Messages;
-import seedu.address.commons.core.index.Index;
-import seedu.address.logic.commands.exceptions.CommandException;
-import seedu.address.model.Model;
-import seedu.address.model.person.Person;
+import seedu.ezwatchlist.commons.core.index.Index;
+import seedu.ezwatchlist.commons.core.messages.Messages;
+import seedu.ezwatchlist.logic.commands.exceptions.CommandException;
+import seedu.ezwatchlist.model.Model;
+import seedu.ezwatchlist.model.show.Show;
/**
- * Deletes a person identified using it's displayed index from the address book.
+ * Deletes a show identified using it's displayed index from the watchlist.
*/
public class DeleteCommand extends Command {
public static final String COMMAND_WORD = "delete";
public static final String MESSAGE_USAGE = COMMAND_WORD
- + ": Deletes the person identified by the index number used in the displayed person list.\n"
+ + ": Deletes the show identified by the index number used in the displayed show list.\n"
+ "Parameters: INDEX (must be a positive integer)\n"
+ "Example: " + COMMAND_WORD + " 1";
- public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Deleted Person: %1$s";
+ public static final String MESSAGE_DELETE_SHOW_SUCCESS = "Deleted Show: %1$s";
private final Index targetIndex;
@@ -33,15 +33,15 @@ public DeleteCommand(Index targetIndex) {
@Override
public CommandResult execute(Model model) throws CommandException {
requireNonNull(model);
- List lastShownList = model.getFilteredPersonList();
+ List lastShownList = model.getFilteredShowList();
if (targetIndex.getZeroBased() >= lastShownList.size()) {
- throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ throw new CommandException(Messages.MESSAGE_INVALID_SHOW_DISPLAYED_INDEX);
}
- Person personToDelete = lastShownList.get(targetIndex.getZeroBased());
- model.deletePerson(personToDelete);
- return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, personToDelete));
+ Show showToDelete = lastShownList.get(targetIndex.getZeroBased());
+ model.deleteShow(showToDelete);
+ return new CommandResult(String.format(MESSAGE_DELETE_SHOW_SUCCESS, showToDelete), true);
}
@Override
diff --git a/src/main/java/seedu/ezwatchlist/logic/commands/EditCommand.java b/src/main/java/seedu/ezwatchlist/logic/commands/EditCommand.java
new file mode 100644
index 00000000000..23dfdc42b18
--- /dev/null
+++ b/src/main/java/seedu/ezwatchlist/logic/commands/EditCommand.java
@@ -0,0 +1,346 @@
+package seedu.ezwatchlist.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.ezwatchlist.logic.parser.CliSyntax.PREFIX_ACTOR;
+import static seedu.ezwatchlist.logic.parser.CliSyntax.PREFIX_DATE_OF_RELEASE;
+import static seedu.ezwatchlist.logic.parser.CliSyntax.PREFIX_DESCRIPTION;
+import static seedu.ezwatchlist.logic.parser.CliSyntax.PREFIX_IS_WATCHED;
+import static seedu.ezwatchlist.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.ezwatchlist.logic.parser.CliSyntax.PREFIX_RUNNING_TIME;
+
+import static seedu.ezwatchlist.model.Model.PREDICATE_ALL_SHOWS;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+
+import seedu.ezwatchlist.commons.core.index.Index;
+import seedu.ezwatchlist.commons.core.messages.Messages;
+import seedu.ezwatchlist.commons.util.CollectionUtil;
+import seedu.ezwatchlist.logic.commands.exceptions.CommandException;
+import seedu.ezwatchlist.model.Model;
+import seedu.ezwatchlist.model.actor.Actor;
+import seedu.ezwatchlist.model.show.Date;
+import seedu.ezwatchlist.model.show.Description;
+import seedu.ezwatchlist.model.show.Genre;
+import seedu.ezwatchlist.model.show.IsWatched;
+import seedu.ezwatchlist.model.show.Movie;
+import seedu.ezwatchlist.model.show.Name;
+import seedu.ezwatchlist.model.show.Poster;
+import seedu.ezwatchlist.model.show.RunningTime;
+import seedu.ezwatchlist.model.show.Show;
+import seedu.ezwatchlist.model.show.TvSeason;
+import seedu.ezwatchlist.model.show.TvShow;
+
+/**
+ * Edits the details of an existing show in the watchlist.
+ */
+public class EditCommand extends Command {
+
+ public static final String COMMAND_WORD = "edit";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the show identified "
+ + "by the index number used in the displayed show list. "
+ + "Existing values will be overwritten by the input values.\n"
+ + "Parameters: INDEX (must be a positive integer) "
+ + "[" + PREFIX_NAME + "NAME] "
+ + "[" + PREFIX_DATE_OF_RELEASE + "DATE OF RELEASE] "
+ + "[" + PREFIX_IS_WATCHED + "WATCHED ('true' or 'false')] "
+ + "[" + PREFIX_DESCRIPTION + "DESCRIPTION] "
+ + "[" + PREFIX_RUNNING_TIME + "RUNNING TIME] "
+ + "[" + PREFIX_ACTOR + "ACTOR]...\n"
+ + "Example: " + COMMAND_WORD + " 1 "
+ + PREFIX_NAME + "Joker "
+ + PREFIX_DATE_OF_RELEASE + "3 October 2019";
+
+ public static final String MESSAGE_EDIT_SHOW_SUCCESS = "Edited Show: %1$s";
+ public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided.";
+ public static final String MESSAGE_DUPLICATE_SHOW = "This show already exists in the watchlist.";
+
+ private final Index index;
+ private final EditShowDescriptor editShowDescriptor;
+
+ /**
+ * @param index of the show in the filtered show list to edit
+ * @param editShowDescriptor details to edit the show with
+ */
+ public EditCommand(Index index, EditShowDescriptor editShowDescriptor) {
+ requireNonNull(index);
+ requireNonNull(editShowDescriptor);
+
+ this.index = index;
+ this.editShowDescriptor = new EditShowDescriptor(editShowDescriptor);
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ List lastShownList = model.getFilteredShowList();
+
+ if (index.getZeroBased() >= lastShownList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_SHOW_DISPLAYED_INDEX);
+ }
+
+ Show showToEdit = lastShownList.get(index.getZeroBased());
+ Show editedShow = createEditedShow(showToEdit, editShowDescriptor);
+
+ if (!showToEdit.isSameShow(editedShow) && model.hasShow(editedShow)) {
+ throw new CommandException(MESSAGE_DUPLICATE_SHOW);
+ }
+ model.setShow(showToEdit, editedShow);
+ model.updateFilteredShowList(PREDICATE_ALL_SHOWS);
+ return new CommandResult(String.format(MESSAGE_EDIT_SHOW_SUCCESS, editedShow), true);
+ }
+
+ /**
+ * Creates and returns a {@code Show} with the details of {@code showToEdit}
+ * edited with {@code editShowDescriptor}.
+ */
+ private static Show createEditedShow(Show showToEdit, EditShowDescriptor editShowDescriptor) {
+ assert showToEdit != null;
+
+ Name updatedName = editShowDescriptor.getName().orElse(showToEdit.getName());
+ Date updatedDateOfRelease = editShowDescriptor.getDateOfRelease().orElse(showToEdit.getDateOfRelease());
+ IsWatched updatedIsWatched = editShowDescriptor.getIsWatched().orElse(showToEdit.isWatched());
+ Description updatedDescription = editShowDescriptor.getDescription().orElse(showToEdit.getDescription());
+ RunningTime updatedRunningTime = editShowDescriptor.getRunningTime().orElse(showToEdit.getRunningTime());
+ Set updatedActors = editShowDescriptor.getActors().orElse(showToEdit.getActors());
+ Poster updatedPoster = editShowDescriptor.getPoster().orElse(showToEdit.getPoster());
+ Set updatedGenres = editShowDescriptor.getGenres().orElse(showToEdit.getGenres());
+
+ if (showToEdit.getType().equals("Movie")) {
+ Movie editedShow = new Movie(updatedName, updatedDescription, updatedIsWatched,
+ updatedDateOfRelease, updatedRunningTime, updatedActors);
+ editedShow.setPoster(updatedPoster);
+ return editedShow;
+ } else { //Tv show
+ int updatedNumberOfEpisodesWatched = showToEdit.getNumOfEpisodesWatched();
+ int updatedTotalNumOfEpisodes = showToEdit.getTotalNumOfEpisodes();
+ List updatedSeasons = editShowDescriptor.getSeasons().orElse(showToEdit.getTvSeasons());
+
+ TvShow editedShow = new TvShow(updatedName, updatedDescription, updatedIsWatched,
+ updatedDateOfRelease, updatedRunningTime, updatedActors, updatedNumberOfEpisodesWatched,
+ updatedTotalNumOfEpisodes, updatedSeasons);
+ editedShow.setPoster(updatedPoster);
+ editedShow.addGenres(updatedGenres);
+
+ return editedShow;
+ }
+ }
+
+ /**
+ * createEditedShowTest is a public method to access the private createEditedShow.
+ * This method is used to testing to retrieve information from private createEditedShow.
+ * @param showToEdit
+ * @param editShowDescriptor
+ * @return
+ */
+ public Show createEditedShowTest(Show showToEdit, EditShowDescriptor editShowDescriptor) {
+ return createEditedShow(showToEdit, editShowDescriptor);
+ }
+ @Override
+ public boolean equals(Object other) {
+ // short circuit if same object
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof EditCommand)) {
+ return false;
+ }
+
+ // state check
+ EditCommand e = (EditCommand) other;
+ return index.equals(e.index)
+ && editShowDescriptor.equals(e.editShowDescriptor);
+ }
+
+ /**
+ * Stores the details to edit the show with. Each non-empty field value will replace the
+ * corresponding field value of the show.
+ */
+ public static class EditShowDescriptor {
+ private Name name;
+ private String type;
+ private Date dateOfRelease;
+ private IsWatched isWatched;
+ private Description description;
+ private RunningTime runningTime;
+ private Set actors;
+ private Poster poster;
+ private Set genres;
+ private int numOfEpisodesWatched;
+ private int totalNumOfEpisodes;
+ private List seasons;
+
+ public EditShowDescriptor() {}
+
+ /**
+ * Copy constructor.
+ * A defensive copy of {@code actors} is used internally.
+ */
+ public EditShowDescriptor(EditShowDescriptor toCopy) {
+ setName(toCopy.name);
+ setType(toCopy.type);
+ setDateOfRelease(toCopy.dateOfRelease);
+ setIsWatched(toCopy.isWatched);
+ setDescription(toCopy.description);
+ setRunningTime(toCopy.runningTime);
+ setActors(toCopy.actors);
+ setPoster(toCopy.poster);
+ setGenres(toCopy.genres);
+ setNumOfEpisodesWatched(toCopy.numOfEpisodesWatched);
+ setTotalNumOfEpisodes(toCopy.totalNumOfEpisodes);
+ setSeasons(toCopy.seasons);
+ }
+
+ /**
+ * Returns true if at least one field is edited.
+ */
+ public boolean isAnyFieldEdited() {
+ return CollectionUtil.isAnyNonNull(name, dateOfRelease, isWatched, description, runningTime, actors);
+ }
+
+ public void setName(Name name) {
+ this.name = name;
+ }
+
+ public Optional getName() {
+ return Optional.ofNullable(name);
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public Optional getType() {
+ return Optional.ofNullable(type);
+ }
+
+ public void setDateOfRelease(Date dateOfRelease) {
+ this.dateOfRelease = dateOfRelease;
+ }
+
+ public Optional getDateOfRelease() {
+ return Optional.ofNullable(dateOfRelease);
+ }
+
+ public void setIsWatched(IsWatched isWatched) {
+ this.isWatched = isWatched;
+ }
+
+ public Optional getIsWatched() {
+ return Optional.ofNullable(isWatched);
+ }
+
+ public void setDescription(Description description) {
+ this.description = description;
+ }
+
+ public Optional getDescription() {
+ return Optional.ofNullable(description);
+ }
+
+ public void setRunningTime(RunningTime runningTime) {
+ this.runningTime = runningTime;
+ }
+
+ public Optional getRunningTime() {
+ return Optional.ofNullable(runningTime);
+ }
+
+ public void setPoster(Poster poster) {
+ this.poster = poster;
+ }
+
+ public Optional getPoster() {
+ return Optional.ofNullable(poster);
+ }
+
+ public void setNumOfEpisodesWatched(int numOfEpisodesWatched) {
+ this.numOfEpisodesWatched = numOfEpisodesWatched;
+ }
+
+ public void setTotalNumOfEpisodes(int totalNumOfEpisodes) {
+ this.totalNumOfEpisodes = totalNumOfEpisodes;
+ }
+
+ /**
+ * Sets {@code actors} to this object's {@code actors}.
+ * A defensive copy of {@code actors} is used internally.
+ */
+ public void setActors(Set actors) {
+ this.actors = (actors != null) ? new HashSet<>(actors) : null;
+ }
+
+ /**
+ * Returns an unmodifiable actor set, which throws {@code UnsupportedOperationException}
+ * if modification is attempted.
+ * Returns {@code Optional#empty()} if {@code actor} is null.
+ */
+ public Optional> getActors() {
+ return (actors != null) ? Optional.of(Collections.unmodifiableSet(actors)) : Optional.empty();
+ }
+
+ /**
+ * Sets {@code genres} to this object's {@code genres}.
+ * A defensive copy of {@code genres} is used internally.
+ */
+ public void setGenres(Set genres) {
+ this.genres = (genres != null) ? new HashSet<>(genres) : null;
+ }
+
+ /**
+ * Returns an unmodifiable genre set, which throws {@code UnsupportedOperationException}
+ * if modification is attempted.
+ * Returns {@code Optional#empty()} if {@code genre} is null.
+ */
+ public Optional> getGenres() {
+ return (genres != null) ? Optional.of(Collections.unmodifiableSet(genres)) : Optional.empty();
+ }
+
+ /**
+ * Sets {@code seasons} to this object's {@code seasons}.
+ * A defensive copy of {@code seasons} is used internally.
+ */
+ public void setSeasons(List seasons) {
+ this.seasons = (seasons != null) ? new ArrayList<>(seasons) : null;
+ }
+
+ /**
+ * Returns an unmodifiable season set, which throws {@code UnsupportedOperationException}
+ * if modification is attempted.
+ * Returns {@code Optional#empty()} if {@code season} is null.
+ */
+ public Optional> getSeasons() {
+ return (seasons != null) ? Optional.of(Collections.unmodifiableList(seasons)) : Optional.empty();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ // short circuit if same object
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof EditShowDescriptor)) {
+ return false;
+ }
+
+ // state check
+ EditShowDescriptor e = (EditShowDescriptor) other;
+
+ return getName().equals(e.getName())
+ && getDateOfRelease().equals(e.getDateOfRelease())
+ && getIsWatched().equals(e.getIsWatched())
+ && getDescription().equals(e.getDescription())
+ && getRunningTime().equals(e.getRunningTime())
+ && getActors().equals(e.getActors());
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/seedu/ezwatchlist/logic/commands/ExitCommand.java
similarity index 69%
rename from src/main/java/seedu/address/logic/commands/ExitCommand.java
rename to src/main/java/seedu/ezwatchlist/logic/commands/ExitCommand.java
index 3dd85a8ba90..facb45d9ddf 100644
--- a/src/main/java/seedu/address/logic/commands/ExitCommand.java
+++ b/src/main/java/seedu/ezwatchlist/logic/commands/ExitCommand.java
@@ -1,6 +1,6 @@
-package seedu.address.logic.commands;
+package seedu.ezwatchlist.logic.commands;
-import seedu.address.model.Model;
+import seedu.ezwatchlist.model.Model;
/**
* Terminates the program.
@@ -9,11 +9,11 @@ public class ExitCommand extends Command {
public static final String COMMAND_WORD = "exit";
- public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting Address Book as requested ...";
+ public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting Ezwatchlist as requested ...";
@Override
public CommandResult execute(Model model) {
- return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true);
+ return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true, false, false);
}
}
diff --git a/src/main/java/seedu/ezwatchlist/logic/commands/GoToCommand.java b/src/main/java/seedu/ezwatchlist/logic/commands/GoToCommand.java
new file mode 100644
index 00000000000..7f77eabbc5f
--- /dev/null
+++ b/src/main/java/seedu/ezwatchlist/logic/commands/GoToCommand.java
@@ -0,0 +1,41 @@
+package seedu.ezwatchlist.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import seedu.ezwatchlist.logic.commands.exceptions.CommandException;
+import seedu.ezwatchlist.model.Model;
+
+/**
+ * Go To short cut key for watchlist
+ */
+public class GoToCommand extends Command {
+
+ public static final String COMMAND_WORD = "";
+ public static final String MESSAGE_USAGE = "Go to page: %1$s";
+ public static final String MESSAGE_SUCCESS = "Go to page: %1$s";
+ public static final String MESSAGE_UNSUCCESSFUL_INPUT = "wrong input";
+
+ private final String shortcut;
+ /**
+ * Creates an GoToCommand to add the specified {@code shortcut}
+ * @param shortcut
+ */
+ public GoToCommand(String shortcut) {
+ requireNonNull(shortcut);
+ this.shortcut = shortcut;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ String pageTitle = model.getPage(shortcut);
+ return new CommandResult(pageTitle, false, false, true, false);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof GoToCommand // instanceof handles nulls
+ && shortcut.equals(((GoToCommand) other).shortcut));
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/commands/HelpCommand.java b/src/main/java/seedu/ezwatchlist/logic/commands/HelpCommand.java
similarity index 84%
rename from src/main/java/seedu/address/logic/commands/HelpCommand.java
rename to src/main/java/seedu/ezwatchlist/logic/commands/HelpCommand.java
index bf824f91bd0..0f091ac3cc8 100644
--- a/src/main/java/seedu/address/logic/commands/HelpCommand.java
+++ b/src/main/java/seedu/ezwatchlist/logic/commands/HelpCommand.java
@@ -1,6 +1,6 @@
-package seedu.address.logic.commands;
+package seedu.ezwatchlist.logic.commands;
-import seedu.address.model.Model;
+import seedu.ezwatchlist.model.Model;
/**
* Format full help instructions for every command for display.
@@ -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);
}
}
diff --git a/src/main/java/seedu/ezwatchlist/logic/commands/ListCommand.java b/src/main/java/seedu/ezwatchlist/logic/commands/ListCommand.java
new file mode 100644
index 00000000000..b29ccb65f44
--- /dev/null
+++ b/src/main/java/seedu/ezwatchlist/logic/commands/ListCommand.java
@@ -0,0 +1,24 @@
+package seedu.ezwatchlist.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.ezwatchlist.model.Model.PREDICATE_ALL_SHOWS;
+
+import seedu.ezwatchlist.model.Model;
+
+/**
+ * Lists all shows in the watchlist to the user.
+ */
+public class ListCommand extends Command {
+
+ public static final String COMMAND_WORD = "list";
+
+ public static final String MESSAGE_SUCCESS = "Listed all shows";
+
+
+ @Override
+ public CommandResult execute(Model model) {
+ requireNonNull(model);
+ model.updateFilteredShowList(PREDICATE_ALL_SHOWS);
+ return new CommandResult(MESSAGE_SUCCESS, false);
+ }
+}
diff --git a/src/main/java/seedu/ezwatchlist/logic/commands/SearchCommand.java b/src/main/java/seedu/ezwatchlist/logic/commands/SearchCommand.java
new file mode 100644
index 00000000000..47523b31ef6
--- /dev/null
+++ b/src/main/java/seedu/ezwatchlist/logic/commands/SearchCommand.java
@@ -0,0 +1,483 @@
+package seedu.ezwatchlist.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import seedu.ezwatchlist.api.exceptions.OnlineConnectionException;
+import seedu.ezwatchlist.api.model.ApiInterface;
+import seedu.ezwatchlist.api.model.ApiManager;
+import seedu.ezwatchlist.commons.core.messages.SearchMessages;
+import seedu.ezwatchlist.logic.commands.exceptions.CommandException;
+import seedu.ezwatchlist.logic.parser.SearchKey;
+import seedu.ezwatchlist.model.Model;
+import seedu.ezwatchlist.model.actor.Actor;
+import seedu.ezwatchlist.model.show.Genre;
+import seedu.ezwatchlist.model.show.Movie;
+import seedu.ezwatchlist.model.show.Name;
+import seedu.ezwatchlist.model.show.Show;
+import seedu.ezwatchlist.model.show.TvShow;
+import seedu.ezwatchlist.model.show.Type;
+
+/**
+ * Finds and lists all shows in watchlist whose name contains any of the argument keywords.
+ * Keyword matching is case insensitive.
+ */
+public class SearchCommand extends Command {
+ public static final String COMMAND_WORD = "search";
+
+ private static final String INPUT_TRUE = "true";
+ private static final String INPUT_YES = "yes";
+ private static final String INPUT_FALSE = "false";
+ private static final String INPUT_NO = "no";
+
+ private ApiInterface onlineSearch;
+ private List nameList;
+ private List typeList;
+ private List actorList;
+ private List genreList;
+ private List isWatchedList;
+ private List fromOnlineList;
+ private List searchResult = new ArrayList<>();
+
+ private boolean isOffline = false;
+
+ public SearchCommand(HashMap> searchShowsHashMap) {
+ nameList = searchShowsHashMap.get(SearchKey.KEY_NAME);
+ typeList = searchShowsHashMap.get(SearchKey.KEY_TYPE);
+ actorList = searchShowsHashMap.get(SearchKey.KEY_ACTOR);
+ genreList = searchShowsHashMap.get(SearchKey.KEY_GENRE);
+ isWatchedList = searchShowsHashMap.get(SearchKey.KEY_IS_WATCHED);
+ fromOnlineList = searchShowsHashMap.get(SearchKey.KEY_FROM_ONLINE);
+
+ try {
+ onlineSearch = new ApiManager();
+ } catch (OnlineConnectionException e) {
+ isOffline = true;
+ }
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+
+ searchByName(model);
+ searchByGenre(model);
+ searchByActor(model);
+
+ filterOutDuplicatesInSearchResult(model);
+
+ if (isOffline) {
+ return new CommandResult(String.format(SearchMessages.MESSAGE_INTERNAL_SHOW_LISTED_OVERVIEW,
+ model.getSearchResultList().size()), false);
+ } else {
+ return new CommandResult(String.format(SearchMessages.MESSAGE_SHOWS_FOUND_OVERVIEW,
+ model.getSearchResultList().size()), false);
+ }
+ }
+
+ /**
+ * Search for shows by name.
+ * @param model Model used.
+ * @throws CommandException If command exception occurred.
+ */
+ private void searchByName(Model model) throws CommandException {
+ if (nameList.isEmpty()) {
+ return;
+ }
+ try {
+ if (requestedSearchFromInternal()) {
+ for (String showName : nameList) {
+ addShowFromWatchListIfSameNameAs(showName, model);
+ }
+ } else if (requestedSearchFromOnline()) {
+ for (String showName : nameList) {
+ addShowFromOnlineIfSameNameAs(showName);
+ }
+ } else if (!requestedFromOnline()) {
+ for (String showName : nameList) {
+ addShowFromOnlineIfSameNameAs(showName);
+ }
+ } else {
+ throw new CommandException(SearchMessages.MESSAGE_INVALID_FROM_ONLINE_COMMAND);
+ }
+ } catch (OnlineConnectionException oce) {
+ for (String showName : nameList) {
+ addShowFromWatchListIfSameNameAs(showName, model);
+ addShowFromDatabaseIfSameNameAs(showName, model);
+ }
+ isOffline = true;
+ }
+ }
+
+ /**
+ * Search for shows by actor.
+ * @param model Model used.
+ * @throws CommandException If command exception occurred.
+ */
+ private void searchByActor(Model model) throws CommandException {
+ if (actorList.isEmpty()) {
+ return;
+ }
+ Set actorSet = new HashSet();
+ for (String actorName : actorList) {
+ if (actorName.isBlank()) {
+ continue;
+ }
+ Actor actor = new Actor(actorName.trim());
+ actorSet.add(actor);
+ }
+
+ if (requestedSearchFromInternal()) {
+ addShowFromWatchListIfHasActor(actorSet, model);
+ } else if (requestedSearchFromOnline()) {
+ throw new CommandException(SearchMessages.MESSAGE_UNABLE_TO_SEARCH_FROM_ONLINE_WHEN_SEARCHING_BY_ACTOR);
+ } else if (!requestedFromOnline()) {
+ addShowFromWatchListIfHasActor(actorSet, model);
+ } else {
+ throw new CommandException(SearchMessages.MESSAGE_INVALID_FROM_ONLINE_COMMAND);
+ }
+ }
+
+ /**
+ * Search for shows by genre.
+ * @param model Model used.
+ * @throws CommandException If command exception occurred.
+ * @throws OnlineConnectionException when not connected to the internet.
+ */
+ private void searchByGenre(Model model) throws CommandException {
+ if (genreList.isEmpty()) {
+ return;
+ }
+ Set genreSet = new HashSet();
+ for (String genreName : genreList) {
+ if (genreName.isBlank()) {
+ continue;
+ }
+ Genre genre = new Genre(genreName.trim());
+ genreSet.add(genre);
+ }
+
+ try {
+ if (requestedSearchFromInternal()) {
+ addShowFromWatchListIfIsGenre(genreSet, model);
+ } else if (requestedSearchFromOnline()) {
+ addMovieFromOnlineIfIsGenre(genreSet);
+ } else if (!requestedFromOnline()) {
+ addMovieFromOnlineIfIsGenre(genreSet);
+ } else {
+ throw new CommandException(SearchMessages.MESSAGE_INVALID_FROM_ONLINE_COMMAND);
+ }
+ } catch (OnlineConnectionException oce) {
+ addShowFromWatchListIfIsGenre(genreSet, model);
+ addShowFromDatabaseIfIsGenre(genreSet, model);
+ isOffline = true;
+ }
+ }
+
+ /**
+ * Adds show from list if it has the same name as in {@code showName}.
+ * @param showName name of the given show.
+ * @param model current model of the program.
+ */
+ private void addShowFromWatchListIfSameNameAs(String showName, Model model) throws CommandException {
+ if (showName.isBlank()) {
+ return;
+ }
+ List filteredShowList = model.getShowFromWatchlistIfHasName(new Name(showName.trim()));
+ addShowToSearchResult(filteredShowList);
+ }
+
+ /**
+ * Adds show from offline database if it has the same name as in {@code showName}.
+ * @param showName name of the given show.
+ * @param model current model of the program.
+ */
+ private void addShowFromDatabaseIfSameNameAs(String showName, Model model) throws CommandException {
+ if (showName.isBlank()) {
+ return;
+ }
+ List filteredShowList = model.getShowFromDatabaseIfHasName(new Name(showName));
+ addShowToSearchResult(filteredShowList);
+ }
+
+ /**
+ * Adds show from list if it has any actor in {@code actorSet}.
+ * @param actorSet Set of actors to be searched for.
+ * @param model current model of the program.
+ */
+ private void addShowFromWatchListIfHasActor(Set actorSet, Model model) throws CommandException {
+ if (actorSet.isEmpty()) {
+ return;
+ }
+ List filteredShowList = model.getShowFromWatchlistIfHasActor(actorSet);
+ addShowToSearchResult(filteredShowList);
+ }
+
+ /**
+ * Adds shows from database if it has any actor in {@code actorSet}.
+ * @param actorSet Set of actors to be searched for.
+ * @param model current model of the program.
+ */
+ private void addShowFromDatabaseIfHasActor(Set actorSet, Model model) throws CommandException {
+ if (actorSet.isEmpty()) {
+ return;
+ }
+ List filteredShowList = model.getShowFromDatabaseIfHasActor(actorSet);
+ addShowToSearchResult(filteredShowList);
+ }
+
+ /**
+ * Adds show from watchlist if it has any genre in {@code genreSet}.
+ * @param genreSet set of actors to be searched for.
+ * @param model current model of the program.
+ */
+ private void addShowFromWatchListIfIsGenre(Set genreSet, Model model) throws CommandException {
+ if (genreSet.isEmpty()) {
+ return;
+ }
+ List