By: FlashSpeed Foundation      Since: Feb 2020      Licence: MIT

1. Introduction

FlashSpeed is a text-based flashcard application specifically designed for university students who are learning a foreign language.

FlashSpeed enhances the revision process by implementing a smarter review system in its design. Users will be tested more frequently on words that they have gotten wrong in a game session.

The main features of FlashSpeed allows users to:

  • create their own decks of cards (flashcards)

  • choose a deck to play (review) which uses the smart review design mentioned above.

1.1. Purpose

This document specifies architecture, software design decisions and implementation for the FlashSpeed application.

1.2. Audience

This document is intended for anyone who wants to understand the system architecture, design and implementation of FlashSpeed.

The following groups are in particular are the intended audience of this document.

  • FlashSpeed developers

  • FlashSpeed features enhancement team members

1.3. How to use

  • Section 3 - Design contains information about the main components of FlashSpeed.

    • Firstly, refer to 3.1 Architecture section to learn more about the overall architecture.

    • Then proceed to sections 3.2 - 3.6 to learn more about each individual component in the architecture.

  • Section 4 - Implementation contains information about the main commands and the implementations of each command used in FlashSpeed.

  • Appendix - Information related to the development process and design choices of FlashSpeed.

2. Setting up

Refer to the guide here.

3. Design

3.1. Architecture

FlashSpeed is mainly built on top of a few core components, runner components, and helper components. The core is where the bulk of the design decisions are made and data processing is performed.

The Architecture Diagram below gives a high-level overview of the FlashSpeed App.

ArchitectureDiagram
Figure 1. Architecture Diagram

Given below is a quick overview of each component.

The Runners:

Main has two classes called Main and MainApp. It is responsible for,

  • At launch: Initializes the core components in the correct sequence, sets up their initial states, and connects them up with each other.

  • At exit: Performs cleanup of components where necessary and shuts down FlashSpeed.

The Helpers:

Commons represents a collection of classes used by multiple other components. The following classes plays an important role at the architecture level:

  • LogsCenter : Writes log messages from many different running components to the FlashSpeed’s log file and console.

  • Index : Provides storage of index numbers and easy conversion between one- and zero-based indices.

  • Messages : Keeps many user-visible messages for invalid user actions.

  • Exceptions : Contains different types of specific exceptions which can occur due to invalid user actions.

The Core:

  • UI: The UI of FlashSpeed.

  • Logic: The command executor.

  • Model: Holds the data and current state of FlashSpeed in-memory.

  • Storage: Reads data from and writes data to a data file on 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 {Component Name}Manager class.

For example, the Logic component (see the class diagram given below) defines it’s API in the Logic.java interface and exposes its functionality using the LogicManager.java class.

LogicClassDiagram
Figure 2. Class Diagram of the Logic Component


3.1.1 How the core components interact with each other

The Sequence Diagram below shows how the core components interact with each other for the scenario where the user issues the command remove 2.

ArchitectureSequenceDiagram
Figure 3. Component interactions for the remove 2 command

The sections below give more details of each component.


3.2. UI component

UiClassDiagram
Figure 4. Structure of the UI Component

API : Ui.java

The UI consists of a MainWindow that is made up of parts e.g.CommandBox, ResultDisplay, DeckListPanel, CardListPanel, 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.

  • HelpWindow will only be shown when executing help command.

  • StatisticsPopUp will only be shown after finishing or stopping a Play session.

  • Either CardListPanel or PlayPanel is displayed depending on the current view.


3.3. Logic component

LogicClassDiagram
Figure 5. Structure of the Logic Component

API : Logic.java

  1. Logic uses the MasterParser class to parse the user command.

  2. This results in a Command object which is executed by the LogicManager.

  3. The command execution can affect the Model (e.g. adding a card).

  4. The result of the command execution is encapsulated as a CommandResult object which is passed back to the Ui.

  5. In addition, the CommandResult object can also instruct the Ui 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("delete 1") API call. .Interactions inside the Logic Component for the delete 1 command image::DeleteSequenceDiagram.png[]

The lifeline for RemoveDeckCommandParser should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.


3.4. Model component

ModelClassDiagram
Figure 6. Structure of the Model Component


ModelClassDeckDiagram
Figure 7. Structure of the Deck Component within the Model Component

API : Model.java

The Model,

  • stores a UserPref object that represents the user’s preferences.

  • stores the Library’s current state and data.

  • stores and manipulates a GameManager object that represents one game session.

  • stores and manipulates a Deck object that represents the deck that the user is viewing when user is in deck view.

  • stores and manipulates a Card object that represents the card that the user is playing with when user is in play view.

  • stores and manipulates View object that represents the view that the user is currently in.

  • exposes an unmodifiable ObservableList<Deck> 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.


3.5. Storage component

