By: CS2113T T09-04 Since: Sep 2019 Licence: MIT
-
JDK
11or above -
IntelliJ IDE
ℹ️IntelliJ by default has Gradle and JavaFx plugins installed.
Do not disable them. If you have disabled them, go toFile>Settings>Pluginsto re-enable them.
-
Fork this repo, and clone the fork to your computer
-
Open IntelliJ (if you are not in the welcome screen, click
File>Close Projectto close the existing project dialog first) -
Set up the correct JDK version for Gradle
-
Click
Configure>Project Defaults>Project Structure -
Click
New…and find the directory of the JDK
-
-
Click
Import Project -
Locate the
build.gradlefile and select it. ClickOK -
Click
Open as Project -
Click
OKto accept the default settings -
Open a console and run the command
gradlew processResources(Mac/Linux:./gradlew processResources). It should finish with theBUILD SUCCESSFULmessage.
This will generate all resources required by the application and tests.
-
Run the
mainand try a few commands -
Run the tests to ensure they all pass.
This project follows oss-generic coding standards. IntelliJ’s default style is mostly compliant with ours but it uses a different import order from ours. To rectify,
-
Go to
File>Settings…(Windows/Linux), orIntelliJ IDEA>Preferences…(macOS) -
Select
Editor>Code Style>Java -
Click on the
Importstab to set the order-
For
Class count to use import with '*'andNames count to use static import with '*': Set to999to prevent IntelliJ from contracting the import statements -
For
Import Layout: The order isimport static all other imports,import java.*,import javax.*,import org.*,import com.*,import all other imports. Add a<blank line>between eachimport
-
Optionally, you can follow the UsingCheckstyle.adoc document to configure Intellij to check style-compliance as you write code.
After forking the repo, the documentation will still have the SGTravel branding and refer to the AY1920S1-CS2113T-T09-4/main repo.
If you plan to develop this fork as a separate product (i.e. instead of contributing to AY1920S1-CS2113T-T09-4/main), you should do the following:
-
Configure the site-wide documentation settings in
build.gradle, such as thesite-name, to suit your own project. -
Replace the URL in the attribute
repoURLinDeveloperGuide.adocandUserGuide.adocwith the URL of your fork.
Set up Travis to perform Continuous Integration (CI) for your fork. See UsingTravis.adoc to learn how to set it up.
After setting up Travis, you can optionally set up coverage reporting for your team fork (see UsingCoveralls.adoc).
|
ℹ️
|
Coverage reporting could be useful for a team repository that hosts the final version but it is not that useful for your personal fork. |
Optionally, you can set up AppVeyor as a second CI (see UsingAppVeyor.adoc).
|
ℹ️
|
Having both Travis and AppVeyor ensures your App works on both Unix-based platforms and Windows-based platforms (Travis is Unix-based and AppVeyor is Windows-based) |
When you are ready to start coding, we recommend that you get some sense of the overall design by reading about Entertainment-Pro’s architecture.
The Architecture Diagram given above explains the high-level design of Entertainment-Pro. Entertainment-Pro adopts a n-tier style architecture where higher layers make use of services provided by lower layers. Here is a quick overview of each layer/component:
This collection includes every class that is directly associated with the user interface of the application. All Ui controller classes are part of this collection.
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 Main is specified in MainPage.fxml
The UI component,
-
Executes user commands using the
Logiccomponent. -
Recives commannd results from
Logiccomponent so that theUIcan be updated with the modified data.
Commons represent a collection of classes common to many other classes. This package includes all enumerations declared in the scope of the project, exceptions, as well as String prompt messages and constants that are used throughout the project.
This collection includes every logical class that deals with the logical processing of information and data. Classes that deal with the command parsing, command execution, autocompletion and prediction as well as API requests come under this collection.
The Model collection defines the class templates for all custom objects created to encapsulate data.
This section describes some noteworthy details on how certain features are implemented.
The command parser functionality enables the user commands to be processed effectively. Its implementation also allows for greater flexibility for developers to be able to introduce new commands to the program without having to deal with the underlying implementation of the command parser itself. This is made possible by giving each root command a class of its own. They inherit the CommandSuper class which contains member variables and functions that can be used to conveniently get the details (see section 1.1) from the user input. There is also an abstract function that needs to be implemented in the child classes. This function is named the executeCommand() , which defines the logic that needs to be implemented for each subroot command that is associated with the particular root command.
Command Structure breakdown Every command that the program accepts has a defined command structure to it. Every command that is accepted by the program can be broken down into 4 parts. Namely , the root command, sub root command, the payload and the input flags and values.
-
Root Command: The root command refers to the first word of the command input. There is a fixed set of root commands that the program accepts.
-
Subroot command: The sub root command refers to the 2nd word of the command input. For every root command, there are a fixed set of accepted subroot commands
-
Payload: The payload refers to the main user input argument for the particular command.
-
Input flags and values: The input flags refer to additional information that the command would require to be processed. Each input flag can be accompanied by a value or list of values separated by commas.
In the CommandDebugger class, a few helper functions are defined to do the spell checking of mistyped commands. The JaccardSimilarity Algorithm was employed to score the similarity between command keywords and the user input to derive the most probable command.
In the CommandStructure class, the overall structure of the commands is defined. It specifies what are the possible sub root commands that are available for each particular root command.
The UML diagram below summarises the relationship:
When the user enters a command, the command parser in the command parser class takes in the input and performs a few validation checks listed below:
-
Determining the root command
-
The Command parser class first determines what the root command is, from the list of all possible root commands listed in the CommandStructure class
-
If there is a spelling error in the root command, the spell checker from the CommandDebugger class is run to determine which is the closest possible root command using the JaccardSimilarity algorithm
-
An object of the determined RootCommand class is then created.Every root command has a class of its own which inherits methods from the CommandSuper class. These methods include functions to getPayload, get input flag values among other functions
-
Upon instantiation of the Root command object, the input is processed to determine other crucial factors of the input command, namely,
-
SubRootCommand
-
The possible list of subRoot commands for the particular Root command is obtained from the CommandStructure class.
-
The Subroot command is then determined
-
If there is a spelling error in the subroot command, the spell checker function from the CommandDebugger class is once again used to determine the closest possible subRoot command using the JaccardSimilarity algorithm
-
-
Payload
-
The payload of the input command everything else less the root, subroot and input flags of the command.
-
-
Input flags
-
The input flags are processed and stored in a map of key to values.
-
-
An input command of the following format:
-
-a flag_value1 , flag_value2 -b flag_value3 will be processed into a map of flag keys to arraylist of values as such:
-
-a : [flag_value1 , flag_value2]
-
-b : [flag_value3
-
-
After the instantiation of the command, the command is then added to a command list which is maintained as an arraylist of commands for convenience. This command list is a static command variable in the CommandStack class. This class exposes methods dealing with the execution and displaying of user commands that were entered in the past. Upon adding to this command list, the command is executed if no spelling error was done in the process of inputting the command. If there was a spelling error, the command is still added but is not executed. The user is instead prompted to decide if the deciphered command is what they meant it to be. If they type a ‘yes’ , the command is then executed. The Diagram below illustrates this process:
This command parser feature is implemented as such to allow for more flexibility for developers to include their own new features and their associated commands without having to change the command parser implementation. To introduce a new command for a new feature that the developer is hoping to add, he/she just has to add the necessary root and subroot command into the commandStructure class and then the necessary logic for the feature in the execute command class of the RootCommand Class. There is no need to change the implementation in the CommandDebugger, CommandParser and CommandStack classes to accommodate the newly added commands.
Design Considerations:
The alternative design that was considered was a nested switch statements. Each layer of nesting would aim to determine one more crucial portion input command. In other words, the outermost nested switch statement would attempt to determine the root command, the next nested switch statement would attempt to determine the subRoot command and so on.
Pros: For simpler applications with very limited commands, this structure may be advantageous as it reduces the source lines of code for the project and contains the entire command parser to a single file.
Cons: Doing so would introduce a lot of possibilities for error. The complexity of the command parser would also quickly blow out of proportions once the command structure gets bigger and more complicated. This would therefore indicate an unscalable code. Additionally, this would also mean the logic in the spellchecker and other commandDebugger functions has to be changed to accommodate the additional commands added.
The auto complete functionality enables greater convenience for the users by auto completing the input for them based on its predictions derived by the input of the user. This functionality is invoked by the [tab] button.
Current implementation:
The autocomplete functionality feature is facilitated mainly by the ContextHelper class in the contexts package. This class contains the functions necessary for the correct predictions to be retrieved. This includes the following few processes:
-
Deducing the incomplete portion of the command
-
Accurately determines the correct portion of the user input that is incomplete and needs autocompletion.
-
For example, for the given input search mov , the incomplete portion is mov while for the input search movies Harry potter an, the incomplete portion of the user input is Harry potter an. Determining this will allow the program to provide accurate and highly contextual predictions for the user
-
This process is handled by the getLastIncompleteWords() function
-
-
Determining at which stage the user pressed the
-
This allows the program to decide what type of help the user needs.
-
For example if the user were to press the button while he/she is still typing the first word, this indicates that the user needs help with the completion of the root command
-
This process is handled by the getAllHints() function.
-
-
Completing the user input based on the list of possible predictions for the user input
-
If the number of possible predictions for the given input is only 1, the entire command is auto-completed to that one prediction. * Otherwise, the program auto completes the user input up to the point where the common substring of the returned predictions end.
-
For example, for a given user input search movies Batman Begi, if the prediction returns one single possibility Batman Begins, then the user input is auto-completed to search movies Batman Begins. If instead for the user input, search movies Harr returns a few possible predictions [Harry Potter and the Chamber of Secrets , Harry Potter and the Order of the Phoenix , Harry Potter and the Goblet of Fire ], the program auto completes the user input to search movies Harry potter and the as beyond this point, the program is unable to decide which of the list of possibilities accurately reflect the intentions of the user.
-
This process is handled by the completeCommand() function.
-
In addition, a few other functions from various relevant classes are used to facilitate the retrieval of the actual predictions. These classes include SearchresultContext, CommandContext , Blacklist and Watchlisthandler. The searchresultContext maintains the search results in its data structures so that the movies that the users search for can be used for predictions too. The commandcontext maintains the list of root commands and sub root commands so that they can be used for predictions and auto completion. In each of the classes, Blacklist and watchlistandler, there are helper functions implemented to retrieve possible predictions from the blacklist and watchlist respectively so that items in these lists can be a part of the search space when the program is doing its predictions.
Given below is an example usage scenario and how this auto completion and prediction mechanism behaves on a high level.
-
User launches the program. The CommandContext class instantiates and populates its data structures with all the command keywords. A default search query is performed to retrieve all current movies showing in theatres. The SearchResultContext class instantiates its data structures with the titles of the search results.
-
The user enters bla into the command textfield and presses the tab. This invokes the getAllHints() function. This in turns invokes the getLastIncompleteWords() function to first determine the incomplete portion of the command
-
The program then determines at which stage of the user input the was invoked. In this case, it was invoked midway as the user was typing the root command the getPossibilitiesForRoot() function from the CommandContext class is then called to return a list of possible root command that the program predicts the user might be intending to type
-
As there is only one possible root command (‘blacklist’) predicted, the command input field is auto-completed to blacklist
-
The user presses the ‘tab’ again. This once again repeats steps 2 and 3. However this time round, the program determines that the user is currently trying to get help for the subRoot command.
-
The getPossibilitiesSubRootGivenRoot() function and the list of possible subRootCommand is for the search command is returned in the form of an arraylist.
-
Since there are 2 different types of subroot commands that are possible for the root command ‘blacklist’, namely add and remove, these 4 possibilities are displayed on the UI for the user to see.
-
Since these 2 possibilities do not start with any common substring, the command input field is not auto-completed to anything.
-
The user may now continue to type an additional r into the command input field and press the tab again. Steps 7 and 8 are repeated. But this time, there is only one possibility for the sub Root command that the user may be trying to type (‘remove’). Hence the command input field is auto-completed to blacklist remove
-
Carrying on, if the user were to press again, the program deduces that the user has already completed typing the root and sub root commands and is requesting autocompletion for the payload.
-
The commandSpecificHints() function is invoked and this in turn invokes the getBlacklistHints() function from the Blacklist class. The contents of the blacklist is then returned as possible options to the user for the user to choose and conveniently have auto-completed.
Design Considerations: ** Alternative: Using a single context class to manage the word bank from which predictions were to be made. So this word bank would contain the root command, sub root command keywords, movie and tv show titles from the search results, blacklisted item as well as watchlist items all under the same class. * Pros: This would reduce the complexity of the code as predictions will be made purely based on the string comparison of the strings in the word bank and the incomplete user input. * Cons: Users will receive irrelevant predictions. As this design prevents the program from differentiating search results from command keywords, the autocomplete feature may give suggestions for movie names when the true intentions of the user is to type in the Root command.
The blacklist features enables users to blacklist certain types of movies or movies that contain a certain keyword. Users will be able to add or remove blacklisted movies and keywords to or from the blacklist. User search results are then filtered and only search results that are not part of the blacklist is henceforth shown to the user.
The Blacklist command syntax is as follows:
-
To add movies to the blacklist:
-
blacklist add <MOVIE_NAME>
-
blacklist add <MOVIE_ITEM_NUMBER_FROM_SEARCH_RESULT>
To add keywords to the blacklist: ** blacklist add <keyword> -k
To remove movies from the blacklist ** blacklist remove <MOVIE_NAME>
To remove keywords to the blacklist: ** blacklist remove <keyword> -k
The blacklist is maintained in a Blacklist class which maintains 3 static arraylists, one for movie item, one for movie titles and one for keywords. The movie item is an object containing the movie title and id. The class also contains many helper functions to interface with these 3 arraylist to add, remove, items from them, initialize them from their storage file, print from them, filter search results based on them as well as get hints when user presses the tab key.
The feature was implemented as such so that the user has the freedom to blacklist certain movies as well as movies containing certain keywords. If only either one of them is used, it might not cater to the needs of the users. For instance, if the user hates the ironman series then he or she can add it to the list of blacklisted keywords in order to prevent it from appearing from future searches rather than having to add all the many movies one by one. Conversely, the user might only dislike the ironman 3 movie and might want to only blacklist that movie. Both possibilities are now possible with the current set up.
Given below is the sequence of actions and function calls that occur when the user decides to add new items to the blacklist Upon starting the program, the blacklist content is initialised from the BlacklistStorage.json file using the load() function in the BlacklistStorage class. The 3 arraylists, ArrayList<String> blackListKeyWords , ArrayList<MovieModel> blackListMovies , ArrayList<String> blackListMoviesTitle are populated with the content from the files If such a file does not exist, the file is created and the 3 arraylists are initialized to be empty The executeCommands() function in the BlacklistCommand class is invoked when the above mentioned commands are entered. As the subRoot command for this example is add , the addToBlackList() function is called. The function retrieves the payload from its class member variables (payload is retrieved from the user input by the CommandSuper class. See section 3.1 for more details) The payload is split into items using the comma as the delimiter. Flag map is also retrieved and the presence of the ‘-k’ input flag is checked If True, the item is regarded as a keyword to be blacklisted If False, the item is regarded as a movie/tv show title to be blacklisted Each item is checked to see if its an integer. If true, the corresponding movie/tv show item is retrieved by the SearchResultContext class which invokes the getItemAtIndex() with the integer as input parameter to retrieve the corresponding movie/tv show at the specified index in the form of a MovieInfoObject object If false, the item is regarded as a string and as a keyword or the title to be blacklisted. If step 5 deemed that the user is attempting to add a keyword to the blacklist, the addToBlacklistKeyWord() function in the Blacklist class is invoked using the item as the keyword to be blacklisted. The item is added to the arraylist of keywords(ArrayList<String> blackListKeyWords) after validation checks have been made to ensure that such an item don’t already exists in the blacklist If step 5 deemed otherwise, one of 2 things will occur If step 6 returned true, addToBlacklistMoviesID() function from the Blacklist class is invoked on the MovieInfoObject object that was retrieved from the SearchResultContext class in step 6. The object is consequently added to the ArrayList<MovieModel> blackListMovies arraylist. If false , addToBlacklistMovie() function is invoked on the item, regarding it as the movie title to be blacklisted and the title is added to the arraylist of movie titles, ArrayList<String> blackListMoviesTitles, in the Blacklist class. In both cases checks are performed to ensure that such an item don’t already exists in the arraylists. If they do, DuplicateEntryException is thrown and the user is prompted that such an item already exists in their Blacklist. The data is then saved to the file immediately so that any abrupt termination of the program will not result in the loss of data for the user.
The sequence diagram below illustrates the process that takes place for the example command: blacklist add 1, Joker
-
Design Considerations:
The alternative implementation would have been to maintain only an arraylist of keywords to be blacklisted. Instead of giving the user the option to blacklist movies and shows either as titles or keywords, they are only allowed to blacklist them as keywords. * Pros The implementation is way simpler as this would only require us to maintain the 1 arraylist of blacklisted keywords instead of 3. * Cons This implementation severely limits the flexibility of the program to be able to blacklist movies and tv shows. For example in cases where the movie title is just a single word like ‘joker’, blacklisting such movies might result in the program user blacklisting movies which have nothing to do with the original movie joker, but were blacklisted anyways simply because they contain the word ‘joker’ in their movie title.
The help feature enables users to get help with available commands. They can type help followed by a root command to get a short guide to help them with that particular command. Th guide includes detailed description about that command’s syntax along with the feature’s description and usage’s instructions.
The help details for each root command was written in a text file that is named after the root command itself. Upon starting the application, the help details are loaded and stored in a static map of Root Command to String description in the HelpStorage class. Whenever this help command is executed, the corresponding help message is then loaded onto the UI and displayed to the user.
-
Design Considerations:
The alternative implementation would have been to hardcode the helper messages in the code itself as a String
-
Pros There is no need for file I/O processing, which reduces the chances for error and simplifies the code
-
Cons It is harder to craft the helper messages through code and it is harder to design them to be easily readable. Additionally, if the helper messages are very long, it can be cumbersome to read and manage in the code.
This feature allows users to be able to view commands that they have entered in the past. This provides convenience to the users in scenarios where they wish to enter the same command again or a slight modification of the same command again.
The command history can be revisited by pressing the button on the keyboard. Doing so will display your historical commands one at a time with each button click. Once the correct command is loaded into the command text field, the user can edit it or press straight away to execute the command.
The correct command to be loaded is maintained by the static variable counter which is incremented on the modulo of the size of the CommandList. The corresponding command at the counter position is then returned and displayed to the user. The static variable lastexec also keeps track of the last time the button was pressed so that if the user did not press for a long time (> 3 seconds), the counter is reset to the latest command so that the latest command is returned and not the command of the current counter value.
The user profile allows users to store their details and preferences in the application, which can be easily accessed by other features (such as Search Profile), making the overall user experience more personalised to each user. There are a total of 7 attributes in the UserProfile.java class and users will be able to edit each of them individually. The attributes are: name, age, playlist names, adult content restriction, genre preferences, genre restriction, and sorting method.
-
userName — Saves the user’s name
-
userAge — Saves the user’s age
-
genreIdPreference — Saves the list genre IDs that the user wants to be able to include in searches quickly
-
genreIdRestriction — Saves the list of genre IDs that the user wants to be able to exclude from searches quickly
-
adult — Saves user’s preference on allowing adult content to be shown to them
-
playlistName — Saves the list of names of playlists that the user has created
-
sortByAlphabetical — Saves user’s preference on wanting search results to be displayed alphabetically
-
sortByHighestRating — Saves user’s preference on wanting search results to be displayed in descending ratings
-
sortByLatestRelease — Saves user’s preference on wanting search results to be displayed with latest release dates first
These attributes are maintained in the UserProfile.java class which maintains a String for user’s name, an int for user’s age, 3 ArrayList, one for genre preferences, one for genre restrictions, and one for playlist names, and 4 boolean values, one represents whether the user allows adult content while the other 3 represent the sorting method selected by the user. The class also contains many helper functions to interface with all these attributes.
The logic for the commands available are maintained in a ProfileCommands.java class which handles the parsing of command attributes (payload and input flags) such that the right helper function in UserProfile.java is called to perform the command. The storage of the user profile is maintained in a EditProfileJson.java class which handles the reading from and writing to the JSON file storing the values in UserProfile so that this data will be available offline.
The UML Diagram below summarises the relationship between the 3 classes:
Given below is an example usage scenario and how the UserProfile mechanism behaves throughout the application’s lifetime. Step 1. The user launches the application for the first time. The UserProfile will be initialised with the initial user profile state. Step 2. The user enters a profile command and the Command Parser an executable command depending on the root and sub-root of the command entered. Relevant root commands that deals with the UserProfile are SetCommand and PreferenceCommand. Step 3. The user’s command is checked for validity before execution. If the command is of invalid format or contains invalid attributes, the user will be prompted to enter another command. If command is valid, execution starts. An example of a valid command is set preference (which will be elaborated further below). Step 4. ProfileCommands is initialised to make changes to UserProfile. Step 5. EditProfileJson is initialised to make changes to the JSON file containing data from UserProfile. Step 6. The changes are reflected to the user using the GUI.
We are using java.util.logging package for logging.
-
The
Loggerfor a class can be obtained usingLogger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME) -
Log level can be indicated using
logger.log(Level.INFO, MESSAGE)which logs messages according to the log level
Logging Levels
-
SEVERE: Critical problem detected which may possibly cause the termination of the application -
WARNING: Can continue, but with caution -
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
Refer to the guide here.
Refer to the guide here.
Refer to the guide here.
Target user profile:
-
Our Javafx app is primarily targeted at people who have huge love and interest for movies and TV shows.
-
Our app hence aims to provide a fast and efficient way to find and/or do movies and TV shows related stuff such as getting ratings and reviews for a particular movie and/or TV shows and etc.
-
The app is also intended for people who prefer desktop apps over other types, have the ability to type fast and prefer typing over other means of input.
-
As such, the app will have a GUI (Graphical User Interface) but most of the user interactions will happen using a CLI (Command Line Interface).
Value proposition:
-
Users will be able to save time and effort doing movies and/or TV show related stuff compared to a typical mouse/GUI driven app.
-
Furthermore, our app intends to provide various entertainment related functionalities all under one roof so that users will not have the need to install different entertainment related apps for different purposes.
Priorities: High (must have) - * * *, Medium (nice to have) - * *, Low (unlikely to have) - *
| Priority | As a … | I want to … | So that I can… |
|---|---|---|---|
|
curious user |
look for movies currently screening in the cinemas |
I can keep up to date with these new movies. |
(For all use cases below, the System refers to the Entertainment Pro and the Actor is the user, unless specified otherwise)
MSS
-
User inputs command to request for list of movies showing in cinemas currently
-
System shows the list of movies to user
Use case ends.
Extensions
-
1a. User inputs command wrongly
Use case ends.
-
Should work on any mainstream OS as long as it has Java
11or higher installed.








