diff --git a/.gitignore b/.gitignore
index 2873e189e1..3c4103bf07 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,9 @@
/out/
/*.iml
+# VSCode setting files
+.vscode/
+
# Gradle build files
/.gradle/
/build/
@@ -15,3 +18,4 @@ bin/
/text-ui-test/ACTUAL.TXT
text-ui-test/EXPECTED-UNIX.TXT
+flashcardList.txt
diff --git a/build.gradle b/build.gradle
index d5e548e85f..fd935b8b8a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -29,11 +29,11 @@ test {
}
application {
- mainClass = "seedu.duke.Duke"
+ mainClass = "com.clanki.Clanki"
}
shadowJar {
- archiveBaseName = "duke"
+ archiveBaseName = "clanki"
archiveClassifier = null
}
@@ -41,6 +41,7 @@ checkstyle {
toolVersion = '10.2'
}
-run{
+run {
standardInput = System.in
+ enableAssertions = true
}
diff --git a/docs/AboutUs.md b/docs/AboutUs.md
index 0f072953ea..a21a07b032 100644
--- a/docs/AboutUs.md
+++ b/docs/AboutUs.md
@@ -1,9 +1,9 @@
-# About us
+# About Us
-Display | Name | Github Profile | Portfolio
---------|:----:|:--------------:|:---------:
- | John Doe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
- | Don Joe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
- | Ron John | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
- | John Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
- | Don Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md)
+| Display | Name | GitHub Profile | Portfolio |
+|:--------------------------------------------------------------------------------------------|:-------------|:------------------------------------------|:---------------------------------|
+|  | Kong Dehao | [GitHub](https://github.com/kdh3799) | [Portfolio](./team/kdh3799) |
+|  | Vu Van Dung | [GitHub](https://github.com/joulev) | [Portfolio](./team/joulev) |
+|
| Song Zijin | [GitHub](https://github.com/SongZijin) | [Portfolio](./team/songzijin) |
+|  | Moses Lee | [GitHub](https://github.com/moseslee9012) | [Portfolio](./team/moseslee9012) |
+|
| Javienne Yeo | [GitHub](https://github.com/javienneyeo) | [Portfolio](./team/javienneyeo) |
diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md
index 64e1f0ed2b..ccf95eddd7 100644
--- a/docs/DeveloperGuide.md
+++ b/docs/DeveloperGuide.md
@@ -1,38 +1,421 @@
# Developer Guide
+
+
+* [Developer Guide](#developer-guide)
+ * [Acknowledgements](#acknowledgements)
+ * [Design & Implementation](#design--implementation)
+ * [Add Flashcard Feature](#add-flashcard-feature)
+ * [Current Implementation](#current-implementation)
+ * [Reason for Current Implementation](#reason-for-current-implementation)
+ * [Alternative Implementation](#alternative-implementation)
+ * [Delete Flashcard Feature](#delete-flashcard-feature)
+ * [Current Implementation](#current-implementation-1)
+ * [Reason for Current Implementation](#reason-for-current-implementation-1)
+ * [Alternative Implementation](#alternative-implementation-1)
+ * [Update Flashcard Feature](#update-flashcard-feature)
+ * [Current Implementation](#current-implementation-2)
+ * [Reason for Current Implementation](#reason-for-current-implementation-2)
+ * [Alternative Implementation](#alternative-implementation-2)
+ * [Review Flashcard Feature](#review-flashcard-feature)
+ * [Current Implementation](#current-implementation-3)
+ * [Reason for Current Implementation](#reason-for-current-implementation-3)
+ * [Alternative Implementation](#alternative-implementation-3)
+ * [Parser](#parser)
+ * [Current Implementation](#current-implementation-4)
+ * [`ParsedInput`](#parsedinput)
+ * [`Parser`](#parser-1)
+ * [Reason for Current Implementation](#reason-for-current-implementation-4)
+ * [Alternative Implementation](#alternative-implementation-4)
+ * [Storage Feature](#storage-feature)
+ * [Current Implementation](#current-implementation-5)
+ * [Reason for Current Implementation](#reason-for-current-implementation-5)
+ * [Alternative Implementation](#alternative-implementation-5)
+ * [Product Scope](#product-scope)
+ * [Target User Profile](#target-user-profile)
+ * [Value Proposition](#value-proposition)
+ * [User Stories](#user-stories)
+
+
+
## Acknowledgements
-{list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well}
+- [addressbook-level2](https://github.com/se-edu/addressbook-level2)
+- [Song Zijin's IP](https://github.com/SongZijin/ip)
-## Design & implementation
+## Design & Implementation
-{Describe the design and implementation of the product. Use UML diagrams and short code snippets where applicable.}
+### Add Flashcard Feature
+The image below shows a partial class diagram involving only the relevant
+classes when an AddCommand is created and executed:
+
-## Product scope
-### Target user profile
+#### Current Implementation
-{Describe the target user profile}
+The current add flashcard allows the user to add a flashcard to the list of
+flashcards, it is implemented through the following steps:
-### Value proposition
+Step 1: The input of user is collected by `getUserCommand()` inside class `Ui`.
-{Describe the value proposition: what problem does it solve?}
+Step 2: The input string will be converted into a `Command` object by being
+passed through `parseCommand(String userInput)` inside `Parser`.
-## User Stories
+In this case, an `AddCommand` will be created and returned, as shown in the
+object diagram below:
+
+
+Step 3: The `execute()` function of `AddCommand` will run, calling
+`addNewFlashcard(questionText, answerText)` of class `FlashcardList` to create
+and add the new flashcard to the list.
+
+Then it will also call `printSuccessfulAddMessage(questionText, answerText)` of
+class `Ui` to display text indicating the successful adding function to the
+user.
+
+At this point, the adding process is completed and the program is ready to take
+another command.
+
+The following sequence diagram show how the add operation works:
+
+
+#### Reason for Current Implementation
+
+Through using `AddCommand` class, which extends `Command` class it increases the
+level of abstraction as the code can now perform the various commands on a class
+level.
+
+Moreover, since the creating of new `Flashcard` of object and adding of the
+newly created flashcard are both done in the same class as where the flashcards
+are stored, this reduces coupling in the program as the `AddCommand` will not
+have access to the inner structure of `FlashcardList`, which stores the list of
+flashcards.
+
+#### Alternative Implementation
+
+- Alternative 1: Have the add command function directly in `FlashcardList`
+
+ - Pros: Easy to implement
+ - Cons: Will require another function in another program to differentiate it
+ from other commands
+
+- Alternative 2: Have the constructor of `Flashcard` include adding the card to
+ list of flashcards
+
+ - Pros: Simplifies code
+ - Cons: Will cause trouble when temporary flashcard (that need not be
+ stored)
+ are created
+
+### Delete Flashcard Feature
+
+The figure below shows a simple class diagram for the Delete Command.
+
+
+
+#### Current Implementation
+
+The current delete flashcard allows the user to remove a flashcard from the list
+of flashcards, it is implemented through the following steps:
+
+Step 1: The input of user is collected by `getUserCommand()` inside class `Ui`.
+
+Step 2: The input string will be converted into a `Command` object by being
+passed through `parseCommand(String userInput)` inside `Parser`.
+
+In this case, a `DeleteCommnad` will be created and returned.
+
+Step 3: The `execute()` function of `DeleteCommand` will run, creating a copy of
+the list of flashcards. Then `findFlashcards(flashcards, query)` is called to
+find the flashcards with questions matching the query, before calling
+`printFlashcardList(matchingFlashcards)` to display the found flashcards.
+
+User input is taken to get the index of the flashcard to be removed.
+`deleteFlashcard` is called from class `flashcardList` to remove the flashcard
+from the original list of flashcards. Finally `printSuccessfulDelete` is called
+from class `Ui` to indicate a successful removal of the flashcard.
+
+The deletion process is now completed and the program will await another
+command.
+
+An overview of how the Delete operation works is shown with the following
+sequence diagram 
+
+#### Reason for Current Implementation
+
+Through using `DeleteCommand` class, which extends `Command` class it increases
+the level of abstraction as the code can now perform the various commands on a
+class level.
+
+In order to minimise the time for users to search for the flashcard to delete,
+they are able to first search for a sub-list of flashcards with matching
+questions as the query. This method makes the deletion process simple even if
+the user does not remember the index of the flashcard.
+
+#### Alternative Implementation
+
+- Alternative 1: Delete flashcard by index from the start
+
+ - Pros: Easy to implement and simplifies code
+ - Cons: Cumbersome to delete if user forgets the flashcard's index and has
+ to
+ search through the whole list of flashcards.
+
+### Update Flashcard Feature
+
+#### Current Implementation
+
+The current update flashcard feature allows users search for a specific
+flashcard and update the contents of this flashcard. It is implemented through
+the following steps:
+
+Step 1: The input of user is collected by `getUserCommand()` inside class `Ui`.
+
+Step 2: The input string will be converted into a `Command` object by being
+passed through `parseCommand(String userInput)` inside `Parser`.
+
+In this case, an `UpdateCommand` will be created and returned.
+
+Step 3: The `execute()` function of `UpdateCommand` will run `queryFlashcards(query)`
+of `FlashcardList`, which will query for flashcards in the current deck that matches
+the query inside either the question or answer and return an ArrayList of `Flashcard`
+called `matchingFlashcards`.
+
+Step 4: Then, `printFlashCards(matchingFlashcards)` inside class `Ui` is called, which
+prints all questions and answers of the list of flashcards, that matches the query,
+to the console
+
+Step 5: Lastly, `runUpdateFlashcard(display)` is executed. This method prompts the user
+for input and updates the specified flashcard based on that input.
+
+At this point, the update flashcard process is completed and the program is read
+to take another command.
+
+An overview of how the Update command works is shown with the following sequence
+diagram 
+
+#### Reason for Current Implementation
+
+Implementing the update flashcard in an `UpdateCommand` class makes it easier
+during the debugging process related to update flashcard feature alone as most
+of the methods and attributes are within this `UpdateCommand` class.
+
+Furthermore, the UpdateCommand has a dedicated function, runUpdateFlashcard(display),
+which handles the updating of the flashcard. This helps to ensure that the code
+remains organized and easy to read, with the updating process separated from other
+code.
+
+#### Alternative Implementation
+
+- Alternative 1: Instead of creating a new arrayList `matchingFlashcards` that
+ store flashcards containing the `query` and then printing the list of
+ flashcards, directly print the flashcards when there is a match with the query
+
+ - Pros: Easier to implement
+ - Cons: Harder to track the total number of flashcards that has `query` and
+ will need to have another way to track the index of the matching
+ flashcards.
+ it will also be more confusing as the index of the user input is not
+ aligned
+ with the index of the arrayList that contains all the flashcards
-|Version| As a ... | I want to ... | So that I can ...|
-|--------|----------|---------------|------------------|
-|v1.0|new user|see usage instructions|refer to them when I forget how to use the application|
-|v2.0|user|find a to-do item by name|locate a to-do without having to go through the entire list|
+- Alternative 2: An alternative implementation could be to have the updating of
+ the flashcard handled directly in FlashcardList.
-## Non-Functional Requirements
+ - Pros: Simple implementation and no need for another function in another
+ program to differentiate it from other commands
+ - Cons: May lead to increased coupling in the program, as the `UpdateCommand`
+ will have access to the inner structure of `FlashcardList` and this may make the
+ code more difficult to read and debug, as the updating process will be
+ combined with other code
+
+### Review Flashcard Feature
-{Give non-functional requirements}
+#### Current Implementation
-## Glossary
+The current review flashcard allows the user to review all the flashcards that
+are due today or before, it is implemented through the following steps:
-* *glossary item* - Definition
+Step 1: The input of user is collected by `getUserCommand()` inside class `Ui`.
-## Instructions for manual testing
+Step 2: The input string will be converted into a `Command` object by being
+passed through `parseCommand(String userInput)` inside `Paser`.
+
+In this case, an `ReviewCommand` will be created and returned.
+
+Step 3: The `execute()` function of `ReviewCommand` will run, calling
+`getFlashCards()` of class `FlashcardList` to get the list of the flashcards.
+
+Then it will iterate through the `FlashcardList` and call the function
+`isDueBeforeToday()` of class `Flashcard` to check if the flashcard is due by
+today.
+
+If the flashcard is due by today,
+`reviewCurrentFlashcard(Ui display, Flashcard flashcard)` of class
+`ReviewCommand` will be called to review the card.
+
+First, the `Ui` will display the question of the current card by calling the
+`getQuestion()` method of class `Flashcard`, and ask user if user is ready to
+view the answer. After user enters any keyboard input, the answer of the current
+card will be shown by calling the `getAnswer()` method of class `Flashcard`, and
+`Ui` will ask the user if he/she has got the card correct. If the user inputs
+"y", then the current `Flashcard` is considered to be cleared and
+`updateDueDateAfterCorrectAnswer()` of `Flashcard` will be called to update its
+`dueDate`. Then Clanki will let user review the next `Flashcard`. If the user
+inputs "n", then the card is considered to be not cleared and
+`updateDueDateAfterIncorrectAnswer()` will be called to update its `dueDate`.
+Then Clanki will let user review the next `Flashcard`. This process will repeat
+until all the `Flashcards` in the `FlashcardList` are iterated.
+
+After the whole `FlashcardList` has been iterated through, a message
+congratulating the user that he/she has completed the reviewing task will be
+displayed.
+
+At this point, the reviewing process is completed and the program is ready to
+take another command.
+
+The following sequence diagram show how the review operation work:
+
+
+#### Reason for Current Implementation
+
+Through using `ReivewCommand` class, which extends `Command` class it increases
+the level of abstraction as the code can now perform the various commands on a
+class level.
+
+Moreover, `ReviewCommand` only has access to the public methods of
+`FlashcardList` and `Flashcard`, this reduces coupling in the program as the
+`ReviewCommand` will not have access to the inner structure of `FlashcardList`
+and `Flashcard`.
+
+#### Alternative Implementation
+
+- Alternative 1: Have the review command function directly in `FlashcardList`
+
+ - Pros: Easy to implement
+ - Cons: Will require another function in another program to differentiate it
+ from other commands
+
+- Alternative 2: After entering the `ReviewCommand`, go back to `Clanki.run()`
+ and take further commands for review process
+
+ - Pros: Simplifies code in `ReviewCommand`
+ - Cons: Will have to pass around a lot of parameters and variables
+
+### Parser
+
+#### Current Implementation
+
+The parser mostly relies on the `ParsedInput` class, which can parse any user
+provided string input in the format of Windows command prompt commands
+(`command body /opt-key opt-value`).
+
+##### `ParsedInput`
+
+Initiated with a string `input`, it splits the input to sections that are of
+use. From there it splits each section further to a "title" (denoted with `=`
+below) and a "body" (denoted with `-` below) part.
+
+```
+command blah blah /opt1 hello /opt2 world blah bleh
+| Part 1 | | Part 2 | | Part 3 |
+|=====| |-------| |==| |---| |==| |-------------|
+```
+
+Then these small subparts are grouped together to a format where the command
+part of the command, the body part and the options can be retrieved
+programmatically.
+
+The command and body can be read with `getCommand()` and `getBody()`
+respectively. `getCommand()` is guaranteed to be non-null.
+
+The options can be read with `getOptionByName(optionKeyName)`. The reason we
+don't have specific `getDate` or `getQuestion` command is because we don't know
+what the user will input and what options we will require for each command. So
+depending on the command, we retrieve the option accordingly with e.g.
+
+```java
+"command blah blah /opt1 hello /opt2 world blah bleh"
+ getOptionByName("opt2") // -> "world blah bleh"
+ getOptionByName("opt3") // -> null
+```
+
+##### `Parser`
+
+This is now just a matter of wrapping `ParsedInput` with suitable error handling
+and logic such that each command will be used to initiate a corresponding
+command class (e.g. `AddCommand`), while errors are handled gracefully.
+
+#### Reason for Current Implementation
+
+We need an intuitive, safe and declarative way to parse the user input.
+Alternative implementations that can only parse specific commands with specific
+options are more imperative, less readable, less maintainable and overall just a
+pain to handle. That's why the two classes are here.
+
+#### Alternative Implementation
+
+No.
+
+### Storage Feature
+
+#### Current Implementation
+
+The current storage feature triggers after every execution of command, updating
+the flashcardList.txt file to be the came as what is stored in the
+`FlashcardList` object.
+
+The entire feature consist of 3 parts, as shown in the class diagram below:
+
+
+1. `FlashcardListEncoder`: takes the list of flashcards from `FlashcardList` and
+ convert them to a list of strings, with heading to indicate the start of the
+ question, answer and deadline portion of a flashcard.
+2. `FlashcardListDecoder`: takes a list of string in specific format (as defined
+ by `FLASHCARD_ARGS_FORMAT`) and decodes the string into an arrayList of
+ flashcards, discarding any string of incorrect format.
+3. `StorageFile`: uses the encoder or decoder to save or load the current state
+ into or from a text file.
+
+The following sequence diagram show how the add operation works:
+
+
+#### Reason for Current Implementation
+
+By separating the decoder and encoder as separate classes, it allows the code
+for the storage system to be more readable by others, allowing them to identify
+and find the chunk of code for each function more easily, and possibly reuse the
+functions if they deem necessary in future versions.
+
+#### Alternative Implementation
+
+- Alternative 1: Have all functions in one `Storage` class
+
+ - Pros: Exceptions can be handled in the same place
+ - Cons: Will cause the code be less organised and readable
+
+## Product Scope
+
+### Target User Profile
+
+Students learning subjects that require a lot of memorisation (history, a new
+language, etc.)
+
+### Value Proposition
+
+This application help users to better remember key points in their upcomming
+tests by providing them a platform to read through and practice answering those
+key learning points.
+
+## User Stories
-{Give instructions on how to do a manual product testing e.g., how to load sample data to be used for testing}
+| Version | As a ... | I want to ... | So that I can ... |
+|---------|-------------------------|---------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------|
+| v1.0 | user | add a card to my flashcard collection | study it later on |
+| v1.0 | user | delete any of my cards | prevent getting asked to review that card later when I am confident I have truly memorised the card |
+| v1.0 | user | review the cards that are due today | remember them better |
+| v1.0 | user | make changes to the q/a any cards I want | keep the info there always updated with what I want myself to memorise |
+| v1.0 | user seeking efficiency | review the cards at an appropriate pace that is most efficient for memorisation | not waste time reviewing when I still remember the cards well |
+| v2.0 | new user | see usage instructions | refer to them when I forget how to use the application |
+| v2.0 | busy user | store the cards somewhere | revisit them next time I open the app |
+| v2.0 | organised user | view a list of all currently stored flashcards | know what are the things I need to remember |
diff --git a/docs/README.md b/docs/README.md
index bbcc99c1e7..bd3448fc45 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,8 +1,10 @@
-# Duke
+# Clanki
-{Give product intro here}
+Clanki is a command-line interface (CLI) application for managing flashcards. It
+allows users to create, manage and study flashcards to assist them with
+memorisation by using a technique called
+[spaced repetition](https://en.wikipedia.org/wiki/Spaced_repetition).
-Useful links:
-* [User Guide](UserGuide.md)
-* [Developer Guide](DeveloperGuide.md)
-* [About Us](AboutUs.md)
+- [User Guide](./UserGuide)
+- [Developer Guide](./DeveloperGuide)
+- [About Us](./AboutUs)
diff --git a/docs/UserGuide.md b/docs/UserGuide.md
index abd9fbe891..9144859006 100644
--- a/docs/UserGuide.md
+++ b/docs/UserGuide.md
@@ -1,42 +1,312 @@
# User Guide
-## Introduction
+Clanki is a command-line interface (CLI) application for managing flashcards. It
+allows users to create, manage and study flashcards to assist them with
+memorisation by using a technique called
+[spaced repetition](https://en.wikipedia.org/wiki/Spaced_repetition).
-{Give a product intro}
+## Quick start
-## Quick Start
+1. Ensure you have Java 11 or above installed.
-{Give steps to get started quickly}
+2. Download the latest version of Clanki from
+ [here](https://github.com/AY2223S2-CS2113-T15-4/tp/releases).
-1. Ensure that you have Java 11 or above installed.
-1. Down the latest version of `Duke` from [here](http://link.to/duke).
+3. Copy the file to the folder you want to use as the home folder for Clanki.
-## Features
+4. Open a command terminal, `cd` into the folder you put the jar file in, and
+ use the `java -jar clanki.jar` command to run the application. You should be
+ greeted with `Welcome to Clanki! Time to start studying!` after a few
+ seconds.
-{Give detailed description of each feature}
+5. Type the command in the command box and press Enter to execute it. Some
+ example commands you can try:
-### Adding a todo: `todo`
-Adds a new item to the list of todo items.
+ - `add /q what is the worst fruit? /a durian`: Add a flashcard with
+ Question:
+ What is the worst fruit? and Answer: Durian to the list of flashcards.
-Format: `todo n/TODO_NAME d/DEADLINE`
+ - `review`: Go through the flashcards that are due today.
-* The `DEADLINE` can be in a natural language format.
-* The `TODO_NAME` cannot contain punctuation.
+ - `bye`: Exit the app.
-Example of usage:
+6. Refer to the Features below for details of each command.
-`todo n/Write the rest of the User Guide d/next week`
+## Features
-`todo n/Refactor the User Guide to remove passive voice d/13/04/2020`
+> Square brackets indicate optional sections of the syntax.
-## FAQ
+### Adding a flashcard
-**Q**: How do I transfer my data to another computer?
+```
+add /q QUESTION /a ANSWER
+```
-**A**: {your answer here}
+- `QUESTION` will be displayed when reviewing. For how the reviewing process
+ works, see "Review flashcards" section below.
-## Command Summary
+- Since a slash character (`/`) can be interpreted as a command option, both
+ `QUESTION` and `ANSWER` must not include (`/`). (In some of the times, `/`
+ might still work normally, that's just you being lucky.)
-{Give a 'cheat sheet' of commands here}
+ - We might fix this in a future version. A far, far future version that
+ might
+ never come...
-* Add todo `todo n/TODO_NAME d/DEADLINE`
+- `/q QUESTION` and `/a ANSWER` can be arranged in any order.
+
+- The first letter of `QUESTION` and `ANSWER` are automatically capitalised.
+
+- If any options are duplicated, all but the last one are ignored.
+
+- The question and answer can include non-Latin characters such as Chinese
+ characters. However, your terminal might have trouble displaying it. If you
+ need such characters, find a terminal that supports it. A relatively modern
+ terminal (more modern than cmd.exe) should do, or buy a Mac since the default
+ Mac terminal does support it.
+
+#### Example
+
+```
+add /q What is the worst fruit? /a Durian
+You have added the following card:
+Q: What is the worst fruit?
+A: Durian
+```
+
+```
+add /a 果物 /q What is the Japanese word for "fruit"?
+You have added the following card:
+Q: What is the Japanese word for "fruit"?
+A: 果物
+```
+
+### Review flashcards
+
+```
+review
+```
+
+- Flashcards that are due on the day will be displayed one after another, queued
+ in a secret order.
+
+- When the user has recalled the answer for a card, or has given up on doing so,
+ they can then press Enter.
+
+- The app will then show the answer and ask the user if they got it right. They
+ can then type `y`/`n` to indicate that they remembered the answer correctly,
+ or they couldn't remember the correct answer, respectively.
+
+ - If `y` is indicated, the card is then set to a new due date in the
+ future.When the user has got it correct, the new date is set to a future
+ date according to a top-secret
+ [Spaced repetition](https://en.wikipedia.org/wiki/Spaced_repetition)
+ schedule that you can try to figure out if you want to explore mysteries.
+ - If `n` is indicated, the card is pushed back to the today's queue for
+ reviewing later, until the user can get it correct.
+ - If a command other than `y` or `n` is indicated, it will be regarded as an
+ invalid command, and then the user will be prompted again to enter a valid
+ answer.
+
+- Then the review session continues with the next card in the queue.
+
+#### Example
+
+```
+> review
+There are 2 cards available for review today.
+---
+Q: What is the worst fruit? (ENTER to view answer)
+A: Durian
+Did you get it right? (y/n) n
+No worries, we will try again later today.
+---
+Q: What is the Japanese word for "fruit"? (ENTER to view answer)
+A: 果物
+Did you get it right? (y/n) y
+Great, you got it right!
+---
+Q: What is the worst fruit? (ENTER to view answer)
+A: Durian
+Did you get it right? (y/n) y
+Great, you got it right!
+---
+Congrats! You have reviewed all the flashcards due today!
+```
+
+### Update flashcards
+
+```
+update QUERY
+```
+
+- `QUERY` is part of the card's question or answer. It is not case sensitive.
+
+- A list of cards that contain this query will be listed, with an index assigned
+ to each.
+
+- The user can then type the index of the card they wish to update with the
+ following syntax
+
+ ```
+ INDEX [/q NEW_QUESTION] [/a NEW_ANSWER]
+ ```
+
+- At least one of `/q` and `/a` must be provided.
+
+- Format of `NEW_QUESTION` and `NEW_ANSWER` is the same as in the `add` command
+ above.
+
+#### Example
+
+```
+update fruit
+Found 2 card(s) with query "fruit":
+[1]
+Q: What is the worst fruit?
+A: Durian
+[2]
+Q: What is the Japanese word for "fruit"?
+A: 果物
+Which one do you want to update?
+1 /q What is the best fruit?
+Understood. The card has been updated to
+Q: What is the best fruit?
+A: Durian
+```
+
+### Delete a flashcard
+
+```
+delete QUERY
+```
+
+- `QUERY` can be part of the card's question or answer.
+
+- A list of cards that contain this query will be listed, with an index assigned
+ to each.
+
+- The user can then type the index of the card they wish to delete. The card
+ with that index (on that list) is then deleted.
+
+#### Example
+
+```
+delete fruit
+Found 2 card(s) with query "fruit":
+[1]
+Q: What is the best fruit?
+A: Durian
+[2]
+Q: What is the Japanese word for "fruit"?
+A: 果物
+Which one do you want to delete?
+2
+Got it. Deleted the flashcard at index 2
+```
+
+### Delete all flashcards in the list
+
+```
+clear
+```
+
+Delete all the flashcards in the list.
+
+#### Example
+
+```
+clear
+All flashcards have been deleted.
+Your list of flashcards is now empty.
+```
+
+### List all flashcards
+
+```
+list all
+```
+
+Display the questions and answers for all the flashcards in the list that have
+been added by the user, regardless of the date.
+
+#### Example
+
+```
+list all
+Here is your list of flashcards:
+[1]
+Q: What is the biggest animal in the world
+A: Antartic blue whale
+[2]
+Q: What are the best food for health
+A: Lemons
+[3]
+Q: What colour is the sun
+A: Red
+```
+
+### List flashcards with specific due date
+
+```
+list DUE_DATE
+```
+
+Display the questions and answers for all the flashcards in the list that has
+the due date specified by the user
+
+- `DUE_DATE` has to be in the format `yyyy-mm-dd`
+
+```
+list 2023-04-15
+Here is your list of flashcards:
+[1]
+Q: What is the biggest animal in the world
+A: Antartic blue whale
+[2]
+Q: What are the best food for health
+A: Lemons
+[3]
+Q: What colour is the sun
+A: Red
+```
+
+### Help menu
+
+```
+help
+```
+
+Display the list of possible commands the user can input.
+
+#### Example
+
+```
+help
+The following are the commands you can use:
+add Adds a flashcard to the current list of flashcards.
+ Parameters: add /q QUESTION /a ANSWER
+ Example: add /q What is the worst fruit? /a Durian
+update Changes the content of flashcard's question or answer.
+ Parameters: update QUERY
+ Example: update fruit
+ Which flashcard do you want to update? 1 /q What is the best fruit?
+delete Removes a flashcard with specified string.
+ Parameters: delete QUERY
+ Example: delete fruit
+review Go through all flashcards that are due today.
+list Lists out the questions and answers in the list of flashcards.
+ Parameters: list all (lists all flashcards)
+ Parameters: list DUE_DATE (list all flashcards with that specified due date)
+ Example: list 2023-05-04
+clear Deletes all the flashcards in the list.
+bye Exit the program.
+```
+
+### Exit program
+
+```
+bye
+```
+
+Exit the program. Bye.
diff --git a/docs/otherImages/ZijinProfile.jpg b/docs/otherImages/ZijinProfile.jpg
new file mode 100644
index 0000000000..b866b96659
Binary files /dev/null and b/docs/otherImages/ZijinProfile.jpg differ
diff --git a/docs/team/javienneyeo.md b/docs/team/javienneyeo.md
new file mode 100644
index 0000000000..0e3659975a
--- /dev/null
+++ b/docs/team/javienneyeo.md
@@ -0,0 +1,53 @@
+# Javienne Yeo's Project Portfolio Page
+
+## Overview of Product
+- Clanki is a study tool to help students with content heavy subjects with a lot of memorization required. It is
+ able to add the questions and answers into flashcards, allowing users to review them when ready.
+
+### Summary of Contributions
+- **New feature:** Added the ability to update the question, answer or due date of a flashcard
+ - What it does: The feature prints out a list of flashcards that contains the query in its question,
+ answer or due date. The feature than allows the user to choose which flashcard to update and how they
+ want it to be updated. The user is able to change the question, answer or due date part of the flashcard.
+ - Justification: This feature is required for users to make any changes to the flashcard. Without
+ this feature, the user would have to delete the flashcard and add a new flashcard in the event the
+ user wants to change the question, answer or due date.
+ - Highlights: Reduces the number of flashcards the user has to look through to find the specific flashcard
+ he/she wants to update as the feature prints out the list of flashcards that have that matching query.
+ In addition, the feature searches for the query in the question, answer and due date. JUnit test codes are
+ also written for this feature.
+- **New feature:** Added the ability to list out the flashcards the user has added
+ - What it does: This feature allows the user to print out all flashcards that the user currently has or
+ print out all the flashcards that is due on a certain date
+ - Justification: This allows the user to have a broad overview of all the flashcards that he/she has
+ added to the list. This is to allow the user to check for whether they have added duplicates into the
+ list or to check whether the contents of the flashcards are all correct before reviewing them.
+ - Highlights: The user is able to view all the flashcards that are due on a certain day by entering
+ this date as an input. This is to allow the user to view all the flashcards with that due date to
+ revise before reviewing it using the review feature. JUnit test codes are also written for this feature.
+- **New feature:** Added the ability to delete all flashcards in the current list of flashcards.
+ - What it does: Allows the user to remove all flashcards that have been added in the list to create a new
+ list of flashcards
+ - Justification: In the event the user has many flashcards in his/her list and wants to delete all of
+ it to start a new list, the user is able to use this function and would not need to delete the
+ flashcards one-by-one. It also allows the user to start a new list of flashcard with just one command.
+
+- **Code contributed:**
+ [RepoSense link](https://nus-cs2113-ay2223s2.github.io/tp-dashboard/?search=t15&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2023-02-17&tabOpen=true&tabType=authorship&zFR=false&tabAuthor=javienneyeo&tabRepo=AY2223S2-CS2113-T15-4%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false)
+
+- **Contributions to UG:**
+ - Added section under `list flashcard` function
+ - Added section under `clear` function
+ - Edited parts of the section under `Update` function
+
+- **Contributions to DG:**
+ - Added implementation details for the `update` function.
+
+
+
+
+
+
+
+
+
diff --git a/docs/team/johndoe.md b/docs/team/johndoe.md
deleted file mode 100644
index ab75b391b8..0000000000
--- a/docs/team/johndoe.md
+++ /dev/null
@@ -1,6 +0,0 @@
-# John Doe - Project Portfolio Page
-
-## Overview
-
-
-### Summary of Contributions
diff --git a/docs/team/joulev.md b/docs/team/joulev.md
new file mode 100644
index 0000000000..de67fc7ece
--- /dev/null
+++ b/docs/team/joulev.md
@@ -0,0 +1,57 @@
+# Vu Van Dung's Project Portfolio Page
+
+## Project: Clanki
+
+Clanki is a command line application one can use to store and update flashcards
+to help them remember the relevant concepts. The user can interact with it by
+typing code into their terminal.
+
+Given below are my contribution to the project.
+
+### Summary of Contributions
+
+- **New feature:** Parser
+
+ - What it does: allows the app to intuitively and safely parse user input
+ - Justification: without it, imperative and specific implementation of the
+ parser for each command might be necessary, which will just be a grand
+ invitation for bugs
+ - Highlights: thanks to this feature, all places in the app where user input
+ is parsed can be handled declaratively. Since we take a lot of user inputs,
+ this is crucial for the app
+
+- **New feature:** Set up the ground work
+
+ - What it does: not a user-facing feature, but this sets up Gradle, tests and
+ an overall file structure along with crucial classes (`Flashcard`,
+ `FlashcardList`, `FlashcardQueue`) that are used in all other places in the
+ app
+ - Justification: make development of the app easier
+
+- **Major contribution:** Improve other features by other members
+
+ - What it does: other members' code might be inconsistent, might be
+ reinventing the wheels, might have hidden bugs, or overall just might be
+ bad; I fixed them to a level I'm at least not depressed when looking at the
+ code.
+
+- **Code contributed:**
+ [RepoSense link](https://nus-cs2113-ay2223s2.github.io/tp-dashboard/?search=joulev&sort=groupTitle%20dsc&sortWithin=title&since=2023-02-17&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=false&tabOpen=true&tabType=authorship&tabAuthor=joulev&tabRepo=AY2223S2-CS2113-T15-4%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code~other&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false)
+
+- **Contributions to UG:**
+
+ - Enhanced the user guide for all sections to make them more accurate and
+ closer to the actual behaviour and output of the app
+ - Added input restrictions (if any) to the UG
+
+- **Contributions to DG:**
+
+ - Added details for the parser
+
+- Other contributions:
+
+ - (Forced to) lead the project. I am basically the one who decided the
+ timeline and did most of the admin stuff
+ - Triaged incoming issues, opened tickets and assigned them to relevant
+ personnels
+ - Reviewed pull requests
diff --git a/docs/team/kdh3799.md b/docs/team/kdh3799.md
new file mode 100644
index 0000000000..992066e78a
--- /dev/null
+++ b/docs/team/kdh3799.md
@@ -0,0 +1,31 @@
+# Kong Dehao - Project Portfolio Page
+
+## Overview: Project Clanki
+
+Clanki is a command line application to store flashcards used for
+memorising information and concepts. The user can interact with
+the app by typing commands into their terminal.
+
+### Summary of Contributions
+
+**New feature:** Added `review` function to remove flashcards
+
+- What it does: The `review` function allows users to review the flashcards that
+ are due today, depending on whether the user gets the answer correctly, the
+ new due date of the flashcards will be updated accordingly.
+- Justification: This feature forms the basic building block of the program to
+ let the user review the flashcards he/she has added.
+- Highlights: This feature involved multiple rounds of user interaction and make
+ corresponding updates based on user response.
+
+**Code contributed:**
+[Reposense link](https://nus-cs2113-ay2223s2.github.io/tp-dashboard/?search=kdh3799)
+
+**Contributions to DG:**
+
+- Added implementation details and UML diagrams for
+ `review` command
+
+**Contributions to team-based tasks**
+
+- Added table of content for DG and rearranged the order of the sections of DG
\ No newline at end of file
diff --git a/docs/team/moseslee9012.md b/docs/team/moseslee9012.md
new file mode 100644
index 0000000000..1508facbdd
--- /dev/null
+++ b/docs/team/moseslee9012.md
@@ -0,0 +1,38 @@
+# Lee Zhi Zhong Moses - Project Portfolio Page
+
+## Overview: Project Clanki
+
+Clanki is a command line application to store flashcards used for
+memorising information and concepts. The user can interact with
+the app by typing commands into their terminal.
+
+### Summary of Contributions
+
+**New feature:** Added `delete` function to remove flashcards
+
+- What it does: The `delete` allows users to remove a specified
+ flashcard from the existing list. Typing "delete" will show the full
+ list of existing flashcards, and prompts the user for the index of
+ the flashcard to be deleted. The user can include a query, or keyword,
+ to find the relevant questions and narrow down the search list.
+- Justification: This feature greatly improves the product as users can
+ easily remove the unnecessary flashcards and study only what is relevant.
+- Highlights: This feature helps reduce the clutter of flashcards, either
+ from obsolete flashcards or cards that were added erroneously.
+
+**Code contributed:**
+[Reposense link](https://nus-cs2113-ay2223s2.github.io/tp-dashboard/?search=moseslee9012&breakdown=true&sort=groupTitle%20dsc&sortWithin=title&since=2023-02-17&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other)
+
+**Contributions to UG:**
+
+- Added section for "Delete Flashcards"
+
+**Contributions to DG:**
+
+- Added implementation details and UML diagrams for
+ `delete` command
+
+**Contributions to team-based tasks**
+
+- Helped refine details and correct small mistakes in the UG
+ and DG
\ No newline at end of file
diff --git a/docs/team/songzijin.md b/docs/team/songzijin.md
new file mode 100644
index 0000000000..0ffeec0f01
--- /dev/null
+++ b/docs/team/songzijin.md
@@ -0,0 +1,52 @@
+# Song Zijin's Project Portfolio Page
+
+## Project: Clanki
+
+Clanki is a command line application one can use to store and update flashcards to help
+them remember the relevant concepts. The user can interact with it by typing code into
+their terminal.
+
+Given below are my contribution to the project.
+
+### Summary of Contributions
+
+- **New feature:** Added ability to add a new command
+ - What it does: allows the user to add a new flashcard with the specified question and answer text.
+ - Justification: This feature is one of the basic building block of the entire program, without it, no
+ other command on modifying the flashcards can work.
+ - Highlights: Since it is the initial step, building this feature included setting the groundwork in other basic
+ classes, such as creating the abstract Command class for other commands to use and FlashcardList class to
+ handle the flashcards that are created. JUnit test codes are also written for this feature.
+ - Credits: idea for the entire structure are reused from Song Zijin's individual project
+ (link [here](https://github.com/SongZijin/ip)), with many inspiration from
+ [addressbook-level2](https://github.com/se-edu/addressbook-level2).
+- **New feature:** Added ability to save current state of all flashcards and load the state upon application start
+ - What it does: allows the user to store the previously recorded flashcards,
+ so that they can come back to review it in another day.
+ - Justification: This feature improves the product significantly, as the user can close the application
+ and come back in another day to review the flashcards they wish to learn.
+ - Highlights: The entire function consist of 3 parts, the encoder, decoder and a class that link the other two
+ together. The feature is also flexible when the stored data is corrupted, with the incorrectly formatted line
+ automatically deleted instead of crushing the whole program.
+ - Credits: the code is improved on the storage code from
+ [addressbook-level2](https://github.com/se-edu/addressbook-level2).
+- **New feature:** Added a help menu
+ - What it does: provides the user some information about what are the commands they can use for this application.
+ - Justification: This feature improves the product, as the user is now able to use the product independently,
+ without the need to look up any user guide of the product.
+
+- **Code contributed:**
+ [RepoSense link](https://nus-cs2113-ay2223s2.github.io/tp-dashboard/?search=&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2023-02-17&tabOpen=true&tabType=authorship&zFR=false&tabAuthor=SongZijin&tabRepo=AY2223S2-CS2113-T15-4%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false)
+
+- **Contributions to UG:**
+ - Added section under Quick Start.
+ - Provided initial draft to `add` function.
+ - Added documentation for `bye` and `help` functions.
+- **Contributions to DG:**
+ - Added implementation details for the `add` function.
+ - Added implementation details for storage function.
+ - Added user stories
+
+- Other contributions:
+ - Reviewed pull requests, such as [#53](https://github.com/AY2223S2-CS2113-T15-4/tp/pull/53)
+ - Posted issues, such as [#61](https://github.com/AY2223S2-CS2113-T15-4/tp/issues/61)
diff --git a/docs/umlDiagrams/AddFlashcard.puml b/docs/umlDiagrams/AddFlashcard.puml
new file mode 100644
index 0000000000..358c4ed4eb
--- /dev/null
+++ b/docs/umlDiagrams/AddFlashcard.puml
@@ -0,0 +1,52 @@
+@startuml
+'https://plantuml.com/sequence-diagram
+
+participant "<> :Clanki" as Main
+participant ":Ui" as Ui
+participant ":Parser" as Parser
+participant ":Add Command" as AddCommand
+participant ":Flashcard List" as FlashcardList
+
+activate Main
+Main -> Ui : User input
+activate Ui
+
+Ui -> Main : userInput : String
+deactivate Ui
+
+Main -> Parser : parseInput(userInput)
+activate Parser
+
+create AddCommand
+Parser -> AddCommand : AddCommand(question, answer)
+activate AddCommand
+
+AddCommand --> Parser
+deactivate AddCommand
+
+Parser -> Main : command : AddCommand
+deactivate Parser
+
+Main -> AddCommand : execute()
+activate AddCommand
+
+AddCommand -> FlashcardList : addNewFlashcard(question, answer)
+activate FlashcardList
+
+FlashcardList --> AddCommand
+deactivate FlashcardList
+
+AddCommand -> Ui : printSuccessfulAddMessage(question, answer)
+activate Ui
+
+
+Ui --> AddCommand
+deactivate Ui
+
+AddCommand --> Main
+deactivate AddCommand
+destroy AddCommand
+
+
+
+@enduml
\ No newline at end of file
diff --git a/docs/umlDiagrams/AddFlashcardClassDiagram.png b/docs/umlDiagrams/AddFlashcardClassDiagram.png
new file mode 100644
index 0000000000..481f48bf70
Binary files /dev/null and b/docs/umlDiagrams/AddFlashcardClassDiagram.png differ
diff --git a/docs/umlDiagrams/AddFlashcardClassDiagram.puml b/docs/umlDiagrams/AddFlashcardClassDiagram.puml
new file mode 100644
index 0000000000..c6457df7a7
--- /dev/null
+++ b/docs/umlDiagrams/AddFlashcardClassDiagram.puml
@@ -0,0 +1,20 @@
+@startuml
+'https://plantuml.com/sequence-diagram
+abstract Command
+Command : execute(flashcardList: FlashcardList, display: Ui) : void
+class AddCommand
+AddCommand : execute(flashcardList: FlashcardList, display: Ui) : void
+class Ui
+Ui : printSuccessfulAddMessage() : void
+class FlashcardList
+FlashcardList : addNewFlashcard(questionText : String, answerText : String) : void
+class Flashcard
+class Parser
+Parser : parseCommands (userInput : String) : Command
+
+Command <|-- AddCommand
+AddCommand ..> Ui : calls >
+AddCommand ..> FlashcardList : calls >
+Parser ..> AddCommand : creates >
+FlashcardList --> "*" Flashcard
+@enduml
\ No newline at end of file
diff --git a/docs/umlDiagrams/AddFlashcardObjectDiagram.png b/docs/umlDiagrams/AddFlashcardObjectDiagram.png
new file mode 100644
index 0000000000..941cf2db29
Binary files /dev/null and b/docs/umlDiagrams/AddFlashcardObjectDiagram.png differ
diff --git a/docs/umlDiagrams/AddFlashcardObjectDiagram.puml b/docs/umlDiagrams/AddFlashcardObjectDiagram.puml
new file mode 100644
index 0000000000..eba65be996
--- /dev/null
+++ b/docs/umlDiagrams/AddFlashcardObjectDiagram.puml
@@ -0,0 +1,9 @@
+@startuml
+object ":Parser" as Parser
+object "command :AddCommand" as AddCommand {
+ questionText :String
+ answerText :String
+}
+
+Parser -> AddCommand : creates >
+@enduml
\ No newline at end of file
diff --git a/docs/umlDiagrams/AddFlashcardSequence.png b/docs/umlDiagrams/AddFlashcardSequence.png
new file mode 100644
index 0000000000..c20b3c8c66
Binary files /dev/null and b/docs/umlDiagrams/AddFlashcardSequence.png differ
diff --git a/docs/umlDiagrams/DeleteFlashcard.png b/docs/umlDiagrams/DeleteFlashcard.png
new file mode 100644
index 0000000000..3c9007ca58
Binary files /dev/null and b/docs/umlDiagrams/DeleteFlashcard.png differ
diff --git a/docs/umlDiagrams/DeleteFlashcard.puml b/docs/umlDiagrams/DeleteFlashcard.puml
new file mode 100644
index 0000000000..5497354995
--- /dev/null
+++ b/docs/umlDiagrams/DeleteFlashcard.puml
@@ -0,0 +1,63 @@
+@startuml
+'https://plantuml.com/class-diagram
+
+!define LOGIC_COLOR #3333C4
+!define LOGIC_COLOR_T1 #7777DB
+!define LOGIC_COLOR_T2 #5252CE
+!define LOGIC_COLOR_T3 #1616B0
+!define LOGIC_COLOR_T4 #101086
+
+participant "<> :Clanki" as Main
+participant ":Ui" as Ui
+participant ":Parser" as Parser
+participant ":Delete Command" as DeleteCommand
+participant ":Flashcard List" as FlashcardList
+
+activate Main
+Main -> Ui : User input
+activate Ui
+
+Ui -> Main : userInput : String
+deactivate Ui
+
+Main -> Parser : parseInput(userInput)
+activate Parser
+
+create DeleteCommand
+Parser -> DeleteCommand :DeleteCommand(query)
+activate DeleteCommand
+
+DeleteCommand -> Parser
+deactivate DeleteCommand
+
+Parser -> Main : command : DeleteCommand
+deactivate Parser
+
+Main -> DeleteCommand : execute()
+activate DeleteCommand
+
+DeleteCommand -> FlashcardList : queryFlashcards(query)
+activate FlashcardList
+
+FlashcardList -> DeleteCommand : matchingFlashcards : ArrayList
+deactivate FlashcardList
+
+DeleteCommand -> FlashcardList : getFlashcards()
+activate FlashcardList
+
+FlashcardList -> DeleteCommand
+deactivate FlashcardList
+
+DeleteCommand -> Ui : printSuccessfulDelete(index)
+activate Ui
+
+Ui -> Main : Display Message
+Ui -> DeleteCommand
+deactivate Ui
+
+DeleteCommand -> Main
+deactivate DeleteCommand
+
+
+
+@enduml
\ No newline at end of file
diff --git a/docs/umlDiagrams/DeleteFlashcardClassDiagram.png b/docs/umlDiagrams/DeleteFlashcardClassDiagram.png
new file mode 100644
index 0000000000..487015fd26
Binary files /dev/null and b/docs/umlDiagrams/DeleteFlashcardClassDiagram.png differ
diff --git a/docs/umlDiagrams/DeleteFlashcardClassDiagram.puml b/docs/umlDiagrams/DeleteFlashcardClassDiagram.puml
new file mode 100644
index 0000000000..95378d6ba7
--- /dev/null
+++ b/docs/umlDiagrams/DeleteFlashcardClassDiagram.puml
@@ -0,0 +1,23 @@
+@startuml
+
+'https://plantuml.com/class-diagram
+abstract Command
+Command : execute(flashcardList: FlashcardList, display: Ui) : void
+class DeleteCommand
+DeleteCommand : execute(flashcardList: FlashcardList, display: Ui) : void
+class Ui
+Ui : printSuccessfulDeleteMessage() : void
+class FlashcardList
+FlashcardList : queryFlashcards(query : String) : ArrayList
+FlashcardList : deleteFlashcard(index : int) : void
+class Flashcard
+class Parser
+Parser : parseCommands (userInput : String) : Command
+
+Command <|-- DeleteCommand
+DeleteCommand ..> Ui : calls >
+DeleteCommand ..> FlashcardList : calls >
+Parser ..> DeleteCommand : creates >
+FlashcardList --> "*" Flashcard
+
+@enduml
\ No newline at end of file
diff --git a/docs/umlDiagrams/ReviewFlashcard-1.png b/docs/umlDiagrams/ReviewFlashcard-1.png
new file mode 100644
index 0000000000..9d7d65d404
Binary files /dev/null and b/docs/umlDiagrams/ReviewFlashcard-1.png differ
diff --git a/docs/umlDiagrams/ReviewFlashcard.puml b/docs/umlDiagrams/ReviewFlashcard.puml
new file mode 100644
index 0000000000..9bef9ccc3e
--- /dev/null
+++ b/docs/umlDiagrams/ReviewFlashcard.puml
@@ -0,0 +1,92 @@
+@startuml
+'https://plantuml.com/sequence-diagram
+
+participant "<> :Clanki" as Main
+participant ":Ui" as Ui
+participant ":Parser" as Parser
+participant ":Review Command" as ReviewCommand
+participant ":Flashcard List" as FlashcardList
+participant ":Flashcard" as Flashcard
+participant ":FlashcardQueue" as FlashcardQueue
+
+activate Main
+Main -> Ui : User input
+activate Ui
+
+Ui -> Main : userInput : String
+
+Main -> Parser : parseInput(userInput)
+activate Parser
+
+create ReviewCommand
+Parser -> ReviewCommand : ReviewCommand()
+activate ReviewCommand
+
+ReviewCommand --> Parser
+deactivate ReviewCommand
+
+Parser --> Main : command : ReviewCommand
+deactivate Parser
+
+Main -> ReviewCommand : execute()
+activate ReviewCommand
+
+ReviewCommand -> FlashcardList : getFlashCards()
+activate FlashcardList
+
+create FlashcardQueue
+FlashcardList -> FlashcardQueue : FlashcardQueue()
+activate FlashcardQueue
+FlashcardQueue --> FlashcardList
+FlashcardList --> ReviewCommand
+deactivate FlashcardList
+
+alt flashcardQueue.isEmpty()
+ ReviewCommand -> Ui : printlnSeveralStrings("There are no cards available for review today.")
+ Ui -> ReviewCommand
+else
+ ReviewCommand -> Ui : printlnSeveralStrings("There are %d cards available for review today.", flashcardQueue.size())
+ Ui -> FlashcardQueue : size()
+ FlashcardQueue --> Ui : String
+ loop while (!flashcardQueue.isEmpty())
+
+ ReviewCommand -> FlashcardQueue : popFlashcard()
+ FlashcardQueue --> ReviewCommand : Flashcard
+ activate Flashcard
+ ReviewCommand -> Flashcard : getQuestion()
+ Flashcard --> ReviewCommand
+ ReviewCommand -> Ui : getUserCommand()
+ Ui --> ReviewCommand
+ ReviewCommand -> Flashcard : getAnswer()
+ Flashcard --> ReviewCommand
+ ReviewCommand -> Ui : getUserCommand()
+ Ui --> ReviewCommand : answerIfCorrect : String
+
+ alt answerIfCorrect.equals("y")
+ ReviewCommand -> Ui :printlnSeveralStrings("Great, you got it right!")
+ Ui --> ReviewCommand
+ ReviewCommand -> Flashcard : updateDueDateAfterCorrectAnswer()
+ Flashcard --> ReviewCommand
+ else answerIfCorrect.equals("n")
+ ReviewCommand -> Ui : printlnSeveralStrings("No worries, we will try again later today.")
+ Ui --> ReviewCommand
+ ReviewCommand -> Flashcard : updateDueDateAfterIncorrectAnswer()
+ Flashcard --> ReviewCommand
+ ReviewCommand -> FlashcardQueue : pushFlashcard(currentFlashcard)
+ FlashcardQueue -> ReviewCommand
+ else
+ ReviewCommand -> Ui : printInvalidInput()
+ Ui --> ReviewCommand
+ end
+ deactivate Flashcard
+ end
+ destroy FlashcardQueue
+end
+
+ReviewCommand -> Ui : printlnSeveralStrings("Congrats! You have reviewed all the flashcards due today!")
+Ui --> ReviewCommand
+deactivate Ui
+ReviewCommand -> Main
+deactivate ReviewCommand
+
+@enduml
\ No newline at end of file
diff --git a/docs/umlDiagrams/StorageClassDiagm.png b/docs/umlDiagrams/StorageClassDiagm.png
new file mode 100644
index 0000000000..0cf7869b72
Binary files /dev/null and b/docs/umlDiagrams/StorageClassDiagm.png differ
diff --git a/docs/umlDiagrams/StorageClassDiagm.puml b/docs/umlDiagrams/StorageClassDiagm.puml
new file mode 100644
index 0000000000..b44fb5076e
--- /dev/null
+++ b/docs/umlDiagrams/StorageClassDiagm.puml
@@ -0,0 +1,27 @@
+@startuml
+class StorageFile {
+ + path : Path
+ + StorageFile()
+ + save( flashcardList : FlashcardList) : void
+ + load() : ArrayList
+}
+class FlashcardEncoder {
+ + {static} encodeFlashcardList( toSave : FlashcardList ) : List
+ - {static} encodeFlashcardToString( flashcard : Flashcard) : String
+}
+class FlashcardDecoder {
+ + {static} decodeFlashcardList( encodedFlashcardList : List) : ArrayList
+ - {static} decodeFlashcardFromString ( encodedFlashcard : String) : Flashcard
+}
+class Clanki {
+ - storageFile : StorageFile
+ + Clanki()
+ + {static} main (args : String[]) : void
+ + run() : void
+}
+
+Clanki *--> "1" StorageFile : controls >
+StorageFile --> FlashcardDecoder : uses >
+StorageFile --> FlashcardEncoder : uses >
+
+@enduml
\ No newline at end of file
diff --git a/docs/umlDiagrams/StorageSequenceDiagram.png b/docs/umlDiagrams/StorageSequenceDiagram.png
new file mode 100644
index 0000000000..2c2cd27533
Binary files /dev/null and b/docs/umlDiagrams/StorageSequenceDiagram.png differ
diff --git a/docs/umlDiagrams/StorageSequenceDiagram.puml b/docs/umlDiagrams/StorageSequenceDiagram.puml
new file mode 100644
index 0000000000..ac19f68d33
--- /dev/null
+++ b/docs/umlDiagrams/StorageSequenceDiagram.puml
@@ -0,0 +1,65 @@
+@startuml
+participant "<> :Clanki" as Main
+participant "storageFile :StorageFile" as Storage
+participant "<> :FlashcardListDecoder" as Decoder
+participant "<> :FlashcardListEncoder" as Encoder
+
+
+activate Main
+create Storage
+Main -> Storage : StorageFile()
+activate Storage
+Storage --> Main
+deactivate Storage
+
+Main -> Storage : load()
+activate Storage
+Storage -> Decoder : decodeFlashcardList(Files.readAllLines(path) :List)
+activate Decoder
+loop for all String in List
+ Decoder -> Decoder : decodeFlashcardFromString(encodedFlashcard :String)
+ activate Decoder
+ opt String does not match pattern
+ Decoder -> Decoder : :StorageOperationException(ENCODED_FLASHCARD_IN_INVALID_FORMAT_UNABLE_TO_DECODE);
+ end
+ Decoder -> Decoder : :Flashcard
+ deactivate Decoder
+end
+Decoder -> Storage : flashcards : ArrayList
+deactivate Decoder
+Storage -> Main : flashcards : ArrayList
+deactivate Storage
+
+Main -> Main : run()
+activate Main
+loop command instanceof ByeCommand
+ <- Main : getUserCommand()
+ -> Main : inputText :String
+ <- Main : parseCommand(inputText)
+ -> Main : command :Command
+ <- Main : execute()
+ --> Main
+ Main -> Storage : save()
+ activate Storage
+ Storage -> Encoder : encodeFlashcardList(flashcardList :FlashcardList)
+ activate Encoder
+ loop for all Flashcard in ArrayList from flashcardlist
+ Encoder -> Encoder : decodeFlashcardFromString(encodedFlashcard :String)
+ activate Encoder
+ Encoder -> Encoder : :String
+ deactivate Encoder
+ end
+ Encoder -> Storage : encodedFlashcardList :FlashcardList
+ deactivate Encoder
+ Storage --> Main
+ deactivate Storage
+end
+Main --> Main
+deactivate Main
+
+
+
+
+
+
+@enduml
\ No newline at end of file
diff --git a/docs/umlDiagrams/UpdateFlashcard.png b/docs/umlDiagrams/UpdateFlashcard.png
new file mode 100644
index 0000000000..6cf1c4214d
Binary files /dev/null and b/docs/umlDiagrams/UpdateFlashcard.png differ
diff --git a/docs/umlDiagrams/UpdateFlashcard.puml b/docs/umlDiagrams/UpdateFlashcard.puml
new file mode 100644
index 0000000000..2d5f18aa3d
--- /dev/null
+++ b/docs/umlDiagrams/UpdateFlashcard.puml
@@ -0,0 +1,70 @@
+@startuml
+'https://plantuml.com/sequence-diagram
+
+title UpdateCommand Sequence Diagram
+
+participant "<> :Clanki" as Main
+participant ":UpdateCommand" as UpdateCommand
+participant ":Ui" as Ui
+participant ":Parser" as Parser
+participant ":Flashcard" as Flashcard
+participant ":Flashcard List" as FlashcardList
+participant ":ParsedInput" as ParsedInput
+
+Main -> Ui: User input
+activate Main
+activate Ui
+
+Ui -> Main : userInput :String
+deactivate Ui
+
+Main -> Parser : parseInput(userInput)
+activate Parser
+
+Parser -> UpdateCommand: UpdateCommand(query)
+activate UpdateCommand
+
+UpdateCommand -> Parser
+deactivate UpdateCommand
+
+Parser -> Main : command : UpdateCommand
+deactivate Parser
+
+Main -> UpdateCommand : execute()
+activate UpdateCommand
+
+UpdateCommand -> FlashcardList: queryFlashcards(query)
+activate FlashcardList
+
+FlashcardList -> UpdateCommand : matchingFlashcards
+deactivate FlashcardList
+
+UpdateCommand -> Ui: printFlashCards(matchingFlashcards)
+activate Ui
+
+Ui -> UpdateCommand
+deactivate Ui
+
+loop while not done
+ UpdateCommand -> Ui: getUserCommand()
+ activate Ui
+
+ Ui -> ParsedInput: ParsedInput(display.getUserCommand())
+ deactivate Ui
+ activate ParsedInput
+
+ ParsedInput -> UpdateCommand
+ deactivate ParsedInput
+
+ UpdateCommand -> Ui : printSuccessfulUpdateMessage(currentFlashcard)
+ activate Ui
+
+ Ui -> UpdateCommand
+ deactivate Ui
+
+ UpdateCommand -> Main
+ deactivate UpdateCommand
+ destroy UpdateCommand
+
+end
+@enduml
\ No newline at end of file
diff --git a/src/main/java/com/clanki/Clanki.java b/src/main/java/com/clanki/Clanki.java
new file mode 100644
index 0000000000..c4c8b4462c
--- /dev/null
+++ b/src/main/java/com/clanki/Clanki.java
@@ -0,0 +1,53 @@
+package com.clanki;
+
+import com.clanki.commands.ByeCommand;
+import com.clanki.commands.Command;
+import com.clanki.exceptions.InvalidStorageFilePathException;
+import com.clanki.exceptions.StorageOperationException;
+import com.clanki.objects.FlashcardList;
+import com.clanki.parser.Parser;
+import com.clanki.storage.StorageFile;
+import com.clanki.ui.Ui;
+
+public class Clanki {
+ private Ui ui;
+ private FlashcardList flashcardList;
+ private StorageFile storageFile;
+
+ public Clanki() {
+ try {
+ this.ui = new Ui();
+ this.storageFile = new StorageFile();
+ this.flashcardList = new FlashcardList(storageFile.load());
+ } catch (InvalidStorageFilePathException | StorageOperationException e) {
+ System.out.println(e.getMessage());
+ }
+ }
+
+ public static void main(String[] args) {
+ new Clanki().run();
+ }
+
+ /**
+ * Function that loop the process of getting an user input, processing the input
+ * and execute the command, until a ByeCommand is inputted.
+ */
+ public void run() {
+ ui.printWelcomeMessage();
+ ui.printSeparationLine();
+ while (true) {
+ String inputText = ui.getUserCommand();
+ Command command = Parser.parseCommand(inputText);
+ command.execute(flashcardList, ui);
+ try {
+ storageFile.save(flashcardList);
+ } catch (StorageOperationException e) {
+ System.out.println(e.getMessage());
+ }
+ if (command instanceof ByeCommand) {
+ return;
+ }
+ ui.printSeparationLine();
+ }
+ }
+}
diff --git a/src/main/java/com/clanki/commands/AddCommand.java b/src/main/java/com/clanki/commands/AddCommand.java
new file mode 100644
index 0000000000..50e6fe6dcc
--- /dev/null
+++ b/src/main/java/com/clanki/commands/AddCommand.java
@@ -0,0 +1,34 @@
+package com.clanki.commands;
+
+import com.clanki.objects.FlashcardList;
+import com.clanki.ui.Ui;
+
+/**
+ * A type of command that will instruct to add a new element into the ArrayList
+ * of flashcards.
+ */
+public class AddCommand extends Command {
+ String questionText;
+ String answerText;
+
+ public AddCommand(String questionText, String answerText) {
+ assert !questionText.isEmpty() : "question text should not be empty";
+ this.questionText = questionText.substring(0, 1).toUpperCase() + questionText.substring(1);
+ assert !answerText.isEmpty() : "answer text should not be empty";
+ this.answerText = answerText.substring(0, 1).toUpperCase() + answerText.substring(1);
+ }
+
+ /**
+ * For testing purposes only.
+ */
+ @Override
+ public String toString() {
+ return "Question to add: " + questionText + " Answer: " + answerText;
+ }
+
+ @Override
+ public void execute(FlashcardList flashcardList, Ui display) {
+ flashcardList.addNewFlashcard(questionText, answerText);
+ display.printSuccessfulAddMessage(questionText, answerText);
+ }
+}
diff --git a/src/main/java/com/clanki/commands/ByeCommand.java b/src/main/java/com/clanki/commands/ByeCommand.java
new file mode 100644
index 0000000000..cef9a57ee4
--- /dev/null
+++ b/src/main/java/com/clanki/commands/ByeCommand.java
@@ -0,0 +1,14 @@
+package com.clanki.commands;
+
+import com.clanki.objects.FlashcardList;
+import com.clanki.ui.Ui;
+
+/**
+ * A type of command that will indicate that the program is ready to close.
+ */
+public class ByeCommand extends Command {
+ @Override
+ public void execute(FlashcardList flashcardList, Ui display) {
+ display.printByeMessage();
+ }
+}
diff --git a/src/main/java/com/clanki/commands/ClearCommand.java b/src/main/java/com/clanki/commands/ClearCommand.java
new file mode 100644
index 0000000000..e062673aed
--- /dev/null
+++ b/src/main/java/com/clanki/commands/ClearCommand.java
@@ -0,0 +1,16 @@
+package com.clanki.commands;
+
+import com.clanki.objects.FlashcardList;
+import com.clanki.ui.Ui;
+
+/**
+ * The type of command when the user wishes to clear all stored flashcards.
+ */
+public class ClearCommand extends Command {
+
+ @Override
+ public void execute(FlashcardList flashcardList, Ui display) {
+ flashcardList.deleteAllFlashcards();
+ display.printClearMessage();
+ }
+}
diff --git a/src/main/java/com/clanki/commands/Command.java b/src/main/java/com/clanki/commands/Command.java
new file mode 100644
index 0000000000..1e843c8316
--- /dev/null
+++ b/src/main/java/com/clanki/commands/Command.java
@@ -0,0 +1,12 @@
+package com.clanki.commands;
+
+import com.clanki.objects.FlashcardList;
+import com.clanki.ui.Ui;
+
+/**
+ * Represents a command that is instructed for the program to conduct. A Command
+ * Object specifies what the type of command is with its subclasses.
+ */
+public abstract class Command {
+ public abstract void execute(FlashcardList flashcardList, Ui display);
+}
diff --git a/src/main/java/com/clanki/commands/DeleteCommand.java b/src/main/java/com/clanki/commands/DeleteCommand.java
new file mode 100644
index 0000000000..66494652e7
--- /dev/null
+++ b/src/main/java/com/clanki/commands/DeleteCommand.java
@@ -0,0 +1,54 @@
+package com.clanki.commands;
+
+import com.clanki.objects.Flashcard;
+import com.clanki.objects.FlashcardList;
+import com.clanki.ui.Ui;
+
+import java.util.ArrayList;
+
+/**
+ * A command that removes an element from the ArrayList of flashcards.
+ */
+public class DeleteCommand extends Command {
+ ArrayList matchingFlashcards;
+ String query;
+
+ public DeleteCommand(String query) {
+ this.matchingFlashcards = new ArrayList<>();
+ this.query = query;
+ }
+
+ @Override
+ public void execute(FlashcardList flashcardList, Ui display) {
+ matchingFlashcards = flashcardList.queryFlashcards(query);
+
+ if (matchingFlashcards.size() == 0) {
+ System.out.printf("Sorry! No flashcards matching \"%s\" was found. Please try again.\n",
+ query);
+ return;
+ }
+
+ System.out.printf("Found %d card(s) with query \"%s\":\n", matchingFlashcards.size(),
+ query);
+ display.printFlashCards(matchingFlashcards);
+ System.out.println("Which one do you want to delete?");
+
+ int index = -1;
+ while (index == -1) {
+ try {
+ index = Integer.parseInt(display.getUserCommand());
+ if (index < 1 || index > matchingFlashcards.size()) {
+ index = -1;
+ throw new Exception();
+ }
+ } catch (Exception e) {
+ System.out.println("Please enter a valid index value.");
+ }
+ }
+
+ ArrayList flashcards = flashcardList.getFlashCards();
+ int actualIndex = flashcards.indexOf(matchingFlashcards.get(index - 1));
+ flashcardList.deleteFlashcard(actualIndex);
+ display.printSuccessfulDelete(index);
+ }
+}
diff --git a/src/main/java/com/clanki/commands/HelpCommand.java b/src/main/java/com/clanki/commands/HelpCommand.java
new file mode 100644
index 0000000000..b4136cb91b
--- /dev/null
+++ b/src/main/java/com/clanki/commands/HelpCommand.java
@@ -0,0 +1,14 @@
+package com.clanki.commands;
+
+import com.clanki.objects.FlashcardList;
+import com.clanki.ui.Ui;
+
+/**
+ * A type of command that indicate that user want to display help menu.
+ */
+public class HelpCommand extends Command {
+ @Override
+ public void execute(FlashcardList flashcardList, Ui display) {
+ display.printHelpMessage();
+ }
+}
diff --git a/src/main/java/com/clanki/commands/ListCommand.java b/src/main/java/com/clanki/commands/ListCommand.java
new file mode 100644
index 0000000000..15ee74781d
--- /dev/null
+++ b/src/main/java/com/clanki/commands/ListCommand.java
@@ -0,0 +1,59 @@
+package com.clanki.commands;
+
+import com.clanki.objects.Flashcard;
+import com.clanki.objects.FlashcardList;
+import com.clanki.ui.Ui;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeParseException;
+import java.util.ArrayList;
+
+//@@author javienneYeo
+public class ListCommand extends Command {
+ String input;
+
+ public ListCommand(String input) {
+ super();
+ this.input = input;
+ }
+
+ private LocalDate convertDate(String dueDate) throws DateTimeParseException {
+ return LocalDate.parse(dueDate);
+ }
+
+ private void printFlashcards(ArrayList flashcards, Ui display) {
+ if (flashcards.size() == 0) {
+ System.out.println("Your list of flashcards is empty.");
+ return;
+ }
+ System.out.println("Here is your list of flashcards:");
+ display.printFlashCards(flashcards);
+ }
+
+ public void printFlashcardsOnDate(ArrayList flashcards, LocalDate date, Ui display) {
+ ArrayList flashcardsOnDate = new ArrayList<>();
+ for (int i = 0; i < flashcards.size(); i++) {
+ Flashcard currentFlashcard = flashcards.get(i);
+ if (currentFlashcard.getDueDate().equals(date)) {
+ flashcardsOnDate.add(currentFlashcard);
+ }
+ }
+ printFlashcards(flashcardsOnDate, display);
+ }
+
+ @Override
+ public void execute(FlashcardList flashcardList, Ui display) {
+ ArrayList flashcards = flashcardList.getFlashCards();
+ if (input.equals("all")) {
+ printFlashcards(flashcards, display);
+ return;
+ }
+
+ try {
+ LocalDate dueDate = convertDate(input);
+ printFlashcardsOnDate(flashcards, dueDate, display);
+ } catch (DateTimeParseException e) {
+ System.out.println("Please enter the date in the format: yyyy-mm-dd");
+ }
+ }
+}
diff --git a/src/main/java/com/clanki/commands/ReviewCommand.java b/src/main/java/com/clanki/commands/ReviewCommand.java
new file mode 100644
index 0000000000..0646f88fa9
--- /dev/null
+++ b/src/main/java/com/clanki/commands/ReviewCommand.java
@@ -0,0 +1,71 @@
+package com.clanki.commands;
+
+import com.clanki.objects.Flashcard;
+import com.clanki.objects.FlashcardList;
+import com.clanki.objects.FlashcardQueue;
+import com.clanki.ui.Ui;
+
+/**
+ * A type of command that will let user review the flashcards due today
+ */
+public class ReviewCommand extends Command {
+
+ public static final String CORRECT_ANSWER_STRING = "y";
+ public static final String INCORRECT_ANSWER_STRING = "n";
+ public static final String LINE_SEPARATOR = "---";
+
+ /**
+ * review the next flashcard in the queue, and update the due date of the flashcard accordingly
+ *
+ * @param display Ui object used for user interfacing
+ * @param flashcardQueue Queue object storing flashcards to be reviewed
+ */
+ private static void reviewNextFlashcard(Ui display, FlashcardQueue flashcardQueue) {
+ display.printlnSeveralStrings(LINE_SEPARATOR);
+ Flashcard currentFlashcard = flashcardQueue.popFlashcard();
+ System.out.printf("Q: %s (ENTER to view answer)", currentFlashcard.getQuestion());
+ display.getUserCommand();
+ display.printlnSeveralStrings(String.format("A: %s", currentFlashcard.getAnswer()));
+ String answerIfCorrect;
+ System.out.print("Did you get it right? (y/n) ");
+ while (true) {
+ answerIfCorrect = display.getUserCommand().toLowerCase();
+ if (answerIfCorrect.equals(CORRECT_ANSWER_STRING)) {
+ display.printlnSeveralStrings("Great, you got it right!");
+ currentFlashcard.updateDueDateAfterCorrectAnswer();
+ break;
+ } else if (answerIfCorrect.equals(INCORRECT_ANSWER_STRING)) {
+ display.printlnSeveralStrings("No worries, we will try again later today.");
+ currentFlashcard.updateDueDateAfterIncorrectAnswer();
+ flashcardQueue.pushFlashcard(currentFlashcard);
+ break;
+ } else {
+ System.out.print("Please enter 'y' or 'n': (y/n) ");
+ }
+ }
+ }
+
+ /**
+ * Review all the flashcards that are due today, and update their due dates accordingly
+ *
+ * @param flashcardList List of all flashcards, regardless of their due dates
+ * @param display Ui object used for user interfacing
+ */
+ @Override
+ public void execute(FlashcardList flashcardList, Ui display) {
+ // Get a queue of flashcards that are due today
+ FlashcardQueue flashcardQueue = new FlashcardQueue(flashcardList.getFlashCards());
+
+ if (flashcardQueue.isEmpty()) {
+ display.printlnSeveralStrings("There are no cards available for review today.");
+ return;
+ }
+ display.printlnSeveralStrings(String
+ .format("There are %d cards available for review today.", flashcardQueue.size()));
+ while (!flashcardQueue.isEmpty()) {
+ reviewNextFlashcard(display, flashcardQueue);
+ }
+ display.printlnSeveralStrings(LINE_SEPARATOR,
+ "Congrats! You have reviewed all the flashcards due today!");
+ }
+}
diff --git a/src/main/java/com/clanki/commands/UnknownCommand.java b/src/main/java/com/clanki/commands/UnknownCommand.java
new file mode 100644
index 0000000000..832e3e0578
--- /dev/null
+++ b/src/main/java/com/clanki/commands/UnknownCommand.java
@@ -0,0 +1,14 @@
+package com.clanki.commands;
+
+import com.clanki.objects.FlashcardList;
+import com.clanki.ui.Ui;
+
+/**
+ * The type of command that indicates user have keyed in an invalid input.
+ */
+public class UnknownCommand extends Command {
+ @Override
+ public void execute(FlashcardList flashcardList, Ui display) {
+ display.printInvalidInput();
+ }
+}
diff --git a/src/main/java/com/clanki/commands/UpdateCommand.java b/src/main/java/com/clanki/commands/UpdateCommand.java
new file mode 100644
index 0000000000..19e2a62866
--- /dev/null
+++ b/src/main/java/com/clanki/commands/UpdateCommand.java
@@ -0,0 +1,90 @@
+package com.clanki.commands;
+
+import com.clanki.exceptions.UpdatedContentIsEmptyException;
+import com.clanki.objects.Flashcard;
+import com.clanki.objects.FlashcardList;
+import com.clanki.parser.ParsedInput;
+import com.clanki.ui.Ui;
+
+import java.util.ArrayList;
+
+//@@author javienneyeo
+/**
+ * The UpdateCommand class represents a command that updates an existing
+ * flashcard in the flashcard list. The command allows users to change the
+ * question, answer or date of the flashcard.
+ *
+ */
+public class UpdateCommand extends Command {
+ private static final String QUESTION_OPTION_IDENTIFIER = "q";
+ private static final String ANSWER_OPTION_IDENTIFIER = "a";
+
+ String query;
+ ArrayList matchingFlashcards;
+
+ public UpdateCommand(String query) {
+ this.matchingFlashcards = new ArrayList<>();
+ this.query = query;
+ }
+
+ /**
+ * Prompts the user for input and updates the specified flashcard based on the
+ * user's input.
+ *
+ * @param display the Ui object for displaying messages to the user
+ */
+ private void runUpdateFlashcard(Ui display) {
+ boolean isDone = false;
+ while (!isDone) {
+ try {
+ ParsedInput input = new ParsedInput(display.getUserCommand());
+ int index = Integer.parseInt(input.getCommand());
+ if (index < 1 || index > matchingFlashcards.size()) {
+ throw new Exception();
+ }
+
+ Flashcard currentFlashcard = matchingFlashcards.get(index - 1);
+
+ String questionText = input.getOptionByName(QUESTION_OPTION_IDENTIFIER);
+ String answerText = input.getOptionByName(ANSWER_OPTION_IDENTIFIER);
+
+ if (questionText == null && answerText == null) {
+ throw new UpdatedContentIsEmptyException();
+ }
+
+ if (questionText != null) {
+ currentFlashcard.setQuestion(questionText);
+ }
+
+ if (answerText != null) {
+ currentFlashcard.setAnswer(answerText);
+ }
+
+ display.printSuccessfulUpdateMessage(currentFlashcard);
+ isDone = true;
+ } catch (UpdatedContentIsEmptyException e) {
+ System.out.println("Please enter the changes to be modified.");
+ } catch (Exception e) {
+ System.out.println("Please enter a valid index value.");
+ }
+ }
+ }
+
+ @Override
+ public void execute(FlashcardList flashcardList, Ui display) {
+ matchingFlashcards = flashcardList.queryFlashcards(query);
+
+ if (matchingFlashcards.size() == 0) {
+ System.out.printf("No flashcards matching \"%s\" was found. Please try again.",
+ query);
+ return;
+ }
+
+ System.out.printf("Found %d card(s) with query \"%s\":\n", matchingFlashcards.size(),
+ query);
+ display.printFlashCards(matchingFlashcards);
+ System.out.println("Which one do you want to update?");
+
+ runUpdateFlashcard(display);
+ }
+}
diff --git a/src/main/java/com/clanki/commands/VoidCommand.java b/src/main/java/com/clanki/commands/VoidCommand.java
new file mode 100644
index 0000000000..c1c5c4294a
--- /dev/null
+++ b/src/main/java/com/clanki/commands/VoidCommand.java
@@ -0,0 +1,15 @@
+package com.clanki.commands;
+
+import com.clanki.objects.FlashcardList;
+import com.clanki.ui.Ui;
+
+/**
+ * The type of command that indicate an invalid input, with the display of why
+ * the input is invalid already handled elsewhere.
+ */
+public class VoidCommand extends Command {
+ @Override
+ public void execute(FlashcardList flashcardList, Ui display) {
+ return;
+ }
+}
diff --git a/src/main/java/com/clanki/exceptions/EmptyFlashcardAnswerException.java b/src/main/java/com/clanki/exceptions/EmptyFlashcardAnswerException.java
new file mode 100644
index 0000000000..31b538623a
--- /dev/null
+++ b/src/main/java/com/clanki/exceptions/EmptyFlashcardAnswerException.java
@@ -0,0 +1,4 @@
+package com.clanki.exceptions;
+
+public class EmptyFlashcardAnswerException extends Exception {
+}
diff --git a/src/main/java/com/clanki/exceptions/EmptyFlashcardQuestionException.java b/src/main/java/com/clanki/exceptions/EmptyFlashcardQuestionException.java
new file mode 100644
index 0000000000..828a9c517e
--- /dev/null
+++ b/src/main/java/com/clanki/exceptions/EmptyFlashcardQuestionException.java
@@ -0,0 +1,4 @@
+package com.clanki.exceptions;
+
+public class EmptyFlashcardQuestionException extends Exception {
+}
diff --git a/src/main/java/com/clanki/exceptions/InputIsEmptyException.java b/src/main/java/com/clanki/exceptions/InputIsEmptyException.java
new file mode 100644
index 0000000000..bd0ff94790
--- /dev/null
+++ b/src/main/java/com/clanki/exceptions/InputIsEmptyException.java
@@ -0,0 +1,4 @@
+package com.clanki.exceptions;
+
+public class InputIsEmptyException extends Exception {
+}
diff --git a/src/main/java/com/clanki/exceptions/InvalidAddFlashcardInputException.java b/src/main/java/com/clanki/exceptions/InvalidAddFlashcardInputException.java
new file mode 100644
index 0000000000..352ab97fed
--- /dev/null
+++ b/src/main/java/com/clanki/exceptions/InvalidAddFlashcardInputException.java
@@ -0,0 +1,4 @@
+package com.clanki.exceptions;
+
+public class InvalidAddFlashcardInputException extends Exception {
+}
diff --git a/src/main/java/com/clanki/exceptions/InvalidStorageFilePathException.java b/src/main/java/com/clanki/exceptions/InvalidStorageFilePathException.java
new file mode 100644
index 0000000000..4cff4e37ab
--- /dev/null
+++ b/src/main/java/com/clanki/exceptions/InvalidStorageFilePathException.java
@@ -0,0 +1,12 @@
+package com.clanki.exceptions;
+
+public class InvalidStorageFilePathException extends Exception {
+ /**
+ * Signals that the given file path does not fulfill the storage filepath
+ * constraints. Copied from:
+ * https://github.com/se-edu/addressbook-level2/blob/master/src/seedu/addressbook/storage/StorageFile.java
+ */
+ public InvalidStorageFilePathException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/com/clanki/exceptions/NoQueryInInputException.java b/src/main/java/com/clanki/exceptions/NoQueryInInputException.java
new file mode 100644
index 0000000000..72f3981da4
--- /dev/null
+++ b/src/main/java/com/clanki/exceptions/NoQueryInInputException.java
@@ -0,0 +1,4 @@
+package com.clanki.exceptions;
+
+public class NoQueryInInputException extends Exception {
+}
diff --git a/src/main/java/com/clanki/exceptions/StorageOperationException.java b/src/main/java/com/clanki/exceptions/StorageOperationException.java
new file mode 100644
index 0000000000..90482ea22d
--- /dev/null
+++ b/src/main/java/com/clanki/exceptions/StorageOperationException.java
@@ -0,0 +1,13 @@
+
+package com.clanki.exceptions;
+
+/**
+ * Signals that some error has occured while trying to convert and read/write
+ * data between the application and the storage file. Copied from:
+ * https://github.com/se-edu/addressbook-level2/blob/master/src/seedu/addressbook/storage/StorageFile.java
+ */
+public class StorageOperationException extends Exception {
+ public StorageOperationException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/com/clanki/exceptions/UpdatedContentIsEmptyException.java b/src/main/java/com/clanki/exceptions/UpdatedContentIsEmptyException.java
new file mode 100644
index 0000000000..1b40a02fda
--- /dev/null
+++ b/src/main/java/com/clanki/exceptions/UpdatedContentIsEmptyException.java
@@ -0,0 +1,4 @@
+package com.clanki.exceptions;
+
+public class UpdatedContentIsEmptyException extends Exception {
+}
diff --git a/src/main/java/com/clanki/objects/Flashcard.java b/src/main/java/com/clanki/objects/Flashcard.java
new file mode 100644
index 0000000000..7c1ec4ab2f
--- /dev/null
+++ b/src/main/java/com/clanki/objects/Flashcard.java
@@ -0,0 +1,92 @@
+package com.clanki.objects;
+
+import java.time.LocalDate;
+
+public class Flashcard {
+ private static final double SPACED_REPETITION_FACTOR = 1.5;
+ private String questionText;
+ private String answerText;
+ private LocalDate dueDate;
+ private int currentPeriodInDays;
+
+ public Flashcard(String questionText, String answerText) {
+ this.questionText = questionText;
+ this.answerText = answerText;
+ this.dueDate = LocalDate.now();
+ this.currentPeriodInDays = 0;
+ }
+
+ public Flashcard(String questionText, String answerText, LocalDate dueDate,
+ int currentPeriodInDays) {
+ this.questionText = questionText;
+ this.answerText = answerText;
+ this.dueDate = dueDate;
+ this.currentPeriodInDays = currentPeriodInDays;
+ }
+
+ public String getQuestion() {
+ return questionText;
+ }
+
+ public void setQuestion(String questionText) {
+ this.questionText = questionText;
+ }
+
+ public String getAnswer() {
+ return answerText;
+ }
+
+ public void setAnswer(String answerText) {
+ this.answerText = answerText;
+ }
+
+ public LocalDate getDueDate() {
+ return dueDate;
+ }
+
+ public void setDueDate(LocalDate dueDate) {
+ this.dueDate = dueDate;
+ }
+
+ public int getCurrentPeriodInDays() {
+ return currentPeriodInDays;
+ }
+
+ public boolean isDueToday() {
+ return this.dueDate.isEqual(LocalDate.now());
+ }
+
+ /**
+ * This function checks if the flashcard is due or overdue Sometimes the user
+ * may not clear all flashcards on time, so we may have cards overdue
+ *
+ * @return true if the card is due or overdue
+ */
+ public boolean isDueBeforeToday() {
+ return !LocalDate.now().isBefore(this.getDueDate());
+ }
+
+ /**
+ * Reset dueDate and currentPeriodInDays as if the
+ * card was just created.
+ */
+ public void updateDueDateAfterIncorrectAnswer() {
+ this.dueDate = LocalDate.now();
+ this.currentPeriodInDays = 0;
+ }
+
+ /**
+ * Update dueDate and currentPeriodInDays according to
+ * the spaced repetition algorithm. Which is very simple, to be honest, but it
+ * does the job.
+ */
+ public void updateDueDateAfterCorrectAnswer() {
+ if (this.currentPeriodInDays == 0) {
+ this.currentPeriodInDays = 1;
+ } else {
+ double newPeriod = this.currentPeriodInDays * SPACED_REPETITION_FACTOR;
+ this.currentPeriodInDays = (int) Math.ceil(newPeriod);
+ }
+ this.dueDate = this.dueDate.plusDays(this.currentPeriodInDays);
+ }
+}
diff --git a/src/main/java/com/clanki/objects/FlashcardList.java b/src/main/java/com/clanki/objects/FlashcardList.java
new file mode 100644
index 0000000000..8c2cf28530
--- /dev/null
+++ b/src/main/java/com/clanki/objects/FlashcardList.java
@@ -0,0 +1,56 @@
+package com.clanki.objects;
+
+import java.util.ArrayList;
+
+public class FlashcardList {
+ private final ArrayList flashcards;
+
+ public FlashcardList() {
+ this.flashcards = new ArrayList<>();
+ }
+
+ public FlashcardList(ArrayList flashcards) {
+ this.flashcards = flashcards;
+ }
+
+ /**
+ * Adds a new flashcard into the list.
+ *
+ * @param questionText Question text of the new flashcard.
+ * @param answerText Answer text of the new flashcard.
+ */
+ public void addNewFlashcard(String questionText, String answerText) {
+ Flashcard newFlashcard = new Flashcard(questionText, answerText);
+ flashcards.add(newFlashcard);
+ }
+
+ public ArrayList getFlashCards() {
+ return flashcards;
+ }
+
+ //@@author javienneyeo
+ /**
+ * Query for flashcards in the current deck that matches the query inside
+ * questions and answers (case-insensitive).
+ */
+ public ArrayList queryFlashcards(String query) {
+ ArrayList matchingFlashcards = new ArrayList<>();
+ String queryLowerCase = query.toLowerCase();
+ for (int i = 0; i < flashcards.size(); i++) {
+ Flashcard currentFlashcard = flashcards.get(i);
+ if (currentFlashcard.getQuestion().toLowerCase().contains(queryLowerCase)
+ || currentFlashcard.getAnswer().toLowerCase().contains(queryLowerCase)) {
+ matchingFlashcards.add(currentFlashcard);
+ }
+ }
+ return matchingFlashcards;
+ }
+
+ public void deleteFlashcard(int index) {
+ flashcards.remove(index);
+ }
+
+ public void deleteAllFlashcards() {
+ flashcards.clear();
+ }
+}
diff --git a/src/main/java/com/clanki/objects/FlashcardQueue.java b/src/main/java/com/clanki/objects/FlashcardQueue.java
new file mode 100644
index 0000000000..f0dc87f9a1
--- /dev/null
+++ b/src/main/java/com/clanki/objects/FlashcardQueue.java
@@ -0,0 +1,34 @@
+package com.clanki.objects;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.Queue;
+
+public class FlashcardQueue {
+ private final Queue flashcards;
+
+ public FlashcardQueue(ArrayList flashcards) {
+ this.flashcards = new LinkedList<>();
+ for (Flashcard flashcard : flashcards) {
+ if (flashcard.isDueBeforeToday()) {
+ this.flashcards.add(flashcard);
+ }
+ }
+ }
+
+ public void pushFlashcard(Flashcard flashcard) {
+ flashcards.add(flashcard);
+ }
+
+ public Flashcard popFlashcard() {
+ return flashcards.poll();
+ }
+
+ public boolean isEmpty() {
+ return flashcards.isEmpty();
+ }
+
+ public int size() {
+ return flashcards.size();
+ }
+}
diff --git a/src/main/java/com/clanki/parser/ParsedInput.java b/src/main/java/com/clanki/parser/ParsedInput.java
new file mode 100644
index 0000000000..5a8d13a97c
--- /dev/null
+++ b/src/main/java/com/clanki/parser/ParsedInput.java
@@ -0,0 +1,80 @@
+package com.clanki.parser;
+
+class Option {
+ private static final String BODY_SEPARATOR = " ";
+
+ private String name;
+ private String value;
+
+ public Option(String optionString) {
+ String[] parts = optionString.split(BODY_SEPARATOR, 2);
+ name = parts[0].trim();
+ value = parts.length > 1 ? parts[1].trim() : "";
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getValue() {
+ return value;
+ }
+}
+
+public class ParsedInput {
+ private static final String OPTION_INDICATOR = "/";
+ private static final String OPTION_SEPARATOR = " " + OPTION_INDICATOR;
+
+ private String command;
+ private String body;
+ private Option[] options;
+
+ public ParsedInput(String input) {
+ // Assuming input = "command blah blah /opt1 hello /opt2 world blah bleh"
+
+ // After trimming:
+ // parts = ["command blah blah", "opt1 hello", "opt2 world blah bleh"]
+ String[] parts = input.split(OPTION_SEPARATOR);
+ options = new Option[parts.length - 1];
+
+ for (int i = 0; i < parts.length; i++) {
+ Option option = new Option(parts[i].trim());
+ if (i == 0) {
+ command = option.getName();
+ body = option.getValue();
+ } else {
+ options[i - 1] = option;
+ }
+ }
+ }
+
+ public String getCommand() {
+ assert command != null;
+ return command;
+ }
+
+ public String getBody() {
+ return body;
+ }
+
+ /**
+ * Returns the value of the option with the given name.
+ *
+ * Example: "command blah blah /opt1 hello /opt2 world blah bleh" then
+ * getOptionByName("opt2") returns "world blah bleh" and getOptionByName("foo")
+ * returns null.
+ *
+ * @param name The name of the option.
+ * @return The (trimmed) value of the option, or null if the option does not
+ * exist. Be careful that the option can be an empty string. If an
+ * option is specified multiple times, the last one will be returned.
+ */
+ public String getOptionByName(String name) {
+ for (int i = options.length - 1; i >= 0; i--) {
+ if (options[i].getName().equals(name)) {
+ return options[i].getValue();
+ }
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/com/clanki/parser/Parser.java b/src/main/java/com/clanki/parser/Parser.java
new file mode 100644
index 0000000000..26b2198287
--- /dev/null
+++ b/src/main/java/com/clanki/parser/Parser.java
@@ -0,0 +1,142 @@
+package com.clanki.parser;
+
+import com.clanki.commands.AddCommand;
+import com.clanki.commands.ByeCommand;
+import com.clanki.commands.ClearCommand;
+import com.clanki.commands.Command;
+import com.clanki.commands.DeleteCommand;
+import com.clanki.commands.HelpCommand;
+import com.clanki.commands.ListCommand;
+import com.clanki.commands.ReviewCommand;
+import com.clanki.commands.UnknownCommand;
+import com.clanki.commands.UpdateCommand;
+import com.clanki.commands.VoidCommand;
+import com.clanki.exceptions.EmptyFlashcardAnswerException;
+import com.clanki.exceptions.EmptyFlashcardQuestionException;
+import com.clanki.exceptions.InputIsEmptyException;
+import com.clanki.exceptions.InvalidAddFlashcardInputException;
+import com.clanki.exceptions.NoQueryInInputException;
+
+public class Parser {
+ private static final String QUESTION_OPTION_IDENTIFIER = "q";
+ private static final String ANSWER_OPTION_IDENTIFIER = "a";
+
+ public static Command parseCommand(String userInput) {
+ try {
+ return parseCommandStrict(userInput);
+ } catch (InvalidAddFlashcardInputException e) {
+ System.out.println(
+ "The input is in an incorrect format, please follow the format in user guide");
+ } catch (EmptyFlashcardQuestionException e) {
+ System.out.println("The question of this card is empty, please enter one.");
+ } catch (EmptyFlashcardAnswerException e) {
+ System.out.println("The answer for this flashcard is empty, please enter one.");
+ } catch (NoQueryInInputException e) {
+ System.out.println("Please enter a query to be searched in the list of flashcards.");
+ } catch (InputIsEmptyException e) {
+ System.out.println("Please enter the command in the format shown in the user guide");
+ }
+ return new VoidCommand();
+ }
+
+ public static Command parseCommandStrict(String userInput)
+ throws InvalidAddFlashcardInputException, EmptyFlashcardQuestionException,
+ EmptyFlashcardAnswerException, NoQueryInInputException, InputIsEmptyException {
+ ParsedInput parsedInput = new ParsedInput(userInput);
+ String command = parsedInput.getCommand();
+ assert !command.isEmpty() : "The command string must not be empty";
+ switch (command) {
+ case "add":
+ return getAddCommand(parsedInput);
+ case "review":
+ return getReviewCommand(parsedInput);
+ case "update":
+ return getUpdateCommand(parsedInput);
+ case "delete":
+ return getDeleteCommand(parsedInput);
+ case "bye":
+ return getByeCommand(parsedInput);
+ case "help":
+ return getHelpCommand(parsedInput);
+ case "list":
+ return getListCommand(parsedInput);
+ case "clear":
+ return getClearCommand(parsedInput);
+ default:
+ return getUnknownCommand(parsedInput);
+ }
+ }
+
+ /**
+ * Constructs an AddCommand from the input of the user, if the input is of an
+ * incorrect format, a respective exception will be thrown.
+ *
+ * @param parsedInput The input collected by Ui from the user, after being
+ * parsed with the ParsedInput class.
+ * @return An AddCommand with the question and answer text extracted from user
+ * input
+ * @throws InvalidAddFlashcardInputException If the start indicators cannot be
+ * found.
+ * @throws EmptyFlashcardQuestionException If the string is empty after
+ * QUESTION_OPTION_IDENTIFIER.
+ * @throws EmptyFlashcardAnswerException If the string is empty after
+ * ANSWER_OPTION_IDENTIFIER.
+ */
+ public static AddCommand getAddCommand(ParsedInput parsedInput)
+ throws InvalidAddFlashcardInputException, EmptyFlashcardQuestionException,
+ EmptyFlashcardAnswerException {
+ String questionText = parsedInput.getOptionByName(QUESTION_OPTION_IDENTIFIER);
+ String answerText = parsedInput.getOptionByName(ANSWER_OPTION_IDENTIFIER);
+ if (questionText == null || answerText == null) {
+ throw new InvalidAddFlashcardInputException();
+ }
+ if (questionText.isEmpty()) {
+ throw new EmptyFlashcardQuestionException();
+ }
+ if (answerText.isEmpty()) {
+ throw new EmptyFlashcardAnswerException();
+ }
+ return new AddCommand(questionText, answerText);
+ }
+
+ public static ReviewCommand getReviewCommand(ParsedInput parsedInput) {
+ return new ReviewCommand();
+ }
+
+ public static UpdateCommand getUpdateCommand(ParsedInput parsedInput)
+ throws NoQueryInInputException {
+ String query = parsedInput.getBody();
+ if (query.isEmpty()) {
+ throw new NoQueryInInputException();
+ }
+ return new UpdateCommand(query);
+ }
+
+ public static DeleteCommand getDeleteCommand(ParsedInput parsedInput) {
+ return new DeleteCommand(parsedInput.getBody());
+ }
+
+ public static ListCommand getListCommand(ParsedInput parsedInput) throws InputIsEmptyException {
+ String date = parsedInput.getBody();
+ if (date.isEmpty()) {
+ throw new InputIsEmptyException();
+ }
+ return new ListCommand(parsedInput.getBody());
+ }
+
+ public static ByeCommand getByeCommand(ParsedInput parsedInput) {
+ return new ByeCommand();
+ }
+
+ public static HelpCommand getHelpCommand(ParsedInput parsedInput) {
+ return new HelpCommand();
+ }
+
+ public static ClearCommand getClearCommand(ParsedInput parsedInput) {
+ return new ClearCommand();
+ }
+
+ public static UnknownCommand getUnknownCommand(ParsedInput parsedInput) {
+ return new UnknownCommand();
+ }
+}
diff --git a/src/main/java/com/clanki/storage/FlashcardListDecoder.java b/src/main/java/com/clanki/storage/FlashcardListDecoder.java
new file mode 100644
index 0000000000..d155d0c88d
--- /dev/null
+++ b/src/main/java/com/clanki/storage/FlashcardListDecoder.java
@@ -0,0 +1,63 @@
+package com.clanki.storage;
+
+import com.clanki.exceptions.StorageOperationException;
+import com.clanki.objects.Flashcard;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * This class modifies the list of flashcards from stored text into an ArrayList
+ * in FlashcardList. Many portions of the code in this class are modified from
+ * the link below: (addressbook-level2)
+ * https://github.com/se-edu/addressbook-level2/blob/master/src/seedu/addressbook/storage/AddressBookDecoder.java
+ */
+public class FlashcardListDecoder {
+ // '/' forward slashes are reserved for delimiter prefixes
+ public static final Pattern FLASHCARD_ARGS_FORMAT = Pattern.compile("q/(?[^/]+)"
+ + " a/(?[^/]+)" + " d/(?[^/]+)" + " p/(?[^/]+)");
+ public static final String GROUP_QUESTION = "question";
+ public static final String GROUP_ANSWER = "answer";
+ public static final String GROUP_DUE_DATE = "dueDate";
+ public static final String GROUP_CURRENT_PERIOD = "currentPeriod";
+
+ /**
+ * Decodes {@code encodedFlashcardList} into an {@code ArrayList}
+ * containing the decoded flashcards.
+ *
+ * @throws StorageOperationException if the {@code encodedFlashcardList} is in
+ * an invalid format.
+ */
+ public static ArrayList decodeFlashcardList(List encodedFlashcardList) {
+ final ArrayList decodedFlashcard = new ArrayList<>();
+ for (String encodedFlashcard : encodedFlashcardList) {
+ try {
+ decodedFlashcard.add(decodeFlashcardFromString(encodedFlashcard));
+ } catch (StorageOperationException e) {
+ System.out.println("A flashcard is formatted incorrectly, they will be deleted.");
+ }
+ }
+ return decodedFlashcard;
+ }
+
+ /**
+ * Decodes {@code encodedFlashcard} into a {@code Flashcard}.
+ *
+ * @throws StorageOperationException if {@code encodedFlashcard} is in an
+ * invalid format.
+ */
+ private static Flashcard decodeFlashcardFromString(String encodedFlashcard)
+ throws StorageOperationException {
+ final Matcher matcher = FLASHCARD_ARGS_FORMAT.matcher(encodedFlashcard);
+ if (!matcher.matches()) {
+ throw new StorageOperationException(
+ "Encoded flashcard in invalid format. Unable to decode.");
+ }
+ return new Flashcard(matcher.group(GROUP_QUESTION), matcher.group(GROUP_ANSWER),
+ LocalDate.parse(matcher.group(GROUP_DUE_DATE)),
+ Integer.parseInt(matcher.group(GROUP_CURRENT_PERIOD)));
+ }
+}
diff --git a/src/main/java/com/clanki/storage/FlashcardListEncoder.java b/src/main/java/com/clanki/storage/FlashcardListEncoder.java
new file mode 100644
index 0000000000..4fa28d3e2e
--- /dev/null
+++ b/src/main/java/com/clanki/storage/FlashcardListEncoder.java
@@ -0,0 +1,46 @@
+package com.clanki.storage;
+
+import com.clanki.objects.Flashcard;
+import com.clanki.objects.FlashcardList;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class modifies the list of flashcards from FlashcardList into a storable
+ * format. Many portions of the code in this class are modified from the link
+ * below: (addressbook-level2)
+ * https://github.com/se-edu/addressbook-level2/blob/master/src/seedu/addressbook/storage/AddressBookEncoder.java
+ */
+public class FlashcardListEncoder {
+ public static final String QUESTION_APPEND = "q/";
+ public static final String ANSWER_APPEND = " a/";
+ public static final String DUE_DATE_APPEND = " d/";
+ public static final String PERIOD_APPEND = " p/";
+
+ /**
+ * Encodes all the {@code Flashcards} in the {@code toSave} into a list of
+ * decodable and readable string presentation for storage.
+ */
+ public static List encodeFlashcardList(FlashcardList toSave) {
+ final List encodedFlashcards = new ArrayList<>();
+ toSave.getFlashCards()
+ .forEach(flashcard -> encodedFlashcards.add(encodeFlashcardToString(flashcard)));
+ return encodedFlashcards;
+ }
+
+ /**
+ * Encodes the {@code flashcard} into a decodable and readable string
+ * representation.
+ */
+ private static String encodeFlashcardToString(Flashcard flashcard) {
+ final StringBuilder encodedFlashcardBuilder = new StringBuilder();
+
+ encodedFlashcardBuilder.append(QUESTION_APPEND).append(flashcard.getQuestion());
+ encodedFlashcardBuilder.append(ANSWER_APPEND).append(flashcard.getAnswer());
+ encodedFlashcardBuilder.append(DUE_DATE_APPEND).append(flashcard.getDueDate().toString());
+ encodedFlashcardBuilder.append(PERIOD_APPEND).append(flashcard.getCurrentPeriodInDays());
+
+ return encodedFlashcardBuilder.toString();
+ }
+}
diff --git a/src/main/java/com/clanki/storage/StorageFile.java b/src/main/java/com/clanki/storage/StorageFile.java
new file mode 100644
index 0000000000..033826c1fb
--- /dev/null
+++ b/src/main/java/com/clanki/storage/StorageFile.java
@@ -0,0 +1,92 @@
+package com.clanki.storage;
+
+import com.clanki.exceptions.InvalidStorageFilePathException;
+import com.clanki.exceptions.StorageOperationException;
+import com.clanki.objects.Flashcard;
+import com.clanki.objects.FlashcardList;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class manages the storing of FlashcardList and the recovering of stored
+ * data each time the application first start up. Many portions of the code in
+ * this class are modified from the link below: (addressbook-level2)
+ * https://github.com/se-edu/addressbook-level2/blob/master/src/seedu/addressbook/storage/StorageFile.java
+ */
+public class StorageFile {
+ /**
+ * Default file path used if the user doesn't provide the file name.
+ */
+ public static final String DEFAULT_STORAGE_FILEPATH = "flashcardList.txt";
+ public static final String FILE_END_STRING = ".txt";
+
+ public final Path path;
+
+ /**
+ * @throws InvalidStorageFilePathException if the default path is invalid
+ */
+ public StorageFile() throws InvalidStorageFilePathException {
+ this(DEFAULT_STORAGE_FILEPATH);
+ }
+
+ /**
+ * @throws InvalidStorageFilePathException if the given file path is invalid
+ */
+ public StorageFile(String filePath) throws InvalidStorageFilePathException {
+ path = Paths.get(filePath);
+ if (!isValidPath(path)) {
+ throw new InvalidStorageFilePathException("Storage file should end with '.txt'");
+ }
+ }
+
+ /**
+ * Returns true if the given path is acceptable as a storage file. The file path
+ * is considered acceptable if it ends with '.txt'
+ */
+ private static boolean isValidPath(Path filePath) {
+ return filePath.toString().endsWith(FILE_END_STRING);
+ }
+
+ /**
+ * Saves the {@code addressBook} data to the storage file.
+ *
+ * @throws StorageOperationException if there were errors converting and/or
+ * storing data to file.
+ */
+ public void save(FlashcardList flashcardList) throws StorageOperationException {
+ try {
+ List encodedFlashcardList = FlashcardListEncoder
+ .encodeFlashcardList(flashcardList);
+ Files.write(path, encodedFlashcardList);
+ } catch (IOException ioe) {
+ throw new StorageOperationException("Error writing to file: " + path);
+ }
+ }
+
+ /**
+ * Loads the {@code AddressBook} data from this storage file, and then returns
+ * it. Returns an empty {@code AddressBook} if the file does not exist, or is
+ * not a regular file.
+ *
+ * @throws StorageOperationException if there were errors reading and/or
+ * converting data from file.
+ */
+ public ArrayList load() throws StorageOperationException {
+ if (!Files.exists(path) || !Files.isRegularFile(path)) {
+ return new ArrayList<>();
+ }
+ try {
+ return FlashcardListDecoder.decodeFlashcardList(Files.readAllLines(path));
+ } catch (FileNotFoundException fnfe) {
+ throw new AssertionError("A non-existent file scenario is already handled earlier.");
+ } catch (IOException ioe) {
+ throw new StorageOperationException("Error writing to file: " + path);
+ }
+ }
+}
diff --git a/src/main/java/com/clanki/ui/Ui.java b/src/main/java/com/clanki/ui/Ui.java
new file mode 100644
index 0000000000..1f417c846f
--- /dev/null
+++ b/src/main/java/com/clanki/ui/Ui.java
@@ -0,0 +1,139 @@
+package com.clanki.ui;
+
+import com.clanki.objects.Flashcard;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Scanner;
+
+/**
+ * Parts of the code below are copied and adapted from TextUi.java of
+ * addressbook-level2, link of original code:
+ * https://github.com/se-edu/addressbook-level2/blob/master/src/seedu/addressbook/ui/TextUi.java.
+ */
+public class Ui {
+ private final Scanner in;
+
+ public Ui() {
+ this(System.in);
+ }
+
+ public Ui(InputStream in) {
+ this.in = new Scanner(in);
+ }
+
+ public String getUserCommand() {
+ return in.nextLine();
+ }
+
+ /**
+ * Display messages to inform user that a flashcard have been successfully
+ * added.
+ *
+ * @param questionText The question of the new flashcard.
+ * @param answerText The answer of the new flashcard.
+ */
+ public void printSuccessfulAddMessage(String questionText, String answerText) {
+ System.out.println("You have added the following card:");
+ System.out.println("Q: " + questionText);
+ System.out.println("A: " + answerText);
+ }
+
+ public void printByeMessage() {
+ System.out.println("Bye. Don't forget to come back later to study!");
+ }
+
+ public void printWelcomeMessage() {
+ System.out.println("Welcome to Clanki! Time to start studying!");
+ }
+
+ /**
+ * Display message to inform user that their input is of a wrong format.
+ */
+ public void printInvalidInput() {
+ System.out.println("The input is in an incorrect format.\n"
+ + "You can view our user guide or type help to see the correct formats for commands.\n");
+ }
+
+ /**
+ * Prints out a message to indicate a successful deletion of a flashcard
+ *
+ * @param index the index of the flashcard at the point of deletion
+ */
+ public void printSuccessfulDelete(int index) {
+ System.out.println("Got it. Deleted the flashcard at index " + index);
+ }
+
+ /**
+ * reused from Kong Dehao ip for general printing to console
+ *
+ * @param message multiple strings to be shown to user
+ */
+ public void printlnSeveralStrings(String... message) {
+ for (String m : message) {
+ System.out.println(m);
+ }
+ }
+
+ /**
+ * Display all the current valid commands the user can key in.
+ */
+ public void printHelpMessage() {
+ System.out.println("The following are the commands you can use:\n"
+ + "add Adds a flashcard to the current list of flashcards.\n"
+ + " Parameters: add /q QUESTION /a ANSWER\n"
+ + " Example: add /q What is the worst fruit? /a Durian\n"
+ + "update Changes the content of flashcard's question or answer.\n"
+ + " Parameters: update QUERY\n" + " Example: update fruit\n"
+ + " Which flashcard do you want to update? 1 /q What is the best fruit?\n"
+ + "delete Removes a flashcard with specified string.\n"
+ + " Parameters: delete QUERY\n" + " Example: delete fruit\n"
+ + "review Go through all flashcards that are due today.\n"
+ + "list Lists out the questions and answers in the list of flashcards.\n"
+ + " Parameters: list all (lists all flashcards)\n"
+ + " Parameters: list DUE_DATE (list all flashcards with that specified due date)\n"
+ + " Example: list 2023-05-04\n"
+ + "clear Deletes all the flashcards in the list.\n"
+ + "bye Exit the program.");
+ }
+
+ public void printSeparationLine() {
+ System.out.println("==========================================================");
+ }
+
+ /**
+ * Prints the question and answer of a single flashcard to the console.
+ *
+ * @Param flashcard the flashcard to be print
+ */
+ public void printFlashCard(Flashcard flashcard) {
+ System.out.println("Q: " + flashcard.getQuestion());
+ System.out.println("A: " + flashcard.getAnswer());
+ }
+
+ /**
+ * Prints the questions and answers of a list of flashcards to the console.
+ *
+ * @param flashcards the list of flashcards to be printed
+ */
+ public void printFlashCards(ArrayList flashcards) {
+ for (int i = 0; i < flashcards.size(); i++) {
+ System.out.println("[" + (i + 1) + "]");
+ printFlashCard(flashcards.get(i));
+ }
+ }
+
+ public void printSuccessfulUpdateMessage(Flashcard updatedFlashcard) {
+ System.out.println("Understood. The card has been updated to");
+ printFlashCard(updatedFlashcard);
+ }
+
+ //@@author javienneyeo
+ /**
+ * Display that the contents of list have been successfully cleared.
+ */
+ public void printClearMessage() {
+ System.out.println("All flashcards have been deleted.");
+ System.out.println("Your list of flashcards is now empty.");
+ }
+}
diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java
deleted file mode 100644
index 5c74e68d59..0000000000
--- a/src/main/java/seedu/duke/Duke.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package seedu.duke;
-
-import java.util.Scanner;
-
-public class Duke {
- /**
- * Main entry-point for the java.duke.Duke application.
- */
- public static void main(String[] args) {
- String logo = " ____ _ \n"
- + "| _ \\ _ _| | _____ \n"
- + "| | | | | | | |/ / _ \\\n"
- + "| |_| | |_| | < __/\n"
- + "|____/ \\__,_|_|\\_\\___|\n";
- System.out.println("Hello from\n" + logo);
- System.out.println("What is your name?");
-
- Scanner in = new Scanner(System.in);
- System.out.println("Hello " + in.nextLine());
- }
-}
diff --git a/src/test/java/com/clanki/objects/FlashcardTest.java b/src/test/java/com/clanki/objects/FlashcardTest.java
new file mode 100644
index 0000000000..d8049e9422
--- /dev/null
+++ b/src/test/java/com/clanki/objects/FlashcardTest.java
@@ -0,0 +1,51 @@
+
+package com.clanki.objects;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.time.LocalDate;
+
+class FlashcardTest {
+ @Test
+ public void constructNewFlashcard_correctlyFormattedInputs_success() {
+ Flashcard testCard = new Flashcard("QUESTION", "ANSWER");
+ assertEquals("QUESTION", testCard.getQuestion());
+ assertEquals("ANSWER", testCard.getAnswer());
+ assertEquals(LocalDate.now(), testCard.getDueDate());
+ assertEquals(0, testCard.getCurrentPeriodInDays());
+ assertEquals(true, testCard.isDueToday());
+ }
+
+ @Test
+ public void updateFlashcard_incorrectAnswer_shouldResetDatesToToday() {
+ Flashcard testCard = new Flashcard("QUESTION", "ANSWER", LocalDate.now(), 1);
+ testCard.updateDueDateAfterIncorrectAnswer();
+ assertEquals(LocalDate.now(), testCard.getDueDate());
+ assertEquals(0, testCard.getCurrentPeriodInDays());
+ }
+
+ @Test
+ public void updateFlashcard_correctAnswer_shouldUpdateDates() {
+ Flashcard testCard = new Flashcard("q1", "a1");
+ testCard.updateDueDateAfterCorrectAnswer();
+ assertEquals(LocalDate.now().plusDays(1), testCard.getDueDate());
+ assertEquals(1, testCard.getCurrentPeriodInDays());
+
+ testCard = new Flashcard("q2", "a2", LocalDate.now(), 1);
+ testCard.updateDueDateAfterCorrectAnswer();
+ assertEquals(LocalDate.now().plusDays(2), testCard.getDueDate());
+ assertEquals(2, testCard.getCurrentPeriodInDays());
+
+ testCard = new Flashcard("q3", "a3", LocalDate.now(), 2);
+ testCard.updateDueDateAfterCorrectAnswer();
+ assertEquals(LocalDate.now().plusDays(3), testCard.getDueDate());
+ assertEquals(3, testCard.getCurrentPeriodInDays());
+
+ testCard = new Flashcard("q4", "a4", LocalDate.now(), 45);
+ testCard.updateDueDateAfterCorrectAnswer();
+ assertEquals(LocalDate.now().plusDays(68), testCard.getDueDate());
+ assertEquals(68, testCard.getCurrentPeriodInDays());
+ }
+}
diff --git a/src/test/java/com/clanki/parser/ParsedInputTest.java b/src/test/java/com/clanki/parser/ParsedInputTest.java
new file mode 100644
index 0000000000..088de8c70f
--- /dev/null
+++ b/src/test/java/com/clanki/parser/ParsedInputTest.java
@@ -0,0 +1,84 @@
+package com.clanki.parser;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class ParsedInputTest {
+ @Test
+ public void getCommand_emptyInput_emptyString() {
+ ParsedInput parsedInput = new ParsedInput("");
+ assertEquals("", parsedInput.getCommand());
+ }
+
+ @Test
+ public void getCommand_oneWordInput_oneWord() {
+ ParsedInput parsedInput = new ParsedInput("command");
+ assertEquals("command", parsedInput.getCommand());
+ }
+
+ @Test
+ public void getCommand_multipleWordsInput_firstWord() {
+ ParsedInput parsedInput = new ParsedInput("command blah blah");
+ assertEquals("command", parsedInput.getCommand());
+ }
+
+ @Test
+ public void getCommand_withSlash_hasTheSlash() {
+ ParsedInput parsedInput = new ParsedInput("command/blah blah");
+ assertEquals("command/blah", parsedInput.getCommand());
+ }
+
+ @Test
+ public void getBody_emptyInput_emptyString() {
+ ParsedInput parsedInput = new ParsedInput("");
+ assertEquals("", parsedInput.getBody());
+ }
+
+ @Test
+ public void getBody_oneWordInput_emptyString() {
+ ParsedInput parsedInput = new ParsedInput("command");
+ assertEquals("", parsedInput.getBody());
+ }
+
+ @Test
+ public void getBody_multipleWordsInput_allWords() {
+ ParsedInput parsedInput = new ParsedInput("command blah blah");
+ assertEquals("blah blah", parsedInput.getBody());
+ }
+
+ @Test
+ public void getBody_optionsWithoutBody_beNull() {
+ ParsedInput parsedInput = new ParsedInput("command /blah blah");
+ assertEquals("", parsedInput.getBody());
+ }
+
+ @Test
+ public void getBody_optionsWithBody_beCorrect() {
+ ParsedInput parsedInput = new ParsedInput("command a b /blah blah");
+ assertEquals("a b", parsedInput.getBody());
+ }
+
+ @Test
+ public void getBody_optionsWithBodyAndTrailingSpaces_beCorrect() {
+ ParsedInput parsedInput = new ParsedInput("command a b /blah blah ");
+ assertEquals("a b", parsedInput.getBody());
+ }
+
+ @Test
+ public void getOptionByName_normalScenarios_beCorrect() {
+ ParsedInput parsedInput = new ParsedInput(
+ "command /opt1 blah /opt2 /opt3 bleh ");
+ assertEquals("blah", parsedInput.getOptionByName("opt1"));
+ assertEquals("", parsedInput.getOptionByName("opt2"));
+ assertEquals("bleh", parsedInput.getOptionByName("opt3"));
+ assertEquals(null, parsedInput.getOptionByName("opt4"));
+ }
+
+ @Test
+ public void getOptionByName_sameOptionMultipleTimes_selectLast() {
+ ParsedInput parsedInput = new ParsedInput(
+ "command /opt1 blah /opt2 /opt3 bleh /opt1 bleh ");
+ assertEquals("bleh", parsedInput.getOptionByName("opt1"));
+ }
+}
diff --git a/src/test/java/com/clanki/parser/ParserTest.java b/src/test/java/com/clanki/parser/ParserTest.java
new file mode 100644
index 0000000000..d47c4fc9e3
--- /dev/null
+++ b/src/test/java/com/clanki/parser/ParserTest.java
@@ -0,0 +1,95 @@
+
+package com.clanki.parser;
+
+import com.clanki.commands.AddCommand;
+import com.clanki.commands.ByeCommand;
+import com.clanki.commands.Command;
+import com.clanki.commands.DeleteCommand;
+import com.clanki.commands.ListCommand;
+import com.clanki.commands.ReviewCommand;
+import com.clanki.commands.UnknownCommand;
+import com.clanki.commands.UpdateCommand;
+import com.clanki.exceptions.EmptyFlashcardAnswerException;
+import com.clanki.exceptions.EmptyFlashcardQuestionException;
+import com.clanki.exceptions.InvalidAddFlashcardInputException;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class ParserTest {
+ @Test
+ public void parserAddCommand_correctlyFormattedInput_successful() {
+ Command parsedCommand = Parser.parseCommand("add /q Question /a Answer");
+ Command expectedCommand = new AddCommand("Question", "Answer");
+ assertEquals(expectedCommand.toString(), parsedCommand.toString());
+ Command parsedCommandNonCap = Parser.parseCommand("add /q question /a answer");
+ Command expectedCommandCap = new AddCommand("Question", "Answer");
+ assertEquals(expectedCommandCap.toString(), parsedCommandNonCap.toString());
+ }
+
+ @Test
+ public void parserAddCommand_answerBeforeQuestion_successful() {
+ Command parsedCommand = Parser.parseCommand("add /a Answer /q Question");
+ Command expectedCommand = new AddCommand("Question", "Answer");
+ assertEquals(expectedCommand.toString(), parsedCommand.toString());
+ }
+
+ @Test
+ public void parserAddCommand_incorrectFormattedInput_invalidInputException() {
+ assertThrows(InvalidAddFlashcardInputException.class,
+ () -> Parser.parseCommandStrict("add Question /a Answer"));
+ assertThrows(InvalidAddFlashcardInputException.class,
+ () -> Parser.parseCommandStrict("add /q Question /w Answer"));
+ }
+
+ @Test
+ public void parserAddCommand_partOfInputMissing_emptyInputPartException() {
+ assertThrows(EmptyFlashcardAnswerException.class,
+ () -> Parser.parseCommandStrict("add /q Question /a "));
+ assertThrows(EmptyFlashcardQuestionException.class,
+ () -> Parser.parseCommandStrict("add /q /a Answer"));
+ }
+
+ @Test
+ public void parserByeCommand_byeCommand_successful() {
+ Command parsedCommand = Parser.parseCommand("bye");
+ assertTrue(parsedCommand instanceof ByeCommand);
+ parsedCommand = Parser.parseCommand("bye whatever");
+ assertTrue(parsedCommand instanceof ByeCommand);
+ }
+
+ @Test
+ public void parserReviewCommand_reviewCommand_successful() {
+ Command parsedCommand = Parser.parseCommand("review");
+ assertTrue(parsedCommand instanceof ReviewCommand);
+ }
+
+ @Test
+ public void parserUnknownCommand_unknownCommand_successful() {
+ Command parsedCommand = Parser.parseCommand("unknown");
+ assertTrue(parsedCommand instanceof UnknownCommand);
+ }
+
+ @Test
+ public void parserUpdateCommand_updateCommand_successful() {
+ Command parsedCommand = Parser.parseCommand("update Question");
+ assertTrue(parsedCommand instanceof UpdateCommand);
+ }
+
+ @Test
+ public void parserDeleteCommand_deleteCommand_successful() {
+ Command parsedCommand = Parser.parseCommand("delete query");
+ assertTrue(parsedCommand instanceof DeleteCommand);
+ }
+
+ @Test
+ public void parserListCommand_listCommand_successful() {
+ Command parsedCommand = Parser.parseCommand("list all");
+ assertTrue(parsedCommand instanceof ListCommand);
+ parsedCommand = Parser.parseCommand("list 2023-04-06");
+ assertTrue(parsedCommand instanceof ListCommand);
+ }
+}
diff --git a/src/test/java/com/clanki/storage/FlashcardListDecoderTest.java b/src/test/java/com/clanki/storage/FlashcardListDecoderTest.java
new file mode 100644
index 0000000000..5f642bab4e
--- /dev/null
+++ b/src/test/java/com/clanki/storage/FlashcardListDecoderTest.java
@@ -0,0 +1,66 @@
+
+package com.clanki.storage;
+
+import com.clanki.objects.Flashcard;
+import com.clanki.objects.FlashcardList;
+import org.junit.jupiter.api.Test;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.clanki.storage.FlashcardListDecoder.decodeFlashcardList;
+import static com.clanki.storage.FlashcardListEncoder.encodeFlashcardList;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+
+class FlashcardListDecoderTest {
+
+ @Test
+ void decodeFlashcardList_correctInput_correctOutput() {
+ List storedString = new ArrayList<>();
+ storedString.add("q/q1 a/a1 d/" + LocalDate.now().toString() + " p/0");
+ storedString.add("q/q2 a/a2 d/" + LocalDate.now().toString() + " p/0");
+ ArrayList decodedFlashcardList = decodeFlashcardList(storedString);
+ Flashcard testCard1 = decodedFlashcardList.get(0);
+ assertEquals("q1", testCard1.getQuestion());
+ assertEquals("a1", testCard1.getAnswer());
+ assertEquals(LocalDate.now(), testCard1.getDueDate());
+ assertEquals(0, testCard1.getCurrentPeriodInDays());
+ Flashcard testCard2 = decodedFlashcardList.get(1);
+ assertEquals("q2", testCard2.getQuestion());
+ assertEquals("a2", testCard2.getAnswer());
+ assertEquals(LocalDate.now(), testCard2.getDueDate());
+ assertEquals(0, testCard2.getCurrentPeriodInDays());
+ }
+
+ @Test
+ void decodeAddressBook_encodeInput_decodeOutput() {
+ FlashcardList flashcardList = new FlashcardList();
+ flashcardList.addNewFlashcard("q1", "a1");
+ flashcardList.addNewFlashcard("q2", "a2");
+ List encodedResult = encodeFlashcardList(flashcardList);
+ ArrayList decodedFlashcardList = decodeFlashcardList(encodedResult);
+ Flashcard testCard1 = decodedFlashcardList.get(0);
+ assertEquals("q1", testCard1.getQuestion());
+ assertEquals("a1", testCard1.getAnswer());
+ assertEquals(LocalDate.now(), testCard1.getDueDate());
+ assertEquals(0, testCard1.getCurrentPeriodInDays());
+ Flashcard testCard2 = decodedFlashcardList.get(1);
+ assertEquals("q2", testCard2.getQuestion());
+ assertEquals("a2", testCard2.getAnswer());
+ assertEquals(LocalDate.now(), testCard2.getDueDate());
+ assertEquals(0, testCard2.getCurrentPeriodInDays());
+ }
+
+ @Test
+ void decodeAddressBook_correctAndInvalidInput_correctOutput() {
+ List storedString = new ArrayList<>();
+ storedString.add("q1 a/a1 d/" + LocalDate.now().toString() + " p/0");
+ storedString.add("q/q2 a/a2 d/" + LocalDate.now().toString() + " p/0");
+ ArrayList decodedFlashcardList = decodeFlashcardList(storedString);
+ assertEquals(1, decodedFlashcardList.size());
+ assertEquals("q2", decodedFlashcardList.get(0).getQuestion());
+ }
+
+}
diff --git a/src/test/java/com/clanki/storage/FlashcardListEncoderTest.java b/src/test/java/com/clanki/storage/FlashcardListEncoderTest.java
new file mode 100644
index 0000000000..bbdd5ca138
--- /dev/null
+++ b/src/test/java/com/clanki/storage/FlashcardListEncoderTest.java
@@ -0,0 +1,35 @@
+
+package com.clanki.storage;
+
+import com.clanki.objects.FlashcardList;
+import org.junit.jupiter.api.Test;
+
+import java.time.LocalDate;
+import java.util.List;
+
+import static com.clanki.storage.FlashcardListEncoder.encodeFlashcardList;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class FlashcardListEncoderTest {
+
+ @Test
+ void encodeAddressBook_correctInput_correctOutput() {
+ FlashcardList flashcardList = new FlashcardList();
+ flashcardList.addNewFlashcard("q1", "a1");
+ flashcardList.addNewFlashcard("q2", "a2");
+ List encodedResult = encodeFlashcardList(flashcardList);
+ String line1 = "q/q1 a/a1 d/" + LocalDate.now().toString() + " p/0";
+ String line2 = "q/q2 a/a2 d/" + LocalDate.now().toString() + " p/0";
+ assertEquals(line1, encodedResult.get(0));
+ assertEquals(line2, encodedResult.get(1));
+ }
+
+ @Test
+ void encodeAddressBook_emptyInput() {
+ FlashcardList flashcardList = new FlashcardList();
+ List encodedResult = encodeFlashcardList(flashcardList);
+ assertTrue(encodedResult.isEmpty());
+ }
+
+}
diff --git a/src/test/java/seedu/duke/DukeTest.java b/src/test/java/seedu/duke/DukeTest.java
deleted file mode 100644
index 2dda5fd651..0000000000
--- a/src/test/java/seedu/duke/DukeTest.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package seedu.duke;
-
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-import org.junit.jupiter.api.Test;
-
-class DukeTest {
- @Test
- public void sampleTest() {
- assertTrue(true);
- }
-}
diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT
index 892cb6cae7..58273c51e7 100644
--- a/text-ui-test/EXPECTED.TXT
+++ b/text-ui-test/EXPECTED.TXT
@@ -1,9 +1,40 @@
-Hello from
- ____ _
-| _ \ _ _| | _____
-| | | | | | | |/ / _ \
-| |_| | |_| | < __/
-|____/ \__,_|_|\_\___|
+Welcome to Clanki! Time to start studying!
+==========================================================
+You have added the following card:
+Q: Q1
+A: A1
+==========================================================
+You have added the following card:
+Q: Q1
+A: A1
+==========================================================
+Found 2 card(s) with query "q1":
+[1]
+Q: Q1
+A: A1
+[2]
+Q: Q1
+A: A1
+Which one do you want to update?
+Understood. The card has been updated to
+Q: q2
+A: A1
+==========================================================
+There are 2 cards available for review today.
+---
+Q: Q1 (ENTER to view answer)A: A1
+Did you get it right? (y/n) Great, you got it right!
+---
+Q: q2 (ENTER to view answer)A: A1
+Did you get it right? (y/n) Please enter 'y' or 'n': (y/n) Please enter 'y' or 'n': (y/n) No worries, we will try again later today.
+---
+Q: q2 (ENTER to view answer)A: A1
+Did you get it right? (y/n) Great, you got it right!
+---
+Congrats! You have reviewed all the flashcards due today!
+==========================================================
+The input is in an incorrect format.
+You can view our user guide or type help to see the correct formats for commands.
-What is your name?
-Hello James Gosling
+==========================================================
+Bye. Don't forget to come back later to study!
diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt
index f6ec2e9f95..8d5df9f7a2 100644
--- a/text-ui-test/input.txt
+++ b/text-ui-test/input.txt
@@ -1 +1,15 @@
-James Gosling
\ No newline at end of file
+add /q q1 /a a1
+add /q q1 /a a1
+update q1
+2 /q q2
+review
+
+y
+
+
+blah
+n
+
+y
+foo 1
+bye
diff --git a/text-ui-test/runtest.bat b/text-ui-test/runtest.bat
index 25ac7a2989..53335711a1 100644
--- a/text-ui-test/runtest.bat
+++ b/text-ui-test/runtest.bat
@@ -16,4 +16,4 @@ java -jar %jarloc% < ..\..\text-ui-test\input.txt > ..\..\text-ui-test\ACTUAL.TX
cd ..\..\text-ui-test
-FC ACTUAL.TXT EXPECTED.TXT >NUL && ECHO Test passed! || Echo Test failed!
+FC ACTUAL.TXT EXPECTED.TXT >NUL && ECHO Test passed! || Echo Test failed!
\ No newline at end of file
diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh
index 1dcbd12021..b6e8516ff8 100755
--- a/text-ui-test/runtest.sh
+++ b/text-ui-test/runtest.sh
@@ -11,7 +11,6 @@ cd text-ui-test
java -jar $(find ../build/libs/ -mindepth 1 -print -quit) < input.txt > ACTUAL.TXT
cp EXPECTED.TXT EXPECTED-UNIX.TXT
-dos2unix EXPECTED-UNIX.TXT ACTUAL.TXT
diff EXPECTED-UNIX.TXT ACTUAL.TXT
if [ $? -eq 0 ]
then