StorageClassDiagram
Figure 8. Structure of the Storage Component

API : Storage.java

The Storage component,

  • can save UserPref objects in JSON format and read it back.

  • can save all the decks and cards created in JSON format and read them back.


3.6. Common classes

Classes used by multiple components are in the com.flashspeed.commons package.


3.7. Logging

We are using java.util.logging package for logging. The LogsCenter class is used to manage the logging levels and logging destinations.

  • The logging level can be controlled using the logLevel setting in the configuration file (See Section 3.8, “Configuration”)

  • The Logger for a class can be obtained using LogsCenter.getLogger(Class) which will log messages according to the specified logging level

  • Currently log messages are output through: Console and to a .log file.

Logging Levels

  • SEVERE : Critical problem detected which may possibly cause the termination of the application

  • WARNING : Can continue, but with caution

  • INFO : Information showing the noteworthy actions by the App

  • FINE : Details that is not usually noteworthy but may be useful in debugging e.g. print the actual list instead of just its size


3.8. Configuration

Certain properties of the application can be controlled (e.g user prefs file location, logging level) through the configuration file (default: config.json).

4. Implementation

This section describes some noteworthy details on how certain features are implemented.


4.1. Creating a Deck

4.1.1. Current Implementation

The create command allows user to create a new Deck in the current Library.

Accepted syntax: create DECK_NAME

This functionality is implemented by getting the Deck based on the index provided. Subsequently, the Card(s) that belongs to the selected Deck will be displayed on CardListPanel via a TableView.

Validation and extraction of input in parser

The validation of the arguments in the create command is performed in CreateDeckCommandParser#parse(). It ensures that the user has entered a non-null deck name.

In CreateDeckCommandParser#parse(), the DECK_NAME of is extracted from the arguments in the create command. The DECK_NAME is converted to a Name object. An CreateDeckCommand object is then constructed with the Deck name as its parameter.

Execution of Command object

When CreateDeckCommand#execute() is executed, an empty Deck with the Name parsed in the CreateDeckCommand will be created when the Model Manager invokes the ModelManager#selectDeck() command. After that, ModelManager#setSelectedDeck() method will be called to update the UI and display the Deck content on CardListPanel. Lastly, the name of the selected Deck will be displayed together with the MESSAGE_SUCCESS on the ResultDisplay panel.

The following sequence diagram shows the sequence of operations due to a create command.

Something
Figure 9. Interactions inside the Model and Logic components when the user enters create Japanese into the input box.

4.1.2. Design Considerations

Aspect: If the user is already viewing another deck and decides to create a new deck, there was a consideration whether to switch the UI for the user view to the new deck or continue to let the user view the current deck.
  • Alternative 1 (current choice): Switch the view to the new Deck

    • Pros: Able to use the new Deck immediately without typing an additional command to select it.

  • Alternative 2: Keep the view at the current Deck

    • Pros: Don’t have to type in an additional command to return back to the current Deck if a new Deck is created

We chose Alternative 1 in the end as we believed that it will be more likely for the user to want to use the new deck immediately after creating it.

Aspect: Naming convention of command key words.

Initially, both CreateDeckCommand and AddCardCommand share the same keyword, which is the add keyword. In order to distinguish these two commands from each other, the Model Manager will check if any deck is currently selected. If there is, AddCardCommandParser#parse() will be called to parse the arguments. Otherwise, CreateDeckCommandParser#parse() will be called.

The benefit of this design is that it results in fewer number of command words. This helps the user on the navigability of the application due to a few number of command words to remember.

However, the glaring disadvantage is that unexpected outcomes are more likely to occur. For example, assume that the user wants to create a new deck. So, he/she types in the following command:

create Deck 2

However, the user has forgotten that a deck is currently being selected. Therefore, the AddCardCommandParser#parse() will be invoked. This is certainly not the expected outcome that the user has expected.

And so, our team has decided to implement the current approach, which is to assign different keywords to these two different feature.

4.2. Selecting a Deck

UI implementation will be discussed in detail only in this section. In other sections, detailed UI implementation will be omitted.

4.2.1. Current Implementation

The select command allows user to view the Card content of a Deck.

Accepted syntax: select INDEX

This functionality is implemented by getting the Deck based on the index provided. Subsequently, the selected Deck will be highlighted on DeckListPanel and the Card(s) that belongs to it will be displayed on CardListPanel via a TableView.

The validation of the arguments in the select command is performed in SelectDeckCommandParser#parse(). It ensures that the user has entered a valid index (valid data type and range). This is also used for separation of parsing logic and model management logic.

In SelectDeckCommandParser#parse(), the INDEX of the selected Deck is extracted from the arguments in the select command. The INDEX is converted to an Index object. An SelectCardCommand object is then constructed with the Index as its parameter.

