Developer Guide
Table of Content
-
Introduction
1.1 Purpose
1.2 Using this Guide -
Setting Up
2.1 Setting up the project in your computer
2.2 Before writing code -
Design
3.1 Architecture
3.2 UI Component
3.3 Parser Component
3.4 Command Component
3.5 AnimeData Component
3.6 User Component
3.7 StorageManager Component -
Implementation
4.1 Estimate Feature
4.2 Browse Feature
4.3 View Anime Information Feature
4.4 Workspace Feature
4.5 Watchlist Management Feature
4.6 Add To Watchlist Feature
4.7 Remove From Watchlist Feature
4.8 View Anime In Watchlist Feature
4.9 Bookmark Feature -
Documentation, Logging, Testing, and DevOps
5.1 Documentation
5.2 Logging
5.3 Testing
5.4 DevOps
Appendix C: Non-Functional Requirements
Appendix D: Instructions For Manual Testing
1. Introduction
Welcome to AniChan!
AniChan is a free desktop command-line application aimed to improve the efficiency of anime translators. With AniChan, translators could manage and plan their time more effectively by identifying the time needed to translate a script. Moreover, AniChan comes with management features such as workspace, watchlist and bookmark that can help them stay organized and focused on their work.
AniChan is written in Java 11 and uses the Object-Oriented Programming (OOP) paradigm which provides us with means to structure a software program into organized and reusable pieces of codes, making it more efficient for future improvements and revisions.
1.1 Purpose
This document is for new and current developers of AniChan. It describes the overall architecture design of AniChan and lays out the current implementation details of our notable features with the rationale and considerations behind each one. It is a living document that will continue to be edited and updated with each major release. The current edition of this document is intended for the v2.1
release.
1.2 Using this Guide
Along the way you might encounter several icons. These icons will provide you with different types of information that you may find useful.
Take note when you see this icon, as it might tell you something important.
This icon represents a friendly tip that might be useful when using our application.
Lastly, text that is blue like this example, are clickable links that will bring you to the relevant part of this developer guide.
2. Setting Up
2.1 Setting up the project in your computer
Ensure that you have the following installed:
- JDK 11.
- IntelliJ IDE (highly recommended).
Firstly, fork this repo and clone a copy into your computer.
If you plan to use Intellij IDEA:
-
Ensure IntelliJ is configured to use JDK 11.
- If you are not at the welcome screen, click
File
>Close Project
to close any existing project. - Click on
Configure
>Structure for New Projects
>Project Settings
>Project
, and ensure theProject SDK
is using JDK 11.
- If you are not at the welcome screen, click
-
Import the project as a Gradle project.
- Click on
Import Project
and locate thebuild.gradle
file and select it. ClickOK
. - If asked, choose to
Open as Project
(notOpen as File
). - Click
OK
to accept the default settings but do ensure that the selected version ofGradle JVM
matches the JDK being used for the project. - Wait for the importing process to finish (it could take a few minutes).
- Click on
-
Verify the setup:
- After the importing is complete, locate the
src/main/java/anichan/Main.java
file, right-click it, and chooseRun Main.main()
. If the setup is correct, you should see the following:
Welcome to AniChan! _ _____ _ /\ (_)/ ____| | / \ _ __ _| | | |__ __ _ _ __ / /\ \ | '_ \| | | | '_ \ / _` | '_ \ / ____ \| | | | | |____| | | | (_| | | | | /_/ \_\_| |_|_|\_____|_| |_|\__,_|_| |_| ------------------------------------------------------------- User: File does not exist. What's your name?
- After the importing is complete, locate the
2.2 Before writing code
2.2.1 Configuring the Coding Style
If you are using IDEA, follow this guide IDEA: Configuring the code style to setup IDEA’s coding style to match ours.
2.2.2 Set up CI
There is no set up required as the project comes with GitHub Actions config files, located in .github/workflows
folder. When GitHub detects these files, it will run the CI for the project automatically at each push to the master branch or to any PR.
2.2.3 Learn the Design
Before starting to write any code, we recommend that you have a look at AniChan’s overall design by reading about it at AniChan’s architecture.
3. Design
The following section describes the architecture design of AniChan. This section starts off by looking at the overall architecture design in a general view, before going into the specific implementation details of the individual features.
3.1 Architecture
This section will help provide insight to the general overview of AniChan’s architecture.
Figure 1: Architecture Design Diagram
The images used are stored in the directory:
docs/images/
. If you wish to update a diagram you may replace the images in this folder.
The Architecture Diagram presented above explains the high-level design of AniChan, and given below is a quick overview of each component involved.
The Main
class is the starting point of the application and has only one class called Main
. It is responsible for:
- At launch: Initializes the various components in the correct sequence, connects them up with each other, and loads any saved data.
- At shut down: Shuts down the components and invokes any clean up methods where necessary.
The rest of AniChan consists of 6 components:
-
Ui
: Manages the user interface of AniChan. -
Parser
: Parses the user input. -
Command
: Executes the command. -
User
: Manages the workspace(s) and user data. -
AnimeData
: Provides data from the anime source files. -
StorageManager
: Reads data from, and writes data to, the hard disk.
Below are some example sequence diagrams to help illustrate the general program flow and how the different objects interact with each other.
The first sequence diagram below shows the program flow of starting AniChan as an existing user.
Figure 2: Starting Up Sequence Diagram
The next sequence diagram shows an instance of command execution.
Figure 3: Command Execution Sequence Diagram
3.2 UI Component
Figure 4: Ui Class Diagram
The Ui component consists of a Ui
class which handles all user input and system output. The Ui is only dependent on the Main
class and does not interact directly with other classes, ensuring a high level of cohesiveness and separation of roles.
The Ui
component actively listens for:
- the execution of commands to print the result of a
Command
. - any exceptions thrown to show an error message to the user, instead of an unexpected program termination.
3.3 Parser Component
Figure 5: Parser Class Diagram
The Parser
component consists of a Parser
class and multiple XYZParser
, each representing a specific command’s parser. The Parser class will first receive a user command from Main
and will proceed to determine the command type.
Once the command type is known, it will then create the respective XYZParser
class.
XYZ here is used generally to represent any command parser. E.g. If a
browse
input was parsed,Parser
will create a new instance ofBrowseParser
.
The created XYZParser
will then parse the parameter and perform input validations, before creating a Command
object to return to Main
.
3.4 Command Component
Figure 6: Command Class Diagram
The Command
component consists of different commands, each represented by XYZCommand
which inherits from the abstract Command
class.
This is similar to the parser above. E.g. A
browse
command would be represented by aBrowseCommand
.
Main
would utilise the Command#execute()
operation to carry out the execution of the Command
which will return a String output that will contain the successful result of the Command
. If the Command
was not successful, an exception will be thrown with details of the failure.
3.5 AnimeData Component
Figure 7: AnimeData Class Diagram
The AnimeData
component is responsible for retrieving offline json data and parsing it into Anime
objects that will be stored in program memory. The AnimeData
will manage an ArrayList of Anime
objects, providing AniChan with an interface for the program to retrieve the source data.
The AnimeData
component:
- can retrieve
Anime
objects using their index. - can view detailed information of each
Anime
object. - can browse the
Anime
catalog with sorting algorithms.
3.6 User Component
Figure 8: User Class Diagram
The User
class inherits from the abstract Human
class and stores the name and gender of the user. It represents the user’s interactions with Workspace
class.
The User
component:
- can provide user information such as
name
,gender
, andhonorific name
. - stores an ArrayList of type
Workspace
. - can add, set, and switch between workspaces.
The Workspace
component:
- can allow
User
to create and get the list ofWatchlist
andBookmark
. - can allow
User
to change his activeWatchlist
.
3.7 StorageManager Component
Figure 9: StorageManager Class Diagram
The StorageManager
component:
- can save workspace created by the user as a folder.
- can save user, watchlist and bookmark data in
.txt
format and read it back using their respective storage class:UserStorage
,WatchlistStorage
, andBookmarkStorage
. - can read script files that are in
.txt
format using the classScriptStorage
.
AniChan saved these data as .txt
files so advanced users will be able to view and manipulate these saved data easily with any available text editor.
4. Implementation
This section introduces the specific implementation details and design consideration of some features in AniChan.
4.1 Estimate Feature
The estimate feature aims to provide translators with better estimates on the time needed to translate a script based on their capability, or by the average translators’ capability. Hence, this feature allows users to better manage and plan their time.
4.1.1 Current Implementation
The estimate feature is facilitated by EstimateCommand
. By running the command estimate
with the relevant field (and parameter), EstimateParser
will construct EstimateCommand
which will be used to execute the user’s instruction.
Given below is an example usage scenario showing how the EstimateCommand
behaves at each step.
Step 1: User executes the command estimate script.txt -wph 300
. The application invokes Parser#getCommand()
and because the command type is “estimate”, EstimateParser#parse()
is invoked to parse, validate, and construct EstimateCommand
with “script.txt” and “300”. The created object is then returned to Main
.
Step 2: EstimateParser
is terminated at this point. The application invokes EstimateCommand#execute()
to execute the user’s instruction.
Step 3: EstimateCommand
first invokes User#getActiveWorkspace()
to identify the workspace the user is currently using, then it invokes StorageManager#loadScriptFile()
to read scriptFileName
(located in the active workspace folder) and store its content in fileContent
.
Every workspace is actually a folder.
The application assumes that the user has the file placed in the active (currently using) workspace.
Step 4: Once the file has been read, it calculates the estimated time using fileContent
and wordsPerHour
, then invokes EstimateCommand#timeNeededToString()
to convert the estimated time into a human-readable format, and finally, returns the result to Main
for it to be printed via Ui#printMessage()
.
If
wordsPerHour
was not specified, the values 400, 500, and 600 words per hour (average translator’s speed) will be used and this will generate 3 timings, unlike the current scenario, only 1 timing will be generated.
Step 5: EstimateCommand
is terminated.
The sequence diagram presented below depicts the interaction between the components for running the command, estimate script.txt -wph 300
.
The sequence diagram shows the interaction from step 2 onward.
Figure 10: Sequence Diagram for estimate script.txt -wph 300
4.1.2 Design Considerations
This section shows the design considerations taken when implementing the estimate feature.
Aspect: When should the application validate the script file
Since the script file specified by the user can be non-existent or empty, it is important to determine when the application should validate the file to ensure efficient use of memory resource.
Approach | Pros | Cons |
---|---|---|
During command execution. | Easy to implement since Command already handle file matters. |
Some memory resource are wasted if the file is invalid. |
During parsing. | No memory resource wasted since invalid file will be detected early. | Decreases cohesion as Parser now has to handle file matters on top of parsing matters. |
Having considered both approach, we have decided to implement the first approach, validate the script file during command execution because we do not want to decrease the cohesion of Parser
, and we find that the memory resource wasted is a worthy exchange for the cohesion preserved.
Aspect: The way user can specify the script file
As the user have to specify a script file for the command, there is a need to decide on whether the user needs to include the file extension as well.
Approach | Pros | Cons |
---|---|---|
Have to specify file extension. | Ensures the correct file will be read. | May affect usability as some user may not know how to identify the file extension and might find it troublesome. |
Do not have to specify file extension. | Users can easily specify the file to read. | May read the wrong file due to identical names but different file extension. |
We have decided to implement the first approach, users should specify the file extension because if the wrong timing is provided to the user, it could end up being a costly mistake. In addition, we also find that this accuracy we are assuring our users far outweighs and compensate for the slight inconvenience introduced.
4.2 Browse Feature
The browse feature is a useful feature that will allow users to quickly look through all the different anime series available in a browsing fashion.browse
is also able to sort the order of how anime are displayed either in alphabetical order or by the anime’s rating.
4.2.1 Current Implementation
The BrowseCommand
is executed by BrowseCommandParser
after parsing the user input. It will then fetch Anime
objects matching the parameters specified by BrowseCommandParser
that are stored in AnimeData
. It extends the Command
class and implements the following operations:
-
BrowseCommand#sortBrowseList()
- Handles any sorting ofAnime
objects. -
BrowseCommand#buildBrowseOutput()
- Creates the output string to be printed to users.
Given above are the two fundamental operations that will carry out the execution of a browse
command.
If no additional parameters is provided, BrowseCommand
will be constructed with default values. This ensures it will still perform the default browse
and return a page of anime series that may be useful.
Before going any further, please refer to this helpful table of the BrowseCommand
parameters for reference. These are the attributes that will define the scope of a browse
command.
Attribute | Value | Function |
---|---|---|
order | 0 | Ascending |
order | 1 | Descending |
sortType | 0 | No Sort |
sortType | 1 | by name |
sortType | 2 | by rating |
sortType | 3 | back to original |
page | >= 1 | page number |
The values which are magic literals here, have already been refactored out into constant variables within the code.
Given below is an example usage scenario to showcase how a BrowseCommand
will behave at each step. This example will utilise a small sample list of AnimeData
and a page size of 3
.
The page size is set to
20
as the default in the actual program.
Step 1: When the user enters the command for browse
, the input will be processed and parsed by Parser
before being further parsed by BrowseParser
.
Step 2: Upon completion of parsing and input validation, BrowseParser
will create an executable BrowseCommand
object and return it to Main
.
Step 3: Once the BrowseCommand
object finishes its execution, it will first process the AnimeData
by calling on its BrowseCommand#sortBrowseList()
operation to identify the order that the list will need to be in (if any).
For this case, since it is a default browse operation, there will be no sorting performed.
Step 4: Now BrowseCommand
will utilise its BrowseCommand#buildBrowseOutput()
operation to access all Anime
objects within the page window, as shown in the diagram below.
Figure 11: Browse Default State Object Diagram
In this example, it fetches the following Anime
objects.
Charlie
Echo
Gamma
If the 2nd page of the list was requested instead using the input browse -p 2
. BrowseCommand#buildBrowseOutput()
will shift its page window down by 1 page as depicted in the diagram below.
Figure 12: Browse Next Page Object Diagram
Step 5: For each Anime
object, it will access the Anime
object’s methods to get the relevant information about that anime series and construct a printable result for the user to view.
Step 6: After all Anime
objects in the page window have been retrieved, it will return the printable result back to BrowseCommand#execute()
. At this point, it will utilise BrowseCommand#sortBrowseList()
again to reset the AnimeData
list to its original form if it has been altered.
Step 7: Once AnimeData
has been sorted, it will return the result back to Main
for printing.
Currently, this result is not very exciting as it is just based on the Anime
object ID which is assigned randomly by our data source, AniList.
A more interesting example scenario would be browsing the 2nd page of a sorted list in ascending order. The only step that would change would be at Step 3, where it will perform sorting of AnimeData
list.
Figure 13: Browse Sorted State Object Diagram
As you can see, even though the page window is at the same position as the previous command, the list is now different as it has been sorted.
From this point on, the operation will continue as per the steps above, but during the final BrowseCommand#sortBrowseList()
call in step 6. It will perform a sort to reset the list.
The reason for why a reset sort is needed is further elaborated in the design consideration.
Here is the sequence diagram to better illustrate the lifecycle of a browse
command upon its execute() call.
Figure 14: Browse Sorted Sequence Diagram
4.2.2 Design Consideration
Here are some various design considerations that was taken when implementing the browse
feature.
Aspect: How should the program handle the sorted list
Since browse
would require a sorted list, it is important to identify a suitable way to sort the list so that it does not affect the original list and have low complexity.
Approach | Pros | Cons |
---|---|---|
Leaving the list unsorted. | - No complexity at all and is the fastest approach. | - List will be unsorted and may cause confusion to users and other operations. |
Resorting the list again. | - The list will be back into its original form before browsing. | - May hinder performance as resorting could take time. - Requires altering of the main list. |
Cloning a duplicate list to sort. | - The list will be back to its original form. - The main list will not be affected at all. |
- Expensive operation that will require large storage and time complexity. |
We have decided to implement the second approach of resorting the list because it is important to have the original order of the list to ensure that other operations will not be affected by browse
. This approach also is the most cost-effective method that will not incur too many expensive memory usage and complex operations.
Aspect: Should the program use an interactive or static browsing approach
Since browse
will access anime series in pages, it is important to decide the way users can access or ‘flip’ through different pages.
Approach | Pros | Cons |
---|---|---|
Interactive browsing, users can flip pages and pick to view specific anime. |
- Fluid and seamless browsing session. - Very good usability and interactivity. |
- Would require BrowseCommand to constantly get user input. |
Static browsing, users specify the page they want to access. | - Completely decoupled from Ui component. - Allows for browse to be more precise in finding what the user wants. |
- Not as seamless as the first approach but still usable. |
We have decided to implement the second approach of having static browsing which users specify the page he wants to access. This is because it was important for the project to be OOP and this approach was a good fit for that requirement. This approach also allows for users to access different pages quickly, meaning more experienced users will be able to utilise it quicker than the first approach.
4.3 View Anime Information Feature
The info
command allows the user to view all the relevant information regarding a specific anime that the user specifies. This allows them to know more about a particular anime.
4.3.1 Current Implementation
The view information command is currently implemented by the InfoCommand
. The user has to give an input of the form info <ANIME_ID>
, and this would allow users to check all the information available for the ANIME_ID they have specified.
Given below is an example of the usage scenario of view information command and how it behaves at each step.
Step 1: Ui
would receive input in the form of info <ANIME_ID>
in the Main
class and pass it into Parser
class.
The ANIME_ID specified has to be an integer value as specified by the index of nime in the AnimeData.
Step 2: The Parser
class would then extract out info
from the input given, which will instantiate a new InfoParser
object.
Step 3: The InfoParser#parse()
method will be invoked, and this will validate the field given by the user.
Once validated, a new InfoCommand
object will be constructed with the ANIME_ID.
InfoCommand
object will be returned back all the way to Main
. InfoParser
is then terminated.
Step 4: Main
will then call InfoCommand#execute()
. In here the ANIME_ID will be validated and AnimeData#returnAnimeInfo()
method is invoked, returning a string containing information regarding that particular ANIME_ID.
Step 5: The string is returned all the way back to Main
and printed out by Ui
.
Step 6: InfoCommand
is terminated.
The diagram below is the sequence diagram from steps 4 onward.
Figure 15: Info feature steps 4 to 6 sequence diagram
4.3.2 Design Consideration
This section shows the considerations taken when designing this feature.
Aspect: Using anime title or Anime ID
The user may at some point want to know more about a particular anime, in which he may or may not know the full title of the anime. It is therefore important to decide on whether to prompt users to give the title of the anime, or just give its ID.
Approach | Pros | Cons |
---|---|---|
Use anime title as input. | - Easier for users who know anime titles. | - Users will have to input the full anime title. - The program has to search through the whole list of anime data. |
Use anime ID as input. | - Users will only need to input one single integer. | - Users will have to search for the anime ID if they do not know the ID. |
We decided to go with the second approach, as it would enhance the user experience of not having to key in the full anime title. It would be very frustrating for the user as well if he misspells the title.
At the same time, using anime ID as input would allow the program to be able to retrieve the full anime information much quicker, instead of having a title to search against the whole data of anime to retrieve information.
4.4 Workspace Feature
Similar to a desktop, AniChan has a workspace feature which allows users to organize data in separate containers and switch between them to avoid intermixing of information.
Workspace
is primarily the layer of code that sits between the user, and the rest of AniChan data management features (e.g. Watchlist
, Bookmark
).
As such, most of the code that manages Workspace
can be found in User.java
and Workspace.java
.
Upon running the program for the first time, a workspace named
Default
is created. A similarly named folder will also be created in/data
directory, managed by ourStorage
class.
4.4.1 Current Implementation
Command Option | Workspace Command | Description field |
---|---|---|
-n |
WorkspaceCommand#createWorkspace() |
Creates new workspace |
-s |
WorkspaceCommand#switchWorkspace() |
Switches to specified workspace |
-l |
WorkspaceCommand#listWorkspace() |
Lists existing workspace(s) |
-d |
WorkspaceCommand#deleteWorkspace() |
Deletes specified workspace |
The WorkspaceCommand
is instantiated by WorkspaceParser
, and it requires 2 parameters:
-
commandOption
(mandatory). -
workspaceName
(mandatory unless option-l
is specified).
Given below is an example usage scenario showing how the command behaves at each step when the user tries to create new Workspace
:
Step 1: User launches the application for the first time. The User
will be initialized with an initial Workspace
named Default
, and the activeWorkspace
pointing to it and workspaceList
ArrayList
containing it.
Figure 16: Workspace Command Initial State
Step 2: User
enters the command workspace -n Netflix Animation Studio
, the input will be processed and parsed by Parser
and then further parsed by WorkspaceParser
.
Step 3: Upon completion of parsing and input validation, WorkspaceParser
will create a WorkspaceCommand
object with the extracted commandOption
and workspaceName
parameter and return it to Main
.
Step 4: Main
calls WorkspaceCommand#execute()
and it checks the commandOption
before running WorkspaceCommand#createWorkspace()
accordingly.
Step 5: WorkspaceCommand
firstly calls User#addWorkspace()
to add a new Workspace
to User
, then makes an empty ArrayList
of Watchlist
using User#setWatchlistList()
for the User
. Finally, it uses storageManager#saveWorkspace()
to save the Workspace
to disk.
Figure 17: Workspace Command After New Workspace Creation
Step 6: If successful, WorkspaceCommand
returns the successfully created workspace message to Main
.
Likewise, the operations to switch, list, and delete follows a similar execution process. The following diagrams will continue from step 6, and will illustrate the changes to the Workspace
ArrayList
.
Step 7: User keys in workspace -s Netflix Animation Studio
to switch active workspace.
Figure 18: Workspace Command After Workspace Switch
Step 8: User keys in workspace -d Default
to delete the workspace named Default
.
Figure 19: Workspace Command After New Workspace Delete
The following sequence diagram illustrates how Workspace
creation in the example above works:
The other options (
-s
,-l
,-d
) follows a similar process, only the list and switch option does not interact withStorageManager
andWatchlist
.
Figure 20: Workspace Command After New Workspace Delete
4.4.2 Design Consideration
This section shows some design considerations taken when implementing the Workspace
feature.
Aspect: How can Workspace
be identified?
As most commands in WorkspaceCommand
operates on an individual Workspace
, there needs to be some way to identify each of them uniquely.
Approach | Pros | Cons |
---|---|---|
Identify using a number ID. | Users can quickly switch and delete Workspace just by keying in a number. |
Operations like delete is irreversible and is not done often, accidentally keying in the wrong number can be catastrophic. |
Identify using name. | If user remembers the name, he can easily switch /delete without using the List command first. |
User may waste time typing long workspace names. |
We have decided to use name
to identify Workspace
as it is more intuitive for the end-user.
This also avoids the need to maintain an integer ID
for each Workspace
.
Aspect: Workspace
name restrictions
As Workspace
is identified by their names, and other classes like Storage
relies on the name to make folders for data storage purposes.
Should we allow the user full discretion to naming Workspace
?
Approach | Pros | Cons |
---|---|---|
Yes | Allows user more flexibility. | Confusing or unexpected names may lead to unexpected outcomes. |
No | Eliminate unexpected names which could lead to unexpected outcomes. | Less flexibility and more code required to enforce. |
For example, a user may provide new workspace__
as a Workspace
name, this may confuse the user in the future when he tries to list
all Workspace
as the space characters are whitespaces. Hence, enforcing no extra whitespaces was implemented.
In addition, we also prevent case-insensitive Workspace
creation, use of special characters, and long names (i.e. length above 30), as they may cause issues when creating folders on certain file systems.
Aspect: Loading Workspace
on program start
As there needs to be an activeWorkspace
set at all times for operations such as adding Watchlist
. How do we determine which Workspace
should be chosen if the User
owns multiple Workspaces
?
Approach | Pros | Cons |
---|---|---|
Scan from data folder and pick first result. |
Able to adapt to changes even if malicious or unexpected edits were made to file system. | User may need to switch to his favorite Workspace on each startup, if any. |
Store last used Workspace . |
User might gain some convenience of not needing to switch to his favorite Workspace . |
Prone to potential issues from the file system. |
We picked the first approach as it is the safer option. By allowing AniChan to scan and adapt to file system changes (e.g. User
accidentally moves/renames/deletes Workspace
on his hard drive), we avoid potential issues of relying on old information created in previous runtime which may hinder user experience.
4.5 Watchlist Management Feature
The watchlist management feature aims to provide translators with a simple way to keep track of anime by being able to group anime based on their own criteria. This allows them to stay organized and focused on their work rather than being concerned over irrelevant issues.
4.5.1 Current Implementation
The watchlist management feature is facilitated by WatchlistCommand
. By running the command watchlist
with the relevant parameter and field, WatchlistParser
will construct WatchlistCommand
which will be used to execute the user’s instruction.
Below is a table describing the 4 parameters supported by the watchlist
command, and the corresponding method that is invoked (required parameters are omitted).
The term active watchlist refers to the watchlist that the user is using to add anime into or remove anime from, and this is tracked by
activeWatchlist
inWorkspace
.
Parameter | Method | Description |
---|---|---|
-n |
WatchlistCommand#createWatchlist() |
Creates a new watchlist |
-l (lowercase letter ‘L’) |
WatchlistCommand#listAllWatchlist() |
Lists all watchlist in the workspace |
-s |
WatchlistCommand#selectWatchlist() |
Selects a watchlist to be the new active watchlist |
-d |
WatchlistCommand#deleteWatchlist() |
Deletes a watchlist |
Given below is an example usage scenario showing how the WatchlistCommand
behaves at each step. In this example, we will look at the watchlist creation process.
Figure 21: WatchlistCommand Initial State
Step 1: User executes the command watchlist -n NewAnime
. The application invokes Parser#getCommand()
and because the command type is “watchlist”, WatchlistParser#parse()
is invoked to parse, validate, and construct WatchlistCommand
with “-n” and “NewAnime”. The created object is then returned to Main
.
Step 2: WatchlistParser
is terminated at this point. The application invokes WatchlistCommand#execute()
to execute the user’s instruction.
Step 3: WatchlistCommand
first invokes User#getActiveWorkspace()
to identify the workspace to add the new watchlist, and according to the instruction “-n”, WatchlistCommand#createWatchlist()
is invoked.
Step 4: It first invokes activeWorkspace.getWatchlistList()
to initialise watchlistList
. A Watchlist
object is then constructed with the name “NewAnime” and validated before it is added to watchlistList
.
The validation checks ensure the watchlist name is unique in
watchlistList
, is not empty, and contains less than or equal to 30 alphanumeric characters and/or spaces.
Step 5: StorageManager#saveWatchlist()
is invoked to save the updated watchlistList
, and finally, the result of this command execution is returned to Main
for it to be printed via Ui#printMessage()
.
The details of all
Watchlist
object for a workspace will be saved in the file “watchlist.txt” in the workspace folder.
Step 6: WatchlistCommand
is terminated.
Figure 22: WatchlistCommand After Create State
All the other parameters in the watchlist
command also follows a similar execution process.
The following diagrams will continue from step 6, and it will show how the activeWatchlist
state will change as the watchlist
command executes with the select (-s
) and delete (-d
) parameter.
The execution with the list parameter (
-l
) is not shown as it does not result in any change to theactiveWatchlist
state.
Step 7: The user executes watchlist -s 2
to set the second watchlist (“New Anime”) in the list as the new active watchlist.
Figure 23: WatchlistCommand After Select State
Step 8: The user now decides that the “NewAnime” watchlist is no longer needed and decides to execute watchlist -d 2
to delete it.
Figure 24: WatchlistCommand After Delete State
The sequence diagram presented below depicts the interaction between the components for running the command, watchlist -n NewAnime
.
The sequence diagram shows the interaction from step 2 onward.
The other parameters (list, select, and delete) follows a similar process, only the list and the select parameter does not interact with the
StorageManager
since they do not modify the watchlist data.
Figure 25: Sequence Diagram for watchlist -n NewAnime
4.5.2 Design Considerations
This section shows the design considerations taken when implementing the watchlist management features.
Aspect: Saving watchlist data
Since watchlist can be created and deleted at any point of time, it is important to decide on when the application should save the watchlist data.
Approach | Pros | Cons |
---|---|---|
Whenever the watchlist data is modified. | Data would not be lost if the application or system crashes midway. | Application might slow down when the data grows large. |
When the user exits the program. | Saving is more efficient and could improve performance. | User may lose their data if the application or system crashes midway. |
Having considered both approach, we have decided to save watchlist data whenever the watchlist data is modified because users may work on the application for long period and unexpected events can always happen. Losing work data can also be a frustrating and costly mistake to the user especially if the data are important.
Aspect: Watchlist name restriction
To create a watchlist, users would have to give it a name, and these names can affect the usability of the application in the long run. Hence, there is a need to decide on whether the watchlist name needs to be restricted to ensure it remains readable in the long run.
Approach | Pros | Cons |
---|---|---|
No restriction. | Users have more flexibility. | This may hinder user’s vision of the input prompt and affects the usability. |
Maximum of 30 alphanumeric characters and/or spaces, but cannot contain spaces only. | Ensure users have a easy to read input prompt. | Users have less flexibility in naming. |
While both approach are valid in their own ways, we have decided to restrict watchlist name to a maximum of 30 alphanumeric characters and/or spaces, but cannot contain spaces only because having a watchlist name that is lengthy or has special characters can muddle up the readability of the input prompt, and that can affect the usability of the application.
4.6 Add To Watchlist Feature
The add
feature allows users to add an anime into the active watchlist. This helps them keep track of the anime they would like to watch next.
4.6.1 Current Implementation
The current implementation of the add to watchlist command requires the user to give an input in the form of add <ANIME_ID>
.
This will allow users to add the ANIME_ID specified by calling the Watchlist#addAnimeToList()
method in the active Watchlist
object.
Add to watchlist command extends the Command
class, and the parse
method in AddToWatchlistParser
class is being called to validate the parameter that the user has entered.
Below is an example usage scenario of how add to watchlist command behaves at each step.
Step 1: Starting from the Main
class, the user first inputs add <ANIME_ID>
. The input will be taken in by the Ui
class, and passed into Parser
through Parser#getCommand(userInput)
.
The ANIME_ID specified has to be an integer value as specified by the index of anime in the AnimeData.
Step 2: The Parser
class would then extract the add
command out of the input and it will instantiate a new AddToWatchlistParser
object.
Step 3: AddToWatchlistParser#parse()
is then called by the Parser
class. This will validate the field that has been given.
A new AddToWatchlistCommand
object is instantiated with the specified ANIME_ID, and is returned back to Main
class.
At this point, AddToWatchlistParser
is terminated.
Step 4: AddToWatchlistCommand#execute()
is then invoked in Main
, which retrieve the active workspace
through AddToWatchlistCommand#getActiveWorkspace()
,
and Watchlist
object from ActiveWorkspace#getActiveWatchlist()
.
Step 5: Watchlist#AddAnimeToList()
will then be called, passing in the anime index. This will then add the anime index into the
ArrayList of integers storing all the anime in that Watchlist
.
Step 6: commandResult
will then be passed back to Main
to be printed out by Ui
, and AddToWatchlistCommand
is terminated
For better illustration, Figure 28 below shows the sequence diagram of steps 4 to 6.
Figure 26: Sequence diagram for Add To Watchlist feature steps 4 to 6
4.6.2 Design consideration
Below shows the considerations taken when implementing the AddToWatchlist
feature.
Aspect: Using anime title or anime ID
This consideration is similar to our info
feature consideration, so below is the same table we find in our info
feature section.
Approach | Pros | Cons |
---|---|---|
Use anime title as input. | Easier for users who remember anime titles. | Users will have to input the full anime title. |
Use anime ID as input. | Users will only need to input one single integer. | Users will have to search for the anime ID if they do not know the ID. |
Similarly, we decided to go with the second approach as this would be much easier for users to key in, and also faster for the program to find the anime the user wants to add into the watchlist.
4.7 Remove From Watchlist Feature
The remove from watchlist feature allows users to remove a particular anime from their currently active watchlist. This would allow them to keep their watchlist clean of the anime that they have watched, leaving only those that they have not watched.
4.7.1 Current Implementation
The remove from watchlist command currently requires the user to give an input in the format: remove <ANIME_ID_IN_WATCHLIST>
. The implementation of remove from watchlist command is similar to the add to watchlist feature with the only difference being that the user has to delete the index of the anime in that watchlist, instead of the actual ANIME_ID.
The usage scenario of remove from watchlist is similar to the add to watchlist command, but we will be using RemoveCommand
, RemoveCommandParser
and Watchlist#removeAnimeFromList()
.
ANIME_ID_IN_WATCHLIST is the index of anime inside the watchlist itself, not the anime index in AnimeData.
4.7.2 Design Consideration
This section describes the design considerations taken when implementing this feature.
Aspect: Which index to use when removing an anime
It is important to decide the what type of input that the user should give, given that they will frequently use the remove command to remove one, or multiple anime from his watchlist.
Approach | Pros | Cons |
---|---|---|
Use anime ID in AnimeData as input. | - Easier for users who remember the anime ID as they added it in to the watchlist before. | - Users will have to search for the anime ID again in the event that he forgets it. - The program has to search through the whole list anime in the watchlist. |
Use anime ID in Watchlist as input. | - Program can easily delete a particular index of an ArrayList | - Users will have to find out the anime index in the watchlist using the view command. |
We have decided to use approach 2 instead of 1, as it will not only will it be much faster for the program to delete one particular index instead of having to search through the whole list, it would be provide better user experience as the user will not need to search for the anime ID that he wants to remove in the case that he does not know the anime ID.
4.8 View All Anime in Watchlist Feature
Users can view the anime that they have stored in his active watchlist, or a specific watchlist, by using the view
command. In doing so, they can easily check what anime they would like to watch next.
4.8.1 Current Implementation
The view
command is currently implemented by the ViewWatchlistCommand
. It is instantiated by Parser
and executed by Main
. This allows users to view all the anime currently inside a Watchlist
object.
An example usage scenario on how view anime in watchlist behaves is given below.
Step 1: In the Main
class, the user will input view [-v WATCHLIST_ID]
. Ui
will take in this input and is passed into Parser
.
Step 2: In Parser
, view
will be extracted out of the input, leading to a new ViewWatchlistParser
object being instantiated.
Step 3: ViewWatchListParser#parse()
is then called in Parser
.
If no parameter is given, then an empty constructor of ViewWatchlistCommand
will be instantiated.
Otherwise, the parameter and field will be validated, and a constructor of ViewWatchlistCommand
with WATCHLIST_ID is instantiated.
Step 4: The ViewWatchlistCommand
object is then returned back to Parser
, and back to Main
. ViewWatchlistParser
is terminated.
Step 5: The ViewWatchlistCommand#execute()
would then be called by Main
, in which the WATCHLIST_ID will be validated.
Step 6: ViewWatchlistCommand#buildAnimeInWatchlist()
will build a string containing all the anime name inside the selected Watchlist
, and it will be returned to Main
, where it will be printed out by Ui
.
Step 7: ViewWatchlistCommand
is terminated.
The figure below is the sequence diagram for steps 5 to 7
Figure 27: Sequence diagram for View Watchlist feature steps 5 to 7
4.9 Bookmark Feature
The bookmark
feature aims to provide the user with the ability to create shortcuts to anime they wish to track. This feature further provides tools to monitor the progress of a series and make informative notes.
4.9.1 Current Implementation
The Bookmark
class uses three ArrayList to store bookmark entries of the user, these ArrayLists maintain information about the anime index, episode and notes. The synchronisation between ArrayList is required so that it enables easy retrieval of bookmark information using the bookmark index on the three ArrayList.
Figure 28: Bookmark Class Diagram
BookmarkCommand
is instantiated by BookmarkParser
, and requires a mandatory BookmarkAction. With the BookmarkAction, the Parser
will determine the required field for the BookmarkCommand
. The table below shows the required field for each action.
Bookmark Command | Bookmark Action | Required field |
---|---|---|
Add | a | Anime ID |
Delete | d | Bookmark ID |
List | l | Not required |
Episode | e | Bookmark ID, Episode |
Note | n | Bookmark ID, Note |
Remove Note | r | Bookmark ID, Note ID |
Info | i | Bookmark ID |
Given below is the example usage scenario and how the Bookmark
command behaves at each step.
The command is using one-based indexing while the program stores and recognises the zero-based indexing. The following example will use one-based to be consistent with the commands.
Step 1: User inputs command bookmark
, the application then calls Parser#getCommand()
and passes the command to it.
Step 2: BookmarkParser
extracts “Bookmark Action” from the command, and based on the command type, it will determine the respective required field. The BookmarkParser
is responsible for validating the input parameters and will throw an AniException
when encountering unrecognised inputs.
For example: bookmark 2 -e 5
, the parser will create BookmarkCommand
and call the methods setBookmarkAction(“e”)
, setBookmarkIndex(2)
and setBookmarkEpisode(5)
.
Step 3: The application calls BookmarkCommand#execute()
and the command will use the BookmarkAction
to do a corresponding validation check on the field before calling the Bookmark Operations.
Below is a list of bookmark operations:
-
Bookmark#addAnimeBookmark()
: Adds theAnime
index provided into the bookmark list. -
Bookmark#getListInString()
: List all entries within theBookmark
using theBookmark
index together with theAnime
name. -
Bookmark#deleteAnimeBookmark()
: Remove theBookmark
index provided from the bookmark list. -
Bookmark#editBookmarkEpisode()
: Edit the current episode for a Bookmark entry . -
Bookmark#addNote()
: Add a note for a Bookmark entry. -
Bookmark#removeNote()
: Remove a note from a Bookmark entry. -
Bookmark#getAnimeInfoFromBookmark()
: Retrieve the anime info on the anime ID kept at the specified bookmark entry. -
Bookmark#getBookmarkEpisode()
: Retrieve the episode field for a Bookmark entry. -
Bookmark#getNoteInString()
: Retrieve notes in a list within the specified bookmark entry.
Step 4: The user executes bookmark -a 430
command to add the anime ID: 430 into the bookmark. Bookmark#addAnimeBookmark()
will then add the anime index to the ArrayList within the bookmark.
Figure 29: Bookmark Entry After Add
The table shows the three ArrayList objects in the column with the bookmark ID. When adding a new anime ID into the bookmark, the bookmark will initialise the anime episode to be 0 together with an empty note object.
Step 4.5: The user executes bookmark -a 1
and bookmark -a 410
to add anime ID 1 and 410 to the bookmark.
Figure 30: Bookmark Entries with more Add
The sequence diagram presented below depicts the interaction between the components for running the command, bookmark -a 430.
The other options follow a similar process, only the list and info bookmark commands does not interact with StorageManager. For example the delete bookmark command calls
deleteBookmarkEntry()
method and modify the bookmark entry usingBookmark#deleteAnimeBookmark()
.
Figure 31: Bookmark Add Command Sequence Diagram
Step 5: The user executes bookmark -l
command to list all anime within the bookmark. Bookmark#getListInString()
will use the Anime index stored in the bookmark list and retrieve the anime name from AnimeData, the method then returns the bookmark index with the anime name.
Listing all anime in bookmark:
1. To Heart 2
2. Cowboy Bebop
3. InuYasha the Movie 2: The Castle Beyond the Looking Glass
Step 6: The user executes bookmark -d 1
command to delete the bookmark entry at bookmark ID 1. Bookmark#deleteAnimeBookmark()
will then remove the Bookmark index from the Bookmark
.
Figure 32: Bookmark Entries After Delete
The ArrayList comes with an inbuilt function to enable easy deletion at index, but the bookmark index of subsequent entries will decrease by 1.
Step 7: The user executes bookmark 1 -e 5
command to edit the episode for the first bookmark entry. Bookmark#editBookmarkEpisode()
will change the episode field for that bookmark entry.
Figure 33: Bookmark Entries After Edit Episode
Step 8: The user executes bookmark 1 -n Schedule push back
command to add a note for a bookmark entry. Bookmark#addNote()
will then add a note to the bookmark entry at bookmark ID 1.
Figure 34: Bookmark Entries After Add Note
Step 9: The user executes bookmark 1
command to view all information of the first bookmark entry. The command will use Bookmark#getAnimeInfoFromBookmark()
to retrieve the detailed anime info for the anime ID at that bookmark, Bookmark#getBookmarkEpisode()
for the tracked episode by the user and Bookmark#getAnimeNotesFromBookmark()
will retrieve all notes in a list format. With all the relevant information on the bookmark entry, the result will be displayed to the user (Figure 26: Bookmark Entries After Edit Episode).
Here is the information for that anime.
Index: 1
Name: Cowboy Bebop
Episodes: 26
Release Date: 03/Apr/1998
Rating: 86
Genre: [Action, Adventure, Drama, Sci-Fi]
Current Episode: 5
Notes for anime:
1. Schedule push back
Step 10: The user executes bookmark 1 -r 1
command to remove a note from the bookmark entry. Bookmark#removeNote()
will remove the note ID 1 from the first bookmark entry. The resulting state of the remove note command will look exactly the same to the state before the note was added.
Figure 35: Bookmark Entries After Remove Note
4.9.2 Design consideration
This section describes the various design considerations taken when implementing the Bookmark
feature.
Aspect: How should the bookmark entries be kept
The first design consideration was the data structure on how the bookmark entries should be maintained. The main issue here was the cohesiveness between the Bookmark
object and the Workspace
.
Approach | Pros | Cons |
---|---|---|
Usage of three ArrayList to store anime index, Episode, and Notes. | - Easy to reference objects within ArrayList using its index and it is easy to implement. | - Require to synchronise the three ArrayList so the same index reference the components of the same bookmark entry. |
Use a BookmarkManager to handle bookmark features. |
- Do not need to maintain multiple ArrayLists. | - One extra layer of unnecessary abstraction (nesting), while introducing more coupling and dependency. |
While both approach have their own benefits, we have decided to use three ArrayList to keep the information of the bookmark entries. Considering the structure of how bookmark is within the workspace, we prefer to directly use the bookmark as the bookmark manager will create another layer of unrequired abstraction.
Aspect: How should bookmark entries keep anime information
The second design consideration was how to keep the reference to the Anime
object. The main issue here was the storage and duplication of data.
Approach | Pros | Cons |
---|---|---|
Keep only the anime index information within the bookmark. | - Easy to add and delete, which also simplifies the information to keep in offline storage. | - Bookmark Commands have to communicate with AnimeData for anime-related functionalities. |
Keep the entire Anime object in Bookmark . |
- Do not need to reference AnimeData for anime information. | - Keep a duplicate of anime data, requires synchronisation. - Hard to keep anime object information offline. |
We have decided to implement the first approach, the bookmark will keep the anime index within bookmark entries. Although the lookup for anime information of a bookmark entry requires a query to AnimeData
, the management and offline storage of the bookmark entries will be more efficient (keeping only anime index). There are more considerations like synchronisation and duplication of the object data that comes with using Anime
object.
5. Documentation, Logging, Testing, and DevOps
This section details the documentation, logging, testing and dev-ops setup used in this project as well as information on how to use them.
5.1 Documentation
We use Jekyll to manage documentation. We recommend that you document your feature implementations and code changes so that other developers are aware of its architecture.
The docs/
folder stores the documentation of this project. You can learn more about how to setup and maintain the project website using this guide.
5.2 Logging
We encourage the use of logger in this project as they provide deeper insights as compared to error messages which can greatly help developers identify bugs and simplify their logging process.
We are using java.util.logging
package for logging. The logger can be accessed using the AniLogger
class.
AniLogger
is used to manage the logging levels and logging destinations. Based on the default settings, logs will be written to both console and file data/AniChan.log
.
The Logger
for a class can be obtained using AniLogger.getAniLogger(Class.class.getName())
which will log messages according to the specified logging level.
We use the following log levels:
-
Level.SEVERE
: A critical failure, which prevents normal execution of the program. -
Level.WARNING
: Indicates a potential problem, but can proceed with caution. -
Level.INFO
: General noteworthy information of the program.
5.3 Testing
Testing is integral to the development of a reliable software. Before making a pull request, please ensure that all tests pass. You are recommended to write tests as you add new code to the program.
We employ both unit and integration tests for our codes. They are located in ./src/test
directory.
5.3.1 Running Tests
There are primarily 2 ways to run the tests.
Method 1: Using IntelliJ
- To run all tests, right-click on the
src/test/java
folder in the project panel and chooseRun Tests
.
Method 2: Using Gradle
- Open a terminal and navigate to project directory, run command
./gradlew clean checkstyleMain checkstyleTest test
.
5.4 Development and Operations (DevOps)
We employ DevOps to ensure consistent release of good quality code and write-ups. These tools allow us to detect and mitigate bugs before they are merged to the repository.
5.4.1 Build Automation
This project uses Gradle for build automation and dependency management.
It automates tasks such as:
- Managing library dependencies.
- Analyzing code for style compliance.
- Running tests.
The gradle configuration file can be located in build.gradle
.
5.4.2 Continuous Integration (CI)
We use GitHub Actions and Travis CI as our tool for Continuous Integration (CI).
Their configuration is complete and there is no need to modify them, these CI will automatically run checks on every commit to AniChan repository.
If you wish to modify these tools, you may find their configurations at:
-
GitHub Workflow:
.github/workflows
-
Travis CI:
.travis.yml
5.4.2.1 Code Coverage
As part of CI, this project uses Codecov to generate coverage reports.
If you wish to configure Codecov for your fork, you may refer to Codecov Docs.
5.4.2.2 Repository-wide checks
In addition to running Gradle checks, CI includes some repository-wide checks. These checks include files from the entire repository and are useful in checking for rules which are hard to enforce on development machines such as line ending requirements.
These checks are POSIX shell scripts (i.e. platform dependent to Linux and macOS).
To run all checks, execute the following at project root ./config/travis/run-checks.sh
.
If you wish to add new checks, simply add the check file with a filename check-*
to the ./config/travis/
and the run-checks.sh
script will automatically run it.
Appendix A: Product Scope
A.1 Target User Profile
- Professional anime translators.
- Works on multiple projects and with various companies.
- Has difficulty managing their time and information.
- Prefers command-line desktop applications.
- Able to type fast.
A.2 Value Proposition
- Helps the target user estimate the time needed to translate their scripts.
- Allows the user to organise work from different projects and companies into workspaces.
- Allows the user to write and refer to notes on different anime series while translating.
- Find relevant information regarding an anime series quickly.
- Organize anime into different watchlist for different genres.
- Portable and works offline.
Appendix B: User Stories
Version | As a … | I want to … | So that I … |
---|---|---|---|
v1.0 | user | create new watchlist | can keep track of anime easily based on my defined criteria |
v1.0 | user | add and remove an anime to a watchlist | can organise my anime into the defined criterion |
v1.0 | forgetful user | find out all watchlist I have created | can find out what watchlist I have |
v1.0 | user | save the data I have created | can remember what I have done |
v1.0 | user | load the data I created previously | can continue to work on these data |
v1.0 | new user | view all available commands | can easily find a specific command and its usage |
v1.0 | user | be able to browse through all anime series | can easily see what anime are available |
v1.0 | user | be able to sort my browsing session | will only browse through relevant data |
v1.0 | freelance translator | be able to create new workspaces | can have a clear separation of my work at different companies |
v1.0 | freelance translator | switch between workspaces | can swap my workspace to a specific company that my current project is tied to |
v1.0 | forgetful user | bookmark specific anime | remember which anime I am currently interested in |
v1.0 | forgetful user | set an episode for each bookmarked anime | can remember which episode I’m watching or translating |
v2.0 | translator | write notes for a bookmark | can remember helpful information regarding a specific anime series |
v2.0 | translator | remove notes for a bookmark | can remove outdated information or mistakes in my notes |
v2.0 | user | view detailed information of a specific anime | can find out more about that anime series |
v2.0 | user | be able to select a watchlist from my list of watchlist to use | can stay focused on working on one watchlist |
v2.0 | user | delete watchlist that I no longer need | can keep my list of watchlist organized and up-to-date |
v2.0 | translator | estimate the time needed to translate a script | can better manage and plan my time |
v2.0 | user | search for a specific anime with a keyword | can find an anime series without remembering its full title |
v2.0 | user | search for all anime in a genre | can find all similar types anime |
Appendix C: Non-Functional Requirements
- The application should work on major operating systems (OS) such as Windows and Linux that have
Java 11
installed. - Users with fast typing speed should be able to accomplish tasks more easily and faster as compared to using mouse.
- The application should be easy to use with an accompanying User Guide and/or Developer Guide.
- The application should utilize memory resources efficiently.
- The application should be responsive.
Appendix D: Instructions for Manual Testing
D.1: Launch and Shutdown
Initial Launch
- Download the jar file and copy it into an empty folder.
- Open a new command prompt or terminal and navigate to the directory containing
AniChan.jar
. - Enter the command
java -jar AniChan.jar
to launch AniChan. - Program will prompt for
Name
andGender
of user, thereafter AniChan will be ready for use.
AniChan’s Input Prompt:
Figure 36: AniChan’s Input Prompt
Shutdown
- To exit AniChan, enter the
exit
command.
D.2: Estimate the time needed to translate a script
- Estimating the time needed to translate a script.
- Prerequisite: Have a non-empty
script.txt
file in the current workspace folder.- Create a
.txt
file and name it “script” such that the file name with the file extension isscript.txt
, then fill it up with some content (i.e. random words and sentences). - Go to the folder containing
AniChan.jar
. -
Open the
data
folder (if it does not exist, restart AniChan). - In the
data
folder, find and open the folder named after the current workspace (if the input prompt showsDefault (myWatchlist) #>
, then look for the folder named “Default” as that is the current workspace name). - Move
script.txt
into this folder (i.e.data/Default/script.txt
).
- Create a
-
Test case:
estimate script.txt
.
Expected: 3 timings are generated and printed. -
Test case:
estimate script.txt -wph 550
.
Expected: 1 timing is generated and printed. - Other incorrect commands to try:
-
estimate
. -
estimate x
(where x is not a.txt
file, is a file path, or consists of more than two files). -
estimate script.txt -wph x
(where x is a negative number or a word).
-
- Prerequisite: Have a non-empty
The file name (including extension) does not have to be
script.txt
, it is named as such for the convenience of testing.
D.3: Browse
- Utilising the
browse
feature.-
Prerequisite: None.
-
Test case:
browse
Expected: Will list 20 anime series according to the anime ID. -
Test case:
browse -s rating
Expected: Will list 20 anime series according to the most popular anime ratings. -
Test case:
browse -s rating -o asc
Expected: Will list 20 anime series from the lowest rated anime series (Ascending order). -
Other incorrect commands to try:
browse -s alpha
-
browse -p x
(where x is a negative number, a word, or an additional parameter.) browse -s name -s rating
browse ---
browse -s rating-o asc
-
D.4: Search by name
- Finding an anime series with
search
feature.-
Prerequisite: None.
-
Test case:
search -n Mushi
Expected: Will return anime with the keyword ‘Mushi’. In this case it would return ‘MUSHI-SHI’. -
Test case:
search -n MUSHI-
Expected: Will return anime with the keyword ‘MUSHI-‘. In this case it would return ‘MUSHI-SHI’. -
Other incorrect commands to try:
search
-
search -n mush -n shi
In this case it would attempt to search formush -n shi
, and return no results. search -n
-
D.5: Search by genre
- Finding all anime series that has a specific genre with
search
feature.-
Prerequisite: None.
-
Test case:
search -g Music
Expected: Will return all anime that has ‘Music’ as its genre. -
Test case:
search -g slice of life
Expected: Will return anime that has ‘Slice of Life’ as its genre. -
Other incorrect commands to try:
search
-
search -g musik
In this case it would attempt to search formusik
, and return no results. search -g
-
D.6: Viewing the information of a specific anime
- Viewing the information of a specific anime.
-
Prerequisite: None.
-
Test case:
info 1
Expected: Lists out the information of the anime with index 1. -
Test case:
info 3
Expected: Lists out the information of the anime with index 3. -
Other incorrect commands to try:
info
-
info x
(where x is a negative number, zero, a word, or a number exceeding the number of anime in the database)
-
D.7: Create a new Workspace
- Create a new Workspace with
workspace
feature-
Prerequisite: None.
-
Test case:
workspace -n Crunchyroll
Expected: Workspace will be successfully created with success message. -
Test case:
workspace -n Crunchyroll__
Expected: Workspace creation will fail with error message stating names must be alphanumeric and spaces only.- Other incorrect commands to try:
workspace
workspace -n
workspace -N
- Other incorrect commands to try:
-
D.8: Switch to a different Workspace
- Switch to a different Workspace with
workspace
feature-
Prerequisite: Workspace must first exist before switching to them. In this example we presume Workspace named
Default
andCrunchyOreo
exists and currently active Workspace is the former. -
Test case:
workspace -s CrunchyOreo
Expected: Workspace will be successfully switched fromDefault
toCrunchyOreo
. -
Test case:
workspace -s CrunchyOreo__
Expected: Workspace switch will fail given how the name request does not meet requirement of being alphanumeric and spaces only.- Other incorrect commands to try:
workspace
workspace -s
workspace -S
- Other incorrect commands to try:
-
D.9: List all Workspace
- List all Workspace with
workspace
feature-
Prerequisite: Workspace must first exist to be able to list them. In this example we presume Workspace named
Default
andCrunchyOreo
exists and currently active Workspace is the former. -
Test case:
workspace -l
Expected: WorkspaceDefault
andCrunchyOreo
will be listed.- Other incorrect commands to try:
workspace =l
- Other incorrect commands to try:
-
D.10: Delete a Workspace
- Delete a Workspace with
workspace
feature-
Prerequisite: Workspace must first exist before deleting them. In this example we presume Workspace named
Default
andCrunchyOreo
exists and currently active Workspace is the latter. -
Test case:
workspace -d Default
Expected: Workspace will be successfully deleted. -
Test case:
workspace -d default
Expected: Workspace deletion will fail given default does not exist.- Other incorrect commands to try:
workspace
workspace -D
workspace -d
workspace -d ..
- Other incorrect commands to try:
-
D.11: Creating watchlist
- Creating a watchlist with a unique name.
-
Prerequisite: None.
-
Test case:
watchlist -n newWatchlist
.
Expected: A empty watchlist named “newWatchlist” is created, and a message indicating the watchlist was created successfully is printed. -
Other incorrect commands to try:
-
watchlist -n
. -
watchlist -n x
(where x is a watchlist name that is not unique in the current workspace, exceeds 30 characters, contains non-alphanumeric characters, or contain spaces only).
-
-
D.12: Listing all watchlist(s)
- Listing all created watchlist(s).
-
Prerequisite: None.
-
Test case:
watchlist -l
Expected: All watchlist entries in the current workspace is printed. -
Other incorrect commands to try:
-
watchlist -l x
(where x is any additional parameters or values).
-
-
D.13: Selecting a watchlist to be the new active watchlist
- Selecting a watchlist to be the new active watchlist.
-
Prerequisite: The current workspace has at least 2 watchlist, and the first watchlist in the list is the active watchlist.
-
Test case:
watchlist -s 2
Expected: The second watchlist in the list becomes the new active watchlist, and a message indicating the name of the new active watchlist is printed. -
Test case:
watchlist -s 1
Expected: An error message indicating that the selected watchlist is already the active watchlist is printed. -
Other incorrect commands to try:
-
watchlist -s
. -
watchlist -s x
(where x is a negative number, a word, or an additional parameter).
-
-
D.14: Deleting a watchlist
- Deleting a watchlist.
-
Prerequisite: The current workspace has at least 2 watchlist, and the first watchlist is the active watchlist.
-
Test case:
watchlist -d 2
Expected: The second watchlist in the list is deleted, and a message indicating the name of the deleted watchlist is printed. -
Test case:
watchlist -d 1
Expected: The first watchlist in the list is deleted, and the first watchlist that is in the list after the deletion becomes the new active watchlist. Then, a message indicating the name of the deleted watchlist, and the name of the new active watchlist is printed. -
Other incorrect commands to try:
-
watchlist -d
. -
watchlist -d x
(where x is a negative number, a word, or an additional parameter).
-
-
D.15: Adding an anime to active watchlist
- Adding an anime to active watchlist.
-
Prerequisite: The active watchlist does not contain the anime to be added.
-
Test case:
add 1
Expected: The anime with index of 1 is added into the active watchlist, and a message with the name of the anime is printed. -
Test case:
add 3
Expected: The anime with index of 3 is added into the active watchlist, and a message with the name of the anime is printed. -
Other incorrect commands to try:
add
-
add x
(where x is a negative number, zero, a word, or a number exceeding the number of anime in the database)
-
D.16: Removing an anime from active watchlist
- Removing an anime from active watchlist.
-
Prerequisite: The active watchlist must contain at least one anime.
-
Test case:
remove 1
Expected: The first anime in the watchlist is deleted, and a message with the name of the anime is printed. -
Test case:
remove 3
Expected: The third anime in the watchlist is deleted, and a message with the name of the anime is printed. -
Other incorrect commands to try:
remove
-
remove x
(where x is a negative number, zero, a word, or a number exceeding the number of anime in the watchlist)
-
D.17: Viewing all anime in a specific or active watchlist
- Viewing all anime in a specific or active watchlist.
-
Prerequisite: The watchlist must contain at least one anime.
-
Test case:
view
Expected: Lists out the name of all anime in the active watchlist. -
Test case:
view 1
Expected: Lists out the name of all anime in the first watchlist. -
Other incorrect commands to try:
-
view x
(where x is a negative number, zero, a word, or a number exceeding the number of watchlist)
-
-
D.18: Listing bookmark entries
- Listing bookmark entries.
-
Prerequisite: None.
-
Test case:
bookmark -l
Expected: All bookmark entries will be listed, if the bookmark list is empty then a bookmark empty message will be printed.
-
D.19: Adding a bookmark entry
- Adding a bookmark entry.
-
Prerequisite: Look up the anime ID using the
info
command. The anime ID is inAnimeData
source. -
Test case:
bookmark -a 1
Expected: The anime ID will be added as a bookmark entry, and a message indicating the name of anime is added to the bookmark. -
Test case:
bookmark -a 0
Expected: No anime is added to the bookmark. Error details show that anime ID cannot be 0. -
Other incorrect commands to try:
bookmark -a
-
bookmark -a x
(where x is a negative number, a word, or an additional parameter)
-
D.20: Deleting a bookmark entry
- Deleting a bookmark entry.
-
Prerequisite: List all bookmark entries using the
bookmark -l
command. Multiple bookmark entries in the list. -
Test case:
bookmark -d 1
Expected: The bookmark ID will be deleted from the bookmark, and a message indicating the name of anime deleted from the bookmark. -
Test case:
bookmark -d 0
Expected: No bookmark entry is deleted from the bookmark. Error details show that bookmark ID cannot be 0. -
Other incorrect commands to try:
bookmark -d
-
bookmark -d x
(where x is a negative number, a word, or an additional parameter)
-
D.21: Editing a bookmark entry episode
- Editing a bookmark entry episode.
- Prerequisite:
- List all bookmark entries using the
bookmark -l
command. Multiple bookmark entries in the list. - View the information of bookmark entry using
bookmark <BOOKMARK_ID>
. The total episode of the anime series.
- List all bookmark entries using the
-
Test case:
bookmark 1 -e 1
Expected: The bookmark episode for that ID will be edited to 1, and a message indicating the anime current episode has been edited. -
Test case:
bookmark 0 -e 1
Expected: No bookmark entry will have its episode edited. Error details show that bookmark ID cannot be 0. - Other incorrect commands to try:
bookmark -e
-
bookmark -e x
(where x is a negative number, a word, or an additional parameter) -
bookmark x -e
(where x is a negative number, a word, or an additional parameter) -
bookmark x -e x
(where x is a negative number, a word, or an additional parameter)
- Prerequisite:
D.22: Adding a note to a bookmark entry
- Adding a note to a bookmark entry.
-
Prerequisite: List all bookmark entries using the
bookmark -l
command. Multiple bookmark entries in the list. -
Test case:
bookmark 1 -n test
Expected: The note will be added to bookmark ID, and a message indicating the note has been edited for that anime. -
Test case:
bookmark 0 -n test
Expected: No bookmark note will be added. Error details show that bookmark ID cannot be 0. -
Other incorrect commands to try:
bookmark -n
-
bookmark -n x
(where x is a negative number, a word, or an additional parameter) -
bookmark x -n
(where x is a negative number, a word, or an additional parameter) -
bookmark x -n x
(where x is a negative number, a word, or an additional parameter)
-
D.23: Removing a note from a bookmark entry
- Removing a note from a bookmark entry.
-
Prerequisite: View the information on bookmark entry using ‘bookmark
'. Multiple notes for that bookmarked anime. -
Test case:
bookmark 1 -r 1
Expected: The first note of the bookmark entry will be removed, and a message indicating the note has been removed from the anime. -
Test case:
bookmark 0 -r 1
Expected: No notes will be removed. Error details show that bookmark ID cannot be 0. -
Other incorrect commands to try:
bookmark -r
-
bookmark -r x
(where x is a negative number, a word, or an additional parameter) -
bookmark x -r
(where x is a negative number, a word, or an additional parameter) -
bookmark x -r x
(where x is a negative number, a word, or an additional parameter)
-
D.24: View information of a bookmark entry
- View information of a bookmark entry.
-
Prerequisite: List all bookmark entries using the
bookmark -l
command. Multiple bookmark entries in the list. -
Test case:
bookmark 1
Expected: All information on a bookmark entry will be printed. -
Test case:
bookmark 0
Expected: No bookmark entry information is displayed. Error details show that bookmark ID cannot be 0. -
Other incorrect commands to try:
-
bookmark -d x
(where x is a negative number, a word, or an additional parameter)
-
-