Developer Guide
ForgetfulNUS is a desktop glossary app for students taking German 1 (LAG1201) and German 2 (LAG2201) in NUS to practise and test their vocabulary, optimised for use via a Command Line Interface (CLI). * This project is based on the AddressBook-Level3 project created by the SE-EDU initiative.
Table of Contents
- Setting Up, Getting Started
- Design
- Implementation
- Documentation, Logging, Testing, Configuration, Dev-Ops
- Appendix: Requirements
- Appendix: Instructions for Manual Testing
Setting Up, Getting Started
Refer to the guide Setting Up and Getting Started.
Design
Architecture
The Architecture Diagram given above explains the high-level design of the App. Given below is a quick overview of each component.
.puml
files used to create diagrams in this document can be found in the diagrams folder. Refer to the PlantUML Tutorial at se-edu/guides to learn how to create and edit diagrams.
Main
has two classes called Main
and MainApp
. It is responsible for,
- At app launch: Initialises the components in the correct sequence, and connects them up with each other.
- At shut down: Shuts down the components and invokes cleanup methods where necessary.
Commons
represents a collection of classes used by multiple other components.
The rest of the App consists of four components.
-
UI
: The UI of the App. -
Logic
: The command executor. -
Model
: Holds the data of the App in memory. -
Storage
: Reads data from, and writes data to, the hard disk.
Each of the four components,
- defines its API in an
interface
with the same name as the Component. - exposes its functionality using a concrete
{Component Name}Manager
class (which implements the corresponding APIinterface
mentioned in the previous point. For example, theLogic
component (see the class diagram given below) defines its API in theLogic.java
interface and exposes its functionality using theLogicManager.java
class which implements theLogic
interface.
How the architecture components interact with each other
The Sequence Diagram below shows how the components interact with each other for the scenario where the user issues the command delete 1
.
The sections below give more details of each component.
UI Component
API :
Ui.java
The UI consists of a MainWindow
that is made up of parts e.g.CommandBox
, ResultDisplay
, PersonListPanel
, StatusBarFooter
etc. All these, including the MainWindow
, inherit from the abstract UiPart
class.
The UI
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 MainWindow
is specified in MainWindow.fxml
The UI
component,
- Executes user commands using the
Logic
component. - Listens for changes to
Model
data so that the UI can be updated with the modified data.
Logic Component
API :
Logic.java
-
Logic
uses theGlossaryBookParser
class to parse the user command. - This results in a
Command
object which is executed by theLogicManager
. - The command execution can affect the
Model
(e.g. adding a Flashcard). - The result of the command execution is encapsulated as a
CommandResult
object which is passed back to theUi
. - In addition, the
CommandResult
object can also instruct theUi
to perform certain actions, such as displaying help to the user.
Given below is the Sequence Diagram for interactions within the Logic
component for the execute("add g/German e/English")
API call.
AddCommandParser
should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
Model Component
API : Model.java
The Model
,
- stores a
UserPref
object that represents the user’s preferences. - stores the glossary data.
- exposes an unmodifiable
ObservableList<Flashcard>
that can be ‘observed’ e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change. - does not depend on any of the other three components.
Storage Component
API : Storage.java
The Storage component,
- can save
UserPref
objects in json format and read it back. - can save the address book data in json format and read it back.
- can save the user scores data in json format and read it back.
JsonAdaptedFlashCard
and JsonAdaptedScore
are Json-friendly versions of
the Model
’s respective FlashCard
and Score
classes. Similarly, JsonScoreList
and
JsonSerializableGlossary
are Json-friendly versions of the Model
’s respective UniqueFlashCardList
and UniqueScoreList
classes. These Json-friendly classes handle Json serialization and deserialization, so glossary and score data can be saved and read from their respective .json files.
Common Classes
Classes used by multiple components are in the seedu.forgetfulnus.commons
package.
Implementation
This section describes some noteworthy details on how certain features are implemented.
Predefined Tags
This feature is facilitated by PredefinedTags
, DifficultyTag
and GenderTag
.
There are two types of predefined tags for each flash cards. They are the DifficultyTag
and GenderTag
.
The following class diagram outlines the structure of the predefined tags and how it interacts with other Model
components.
To go for a more OOP solution, both DifficultyTag
and GenderTag
were made to extend from an abstract class PredefinedTag
.
Referring to the class diagram above, note that DifficultyTag
also implements Comparable
for use in the sorting feature. As ForgetfulNUS does not support sorting by GenderTag
, it does not implement Comparable
.
The following activity diagram summarises what happens for the DifficultyTag
when a user executes the Add command:
For GenderTag
the activity diagram is similar, with the default tag being set to NONE
instead. Note that even if a flashcard does not appear to have a GenderTag
on the UI, each flashcard will always have a GenderTag
. The UI will just not display anything for a NONE
state.
Quizzing
The proposed quiz feature for users to test their vocabulary is facilitated by Model
and Command
. It does so by allowing a command to set Model
to quiz mode. When the model is in quiz mode, it will take in commands allowing users to attempt to type the correct definition, skip the flashcard under test or end the quiz.
It implements the following operations:
-
Glossary#quiz(Model)
— Starts the quizzing with the displayed flashcard list. -
Glossary#try(Model)
— Attempt to type the correct English definition of the German phrase on the current flashcard. -
Glossary#next(Model)
— Skips the current flashcard under test. -
Glossary#end(Model)
— Ends the quiz.
These operations are exposed in Ui
as commands. They are implemented by QuizCommand
, TryCommand
, NextCommand
and EndQuizCommand
respectively.
Given below is an example usage scenario and how the quiz mechanism behaves at each step.
Step 1: The user launches the application with an existing list of flashcard. Flashcards from this list will be tested in the order of their index, and the list can be customised by using the find <search phrase>
command.
Step 2: User enters quiz
and the program will execute the QuizCommand on the current model. The model will be set to quiz mode and will be expecting quiz commands like try <attempt>
, next
and end
. The Ui will update to hide all the English definitions on the flashcards. The first flashcard on the list will be tested when the quiz begins.
Step 3: The user enters try <attempt>
and the GlossaryParser#parse(String)
will parse input into a TryCommand with the attempt. If the attempt matches the English definition of the flashcard, the flashcard index, score and question count in model
increment. The Ui will update to show the English definition of the current flashcard. The next flashcard on the list will be tested.
If the attempt does not match, step 3 will repeat.
Alternatively, the user can enter next
to execute the NextCommand on the model. The flashcard index and question count in model
will increment, the Ui will update to show the English definition of the current flashcard andthe next flashcard and the next flashcard will be tested.
Step 4: The quiz mode will end when there is no next flashcard i.e. current flashcard is the last on the list, and the user attempts the English definition correctly with try <attempt>
or the user skips the card with next
. Alternatively, the quiz can be ended early at any point during the quiz when the user enters end
, letting the program execute the EndQuizCommand on the current model. The Ui will update to show the English definitions on all the flashcards in the flashcard list.
The following activity diagram outlines the process of quizzing:
The following sequence diagram shows how the quiz operation works with respect to the logic component:
Quizzing also works in tandem with find
and sort
features, allowing users to limit the phrases tested and change the order of testing.
During quizzing, only try
, next
and end
commands are allowed to be used. All other commands except help
and exit
will remind the user that they are in quiz mode and to end the quiz first before using any other commands.
Random Quizzing
This feature is facilitated by RandomQuizCommand
and RandomQuizCommandParser
and Model
.
RandomQuizCommand
implements the method:
-
RandomQuizCommand#execute(Model)
— Returns aCommandResult
which begins a round of vocabulary quiz containing the specified number of flashcards randomly selected from the existing glossary.
RandomQuizCommandParser
implements the method:
-
RandomQuizCommandParser#parse(String)
— Returns aRandomQuizCommand
which ensures the specified number of flashcards is valid.
Model
implements the method:
-
Model#setRandomQuizMode(boolean)
— Sets the state of Model to randomQuizMode and backs up or retrieves the original glossary depending on the boolean value.
Given below is an example usage scenario and how the random quiz mechanism behaves at each step.
Step 1. The user launches the application with an existing glossary of flashcards that the user added previously.
Step 2. The user executes random 5
command to randomly select 5 flashcards from the existing glossary to test his/her own vocabulary. The random 5
command calls RandomQuizCommandParser#parse(String)
which checks the validity of the argument given to random
command. This then leads to the calling of RandomQuizCommand#execute(Model)
, which in turn calls the Model#setRandomQuizMode(boolean)
.
Step 3. The change of state of the Model resulting from Model#setRandomQuizMode(boolean)
starts a round of vocabulary quiz for the user when the boolean parameter provided is true.
The following sequence diagram shows how the random quiz mechanism works:
The following activity diagram summarises what happens when a user executes the random command:
Saving Score History
This feature saves scores from previous rounds of quizzing. It is facilitated by classes in the Command
, Model
and Storage
components.
Scores are encapsulated in the Model
by Score
objects, which store scoring information that can be retrieved from the following methods:
-
Score#getScore()
- Returns the number of flashcards answered correctly in the quizzing round -
Score#getNumQuestions()
- Returns the number of flashcards tested in the quizzing round -
Score#getFlashcards()
- Returns a list of flashcards tested in the quizzing round
Each time the quiz mode is entered and ended, a Score
object is created and added to a ScoreList
, where the following methods are implemented:
ScoreList#addScore()
ScoreList#getScoreList()
The following activity diagram summarises how scores are incremented:
Design Considerations:
Storing Flashcard
s in each Score
:
-
Current Implementation: Each
Score
object contains a list of eachFlashcard
tested in a round. This is a potentially storage-intensive feature that could result in the same flashcard being saved in multiple scores. This would cause duplication of data in thescores.json
data storage file. -
Alternative: Currently, the only score information displayed to the user on use of the
scores
command are:- Number of flashcards answered correctly
- Number of flashcards tested
- List of German phrases tested
Therefore, each
Score
object could just store a list of German phrases tested (as Strings) instead of a list ofFlashcard
s. This reduces storage use as eachFlashcard
object contains additional data fields that are not used in the score history feature, like tags. -
Justification for Current Implementation: Storing all
Flashcard
s allows possible future extensions of the score history feature, such as:- A feature to test all flashcards associated with a particular score, allowing users to improve their performance
- A feature to display whether each flashcard was answered correctly in a particular round of quizzing. Storing
Flashcard
s allows this information to easily be added to eachFlashcard
as a data field.
Sorting
The Sort feature is implemented as a way for users to further customise their view of the glossary and make it easier for them to find phrases they want.
Sorting is implemented as a SortCommand
class which extends from the abstract Command
class and makes use of a SortCommandParser
and GlossaryParser
to parse the parameters input by the user.
This is in line with the original AddressBook3’s Command pattern.
SortCommand
relies on several pre-defined Comparator
objects to execute the sorting, one of which is selected for use when
the user’s input is successfully parsed by SortCommandParser
. For example, when the user inputs sort english
, a SortCommand object is created
with a Comparator
to compare the EnglishPhrase
s of each FlashCard
object in the Glossary
.
This class diagram outlines the structure of SortCommand
and SortCommand
and how they interact with
other aspects of the program.
The following sequence diagram briefly outlines the execution process with respect to the Logic
component when a user enters the command “sort english”:
- The user command is first passed into
LogicManager
, which calls uponGlossaryParser
to parse the command. -
GlossaryParser
identifies the input as a command to sort the glossary, creates aSortCommandParser
and calls itsparse(String)
method. - The new
SortCommandParser
parses the parameter and creates a newSortCommand
. -
LogicManager
calls the newSortCommand
’sexecute(model)
method. -
execute()
callsSortCommand
’s owngetSortedGlossary()
method to obtain a sortedGlossary
. - The sorted
Glossary
replaces the currentGlossary
inModel
. - The result of the command execution is encapsulated as a CommandResult object which is passed back to the
Ui
.
Note: The lifeline for SortCommandParser
should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
Alternatives:
-
Sorting replaces the entire Glossary with a new sorted Glossary (current implementation)
- Pros: Fairly adaptable from existing commands
- Cons: Large glossary size may lead to computational delays and overhead
-
Sorting sorts the current Glossary in place instead of creating a new Glossary
- Pros: Less computational overhead
- Cons: Original AB3 implementation uses immutable Glossary equivalent, requires very significant refactoring of code to achieve. Using a mutable Glossary also makes the code more vulnerable.
Documentation, logging, testing, configuration, dev-ops
Appendix: Requirements
Product scope
Target user profile:
ForgetfulNUS is targeted at students taking level 1000-2000 German language modules (LAG1201 and LAG2201) at the NUS Center of Language Studies who can type fast and prefer typing to mouse interactions.
Value proposition:
A flashcard CLI app designed to cater to the specific needs of the target user to help them learn their German vocabularies.
User Stories
Priorities: High (must have) - * * *
, Medium (nice to have) - * *
, Low (unlikely to have) - *
Priority | As a… | I want to… | So that I… |
---|---|---|---|
*** | user | add a flashcard with German phrase and meaning | can refer it or use it to test myself later. |
*** | user | list out all the flashcards with index | can look through the phrases and their meanings to study. |
*** | user | delete a flash card by index | can remove flashcards that I deem irrelevant. |
*** | user | test myself with the flashcards | can be quizzed on the phrases and their meanings. |
** | user | my flashcards to be saved (storage) | can use them when I next launch the app. |
** | user | to sort my flashcards in certain ways | can navigate the glossary more easily. |
** | user | test myself with a randomised quiz | can do a quick quiz to jog my memory. |
** | user | edit an existing flashcard’s fields | can make any changes to existing flashcards if I want to. |
** | user | search for a specific flashcard | can find a specific flashcard more easily. |
** | user | save the scores of my previous quizzes | can keep track of my progress easily. |
** | user | reset the glossary | can start from scratch with my own personalised data. |
* | user | be reminded to quiz myself daily | can be reminded to consistently put in effort to revise. |
* | user | hard reset my score history | can start over from scratch. |
Use Cases
(For all use cases below, the System is ForgetfulNUS
and the Actor is the user
, unless specified otherwise)
Use Case: UC1 - Add a flashcard
MSS:
- User adds a flashcard with German phrase and meaning.
-
ForgetfulNUS adds the flashcard and display the newly-added flashcard.
Use case ends.
Extensions:
-
1a. ForgetfulNUS detects less than 2 fields for the flashcard.
- 1a1. ForgetfulNUS requests the User to input a German phrase and an English translation for the flashcard.
- 1a2. User enters a new flashcard or terminates the process.
Steps 1a1-1a2 are repeated until the user input is correct or the user terminates the process.
Use case ends.
Use case: UC2 - List all Flashcards
MSS:
- User requests ForgetfulNUS to list all the flashcards.
-
ForgetfulNUS shows the list of flashcards.
Use case ends.
Extensions:
-
1a. ForgetfulNUS detects incorrect command.
-
1a1. ForgetfulNUS shows error and asks for a command in the correct format.
-
1a2. User enters a command.
Use case ends.
-
Use case: UC3 - Delete a Flashcard
MSS:
- User deletes a flashcard by the index.
- ForgetfulNUS deletes the flashcard and displays the information of the deleted flashcard.
Use case ends.
Extensions:
-
1a. User inputs an invalid index (e.g -1)
-
3a1. ForgetfulNUS shows error and asks for a command in the correct format.
-
3a2. User enters a command with the correct format.
Use case ends.
-
Use case: UC4 - Self-testing with Flashcards
MSS:
- User requests to start self-testing.
- ForgetfulNUS displays a German word.
- User inputs the corresponding English translation.
-
ForgetfulNUS displays the results of User’s answer.
Steps 2-4 are repeated until there are no more words to be tested.
Use case ends.
Extensions:
-
2a. User chooses to skip the German word.
-2a1. ForgetfulNUS displays the next German word.
-
3a. User inputs the wrong English translation.
- 3a1. ForgetfulNUS prompts the user to try again.
-
4a. At any time, User chooses to stop self-testing.
- 4a1. ForgetfulNUS stops self-testing.
Use case ends.
Use case: UC5 - Sorting the Glossary
MSS:
- User requests to sort the glossary according to a parameter.
-
ForgetfulNUS sorts the glossary according to the parameter.
Use case ends.
Extensions:
-
1a. The glossary is currently empty.
-
1a1. ForgetfulNUS informs the user that the glossary is empty.
-
1a2. User enters another command.
Use case ends.
-
Use case: UC6 - Self-testing with specified number of randomised Flashcards
MSS:
- User requests to start random self-testing with specified number of flashcards.
- ForgetfulNUS displays a random German word.
- User inputs the corresponding English translation.
-
ForgetfulNUS displays the results of User’s answer.
Steps 2-4 are repeated until the number of flashcards tested has hit the number specified by the user.
Use case ends.
Extensions:
-
2a. User chooses to skip the German word.
-2a1. ForgetfulNUS displays the next random German word.
-
3a. User inputs the wrong English translation.
- 3a1. ForgetfulNUS prompts the user to try again.
-
4a. At any time, User chooses to stop self-testing.
- 4a1. ForgetfulNUS stops self-testing.
Use case ends.
Non-Functional Requirements
- Should work on any mainstream OS as long as it has Java 11 or above installed.
- A user with above average typing speed for regular English text should be able to accomplish most of the tasks faster using commands than using the mouse.
- German diacritics (eg. ä) should be fully supported in being saved and displayed by the UI.
- Verification of user input in testing mode should not take more than 2 seconds.
- Navigating the glossary should not be tedious for the user.
Glossary
- CLI: Command Line Interface
- Mainstream OS: Windows, Linux, Unix, OS-X
- Flashcard: An item containing (a) a German phrase (b) the corresponding English definition (c) an associated Difficulty Tag (d) (optional) an associated Gender Tag (e) (optional) one or more Tags
- German phrase: German text of any length
- Index: Position of flashcard in the list of flashcards displayed to the user
Appendix: Instructions for Manual Testing
Given below are instructions to test the app manually.
Launching and Shutting Down
-
Initial launch
-
Download the jar file and copy into an empty folder
-
Double-click the jar file.
Expected: Shows the GUI with a set of sample flashcards. The window size may not be optimal.
-
-
Saving window preferences
-
Resize the window to an optimal size. Move the window to a different location. Close the window.
-
Re-launch the app by double-clicking the jar file.
Expected: The most recent window size and location is retained.
-
Deleting a Flashcard
-
Deleting a flashcard while all flashcards are being shown
-
Prerequisites: List all flashcards using the
list
command. Multiple flashcards in the glossary. -
Test case:
delete 1
Expected: First flashcard is deleted from the glossary. Details of the deleted flashcard shown in the status message. -
Test case:
delete 0
Expected: No flashcard is deleted. Error details shown in the status message. Status bar remains the same. -
Other incorrect delete commands to try:
delete
,delete x
,...
(where x is larger than the list size)
Expected: Similar to previous.
-
-
Deleting a flashcard after using
find
-
Prerequisites: Multiple flashcards in the glossary.
find
command is used to filter the list. -
Test case:
delete 1
Expected: First flashcard in the filtered glossary is deleted. Filtered glossary is still shown. Details of the deleted flashcard shown in the status message. -
Test case:
delete 0
Expected: No flashcard is deleted. Error details shown in the status message. Status bar remains the same. -
Other incorrect delete commands to try:
delete
,delete x
,...
(where x is larger than the list size)
Expected: Similar to previous.
-
Sorting Flashcards
-
Sorting the glossary while all flashcards are being shown
-
Prerequisites: List all flashcards using the
list
command. Multiple flashcards in the glossary. -
Test case:
sort german
Expected: Glossary is sorted according to alphabetical order of the German phrases. -
Other sorting parameters to try:
english
,latest
,easytohard
,reversegerman
,...
Expected: Glossary is successfully sorted according to the parameter input.
-
-
Sorting an empty glossary
-
Prerequisites: Glossary is empty (easily done through
clear
). -
Test case:
clear
to empty the glossary, thensort x
(where x can be any sorting parameter) Expected: Error message shown in message box due to empty glossary.
-
-
Sorting the glossary after using
find
-
Prerequisites: Multiple flashcards in the glossary.
find
command is used to filter the list. -
Test case:
sort german
Expected: Filtered list is sorted according to alphabetical order of the German phrases. Original glossary is also sorted. Filtered glossary still shown. -
Other sorting parameters to try:
english
,latest
,easytohard
,reversegerman
,...
Expected: Glossary is successfully sorted according to the parameter input. Original glossary is also sorted. Filtered glossary still shown.
-
Viewing Past Scores
- Viewing a non-empty score list
- Prerequisites:
- List all flashcards using the
list
command. - Have multiple flashcards in the glossary. Ensure your glossary does not already contain a flashcard with German phrase ‘vergesslichkeit’ and English phrase ‘forgetfulness’, as it will be added in this test.
- For simplicity, let the score list be empty (easily achieved through
reset scores
).
- List all flashcards using the
-
Test case:
quiz
->end quiz
->scores
Expected: The score and German phrases tested in this attempt are shown. -
[To be executed immediately after (b)] Test case:
add g/Vergesslichkeit e/forgetfulness
->quiz
->end quiz
->scores
Expected: The score and German phrases tested in this attempt are shown at the top of the list, above the previous attempt. - [To be executed immediately after (c)]
Test case:sort latest
->delete 1
->quiz
->end quiz
->scores
Expected: The list of flashcards tested is the same as in (b), so this score is considered a duplicate. The score list displayed will remain the same as in ©.
- Prerequisites:
- Viewing an empty score list
- Prerequisites: Score list is empty (easily done through
reset scores
) - Test case:
scores
Expected: A message is shown stating that no past scores have been saved.
- Prerequisites: Score list is empty (easily done through
Saving Data
-
Dealing with missing/corrupted data files
- To simulate a missing glossary file, delete the
glossary.json
JSON file in the data folder, then launch the program.
Expected: The program successfully launches containing the default sample data. - To simulate a missing score file, delete the
scores.json
JSON file in the data folder, then launch the program. Expected: The program successfully launches containing an empty score list. - To simulate a corrupted glossary or score file, edit the
glossary.json
orscores.json
JSON files to include incorrect JSON syntax. (e.g. Add a line “this is invalid” to the bottom of the file).
Expected: The program successfully launches containing the default sample data, and the old invalidglossary.json
orscores.json
is overwritten.
- To simulate a missing glossary file, delete the