When SelectDeckCommand#execute() is executed a list of currently available Deck is requested from the ModelManager#getFilteredDeckList() method. The ModelManager#selectDeck() command will be invoked to update the variable that keeps track of the current Deck. After that, ModelManager#setSelectedDeck() method will be called to update the UI by highlighting the selected Deck on DeckListPanel and displaying its content on CardListPanel. Lastly, the name of the selected Deck will be displayed together with the MESSAGE_SUCCESS on the ResultDisplay panel.

4.2.2. Design Considerations

The UI will have to be constantly updated when we select to view a Deck, and other Decks might be selected afterward. As a result, an ObservableValue<Deck> variable will have to be updated constantly via the ModelManager#setSelectedDeck() method. Various event listeners are implemented in the UI classes (e.g CardListPanel, DeckListPanel) in order to instantly react if there is any changes to the selected Deck.

SelectDeckSequenceDiagram
Figure 10. Interactions inside Model, Logic and UI components to reflect UI changes when selecting a deck


4.3. Adding a Card

4.3.1. Current Implementation

The add command allows user to create a new Card in the current Deck.

Accepted syntax: add FRONT_VALUE:BACK_VALUE

This functionality is implemented by getting the Deck based on the current deck selected. The Model Manager will be responsible of keeping track of the current deck. Subsequently, the Model Manager creates a new card adds it to the current Deck. The display on CardListPanel will be updated via updating the TableView.

Validation and extraction of input in parser

The validation of the arguments in the add command is performed in AddCardCommandParser#parse(). It ensures that the user has entered a non-null front value as well as a non-null back value. The lack thereof will cause a InvalidFaceValueException to be thrown.

In AddCardCommandParser#parse(), the FRONT_VALUE and the BACK_VALUE are extracted from the arguments in the add command. Both values will be converted to a FrontFace object and a BackFace object respectively. A AddCardCommand object is then constructed with the 'FrontFace' and 'BackFace' objects as its parameters.

Execution of Command object

When AddCardCommand#execute() is called, a Card object with the FrontFace and BackFace parsed in the CreateDeckCommand will be created when the Model Manager invokes the ModelManager#addCard() command. After that, ModelManager#setSelectedDeck() method will be called to update the UI and display the Deck content on CardListPanel. Lastly, the name of the selected Deck will be displayed together with the MESSAGE_SUCCESS on the ResultDisplay panel.

The following sequence diagram shows the sequence of operations due to an add command.

AddSequenceDiagram
Figure 11. Interactions inside Model and Logic components when user enters add ありがとう:thanks into the input box.

4.4. Editing a Card: Shortcut Formats

4.4.1. Current Implementation

The edit command allows for the values of a Card’s face(s) to be changed.

Accepted syntax:

  • edit INDEX FRONT:BACK

  • edit INDEX :BACK

  • edit INDEX FRONT:

This functionality is implemented by replacing the Card to be edited in the Deck with a new Card containing the new face values (FRONT and BACK). The shortcut versions of the command (second and third formats above) allows for one face value of the Card to be edited while preserving the other face value. In this situation, the unedited face value in the new Card will be a blank string (since either FRONT or BACK will be a blank string). Subsequently, this blank value will be replaced by the associated value in the Card to be replaced.

Below is a summary of the operations flow during the editing of a card.

EditCardActivityDiagram
Figure 12. Operations flow during an Edit command.
Validation and extraction of input in parser

The validation of the arguments in the edit command is performed in EditCommandParser#parse(). Note that the validation only checks that the command is well-formed, i.e. understandable by FlashSpeed. It does not check for the validity of the command in the current environment (e.g. if we are currently in Deck view or not). This is for separation of parsing logic and model management logic.

In EditCommandParser#parse(), the INDEX of the card to be edited and the new face value(s) are extracted from the arguments in the edit command. The INDEX is converted to an Index object. An EditCardCommand object is then constructed with the Index and the new Card as its parameters.

Execution of Command object

When EditCardCommand#execute() is executed, the environment is then checked. The edit command is only valid when we are currently in a Deck, thus a check on the current view is performed using ModelManager#getView(). Then the Index of the card to be edited is checked by ensuring it is in the range of [0, size of current Deck) using ModelManager#getCurrentDeck().getSize().

To perform a replacement of a Card in the current Deck, we need the old Card and the new Card. The old Card is required so we can know which Card is to be replaced via an equality check and also to know the face value which needs to be preserved (if needed). We can get the Card to be edited using ModelManager#getCard() with the provided Index. The new Card can then be created. We can then perform the replacement using ModelManager#replaceCard() with the old Card and the new Card as the parameters.

The following sequence diagram shows the sequence of operations due to an edit command.

EditSequenceDiagram
Figure 13. Operations performed for the edit 1 fr:bk command.

4.4.2. Design Considerations

Aspect: How the replacement Card is formed during the start of execution
  • Alternative 1 (current choice): A blank string in a face of the new Card means we use the face value in the Card to be replaced.

    • Pros: Easy to implement. Can use the extracted values in the arguments as is. Let the final step (UniqueCardList#replace()) handle the replacement logic.

    • Cons: From EditCardCommandParser until the end of the command execution in LogicManager, there may exist a Card with a face containing a blank string. May not be a desirable object to have.

  • Alternative 2: Get the Card to be edited directly in EditCardCommandParser so we can immediately produce the new Card with its final face values.

    • Pros: The Card to replace the old Card will be fully formed from the start.

    • Cons: No separation of parsing and model management logic since we would need to do a view check and get a Card from the current Deck all while in the parser.


4.5. Starting a play session

4.5.1. Current Implementation

The play command creates a new session to play with a specific deck.

Accepted syntax: play INDEX

The play command changes the mode of the application to PLAY mode and creates a new session with the Deck at the given INDEX. The value of the FRONT of the selected Deck will be displayed to the user.

Validation and extraction of input in parser

The first validation of the play command is performed in PlayCommandParser#parse(). The validation only checks that the play command has the correct format as the INDEX argument is given by the user and it is performed on the login level.

In PlayCommandParser#parse(), the INDEX of the deck is extracted from the arguments in the play command. The INDEX is converted to an Index object. An PlayCommand object is then constructed with the Index.

Execution of Command object

After the object of the PlayCommand' is constructed, `PlayCommand#execute() will be executed and the second validation of the play command is performed. This validation firstly checks if the given INDEX argument is a non-negative integer and is within the number of cards in the selected Deck. Then the validation checks if there is any card currently in the selected deck by checking if the FRONT face and BACK face of the card returned by ModelManager#play() are both empty.

A valid play command will change the MODE of the ModelManager to PLAY mode and a GameManager object will be constructed in ModelManager. The first card of the selected deck is obtained using deck#asUnmodifiableObservableList().get(0) and returned to UI. The FRONT face of the first card will be displayed to the user.

The following sequence diagram shows how the play operation works.

PlaySequenceDiagram
Figure 14. Interactions inside Logic and Model components when play 1 is executed


4.6. Flipping a Card

4.6.1. Current Implementation

The flip command flips a card in the selected deck to view the BACK face of the card.

Accepted syntax: flip

The flip command displays the BACK face of the card that the user is currently playing with to the user so that user is able to check if his or her answer is correct.

Validation and extraction of input in parser

No user parameter is required, hence a parser is not needed.

Execution of Command object

An FlipCommand object is constructed and FlipCommand#execute() is executed. In FlipCommand#execute(), validation for the flip command is performed. The validation will check if ModelManager is in PLAY mode using ModelManager#getMode(). if ModelManager is in PLAY mode, then the validation will check if the card has been flipped by checking if the returned BACK face of the card is empty since a card can only be flipped once.

After that, ModelManager#flip() will be executed. In ModelManager#flip(), GameManager#flip() will be executed and the BACK face of the card is obtained using GameManager#cards.get(counter).getBackFace() and returned to ModelManager.

A valid flip command returns the BACK face of the card that the user is currently playing to the UI and displays it to the user.

The following sequence diagram shows how the flip operation works.

FlipSequenceDiagram
Figure 15. Interactions inside Logic and Model components when flip is executed


4.7. Answering in a play session

4.7.1. Current Implementation

User answers to the card that he or she is currently playing with using yes or no command.

Accepted syntax: yes or no

After flipping the card, users indicates if he or she gets the correct answer by using yes and no command.

Validation and extraction of input in parser

No user parameter is required, hence a parser is not needed.

Execution of Command object

An AnswerYesCommand or AnswerNoCommand object is constructed and AnswerYesCommand#execute() or AnswerNoCommand#execute() is executed accordingly. Validation for the yes and no command is performed to check if if ModelManager is in PLAY mode using ModelManager#getMode(). if ModelManager is in PLAY mode, then the validation will check if the card has been flipped using ModelManager#getGame().isFlipped() since a card should not have been flipped before user answers to the card.

After that, ModelManager#answerYes() or ModelManager#answerNo() will be executed accordingly. In ModelManager#answerYes() and ModelManager#answerNo(), GameManager#answerYes() and GameManager#answerNo() will be executed accordingly and the next card is obtained using GameManager#cards.get(counter) and returned to ModelManager. ModelManager will check if ModelManager will check if the session has ended as the user have run through every card in the deck by checking if the returned card is empty.

A valid yes or no command returns the next card to the UI and the FRONT face of the card is displayed to the user.

The following sequence diagrams show how the yes and no operation work.

AnswerYesSequenceDiagram
Figure 16. Interactions inside Logic and Model components when yes is executed
AnswerNoSequenceDiagram
Figure 17. Interactions inside Logic and Model components when no is executed

4.7.2. Design Considerations

Aspect: Using yes and no instead of the actual answer.
  • Alternative 1 (current choice): Using a simple yes or no

    • Pros: User can definitively choose if their answer was correct or not. This leads to accurate evaluation and statistics calculation.

    • Cons: Not as interactive as if the user were to type in the correct word/sentence itself.

  • Alternative 2: Typing in the actual answer itself.

    • Pros: More interactive to the user.

    • Cons: Typos or slightly incomplete (but correct) answers can be typed it by the user. As the answers typed in mush exactly match the one on the card, it may result in inaccurate evaluation and statistics calculation at the end of the game.

4.8. Logging

We are using java.util.logging package for logging. The LogsCenter class is used to manage the logging levels and logging destinations.

  • The logging level can be controlled using the logLevel setting in the configuration file (See Section 3.8, “Configuration”)

  • The Logger for a class can be obtained using LogsCenter.getLogger(Class) which will log messages according to the specified logging level

  • Currently log messages are output through: Console and to a .log file.

Logging Levels

  • SEVERE : Critical problem detected which may possibly cause the termination of the application

  • WARNING : Can continue, but with caution

  • INFO : Information showing the noteworthy actions by the App

  • FINE : Details that is not usually noteworthy but may be useful in debugging e.g. print the actual list instead of just its size

5. Documentation

Refer to the guide here.

6. Testing

Refer to the guide here.

7. Dev Ops

Refer to the guide here.

Appendix A: Product Scope

Target user profile:

  • has a need to memorize a large number of new vocabulary words in a foreign language

  • prefer desktop apps over other types

  • can type fast

  • prefers typing over mouse input

  • is reasonably comfortable using CLI apps

  • can accomplish most tasks faster via CLI, compared to a hypothetical GUI-version

Value proposition: study new vocabulary words anytime and anywhere

Appendix B: User Stories

Priorities: High (must have) - * * *, Medium (nice to have) - * *, Low (unlikely to have) - *

Priority As a …​ I want to …​ So that I can…​

* * *

new user

see usage instructions

refer to instructions when I forget how to use the App

* * *

user

create a new deck

* * *

user

delete a deck

remove decks that I no longer need

* * *

user

list all decks

check what decks I can choose from to use

* * *

user

add a card to a deck

add words that I want to practice with

* * *

user

delete a card from a deck

remove words that I no longer want to practice with

* * *

user

show both sides of a card

check the translation of a word

* * *

user

show all cards in a deck (view deck)

* * *

user

edit a card in a deck

update or enhance the content of a card

* * *

user

delete all decks

start afresh with a clean slate program

* * *

user

delete all cards in a deck

start afresh with a clean deck of the same name

* * *

user

exit the program by typing

exit the program without using the mouse

* *

user

have a spaced-repetition system

memorize new words even more effectively

* *

user

find a deck by name

locate the deck without having to go through the entire list of decks

* *

user

find a specific card by name in any language

locate the card without having to go through the entire list of decks and cards

* *

user

be able to choose which side of the card to see first

have two ways of memorizing new words.

* *

user

keep track of how many cards I have visited

so that I can see my progress of learning a deck

*

user

clone a deck of cards

so that I can create custom sets of decks from existing decks quickly

*

user

add audio files to cards

add more information such as the correct pronunciation to the card

*

user

choose to have a card I appear more times

have cards that are harder to memorize appear more frequently

*

user

timer for going through a deck

see how much time it took me to memorize a deck of cards

Appendix C: Use Cases

(For all use cases below, the System is the FlashSpeed and the Actor is the user, unless specified otherwise)

UC01: Help

MSS:

  1. User requests help.

  2. FlashSpeed pops up a new small window and shows all possible commands and their usage.

    Use case ends.


UC02: Create a new deck

MSS:

  1. User requests to create a deck of a certain name.

  2. FlashSpeed creates a new deck and the deck shows up on the UI.

    Use case ends.

Extensions

  • 2a. The given name already exists.

    • 2a1. FlashSpeed shows an error message.

      Use case resumes at step 1.


UC03: Delete a deck

MSS:

  1. FlashSpeed shows a list of decks.

  2. User chooses a deck and deletes it.

  3. The deck disappears from the list of decks.

    Use case ends.

Extensions

  • 1. The list is empty.

    Use case ends.

  • 2a. The given index is invalid.

    • 2a1. FlashSpeed shows an error message.

      Use case resumes at step 1.


UC04: View a deck (select)

MSS:

  1. FlashSpeed shows a list of all decks.

  2. Uer chooses a deck and requests to view that deck.

  3. FlashSpeed shows a list of all cards in the deck.

    Use case ends.

Extensions

  • 1. The list is empty.

    Use case ends.

  • 2a. The given index is invalid.

    • 2a1. FlashSpeed shows an error message.

      Use case resumes at step 1.

UC05: Add a card to a deck

MSS:

  1. FlashSpeed shows a list of decks.

  2. User chooses a deck and requests to view that deck.

  3. FlashSpeed shows a list of all cards in the deck.

  4. User requests to add a specific card in the deck.

  5. FlashSpeed adds the card and the card shows up in the deck.

    Use case ends.

Extensions

  • 1a. The list is empty.

    Use case ends.

  • 2a. The given deck index is invalid.

    • 2a1. FlashSpeed shows an error message.

      Use case resumes at step 1.

  • 4a. The deck already contains the same card the user requested to add.

    • 4a. FlashSpeed shows an error message.

      Use case resumes at step 3.


UC06: Delete a card from a deck

MSS:

  1. FlashSpeed shows a list of decks.

  2. User chooses a deck and requests to list all cards in that deck.

  3. FlashSpeed shows a list of all cards in the deck.

  4. User requests to delete a specific card in the deck.

  5. FlashSpeed deletes the card and the card disappears from the deck.

    Use case ends.

Extensions

  • 1a. The list is empty.

    Use case ends.

  • 2a. The given index is invalid.

    • 2a1. FlashSpeed shows an error message.

      Use case resumes at step 1.

  • 3a. The deck is empty.

    Use case ends.

  • 4a. The given index is invalid.

    • 4a1. FlashSpeed shows an error message.

      Use case resumes at step 3.


UC07: Edit a card in a deck

MSS:

  1. FlashSpeed shows a list of decks.

  2. User chooses a deck and requests to list all cards in that deck.

  3. FlashSpeed shows a list of all cards in the deck.

  4. User requests to edit a specific card in the deck.

  5. FlashSpeed edits the card.

    Use case ends.

Extensions

  • 1a. The list is empty.

    Use case ends.

  • 2a. The given index is invalid.

    • 2a1. FlashSpeed shows an error message.

      Use case resumes at step 1.

  • 3a. The deck is empty.

    Use case ends.

  • 4a. The given index is invalid.

    • 4a1. FlashSpeed shows an error message.

      Use case resumes at step 3.


UC08: Delete all decks

MSS:

  1. User requests to delete all decks.

  2. FlashSpeed deletes all decks.

Use case ends.


UC09: Exit

MSS:

  1. User requests to exit FlashSpeed.

User case ends.


UC10: Playing a deck

MSS:

  1. FlashSpeed shows a list of all decks.

  2. User chooses a deck and requests to play that deck.

  3. FlashSpeed changes into game view and starts the game.

    Use case ends.

Extensions

  • 1. The list is empty.

    Use case ends.

  • 2a. The given index is invalid.

    • 2a1. FlashSpeed shows an error message.

      Use case resumes at step 1.


UC10: Playing a game.

MSS:

  1. FlashSpeed shows a list of all decks.

  2. User chooses a deck and requests to play that deck.

  3. FlashSpeed changes into game view and starts the game.

  4. FlashSpeed shows a card.

  5. User flips the card.

  6. FlashSpeed shows the other side of the card.

  7. User types in yes or no to indicate the correctness of their answer.

  8. FlashSpeed goes to next card.

  9. Use case repeats from step 4 to step 7 until all cards are answered and the game ends.

  10. FlashSpeed shows the statistics of game.

    Use case ends.

Extensions

  • 1a. The list is empty.

    Use case ends.

  • 2a. The given index is invalid.

    • 2a1. FlashSpeed shows an error message.

      Use case resumes at step 1.

  • 3a. The deck is empty.

    • 3a1. FlashSpeed shows an error message. Use case ends.

  • 4a. The given index is invalid.

    • 4a1. FlashSpeed shows an error message.

      Use case resumes at step 3.

Appendix D: Non Functional Requirements

  1. Should work on any mainstream OS as long as it has Java 11 or above installed.

  2. Should be able to hold up to 1000 decks without a noticeable sluggishness in performance for typical usage.

  3. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse.

Appendix E: Glossary

Mainstream OS

Windows, Linux, Unix, macOS.

Deck

A Deck is a group of Cards.

Card

A Card mimics a physical flashcard. It has two faces. One side for prompting the user and the other side for the content the user wants to memorize.

Smarter Review System

The smarter review system is adopted from the well known Space Repetition System. Cards that are answered wrongly in Play Mode will be shown more frequently in this system.

Space Repetition System

Spaced repetition is an evidence-based learning technique that is usually performed with flashcards. Newly introduced and more difficult flashcards are shown more frequently while older and less difficult flashcards are shown less frequently in order to exploit the psychological spacing effect.

View

The state or mode which FlashSpeed is in. There are three different views in FlashSpeed.

Library View

When no deck is selected and no cards are shown.

library
Figure 18. In Library view. No deck is selected.
Deck View

When a deck is selected and its cards are shown.

Ui
Figure 19. In Deck view. A deck has been selected.
Play View

When in a study session of a deck.

Ui2
Figure 20. In Play view. A deck is being studied.

Appendix F: Product Survey

Anki

Author: Damien Elmes

Pros:

  • Study algorithm is useful for easily memorizing content.

  • Extensive card browser

Cons:

  • Not as appealing UI.

  • Not friendly for fast typist to execute commands/tasks (no CLI).

Appendix G: Instructions for Manual Testing

Given below are instructions to test the app manually.

These instructions only provide a starting point for testers to work on; testers are expected to do more exploratory testing.

G.1. Launch and Shutdown

  1. Initial launch

    1. Download the jar file and copy into an empty folder

    2. Double-click the jar file
      Expected: Shows the GUI with a set of sample decks and cards. The minimum optimum window size for the app is already set.

G.2. Help functionality

  1. Opening Help Window

    1. Prerequisites: None

    2. Test case: help
      Expected: Opens the help window

    3. Test case: Pressing the F1 key on the keyboard.
      Expected: Opens the help window

  2. Closing the Help Window

    1. Prerequisites: Help Window is opened

    2. Test case: Pressing the Alt + F4 keys on the keyboard.
      Expected: Closes the help window

    3. Test case: Pressing the X button on the help window.
      Expected: Closes the help window

  3. Reading off from where you last left off.

    1. Open the help window.

    2. Scroll down to any part of the User Guide to read.

    3. Close the help window.

    4. Re-launch the help window.
      Expected: The help window opens to the page you last left off from.

G.3. Deck functionality

  1. Creating a deck.

    1. Prerequisites: None

    2. Test case: create Russian
      Expected: Deck shows up on the deck list on the left panel and the (currently empty) card list is shown on the right panel.

    3. Test case: create x (where x is a deck name that already exists)
      Expected: No new deck is created. Error details shown in the status message. Status bar remains the same.

    4. Other incorrect create commands to try: create, `create ` Expected: Similar to previous

  2. Deleting a deck from the deck list shown on the left panel.

    1. Prerequisites: Ensure that FlashSpeed contains at least 1 Deck, which can be seen on the left panel.

    2. Test case: remove 1
      Expected: First deck is deleted from the list. Name of the deleted deck is shown in the status message. Timestamp in the status bar is updated.

    3. Test case: remove 0
      Expected: No deck is deleted. Error details shown in the status message. Status bar remains the same.

    4. Other incorrect delete commands to try: remove, remove x (where x is larger than the list size)
      Expected: Similar to previous.

  3. Renaming a deck.

    1. Prerequisites: Ensure that FlashSpeed contains at least 1 Deck, which can be seen on the left panel.

    2. Test case: rename 1 Russian
      Expected: New deck name shows up on the deck list on the left panel.

    3. Test case: rename 0 Russian
      Expected: No deck is renamed. Error details shown in the status message. Status bar remains the same.

    4. Other incorrect create commands to try: rename Russian, rename, rename x Russian (where x is larger than the list size), rename 1 y (where y is a deck name that already exists)
      Expected: Similar to previous.

G.4. Card functionality

  1. Adding a card to a deck.

    1. Prerequisites: A deck needs to be selected first via select INDEX

    2. Test case: add 아녕하세요 : hello
      Expected: Card shows up on the card list on the right panel.

    3. Test case: add 안녕:하세요:hello Expected: No card is created. Error details shown in the status message. Status bar remains the same.

    4. Other incorrect create commands to try: add 안녕하세요: `, `add :hello
      Expected: Similar to previous

  2. Deleting a card from the card list shown on the right panel.

    1. Prerequisites: A deck needs to be selected first via select INDEX

    2. Test case: delete 1
      Expected: First card is deleted from the list. Name of the deleted card is shown in the status message. Timestamp in the status bar is updated.

    3. Test case: delete 0
      Expected: No card is deleted. Error details shown in the status message. Status bar remains the same.

    4. Other incorrect delete commands to try: delete, delete x (where x is larger than the list size)
      Expected: Similar to previous.

  3. Editing a card.

    1. Prerequisites: A deck needs to be selected first via select INDEX

    2. Test case: edit 1 안녕 : Hi!
      Expected: New card information is reflected on the card list on the left panel.

    3. Test case: edit 1 안녕:
      Expected: New card information (the front side) is reflected on the card list on the left panel.

    4. Test case: edit 1 : Hi!
      Expected: New card information (the back side) is reflected on the card list on the left panel.

    5. Test case: edit 0 안녕하세요: Hi!
      Expected: No card is edited. Error details shown in the status message. Status bar remains the same.

    6. Other incorrect create commands to try: edit, edit test : test test, edit x Russian : 러시안어 (where x is larger than the list size), rename 1 y (where y is a card that already exists)
      Expected: Similar to previous. === Game-play functionality

  4. Starting a game

    1. Prerequisites: Ensure that FlashSpeed contains at least 1 Deck, which can be seen on the left panel.

    2. Test case: play 1
      Expected: First deck is chosen and play mode starts.

    3. Test case: play 0
      Expected: No deck chosen and played. Error details shown in the status message.

    4. Other incorrect delete commands to try: play, play x (where x is larger than the list size)
      Expected: Similar to previous.

  5. Flipping a card in game

    1. Prerequisites: FlashSpeed is in a game session and the current card has not been flipped yet.

    2. Test case: flip
      Expected: The back side of the card is shown.

    3. Test case: flip when the back side of the card is already shown
      Expected: Nothing happens. Error details shown in the status message.

  6. Answering yes or no in game

    1. Prerequisites: FlashSpeed is in a game session and the current card has already been flipped.

    2. Test case: yes or no
      Expected: The next card is shown or the game ends.

    3. Test case: yes or no when the card is not yet flipped.
      Expected: Nothing happens. Error details shown in the status message.

  7. Ending a game

    1. Prerequisites: FlashSpeed is in a game session.

    2. Test case: flip + yes until the game ends
      Expected: A statistics screen will popup showing the statistics of the game.

G.5. Game statistics

  1. Display game statistics

    1. Prerequisites: Ensure that FlashSpeed contains at least 1 Deck, which can be seen on the left panel.

    2. Test case:

      1. Start a game session with a deck.

      2. Cycle through flip, yes or no.

      3. Take note of the progress bar / counters / number of cards answered wrongly.

      4. When the game end, the statistics of your game will show up.

      5. Ensure that the numbers you have calculated / seen / taken note of tally up.

Appendix H: Effort

Overview FlashSpeed, a flashcard application, is aimed specifically at helping University students with learning a new language. FlashSpeed is significantly different from Address Book 3 (AB3) although it is based off of it. In addition to changing existing features in AB3, we needed to add additional features that would complete our flashcard application. For example, the Play feature is a new feature introduced in FlashSpeed.

Challenges and Effort

  • Card and Deck Model
    AB3 contained a person class, which we wanted to refactor into our card class since each card contained information that could be easily refactored from person. However, since we wanted it to be possible to have many cards in a deck and many decks in a library, there was a design consideration of how we should implement deck. After much discussion, we decided to implement it as what can be described as a "layered AB3" implementation, where in AB3, an addressbook contains persons, but now we have a bigger library that contains multiple addressbooks.

  • Play feature
    Since this was a brand new feature that was created from scratch, there were multiple suggestions from different members for best way to implement it. After much discussion and some compromise from each design, we came out with our final implementation. Additionally, since it was a new feature, even after we did the initial implementation, there were things that we did not foresee, such as how the UI would handle the different views depending on the state of the game and the defensive code to implement to prevent bugs.

  • User Interface Design
    We were brainstorming for a good interface that could support the three views that we had: Library View, Deck View and Play View. We managed to create about 2-3 mock-ups that everyone thought looked really good. We then examined the User Experience (UX) advantages between the mock-ups and decided ultimately that an enhanced AB3 layout was the best for our flashcard purposes.

  • User Interface Implementation
    As none of the members in our team were experienced with UI design, our initial mock-up and vision of the UI proved too difficult for us to implement within our time frame. However, Robert (our UI Lead) focused on building and designing the UI while the rest of the team focused on the functional components of FlashSpeed. Robert would ask us for input based on the experimental UIs that he had designed and improve it based on our suggestions.

  • Design Considerations
    For each major enhancement, there were always a few design alternatives to choose from. As each team member felt that their design considerations had merit, in our first major enhancement meeting, we spent 2 hours deciding on the initial design implementation. After that meeting, our team decided on a faster way to resolve such cases by spending a maximum of 30 minutes discussing before casting a vote. It worked well for the future meetings.

Achievements

  1. We managed to create FlashSpeed to what we envisioned it to be with the features we wanted.

  2. Although we did not manage to implement all the user-stories that we brain-stormed, we implemented all the essential functionality of FlashSpeed plus a few more nice features.

  3. FlashSpeed can safely support most digitized written languages in the world, due to our stringent parser checks and language support from Java.

  4. We learnt a lot about important processes and considerations in software development, e.g consistent coding style, version control skills, documentation writing, extensive testing, etc.