diff --git a/README.md b/README.md index e243ece764..68e9e84737 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Duke project template +# FlashBang This is a project template for a greenfield Java project. It's named after the Java mascot _Duke_. Given below are instructions on how to use it. diff --git a/build.gradle b/build.gradle index ea82051fab..b629f2d1c8 100644 --- a/build.gradle +++ b/build.gradle @@ -29,11 +29,11 @@ test { } application { - mainClass.set("seedu.duke.Duke") + mainClass.set("seedu.duke.Flashbang") } shadowJar { - archiveBaseName.set("duke") + archiveBaseName.set("Flashbang") archiveClassifier.set("") } diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 0f072953ea..36dc11f521 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -1,9 +1,10 @@ # About us -Display | Name | Github Profile | Portfolio ---------|:----:|:--------------:|:---------: -![](https://via.placeholder.com/100.png?text=Photo) | John Doe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Don Joe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Ron John | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | John Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Don Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) + +| Display | Name | Github Profile | Portfolio | +|-----------------------------------------------------|:--------------------:|:---------------------------------------------:|:---------------------------------------------------------------------------------------------------------------------------------:| +| ![](https://via.placeholder.com/100.png?text=Photo) | Mikolaj Jedrzejewski | [Github](https://github.com/mikolajed) | [Portfolio](https://ay2425s1-cs2113-t11-2.github.io/tp/team/mikolajed.html) | +| ![](https://via.placeholder.com/100.png?text=Photo) | Ranee Ng | [Github](https://github.com/raneeng) | [Portfolio](https://ay2425s1-cs2113-t11-2.github.io/tp/team/raneeng.html) | +| ![](https://via.placeholder.com/100.png?text=Photo) | Frederick | [Github](https://github.com/frederickemerson) | [Portfolio](https://ay2425s1-cs2113-t11-2.github.io/tp/team/frederick.html) | +| ![](https://via.placeholder.com/100.png?text=Photo) | Nguyen Hoang Minh Ngoc (Angelina) | [Github](https://github.com/angelinawong1210) | [Portfolio](https://ay2425s1-cs2113-t11-2.github.io/tp/team/angelinawong1210.html) | +| ![](https://via.placeholder.com/100.png?text=Photo) | Ethan Soh | [Github](https://github.com/Paulifyer) | [Portfolio](https://ay2425s1-cs2113-t11-2.github.io/tp/team/ethansoh.html) | diff --git a/docs/Developer Guide _ tp.pdf b/docs/Developer Guide _ tp.pdf new file mode 100644 index 0000000000..3eb9cc8495 Binary files /dev/null and b/docs/Developer Guide _ tp.pdf differ diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 64e1f0ed2b..1838fe2256 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -3,36 +3,150 @@ ## Acknowledgements {list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well} +Formatting of Developer's Guide was done with reference to [AddressBook-Level3 developer guide](https://se-education.org/addressbook-level3/DeveloperGuide.html#common-classes). ## Design & implementation {Describe the design and implementation of the product. Use UML diagrams and short code snippets where applicable.} +### Parser component +API: `Parser.java` + +Parser's role is to given user input create a command which then can be executed. This particular implementation follows +**Factory design pattern**. It exposes a general purpose method for parsing command `parseCommand(String input)` and then it determines +command types and creates one of the type. Regular expressions are heavily used for extracting information from input. +More details are presented on a sequence diagram someName. + +![Diagram Description](./diagrams/ParsingSequenceDiagram.png) + +#### Alternative approaches/Possible improvements: +- Command factory could be moved to a separate class +- Creating a lexer object might be a desirable approach if the commands where much more complex + +#### Structure +Below is a partial class diagram showing the interactions of the `Parser` class. +![Parser class diagram](./diagrams/ParserPartialClassDiagram.png) + +The sequence diagram below illustrates the interactions taking `parseCommand(“delete --m cs2113 --i 1”)` as an example. +![Sample delete call sequence diagram](./diagrams/ParserDeleteSequenceDiagram.png) + +#### Example +How the `Parser` component works: +The `Parser` receives the command input. +It identifies the command type using `parseCommandType`. +Depending on the command type, it creates the corresponding command object (e.g., `AddCommand`). +The created command is executed, producing a `CommandResult`. +The `CommandResult` is then used by `Ui` to provide feedback to the user. + +### Ui component +API: `Ui.java` + +Below is a partical class diagram showing the interactions of the `Ui` class. +![Ui class diagram](./diagrams/UIClassDiagram.png) + +The sequence diagram below illustrates the interactions between the user and this class when the program is executed. +![Ui interactions sequence diagram](./diagrams/UISequenceDiagram.png) + +How the `Ui` component works: +The `Ui` serves as a centralized utility that handles all outputs. +When a user execute the app, this class displays the welcome message and all available commands. After that, based on users' inputs, it handles the output that is processed by other classes. + +### Storage component +API: `Storage.java` + +Below is a class diagram showing the interactions of the `Storage` class. +![Storage class diagram](./diagrams/StorageClassDiagram.png) + +The sequence diagram below illustrates the interactions taking `writeFlashBookToFile()` and `readFlashCardsFromFile()`. +![Sample delete call sequence diagram](./diagrams/StorageSequenceDiagram.png) + +How the `Storage` component works: + The `Storage` component is initialized with a directory path where flashcard data will be stored. + To save data: + `writeFlashBookToFile()` is called, iterating through each `FlashCardSet` in `FlashBook`. + For each `FlashCardSet`, a corresponding file is created in the directory, and each Card in the set is written to this file. + To load data: + `readFlashCardsFromFile()` checks the directory for flashcard files. + For each file found, `readFlashCardSetFromFile()` is called to read the cards and create a `FlashCardSet`. + The `FlashCardSet` is then added back to the `FlashBook`, reconstructing the flashcard library in memory. ## Product scope ### Target user profile -{Describe the target user profile} +- Has a need to create flashcards for their studies +- Needs to be able to test themselves on flashcard content +- Needs to be able to track how well they understand the modules they take +- Can type fast +- Prefers typing to mouse interactions +- Is used to using CLI applications ### Value proposition -{Describe the value proposition: what problem does it solve?} +Give university students a simple and effective flashcard application which allows them to create flashcards for the +many modules and topics they have for schools. Flashcards are used to test the student's knowledge and also organise the +content of the module in a simple and clear way. ## User Stories -|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| - +| 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 | +| v1.0 | student | view existing flashcards | I can test my knowledge to study efficiently | +| v1.0 | student | view existing flashcards | I can review and learn material | +| v1.0 | crammer | delete flashcards which im confident at | I can focus on my areas of weakness | +| v1.0 | student | review flashcards that I have answered incorrectly | I can identify my knowledge gaps | +| v2.0 | student | have a timer within the app | I am able to time myself taking the quizzes within the app itself for better learning | +| v2.0 | student | view all incorrect flashcards in previous quizzes | I can focus more on my weak areas | +| v2.0 | user | search for flashcards based on keyword and module | to test myself on specific topics | +| v2.0 | student | keep track of how many right and wrong answers in quizzes | so I can focus on how well versed I am in a topic | +| v2.0 | student | filter flashcards by difficulty | I can choose which ones to focus on based on my current level of understanding | ## Non-Functional Requirements -{Give non-functional requirements} +* Should work on any mainstream OS as long as it has Java `17` or above installed +* Should be able to handle any number of modules as long as there are no repeated module names +* A user with strong typing ability should be able to use the application faster than with a mouse ## Glossary -* *glossary item* - Definition +* **Flashcard** - a card containing a small amount of information as an aid to learning. +Contains a question and an answer related to a topic. ## Instructions for manual testing -{Give instructions on how to do a manual product testing e.g., how to load sample data to be used for testing} +### Launch and shutdown +#### Initial launch +1. Download the jar file and copy into an empty folder +2. Open the jar file using your command line with the command: +`java -jar {Path of File}` +#### Shutdown +1. Type `quit` to exit the application +2. Quitting the application also saves all changes made by the user during runtime
+Expected: Application exits and the text files in `./data` are updated accordingly +#### Adding Flashcards +1. Adding flashcards into the flash book + 1. Test case: `add --m CS2113 --q What is OOP? --a Object-Oriented Programming`
+ Expected: A Card with question "What is OOP" and answer "Object-Oriented Programming" in the module "CS2113" + 2. Test case: `add`
+ Expected: An error is thrown and caught printing out "uh oh bad command" + 3. Other incorrect add commands to try: `add --q`, `add --a` (with missing fields or empty fields)
+ Expected: Similar to previous +#### Deleting Flashcards +##### Prerequisites: There are a several flashcards in the flash book listed out using the view command +1. Deleting flashcards from the flash book + 1. Test case: `delete --m CS2113 --i 1`
+ Expected: the first flashcard in the CS2113 flash card set is deleted. Details of the deleted card should be shown + 2. Test case: `delete`
+ Expected: An error is thrown and caught printing out "uh oh bad command" + 3. Other incorrect variations to try: `delete --m ModuleNotInList --i 0`, `delete --m` + (with missing fields or modules not in the flash book)
+ Expected: Similar to previous +#### Flashbang +##### Prerequisites: There are a several flashcards and flashcard sets in the flash book +1. Quizzing users on the flashcards in a module + 1. Test case: `flashbang --m CS2113`
+ Expected: Each question within the module is displayed sequentially + where users are prompted to reveal the answer to the question with `y` or `n` + 2. Incorrect variation to try: `flashbang`, `flashbang --m`
+ Expected: An error is thrown and caught printing out "uh oh bad command" + 3. Other incorrect variations to try: `flashbang --m ModuleNotInList`
+ Expected: An error is thrown and caught printing out "uh oh bad command" diff --git a/docs/README.md b/docs/README.md index bbcc99c1e7..8af26b5d69 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,8 +1,11 @@ -# Duke +# FlashBang -{Give product intro here} +FlashBang is a CLI app designed to provide students with a smart way of studying for their modules. The app will manage a limited number of flashcards for a small number of modules, optimized for users who prefer a CLI. Useful links: * [User Guide](UserGuide.md) * [Developer Guide](DeveloperGuide.md) * [About Us](AboutUs.md) + +PPP +[Frederick](./team/frederickemerson.md) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index d6cf4c3b3a..655326e408 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -2,41 +2,232 @@ ## Introduction -{Give a product intro} +**FlashBang** is a CLI app designed to provide students with a smart way of studying for their modules. The app will manage a limited number of flashcards for a small number of modules, optimized for users who prefer a CLI. -## Quick Start +## Target User Profile -{Give steps to get started quickly} +NUS students who want to review their modules using flashcards. The user: -1. Ensure that you have Java 17 or above installed. -1. Down the latest version of `Duke` from [here](http://link.to/duke). +- Has a need to create flashcards for their studies +- Needs to be able to test themselves on flashcard content +- Needs to be able to track how well they understand the modules they take +- Can type fast +- Prefers typing to mouse interactions +- Is used to using CLI applications -## Features +## Value Propositions -{Give detailed description of each feature} +The app will provide NUS students with a smart way of studying for their modules. The app will manage a limited number of flashcards for a small number of modules, optimized for users who prefer a CLI. -### Adding a todo: `todo` -Adds a new item to the list of todo items. +## Quick Start + +1. Ensure that you have Java 17 or above installed. -Format: `todo n/TODO_NAME d/DEADLINE` +2. Down the latest version of `FlashBang` from [here](https://github.com/AY2425S1-CS2113-T11-2/tp/releases). -* The `DEADLINE` can be in a natural language format. -* The `TODO_NAME` cannot contain punctuation. +3. Copy the jar file into an empty folder. -Example of usage: +4. Open a command window in that folder. -`todo n/Write the rest of the User Guide d/next week` +5. Run the command java -jar {filename}.jar e.g., java -jar Duke.jar (i.e., run the command in the same folder as the jar file). -`todo n/Refactor the User Guide to remove passive voice d/13/04/2020` +## Features -## FAQ +The app allows for creating and managing flashcards each of which contains +a question and an answer. Flashcards are organized into modules. Following +is a list of command which are supported with examples. -**Q**: How do I transfer my data to another computer? +### Adding flashcards: `add` -**A**: {your answer here} +Add a flashcard to a flashcard set.
+Topics are optional fields that are used to enhance organisation +**Note* It is not allowed to have the delimiter " | " in the questions and answers. -## Command Summary +```bash +add --m [MODULE NAME] --q [QUESTION] --a [ANSWER] +``` +or +```bash +add --m [MODULE NAME] --t [TOPIC NAME] --q [QUESTION] --a [ANSWER] +``` + +**Examples:** +```bash +add --m CS2113 --q "What is OOP?" --a "Object-Oriented Programming" +add --m CS1010 --q "What is a variable?" --a "A storage location in memory with a name" +add --m MA1521 --q "What is the derivative of sin(x)?" --a "cos(x)" +add --m CS2113 --t OOP --q "What is an Object?" --a "An entity with state and behaviour" +``` + +### Deleting flashcards: `delete`, `deleteall` + +To delete one flashcard: + +```bash +delete --m [MODULE NAME] --i [INDEX] +``` + +To delete all flashcards in a set: + +```bash +deleteall --m [MODULE NAME] +``` + +**Examples:** +```bash +delete --m CS2113 --i 2 # Deletes second flashcard in the module CS2113 +delete --m MA1521 --i 5 # Deletes fifth flashcard in the module MA1521 +deleteall --m CS1010 # Deletes all flashcards in the module CS1010 +``` + +### Viewing all flashcards: `view` +Lists all flashcards for every module. +```bash +view --all +``` + +**Example:** +```bash +view --all +``` + +### Viewing all flashcards in a module without the answers: `flashbang` + +```bash +flashbang --m [MODULE NAME] +``` + +**Example:** +```bash +flashbang --m CS1010 +``` +``` +Question: "What is a variable?" +Reveal answer? (Q to quit) (Y/N) +Y +Answer: "A storage location in memory with a name." +Next question: "What is a constant?" +Reveal answer? (Q to quit) (Y/N) +Q +Bye!! +``` + +### Filter flashcards by module: `view` +```bash +view --m [MODULE NAME] +``` + +**Example:** +```bash +view --m CS2113 +``` + +### Edit flashcard: `edit` + +Edits an existing flashcard. + +```bash +edit --m [MODULE NAME] --i [INDEX] --q [NEW QUESTION] --a [NEW ANSWER] +``` + +Or + +```bash +edit --m [MODULE NAME] --i [INDEX] # Prompts for inputs +``` + +**App Prompts:** +``` +App: New Question: [NEW QUESTION] +App: New Answer: [NEW ANSWER] +``` + +**Examples:** +```bash +edit --m CS1010 --i 2 --q "What is a constant?" --a "A value that cannot be changed once initialized." +edit --m MA1521 --i 3 --q "What is the integral of 1/x?" --a "ln|x| + C" + +__________________________________________________ +> edit --m CS2113 --i 1 +Old Question : Question 2 +Do you want to change Question (y/n): +> y +Enter new Question : +> What does OOP stand for? +Old Answer : Answer for question 2 +Do you want to change Answer (y/n): +> y +Enter new Answer : +> Object-oriented programming +__________________________________________________ +Successfully edited flashcard + +``` + +### Search for flashcards in a module by topic or by a search term +Searches for existing flashcards that contain the search term or have topics that contain the search term. +
+To search by topic, add the `/t` flag after the module name. +
+When searching, the search term is case-sensitive. +```bash +search --m [MODULE NAME] /t --s [SEARCH TERM] +``` +*or* +```bash +search --m [MODULE NAME] --s [SEARCH TERM] +``` +**Examples:** +```bash +search --m CS2113 --s state + ____________________________________________________________ + 1. WHAT is an Object: + An entity with a state and a behaviour + topic: OOP + + ____________________________________________________________ +search --m CS2113 /t --s OOP + ____________________________________________________________ + 1. What is OOP: + Object-Oriented Programing + topic: OOP + 2. WHAT is an Object: + An entity with a state and a behaviour + topic: OOP + + ____________________________________________________________ + +``` +### Quitting the app: `quit` +Quits the app session. +```bash +quit +``` + +**Example:** +```bash +quit +``` + + +## Command summary + +| Command | Description | +| --- |------------------------------------------------------------------------------| +| Add flashcards | ```add --m [Module Name] {--t [Topic] (optional)} --q [Question] --a [Answer]``` | +| Delete one flashcard | ```delete --m [MODULE NAME] --i [INDEX]``` | +| Delete all flashcards in a set | ```deleteall --m [MODULE NAME]``` | +| View all flashcards in every module | ```view --all``` | +| View all flashcards in a module without the answers | ```flashbang --m [MODULE NAME``` | +| Filter flashcards by module | ```view --m [MODULE NAME]``` | +| Edit flashcard | ```edit --m [MODULE NAME] --i [INDEX] --q [NEW QUESTION] --a [NEW ANSWER]``` | +| Quit the app | ```quit``` | + +Note that specifying multiple command keywords in the input will be understood as command of the first type. + +## FAQs +Q: Can I add two flashcards same question but different answer. + +A: Yes. Adding the two flashcards with both the same answer and question will work. -{Give a 'cheat sheet' of commands here} -* Add todo `todo n/TODO_NAME d/DEADLINE` diff --git a/docs/UserGuide.pdf b/docs/UserGuide.pdf new file mode 100644 index 0000000000..3ecab0c06d Binary files /dev/null and b/docs/UserGuide.pdf differ diff --git a/docs/diagrams/ParserDeleteSequenceDiagram.png b/docs/diagrams/ParserDeleteSequenceDiagram.png new file mode 100644 index 0000000000..e825d72cf1 Binary files /dev/null and b/docs/diagrams/ParserDeleteSequenceDiagram.png differ diff --git a/docs/diagrams/ParserDeleteSequenceDiagram.puml b/docs/diagrams/ParserDeleteSequenceDiagram.puml new file mode 100644 index 0000000000..948284d106 --- /dev/null +++ b/docs/diagrams/ParserDeleteSequenceDiagram.puml @@ -0,0 +1,60 @@ +@startuml + +participant ":Flashbang" +participant ":Parser" +participant "flashBook:FlashBook" +participant "command:DeleteCommand" +participant "cs2113:FlashcardSet" +participant "result:CommandResult" + +":Flashbang" -> ":Parser" : parseCommand("delete --m cs2113 --i 1") +activate ":Parser" + +":Parser" -> ":Parser" : parseCommandType("delete --m cs2113 --i 1") +activate ":Parser" +deactivate ":Parser" + +":Parser" -> ":Parser" : createDeleteCommand("delete --m cs2113 --i 1") +activate ":Parser" + +":Parser" -> "flashBook:FlashBook" : getInstance() +activate "flashBook:FlashBook" +return flashBook + +":Parser" -> "flashBook:FlashBook" : getFlashcardSet("cs2113") +activate "flashBook:FlashBook" +return cs2113 +deactivate "flashBook:FlashBook" + +create "command:DeleteCommand" +":Parser" -> "command:DeleteCommand" : DeleteCommand(cs2113, 1) +activate "command:DeleteCommand" +return command + +":Parser" --> ":Parser" : command +deactivate ":Parser" +":Parser" --> ":Flashbang" : command +deactivate ":Parser" + +":Flashbang" -> "command:DeleteCommand" : execute() +activate "command:DeleteCommand" + +"command:DeleteCommand" -> "cs2113:FlashcardSet" : removeCard(targetCard) +activate "cs2113:FlashcardSet" +"cs2113:FlashcardSet" -> "cs2113:FlashcardSet" : remove(targetCard) +activate "cs2113:FlashcardSet" +deactivate "cs2113:FlashcardSet" +deactivate "cs2113:FlashcardSet" + +create "result:CommandResult" +"command:DeleteCommand" -> "result:CommandResult" : CommandResult("success message") +activate "result:CommandResult" +return result + +destroy "result:CommandResult" + +return result + +destroy "command:DeleteCommand" + +@enduml diff --git a/docs/diagrams/ParserPartialClassDiagram.png b/docs/diagrams/ParserPartialClassDiagram.png new file mode 100644 index 0000000000..0d170ec658 Binary files /dev/null and b/docs/diagrams/ParserPartialClassDiagram.png differ diff --git a/docs/diagrams/ParserPartialClassDiagram.puml b/docs/diagrams/ParserPartialClassDiagram.puml new file mode 100644 index 0000000000..3d9b7f3195 --- /dev/null +++ b/docs/diagrams/ParserPartialClassDiagram.puml @@ -0,0 +1,20 @@ +@startuml + +class Flashbang +class CommandResult +class Parser +abstract class "Command" +class XYZCommand + +Flashbang --> Parser + +Parser --> "Command" : calls +Parser --> XYZCommand : creates + +"Command" <|-- XYZCommand + +"Command" --> CommandResult : creates +CommandResult --> Flashbang + + +@enduml diff --git a/docs/diagrams/ParsingSequenceDiagram.png b/docs/diagrams/ParsingSequenceDiagram.png new file mode 100644 index 0000000000..9a0bbd148f Binary files /dev/null and b/docs/diagrams/ParsingSequenceDiagram.png differ diff --git a/docs/diagrams/ParsingSequenceDiagram.puml b/docs/diagrams/ParsingSequenceDiagram.puml new file mode 100644 index 0000000000..7ba3082424 --- /dev/null +++ b/docs/diagrams/ParsingSequenceDiagram.puml @@ -0,0 +1,42 @@ +@startuml +!include Style.puml + +actor User as user +participant Parser +participant FlashBook +participant FlashCardSet +participant Command as command + +user -> Parser : parseCommand(input) +activate Parser + +Parser -> Parser : parseCommandType(input) +activate Parser + Parser <-- Parser : commandType +deactivate Parser + +alt commandType is valid + Parser <-- Parser : extractFields() + Parser -> Parser : create a command + activate Parser + Parser -> FlashBook : getModule() + activate FlashBook + + FlashBook -> FlashCardSet : getFlashCardSet() + activate FlashCardSet + FlashCardSet --> Parser : FlashCardSet + deactivate FlashCardSet + + deactivate FlashBook + Parser --> command : ValidCommand + deactivate Parser +else commandType is invalid + Parser -> Parser : create a command + activate Parser + Parser --> command : InvalidCommand + deactivate Parser +end + +user <-- command : Command instance +deactivate Parser +@enduml diff --git a/docs/diagrams/StorageClassDiagram.png b/docs/diagrams/StorageClassDiagram.png new file mode 100644 index 0000000000..215db6fe63 Binary files /dev/null and b/docs/diagrams/StorageClassDiagram.png differ diff --git a/docs/diagrams/StorageClassDiagram.puml b/docs/diagrams/StorageClassDiagram.puml new file mode 100644 index 0000000000..1010615686 --- /dev/null +++ b/docs/diagrams/StorageClassDiagram.puml @@ -0,0 +1,56 @@ +@startuml + +class Storage { + + Storage(String) + + writeFlashBookToFile(FlashBook) + + readFlashCardsFromFile(): HashMap + - createDir(): void + - cardFormatter(String): Card + - readFlashCardSetFromFile(String, File): FlashCardSet + - createFile(File): void +} + +class FlashBook { + - instance: FlashBook + - allFlashCardSets: HashMap + + getInstance(): FlashBook + + setInstance(flashCards: HashMap): void + + getAllFlashCardSets(): HashMap + + addFlashCardSet(module: String): void + + getFlashCardSet(module: String): FlashCardSet +} + +class FlashCardSet { + - flashCardSet: ArrayList + - moduleName: String + + FlashCardSet(module: String) + + FlashCardSet(module: String, flashCardSet: ArrayList) + + getModuleName(): String + + getFlashCardSet(): ArrayList + + getCard(cardIndex: int): Card + + addCard(toAdd: Card): void + + removeCard(toRemove: Card): void + + viewFlashCards(module: String): void + + performFlashBang(): void + + iterator(): Iterator +} + +class Card { + - question: String + - answer: String + - topic: String + + Card(question : String, answer : String) + + Card(question : String, answer : String, topic : String) + + getQuestion() : String + + getTopic() : String + + setQuestion(question : String) : void + + getAnswer() : String + + setAnswer(answer : String) : void + + toWritableString() : String + + toString() : String +} + +Storage --> FlashBook : "uses" +FlashBook --> "1" FlashCardSet +FlashCardSet --> "*" Card +@enduml diff --git a/docs/diagrams/StorageSequenceDiagram.png b/docs/diagrams/StorageSequenceDiagram.png new file mode 100644 index 0000000000..a24f768210 Binary files /dev/null and b/docs/diagrams/StorageSequenceDiagram.png differ diff --git a/docs/diagrams/StorageSequenceDiagram.puml b/docs/diagrams/StorageSequenceDiagram.puml new file mode 100644 index 0000000000..a84c8cc683 --- /dev/null +++ b/docs/diagrams/StorageSequenceDiagram.puml @@ -0,0 +1,87 @@ +@startuml +actor Client +participant Storage +participant File +participant FlashCardSet +participant Card +participant FlashBook +participant FileWriter + +== Initialization and Directory Creation == +Client -> Storage: Storage(directoryPath) +activate Storage +Storage -> File: File(directoryPath) +activate File +File --> Storage: File instance +deactivate File + +Storage -> Storage: createDir() +alt Directory doesn't exist + Storage -> File: mkdirs() + File --> Storage: Success/Failure +else Directory exists + Storage -> Storage: Directory ready +end +deactivate Storage + +== Writing FlashBook to File == +Client -> Storage: writeFlashBookToFile(flashBook) +activate Storage +Storage -> FlashBook: getAllFlashCardSets() +FlashBook --> Storage: HashMap + +loop HashMap + Storage -> File: File(directory, module+".txt") + activate File + File --> Storage: File instance + deactivate File + + Storage -> Storage: createFile(File) + Storage -> FileWriter: FileWriter(flashCardSetFile) + activate FileWriter + FileWriter --> Storage: FileWriter instance + deactivate FileWriter + + loop FlashCardSet + Storage -> Card: toWritableString() + activate Card + Card --> Storage: formattedString + deactivate Card + Storage -> FileWriter: write(formattedString) + end + + FileWriter -> FileWriter: close() +end +Storage --> Client: Write Complete +deactivate Storage + +== Reading FlashCards from File == +Client -> Storage: readFlashCardsFromFile() +activate Storage + +Storage -> File: directory.listFiles(".txt files") +alt Files found + loop for each File + Storage -> FlashCardSet: FlashCardSet(module) + activate FlashCardSet + FlashCardSet --> Storage: FlashCardSet instance + + loop File + File -> Storage: line (card data) + Storage -> Storage: cardFormatter(line) + activate Card + Card --> Storage: Card instance + deactivate Card + + Storage -> FlashCardSet: addCard(Card) + end + Storage -> File: close() + deactivate FlashCardSet + end + Storage --> Client: HashMap +else No files found + Storage --> Client: Empty HashMap +end +deactivate Storage + +@enduml diff --git a/docs/diagrams/Style.puml b/docs/diagrams/Style.puml new file mode 100644 index 0000000000..3ce60ae4bf --- /dev/null +++ b/docs/diagrams/Style.puml @@ -0,0 +1,5 @@ +!define LOGIC_COLOR #3333C4 +!define LOGIC_COLOR_T1 #7777DB +!define LOGIC_COLOR_T2 #5252CE +!define LOGIC_COLOR_T3 #1616B0 +!define LOGIC_COLOR_T4 #101086 \ No newline at end of file diff --git a/docs/diagrams/UIClassDiagram.png b/docs/diagrams/UIClassDiagram.png new file mode 100644 index 0000000000..8c71f43011 Binary files /dev/null and b/docs/diagrams/UIClassDiagram.png differ diff --git a/docs/diagrams/UISequenceDiagram.png b/docs/diagrams/UISequenceDiagram.png new file mode 100644 index 0000000000..339283da02 Binary files /dev/null and b/docs/diagrams/UISequenceDiagram.png differ diff --git a/docs/diagrams/UiClassDiagram.puml b/docs/diagrams/UiClassDiagram.puml new file mode 100644 index 0000000000..61650b678d --- /dev/null +++ b/docs/diagrams/UiClassDiagram.puml @@ -0,0 +1,16 @@ +@startuml +skinparam classAttributeIconSize 0 + +title UI - Class Diagram +class Ui { + - scanner: Scanner + + Ui() + + {static} displayCommands(): void + + {static} welcomeMessage(): void + + {static} printResponse(text: String): void + + {static} getRequest(): String + + {static} displayGetNewPromptFromUser(prompt: String): void + + {static} displayConfirmationQuestion(prompt: String): void + + {static} displayOldStoredValue(prompt: String, value: String): void +} +@enduml diff --git a/docs/diagrams/UiSequenceDiagram.puml b/docs/diagrams/UiSequenceDiagram.puml new file mode 100644 index 0000000000..4a129689eb --- /dev/null +++ b/docs/diagrams/UiSequenceDiagram.puml @@ -0,0 +1,41 @@ +@startuml +!include docs/diagrams/Style.puml + +actor User + +User -> Ui : welcomeMessage() +activate Ui +Ui -> Ui : displayCommands() +deactivate Ui + +User -> Ui: printResponse() +activate Ui +Ui -> Ui : displayCommands() +deactivate Ui + +User -> Ui : getRequest() +activate Ui +Ui -> Ui : return scanner.nextLine(); +deactivate Ui + +User -> Ui : displayGetNewPromptFromUser("request") +activate Ui +Ui -> User : "Enter new request:" +deactivate Ui + +User -> Ui: displayConfirmationQuestion("prompt") +activate Ui +Ui -> User : "Do you want to change: " +deactivate Ui + +User -> Ui: displayOldStoredValue("prompt", "value") +activate Ui +Ui -> User : "Old: " +deactivate Ui + + + + + + +@enduml \ No newline at end of file diff --git a/docs/team/angelinawong1210.md b/docs/team/angelinawong1210.md new file mode 100644 index 0000000000..8f9b30dbc3 --- /dev/null +++ b/docs/team/angelinawong1210.md @@ -0,0 +1,37 @@ +# Nguyen Hoang Minh Ngoc - Project Portfolio Page + +## Overview + +**FlashBang** is a CLI app designed to provide students with a smart way of studying for their modules. The app will manage a limited number of flashcards for a small number of modules, optimized for users who prefer a CLI. + +To me, this project is a precious opportunity to build a comprehensive CLI app in a team, covering different tasks such as programming, documentation and testing. I also learned how to use the Git for better code collaboration, as well as practice essential coding conventions. + +## Summary of Contributions + +### Code Contributed +Here is the link to my [RepoSense report](https://nus-cs2113-ay2425s1.github.io/tp-dashboard/?search=angelinawong1210&breakdown=true&sort=groupTitle%20dsc&sortWithin=title&since=2024-09-20&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other). + +### Enhancements implemented +1. Implemeted the function of viewing all flashcards - **ViewCommand**, including creating the code and JUnit test cases. This function plays an essential role as it displays all the flashcards within a module. + +2. Updated the **Ui** class with welcome message and list of available commands of the app. + +3. Error handling enhancement for the **AddCommand** to capture invalid module names, questions and answers. +- For the module names, the app restricts users from creating flashcard set with empty module name. +- For the questions and answers, the app restricts users from including delimiters "|" in any questions or answers to avoid confusion in the storage process. + +### Contributions to the UG +- Updated the User Guide with **Target User Profile** and **Value Proposition** to provide an overview about the app for users. +- Added the Command Summary for user's quick reference. +- Updated proper examples for the **Edit** function. + +### Contributions to the DG: +- Created the class diagram and sequence diagram for the Ui class. +- Based on the created diagrams, wrote the interpretations. +- Fix minor cosmetics issue to enhance the readability. + +### Team-based tasks: +- Updated the Javadoc for some parts of the code base to improve readability. + +### Review/mentoring contributions: +- Helped to review and merge the pull requests for other team members: [List of PRs reviewed by me](https://github.com/AY2425S1-CS2113-T11-2/tp/pulls?q=is%3Apr+reviewed-by%3A%40me+is%3Aclosed) diff --git a/docs/team/frederickemerson.md b/docs/team/frederickemerson.md new file mode 100644 index 0000000000..3432c278fe --- /dev/null +++ b/docs/team/frederickemerson.md @@ -0,0 +1,47 @@ +# Frederick Amal Emerson's Project Portfolio Page + +## Project: Flashbang +Flashbang - is a desktop application for creating flashcards and and learning in an effective way. The user interacts with it using a CLI with predefied set of commands. It is written in Java, and has about 1000LoC. + +# Summary of Contributions +Given below are my contributions to the project. + +- ## Features +- Added ability to load and save flashcards to the user's system +- Added Edit command so users can edit saved flashcards and provided various flexibility options to make the process + smoother +- Added Help Command for easy viewing of available Commands +- Set up basic types Class,FlashCardSet,FlashCard + +- ## Enhancement +- Developed the core command for the Flashbang session (#189) and (#190), enabling users to engage in a flashcard-based Q&A session + with options to reveal answers upon request. Enhanced the command with a timer component to allow users to track the + time spent on each flashcard and the entire flashcard set. +- Added UI fixes : +- - Added Code Indication Pointer so users are aware of the typing zone to refine user input handling +- - Removed the repetitive spam of available commands for every invalid command with a simple direction to +- - Refactored the Card's ToString component so its more compact, informative and fits in with other command's invokation +- - Conducted refactoring of the `Parser` class (#83) to streamline command parsing using regular expressions by mainly + fixing regex expressions and logic issues +- - Resolved issues and enhanced the `view` command (#194) and (#196) to ensure accurate display of flashcards. + +- ## User Guide +- Wrote feature sections: `view`, `edit`, `search` + - **Edit**: Detailed instructions on how users can edit flashcards. + - **View**: Explained the process for viewing flashcards. + - **search**: Provided a comprehensive guide on using the search feature. + +- ## Developer Guide +- Wrote ‘Storage component’ section: + - Explained the role and functionality of the storage in loading and storing flashcards. +- Made Storage Class Diagram: + - Created a visual representation of the storage class structure and the types of object involved. +- Made Storage Sequence Diagram: + - Illustrated the sequence of operations involved in both writing and reading flashcards. + +- ## Testing +- Wrote testcases for ViewAll Command (#206) + +- ## Links +- **Code contributed:** [link](https://nus-cs2113-ay2425s1.github.io/tp-dashboard/?search=frederickemerson&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&since=2024-09-20&tabOpen=true&tabType=authorship&checkedFileTypes=docs~functional-code~test-code~other&tabAuthor=frederickemerson&tabRepo=AY2425S1-CS2113-T11-2%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false) +- **PRs:** [list of PRs from GitHub](https://github.com/AY2425S1-CS2113-T11-2/tp/pulls?q=+is%3Apr+author%3Afrederickemerson+) 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/mikolajed.md b/docs/team/mikolajed.md new file mode 100644 index 0000000000..48f268106c --- /dev/null +++ b/docs/team/mikolajed.md @@ -0,0 +1,27 @@ +# Mikolaj Jedrzejewski's Project Portfolio Page + +## Project: Flashbang +Flashbang - is a desktop application for creating flashcards and and learning in an effective way. The user interacts with it using a CLI with predefied set of commands. It is written in Java, and has about 1000LoC. + +Given below are my contributions to the project. + +- **New feature** Added ability to perform a quiz for a set of flashcards as it is called - Flashbang. + - What is does: the part implemented by me goes through all flashcards and displays a question and asks user about the answer. + - Justification: it is an essential part of the app to learn from flashcards - getting a question and not revealing the answer. + - Highlights: It is the most complex command that is used - it uses UI component for both input and output. +- **New feature/Enhancement** Did major refactoring of Parser class. + - It uses regular expression for extracting command type and the parameters. + - It uses a factory design pattern to create commands. + - Command creation separate from execution. +- **New feature** Added ability to display formatted outputs in the UI class and getting inputs. +- **New feature** Implemented command for viewing all flashcards in the app. +- **Enhancement** Removed dependency of command execution from Storage component. + - Was it hard: No, only one command was using it and that responsibility was moved elsewhere. +- **Testing** Added JUnit tests for the Parser class. +- **Code contributed:** [link](https://nus-cs2113-ay2425s1.github.io/tp-dashboard/?search=mikolajed&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2024-09-20&tabOpen=true&tabType=authorship&tabAuthor=mikolajed&tabRepo=AY2425S1-CS2113-T11-2%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false) +- **Documentation** + - Added sequence diagram for Parser and described its workings. + - Updated UG with a few LoC + - Unified style for PlantUML diagrams. +- **Community** + - PRs: [list of PRs from GitHub](https://github.com/AY2425S1-CS2113-T11-2/tp/pulls?q=is%3Apr+is%3Aclosed+reviewed-by%3A%40me) diff --git a/docs/team/mikolajed.pdf b/docs/team/mikolajed.pdf new file mode 100644 index 0000000000..8682c21817 Binary files /dev/null and b/docs/team/mikolajed.pdf differ diff --git a/docs/team/raneeng.md b/docs/team/raneeng.md new file mode 100644 index 0000000000..423a772f20 --- /dev/null +++ b/docs/team/raneeng.md @@ -0,0 +1,68 @@ +# Project Portfolio Page (PPP) + +## Overview +**FlashBang** is a CLI app designed to provide students with a smart way of studying for their modules. The app will manage a limited number of flashcards for a small number of modules, optimized for users who prefer a CLI. + +## Summary of Contributions + +### Code Contributed +[RepSense Link](https://nus-cs2113-ay2425s1.github.io/tp-dashboard/?search=raneeng&breakdown=true&sort=groupTitle%20dsc&sortWithin=title&since=2024-09-20&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other) + +### Enhancements Implemented +1. **Command Classes:** + - **How**: Implemented classes such as `AddCommand`, `DeleteCommand`, `FlashBangCommand`, etc. + +2. **Command Class Testing:** + - **How**: Used JUnit framework to write tests covering different scenarios and edge cases. + +3. **Show FlashBang Percentage:** + - **How**: Added methods to calculate and display the percentage based on user performance. + +4. **Show FlashBang Mistakes:** + - **How**: Implemented methods to track incorrect answers and present them to the user. + +### Contributions to User Guide (UG) +[UG](https://ay2425s1-cs2113-t11-2.github.io/tp/UserGuide.html) +- Wrote feature sections: `add`, `delete`, `flashbang` + - **Add**: Detailed instructions on how users can add new flashcards. + - **Delete**: Explained the process for removing flashcards. + - **FlashBang**: Provided a comprehensive guide on using the flashbang feature. + +### Contributions to Developer’s Guide (DG) +[DG](https://ay2425s1-cs2113-t11-2.github.io/tp/DeveloperGuide.html) +- Wrote ‘Parser component’ section: + - Explained the role and functionality of the parser in interpreting user commands. +- Made Parser Partial Class Diagram: + - Created a visual representation of the parser structure. +- Made Parser Delete Sequence Diagram: + - Illustrated the sequence of operations for the delete command. + +### Contributions to Team-Based Tasks +1. Conducting Code Reviews and Providing Feedback +2. Maintaining the Issue Tracker +3. Updating User Docs + +### Review/Mentoring Contributions: +[Example 1](https://github.com/AY2425S1-CS2113-T11-2/tp/pull/160) +[Example 2](https://github.com/AY2425S1-CS2113-T11-2/tp/pull/146) + +### Contributions Beyond the Project Team +[Bugs reported in other team's products](https://github.com/raneeng/ped/issues) + +## DG Extract + +#### Structure +Below is a partial class diagram showing the interactions of the `Parser` class. +![Parser class diagram](./../diagrams/ParserPartialClassDiagram.jpg) + +The sequence diagram below illustrates the interactions taking `parseCommand(“delete --m cs2113 --i 1”)` as an example. +![Sample delete call sequence diagram](./../diagrams/ParserDeleteSequenceDiagram.png) + +#### Example +How the `Parser` component works: +1. The `Parser` receives the command input. +2. It identifies the command type using `parseCommandType`. +3. Depending on the command type, it creates the corresponding command object (e.g., `AddCommand`). +4. The created command is executed, producing a `CommandResult`. +5. The `CommandResult` is then used by `Ui` to provide feedback to the user. + diff --git a/docs/team/raneeng.pdf b/docs/team/raneeng.pdf new file mode 100644 index 0000000000..2ca4d2fc77 Binary files /dev/null and b/docs/team/raneeng.pdf differ 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/main/java/seedu/duke/Flashbang.java b/src/main/java/seedu/duke/Flashbang.java new file mode 100644 index 0000000000..24fee323be --- /dev/null +++ b/src/main/java/seedu/duke/Flashbang.java @@ -0,0 +1,66 @@ +package seedu.duke; + +import seedu.duke.flashutils.commands.Command; +import seedu.duke.flashutils.commands.CommandResult; +import seedu.duke.flashutils.commands.DeleteCommand; +import seedu.duke.flashutils.commands.QuitCommand; +import seedu.duke.flashutils.types.FlashBook; +import seedu.duke.flashutils.utils.Parser; +import seedu.duke.flashutils.utils.Storage; +import seedu.duke.flashutils.utils.Ui; + +import java.io.IOException; + +import static seedu.duke.flashutils.utils.Ui.displayCommands; + +public class Flashbang { + /** + * Main entry-point for the java.duke.Flashbang application. + */ + + private Ui ui; + private Storage storage; + private FlashBook flashBook; + + private Flashbang(String dataPath) { + ui = new Ui(); + storage = new Storage(dataPath); + try { + FlashBook.setInstance(storage.readFlashCardsFromFile()); + } catch (IOException e) { + System.out.println(e.getMessage()); + } + flashBook = FlashBook.getInstance(); + } + + private void run() { + Ui.welcomeMessage(); + displayCommands(); + String input = ""; + Command command = null; + while (!(command instanceof QuitCommand)) { + try { + input = Ui.getRequest().trim(); + command = Parser.parseCommand(input); + CommandResult result = command.execute(); + Ui.printResponse(result.feedbackToUser); + storage.writeFlashBookToFile(FlashBook.getInstance()); + if (command instanceof DeleteCommand && ((DeleteCommand) command).getTargetCard() == null) { + storage.deleteFlashCardSetFile(((DeleteCommand) command).getTargetSet().getModuleName()); + } + } catch (IllegalArgumentException e) { + Ui.printResponse(e.getMessage()); + displayCommands(); + } catch (IOException e) { + Ui.printResponse(e.getMessage() + "\nAn IO Exception has been detected, please reset the App!"); + } + } + } + + /** + * Main function to run the Flashbang app + */ + public static void main(String[] args) { + new Flashbang("./data").run(); + } +} diff --git a/src/main/java/seedu/duke/flashutils/commands/AddCommand.java b/src/main/java/seedu/duke/flashutils/commands/AddCommand.java new file mode 100644 index 0000000000..31754f427e --- /dev/null +++ b/src/main/java/seedu/duke/flashutils/commands/AddCommand.java @@ -0,0 +1,85 @@ +package seedu.duke.flashutils.commands; + +import seedu.duke.flashutils.types.Card; +import seedu.duke.flashutils.types.FlashCardSet; +import seedu.duke.flashutils.utils.Ui; + +/** + * Adds a flashcard to flashcard set. + */ +public class AddCommand extends Command { + + // Confirmation message to be displayed to user, with placeholder for flashcard details + public static final String SUCCESS_MESSAGE = "Successfully added flashcard to module '%1$s'"; + + private Card cardToAdd; + private FlashCardSet targetSet; + + /** + * Constructs a new Add Command with specified module, question and answer + * This creates new card and add it to the desired module + * + * @param module + * @param question + * @param answer + */ + public AddCommand(FlashCardSet module, String question, String answer) { + if (module == null || question == null || answer == null) { + throw new NullPointerException("Please enter a valid input"); + } + + String currentModuleName = module.getModuleName(); + if (currentModuleName.contains("--m") || currentModuleName.trim().isEmpty()) { + throw new IllegalArgumentException(); + } + + cardToAdd = new Card(question, answer); + this.targetSet = module; + } + + /** + * Constructs a new Add Command that add an existing card into the module + * + * @param cardToAdd + * @param module + */ + public AddCommand(FlashCardSet module, Card cardToAdd) { + String currentModuleName = module.getModuleName(); + if (currentModuleName.contains("--m") || currentModuleName.trim().isEmpty()) { + throw new IllegalArgumentException("Please enter a valid module name"); + } + + this.cardToAdd = cardToAdd; + this.targetSet = module; + } + + /** + * Gets the card to be added + * + * @return The Card object to be added + */ + public Card getCardToAdd() { + return cardToAdd; + } + + /** + * Gets the module name of the card + * + * @return The target module + */ + public FlashCardSet getTargetSet() { + return targetSet; + } + + /** + * Prints result of the command, + * which includes the success message and the Card to be added + * + * @return The result of the command + */ + @Override + public CommandResult execute() { + targetSet.addCard(cardToAdd); + return new CommandResult(String.format(SUCCESS_MESSAGE, targetSet.getModuleName())); + } +} diff --git a/src/main/java/seedu/duke/flashutils/commands/Command.java b/src/main/java/seedu/duke/flashutils/commands/Command.java new file mode 100644 index 0000000000..833566fb90 --- /dev/null +++ b/src/main/java/seedu/duke/flashutils/commands/Command.java @@ -0,0 +1,13 @@ +package seedu.duke.flashutils.commands; + + +/** + * Represents an executable command. + */ +public abstract class Command { + + /** + * Executes the command and returns the result. + */ + public abstract CommandResult execute(); +} diff --git a/src/main/java/seedu/duke/flashutils/commands/CommandResult.java b/src/main/java/seedu/duke/flashutils/commands/CommandResult.java new file mode 100644 index 0000000000..c64a6c9217 --- /dev/null +++ b/src/main/java/seedu/duke/flashutils/commands/CommandResult.java @@ -0,0 +1,29 @@ +package seedu.duke.flashutils.commands; + +/** + * Represents the result of a command execution. + */ +public class CommandResult { + + // The feedback message displayed to user after command is executed + public String feedbackToUser; + + /** + * Construct a Command Result with specified feedback to user + * + * @param feedbackToUser + */ + public CommandResult(String feedbackToUser) { + this.feedbackToUser = feedbackToUser; + } + + /** + * Gets feedbackToUser + * + * @return feedbackToUser + */ + public String getFeedbackToUser() { + return feedbackToUser; + } + +} diff --git a/src/main/java/seedu/duke/flashutils/commands/DeleteCommand.java b/src/main/java/seedu/duke/flashutils/commands/DeleteCommand.java new file mode 100644 index 0000000000..afd57eea2d --- /dev/null +++ b/src/main/java/seedu/duke/flashutils/commands/DeleteCommand.java @@ -0,0 +1,71 @@ +package seedu.duke.flashutils.commands; + + +import seedu.duke.flashutils.types.Card; +import seedu.duke.flashutils.types.FlashBook; +import seedu.duke.flashutils.types.FlashCardSet; + +/** + * Removes flashcard from flashcard set. + */ +public class DeleteCommand extends Command { + + // Confirmation message to be displayed to user, with placeholder for flashcard details + public static final String SUCCESS_MESSAGE = "Successfully deleted flashcard(s): %1$s\n"; + + public static final int INDEX_OFFSET = 1; + + private Card targetCard; + private FlashCardSet targetSet; + + /** + * Constructs a Delete Command with specified module and card index + * + * @param module FlashCardSet to perform DeleteCommand on + * @param cardIndex Index of card to delete + */ + public DeleteCommand(FlashCardSet module, int cardIndex) throws IndexOutOfBoundsException { + targetSet = module; + if (cardIndex > 0) { + targetCard = targetSet.getCard(cardIndex - INDEX_OFFSET); + } else { + targetCard = null; + } + } + + /** + * Gets the target module that has flashcard to be deleted + * + * @return The module having the flashcard to be deleted + */ + public FlashCardSet getTargetSet() { + return targetSet; + } + + /** + * Gets the target card to be deleted + * @return The card to be deleted + */ + public Card getTargetCard() { + return targetCard; + } + + /** + * Prints result of the command, + * which includes the success message and the {@code Card} or {@code FlashCardSet} to be deleted + * + * @return The result of the command + */ + @Override + public CommandResult execute() { + CommandResult deleteResult; + if (targetCard != null) { + targetSet.removeCard(targetCard); + deleteResult = new CommandResult(String.format(SUCCESS_MESSAGE, targetCard)); + } else { + FlashBook.getInstance().deleteFlashCardSet(targetSet.getModuleName()); + deleteResult = new CommandResult(String.format(SUCCESS_MESSAGE, targetSet.toString())); + } + return deleteResult; + } +} diff --git a/src/main/java/seedu/duke/flashutils/commands/EditCommand.java b/src/main/java/seedu/duke/flashutils/commands/EditCommand.java new file mode 100644 index 0000000000..7d05d2ec18 --- /dev/null +++ b/src/main/java/seedu/duke/flashutils/commands/EditCommand.java @@ -0,0 +1,141 @@ +package seedu.duke.flashutils.commands; + +import seedu.duke.flashutils.types.Card; +import seedu.duke.flashutils.types.FlashCardSet; + +import static seedu.duke.flashutils.utils.Ui.getRequest; +import static seedu.duke.flashutils.utils.Ui.displayConfirmationQuestion; +import static seedu.duke.flashutils.utils.Ui.displayOldStoredValue; +import static seedu.duke.flashutils.utils.Ui.displayGetNewPromptFromUser; + +/** + * Updates information in an existing flashcard. + */ +public class EditCommand extends Command { + + // Confirmation message to be displayed to user, with placeholder for flashcard details + public static final String SUCCESS_MESSAGE = "Successfully edited flashcard"; + + public static final int INDEX_OFFSET = 1; + + private Card cardToEdit; + private Card newCard; + private FlashCardSet targetSet; + + /** + * Constructs an Edit Command with specified module, index, new question and new answer + * + * @param module FlashCardSet to perform EditCommand on + * @param cardIndex Index of card to edit + * @param newQuestion String to replace question of Card + * @param newAnswer String to replace answer of Card + */ + public EditCommand(FlashCardSet module, int cardIndex, String newQuestion, String newAnswer) { + this.targetSet = module; + this.cardToEdit = targetSet.getCard(cardIndex - INDEX_OFFSET); + this.newCard = new Card(newQuestion, newAnswer); + } + + /** + * Construct an Edit Command with specified module and index + * The new question and answer will be collected from user later on + * + * @param module FlashCardSet to perform EditCommand on + * @param cardIndex Index of card to edit + */ + public EditCommand(FlashCardSet module, int cardIndex) throws IndexOutOfBoundsException { + this.targetSet = module; + this.cardToEdit = targetSet.getCard(cardIndex - INDEX_OFFSET); + this.newCard = getUpdatedQuestionAnswerFromUser(cardToEdit); + } + + protected Card getUpdatedQuestionAnswerFromUser(Card cardToEdit) { + this.newCard = new Card(cardToEdit.getQuestion(), cardToEdit.getAnswer()); + + // Update question + String updatedQuestion = updateField(cardToEdit.getQuestion(), "Question"); + newCard.setQuestion(updatedQuestion); + + // Update answer + String updatedAnswer = updateField(cardToEdit.getAnswer(), "Answer"); + newCard.setAnswer(updatedAnswer); + + return newCard; + } + + /** + * Prompts the user to confirm if they want to update the specified field. + * If the user confirms, it will prompt for new input until valid data is entered. + * + * @param oldFieldValue the current value of the field + * @param fieldName the name of the field (e.g., "Question" or "Answer") + * @return the updated value entered by the user + */ + private String updateField(String oldFieldValue, String fieldName) { + displayOldStoredValue(fieldName, oldFieldValue); + displayConfirmationQuestion(fieldName); + + String confirmation = getRequest(); + + if (confirmation.equalsIgnoreCase("y")) { + String newFieldValue; + do { + displayGetNewPromptFromUser(fieldName); + newFieldValue = getRequest(); + + // Check if the new value is empty + if (newFieldValue == null || newFieldValue.trim().isEmpty()) { + System.out.println(fieldName + " cannot be empty. Please try again."); + } + } while (newFieldValue == null || newFieldValue.trim().isEmpty()); + + return newFieldValue; + } else if (confirmation.equalsIgnoreCase("n")) { + System.out.println("Alright, Noted! Using Old " + fieldName); + return oldFieldValue; // Return the old value if the user opts not to change it + } else { + System.out.println("Sorry! Unknown option entered. Using Old " + fieldName); + return oldFieldValue; // Return the old value in case of an unknown option + } + } + + /** + * Prints result of the command, + * which includes the success message and the Card to be edited + * + * @return The result of the command + */ + @Override + public CommandResult execute() { + cardToEdit.setQuestion(newCard.getQuestion()); + cardToEdit.setAnswer(newCard.getAnswer()); + return new CommandResult(SUCCESS_MESSAGE); + } + + /** + * Gets the module that has the card to be edited + * + * @return The module having the card to be edited + */ + public FlashCardSet getTargetSet() { + return targetSet; + } + + /** + * Gets the card to be edited + * + * @return The card to be edited + */ + public Card getCardToAdd() { + return cardToEdit; + } + + /** + * Gets the updated card + * + * @return The updated card + */ + public Card getNewCard() { + return newCard; + } +} diff --git a/src/main/java/seedu/duke/flashutils/commands/FlashbangCommand.java b/src/main/java/seedu/duke/flashutils/commands/FlashbangCommand.java new file mode 100644 index 0000000000..bbaa07b6a3 --- /dev/null +++ b/src/main/java/seedu/duke/flashutils/commands/FlashbangCommand.java @@ -0,0 +1,49 @@ +package seedu.duke.flashutils.commands; + +import seedu.duke.flashutils.types.FlashCardSet; + +/** + * Starts a FlashBang session, where questions for each flashcard are displayed + * and users can choose to display answers. + */ +public class FlashbangCommand extends Command { + // Confirmation message to be displayed to user, with placeholder for flashcard details + public static final String SUCCESS_MESSAGE = "Successful FlashBang for flashcard set: %1$s"; + private final FlashCardSet targetSet; + private long timerThreshold; + + + /** + * Constructs the Flashbang Command with specified target set (module) + * + * @param targetSet represents the FlashCardSet to be tested on + */ + public FlashbangCommand(FlashCardSet targetSet) { + this.targetSet = targetSet; + } + + public FlashbangCommand(FlashCardSet targetSet, long timerThreshold) { + this.targetSet = targetSet; + this.timerThreshold = timerThreshold; + } + + public long getTimerThreshold() { + return timerThreshold; + } + + /** + * Prints result of the command, + * which includes the success message and the module to be displayed + * + * @return The result of the command + */ + @Override + public CommandResult execute() { + targetSet.performFlashBang(timerThreshold); + return new CommandResult(String.format(SUCCESS_MESSAGE, targetSet.getModuleName())); + } + + public FlashCardSet getTargetSet() { + return targetSet; + } +} diff --git a/src/main/java/seedu/duke/flashutils/commands/HelpCommand.java b/src/main/java/seedu/duke/flashutils/commands/HelpCommand.java new file mode 100644 index 0000000000..a4f596a0ee --- /dev/null +++ b/src/main/java/seedu/duke/flashutils/commands/HelpCommand.java @@ -0,0 +1,27 @@ +package seedu.duke.flashutils.commands; + +public class HelpCommand extends Command { + @Override + public CommandResult execute() { + String availableCommands = "Available Commands: \n" + + " 1. Add a flashcard: \n" + + " \t add --m [Module Name] {--t [Topic] (optional)} --q [Question] --a [Answer] \n" + + " 2. View all flashcards of a module: \n" + + " \t view --m [Module Name] \n" + + " 3. View all flashcards: \n" + + " \t view --all\n" + + " 4. Delete a flashcard: \n" + + " \t delete --m [Module Name] --i [Index] \n" + + " \t deleteall --m [Module Name] \n" + + " 5. Edit a flashcard: \n" + + " \t edit --m [Module Name] --i [Index] --q [New Question] --a [New Answer] \n" + + " 6. Flashbang - view all the flashcards of a module without seeing the answers: \n" + + " \t flashbang --m [Module Name] --t [time] [unit (second/seconds/minute/minutes)]\n" + + " 7. Search for flashcards: \n" + + " \t search --m [Module Name] {/t (optional)} --s [Search Term] \n" + + " 8. Quit the app: \n" + + " \t quit"; + + return new CommandResult(availableCommands); + } +} diff --git a/src/main/java/seedu/duke/flashutils/commands/InvalidCommand.java b/src/main/java/seedu/duke/flashutils/commands/InvalidCommand.java new file mode 100644 index 0000000000..369c0a9519 --- /dev/null +++ b/src/main/java/seedu/duke/flashutils/commands/InvalidCommand.java @@ -0,0 +1,18 @@ +package seedu.duke.flashutils.commands; + +public class InvalidCommand extends Command { + private String errorMessage; + + public InvalidCommand() { + this.errorMessage = "uh oh bad command"; + } + + public InvalidCommand(String errorMessage) { + this.errorMessage = errorMessage; + } + + @Override + public CommandResult execute() { + throw new IllegalArgumentException(errorMessage); + } +} diff --git a/src/main/java/seedu/duke/flashutils/commands/QuitCommand.java b/src/main/java/seedu/duke/flashutils/commands/QuitCommand.java new file mode 100644 index 0000000000..43296a5ab8 --- /dev/null +++ b/src/main/java/seedu/duke/flashutils/commands/QuitCommand.java @@ -0,0 +1,18 @@ +package seedu.duke.flashutils.commands; + +/** + * Terminates the program. + */ +public class QuitCommand extends Command { + + /** + * Prints result of the command, + * which includes the success message + * + * @return The result of the command + */ + @Override + public CommandResult execute() { + return new CommandResult("Quit Flash Session"); + } +} diff --git a/src/main/java/seedu/duke/flashutils/commands/SearchCommand.java b/src/main/java/seedu/duke/flashutils/commands/SearchCommand.java new file mode 100644 index 0000000000..14fd4226f1 --- /dev/null +++ b/src/main/java/seedu/duke/flashutils/commands/SearchCommand.java @@ -0,0 +1,43 @@ +package seedu.duke.flashutils.commands; + +import seedu.duke.flashutils.types.Card; +import seedu.duke.flashutils.types.FlashCardSet; + +/** + * Represents searching for flashcards which match the term + */ +public class SearchCommand extends Command { + private final String searchTerm; + private final boolean byTopic; + private final FlashCardSet targetSet; + + /** + * Constructs a search command + * @param searchTerm represents the term that all found cards must contain + * @param byTopic represents whether the search term only checks topics + * @param targetSet represents the module being searched from + */ + public SearchCommand(String searchTerm, boolean byTopic, FlashCardSet targetSet) { + this.searchTerm = searchTerm; + this.byTopic = byTopic; + this.targetSet = targetSet; + } + + @Override + public CommandResult execute() { + StringBuilder matchingCards = new StringBuilder(); + int counter = 0; + for (Card card : targetSet) { + if ((byTopic && card.getTopic().contains(searchTerm)) + || (card.getAnswer().toLowerCase().contains(searchTerm) + || card.getQuestion().toLowerCase().contains(searchTerm))) { + matchingCards.append(++counter).append(". ").append(card).append("\n"); + } + } + if (matchingCards.isEmpty()) { + return new CommandResult("No Cards found :("); + } else { + return new CommandResult(matchingCards.toString()); + } + } +} diff --git a/src/main/java/seedu/duke/flashutils/commands/ViewAllCommand.java b/src/main/java/seedu/duke/flashutils/commands/ViewAllCommand.java new file mode 100644 index 0000000000..0a46556d3d --- /dev/null +++ b/src/main/java/seedu/duke/flashutils/commands/ViewAllCommand.java @@ -0,0 +1,32 @@ +package seedu.duke.flashutils.commands; + +import seedu.duke.flashutils.types.Card; +import seedu.duke.flashutils.types.FlashBook; +import seedu.duke.flashutils.types.FlashCardSet; + +import java.util.HashMap; +import java.util.Map; + +public class ViewAllCommand extends Command { + @Override + public CommandResult execute() { + HashMap sets = FlashBook.getInstance().getAllFlashCardSets(); + StringBuilder sb = new StringBuilder(); + + for (Map.Entry entry : sets.entrySet()) { + sb.append("MODULE NAME: ").append(entry.getKey()).append("\n"); + int flashCardIndex = 1; + for (Card card : entry.getValue()) { + sb.append(String.format("#%1$s -> ",flashCardIndex)).append(card.toString()).append("\n"); + flashCardIndex+=1; + } + sb.append("\n"); + } + + if (sb.length() >= 2 && sb.substring(sb.length() - 2).equals("\n\n")) { + sb.delete(sb.length() - 2, sb.length()); + } + + return new CommandResult(sb.toString()); + } +} diff --git a/src/main/java/seedu/duke/flashutils/commands/ViewCommand.java b/src/main/java/seedu/duke/flashutils/commands/ViewCommand.java new file mode 100644 index 0000000000..6b73ded6b2 --- /dev/null +++ b/src/main/java/seedu/duke/flashutils/commands/ViewCommand.java @@ -0,0 +1,49 @@ +package seedu.duke.flashutils.commands; + +import seedu.duke.flashutils.types.FlashCardSet; + +/** + * Displays flashcards for specified flashcard set. + */ +public class ViewCommand extends Command { + + // Confirmation message to be displayed to user, with placeholder for flashCardSet details + public static final String SUCCESS_MESSAGE = "All flashcards have been displayed for set: %1$s"; + public String currentModule; + + private FlashCardSet targetSet; + + /** + * Constructs a ViewCommand with a specified module + * @param module FlashCardSet to view + */ + public ViewCommand(FlashCardSet module) { + this.targetSet = module; + } + + /** + * Gets the module whose flashcards will be displayed + */ + public void getModuleToView() { + currentModule = targetSet.getModuleName(); + } + + /** + * Prints the command result, + * which includes the success message and the flashcards under that module + */ + @Override + public CommandResult execute() { + getModuleToView(); + targetSet.viewFlashCards(currentModule); + return new CommandResult(String.format(SUCCESS_MESSAGE, targetSet.getModuleName())); + } + + /** + * Gets the module to be displayed + * @return The module to be displayed + */ + public FlashCardSet getTargetSet() { + return targetSet; + } +} diff --git a/src/main/java/seedu/duke/flashutils/exceptions/FlashCardSetDoesNotExistException.java b/src/main/java/seedu/duke/flashutils/exceptions/FlashCardSetDoesNotExistException.java new file mode 100644 index 0000000000..0b9e796111 --- /dev/null +++ b/src/main/java/seedu/duke/flashutils/exceptions/FlashCardSetDoesNotExistException.java @@ -0,0 +1,6 @@ +package seedu.duke.flashutils.exceptions; + +public class FlashCardSetDoesNotExistException extends Exception { + + public FlashCardSetDoesNotExistException() {} +} diff --git a/src/main/java/seedu/duke/flashutils/types/Card.java b/src/main/java/seedu/duke/flashutils/types/Card.java new file mode 100644 index 0000000000..99a42d9a87 --- /dev/null +++ b/src/main/java/seedu/duke/flashutils/types/Card.java @@ -0,0 +1,54 @@ +package seedu.duke.flashutils.types; + +/** + * Represents an individual flashcard with a question, answer and topic + */ +public class Card { + private String question; + private String answer; + private String topic; + + public Card(String question, String answer) { + this.question = question; + this.answer = answer; + this.topic = ""; + } + + public Card(String question, String answer, String topic) { + this.question = question; + this.answer = answer; + this.topic = topic; + } + + public String getQuestion() { + return question; + } + + public String getTopic() { + return topic; + } + + public void setQuestion(String question) { + this.question = question; + } + + public String getAnswer() { + return answer; + } + + public void setAnswer(String answer) { + this.answer = answer; + } + + public String toWritableString() { + return String.format("%1$s | %2$s | %3$s", question, answer, topic); + } + + @Override + public String toString() { + if(topic==null || topic.isEmpty() || topic.equalsIgnoreCase("null")){ + return String.format("%1$s : %2$s", question, answer); + } + return String.format("%1$s : %2$s (Topic: %3$s)", question, answer, topic); + } +} diff --git a/src/main/java/seedu/duke/flashutils/types/FlashBook.java b/src/main/java/seedu/duke/flashutils/types/FlashBook.java new file mode 100644 index 0000000000..4e439db662 --- /dev/null +++ b/src/main/java/seedu/duke/flashutils/types/FlashBook.java @@ -0,0 +1,55 @@ +package seedu.duke.flashutils.types; + +import java.util.HashMap; + +/** + * Represents the complete list of flashcards + */ +public class FlashBook { + + private static FlashBook instance = new FlashBook(); + + private final HashMap allFlashCardSets; + + private FlashBook() { + this.allFlashCardSets = new HashMap<>(); + } + + private FlashBook(HashMap flashCards) { + this.allFlashCardSets = flashCards; + } + + public static FlashBook getInstance() { + if (instance == null) { + instance = new FlashBook(); + } + return instance; + } + + public static void setInstance(HashMap flashCards) { + instance = new FlashBook(flashCards); + } + + public HashMap getAllFlashCardSets() { + return allFlashCardSets; + } + + public void addFlashCardSet(String module) { + allFlashCardSets.put(module, new FlashCardSet(module)); + } + + public FlashCardSet getFlashCardSet(String module) { + if (allFlashCardSets.get(module) == null) { + addFlashCardSet(module); + } + return allFlashCardSets.get(module); + } + public void deleteFlashCardSet(String module) { + allFlashCardSets.remove(module); + } + + public boolean flashCardSetExists(String module) { + return allFlashCardSets.get(module) != null; + } + +} diff --git a/src/main/java/seedu/duke/flashutils/types/FlashCardSet.java b/src/main/java/seedu/duke/flashutils/types/FlashCardSet.java new file mode 100644 index 0000000000..8cb609623d --- /dev/null +++ b/src/main/java/seedu/duke/flashutils/types/FlashCardSet.java @@ -0,0 +1,156 @@ +package seedu.duke.flashutils.types; + +import seedu.duke.flashutils.utils.Ui; + +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +/** + * Represents the list of flashcards of the same module + */ +public class FlashCardSet implements Iterable { + + private final ArrayList flashCardSet; + private final String moduleName; + + public FlashCardSet(String module) { + this.moduleName = module; + this.flashCardSet = new ArrayList<>(); + } + + public FlashCardSet(String module, ArrayList flashCardSet) { + this.moduleName = module; + this.flashCardSet = flashCardSet; + } + + public String getModuleName() { + return this.moduleName; + } + + public ArrayList getFlashCardSet() { + return this.flashCardSet; + } + + public Card getCard(int cardIndex) throws IndexOutOfBoundsException { + if (!indexIsValid(cardIndex)) { + throw new IndexOutOfBoundsException(); + } + return this.flashCardSet.get(cardIndex); + } + + public void addCard(Card toAdd) { + flashCardSet.add(toAdd); + assert flashCardSet.contains(toAdd); + } + + public void removeCard(Card toRemove) { + flashCardSet.remove(toRemove); + assert !flashCardSet.contains(toRemove); + } + + + // Displays all flashcards (view command) in FLashCardSet + public void viewFlashCards(String module) { + String currentModule = getModuleName(); + assert (currentModule != null); + if ((currentModule != null) && (currentModule.equals(module)) && (!flashCardSet.isEmpty())) { + int index = 1; + System.out.println("_".repeat(50)); + for (Card flashCard : flashCardSet) { + System.out.println(index + ". " + flashCard); + System.out.println("_".repeat(50)); + index++; + } + } else if (flashCardSet.isEmpty()) { + Ui.printResponse("No flashcards found for this module."); + } + } + + public void performFlashBang(long timerThreshold) { + //start keeps track of time spent in answering all questions, + // recurring keeps track of time spent in answering each question + Date start = new Date(); + Date recurring = new Date(); + // variables to store the number of correct and wrong answers + int flashCardIndex = 0; + int correctAnswers = 0; + int wrongAnswers = 0; + + List mistakes = new ArrayList<>(); + for (Card card : flashCardSet) { + Ui.printResponse("Flashcard no." + flashCardIndex + "\n\t" + card.getQuestion()); + Ui.printResponse("Reveal the answer? (y/n)"); + String reveal = Ui.getRequest(); + + while (!reveal.equalsIgnoreCase("y") && !reveal.equalsIgnoreCase("n")) { + Ui.printResponse("Invalid input. Please enter 'y' or 'n'."); + reveal = Ui.getRequest(); + } + if (reveal.equals("y")) { + System.out.println("Answer : "+card.getAnswer()); + } + + Ui.printResponse("Did you get the correct answer? (y/n)"); + String answerCorrect = Ui.getRequest(); + + while (!answerCorrect.equalsIgnoreCase("y") && !answerCorrect.equalsIgnoreCase("n")) { + Ui.printResponse("Invalid input. Please enter 'y' or 'n'."); + answerCorrect = Ui.getRequest(); + } + + if (answerCorrect.equals("y")) { + correctAnswers+=1; + } else if (answerCorrect.equals("n")) { + wrongAnswers+=1; + mistakes.add(card); // Add card to the mistake list + } + + double timeSpentPerQuestion = Math.round(((new Date()).getTime()-recurring.getTime())/1000.00); + + Ui.printResponse("You spent "+timeSpentPerQuestion+" seconds reviewing this flashcard."); + recurring = new Date(); + + if(timerThreshold > 0) { + if (recurring.getTime() - start.getTime() > timerThreshold) { + Ui.printResponse("Oops You've run out of time! "); + } + } + flashCardIndex++; + } + + // Calculate percentage of right/wrong answers + int totalAnswers = correctAnswers + wrongAnswers; + double correctPercentage = (double) correctAnswers / totalAnswers * 100; + System.out.println("Your score is: " + correctPercentage + "% (" + correctAnswers + "/" + totalAnswers + ")"); + // Print mistakes list + System.out.println("You answered the following flashcards incorrectly:\n"); + for (Card card : mistakes) { + System.out.println(card.toString()); + } + } + + @Override + public String toString() { + StringBuilder setString = new StringBuilder(String.format("MODULE: %1$s\n", moduleName)); + for (Card card : flashCardSet) { + setString.append(card.toString()).append("\n"); + } + return setString.toString(); + } + + public int getNumberOfFlashcards() { + return flashCardSet.size(); + } + + public boolean indexIsValid(int index) { + return index >= 0 && index < flashCardSet.size(); + } + + @Override + public Iterator iterator() { + return flashCardSet.iterator(); + } + +} diff --git a/src/main/java/seedu/duke/flashutils/utils/Parser.java b/src/main/java/seedu/duke/flashutils/utils/Parser.java new file mode 100644 index 0000000000..60c399608c --- /dev/null +++ b/src/main/java/seedu/duke/flashutils/utils/Parser.java @@ -0,0 +1,256 @@ +package seedu.duke.flashutils.utils; + +import seedu.duke.flashutils.commands.AddCommand; +import seedu.duke.flashutils.commands.Command; +import seedu.duke.flashutils.commands.DeleteCommand; +import seedu.duke.flashutils.commands.EditCommand; +import seedu.duke.flashutils.commands.FlashbangCommand; +import seedu.duke.flashutils.commands.HelpCommand; +import seedu.duke.flashutils.commands.InvalidCommand; +import seedu.duke.flashutils.commands.QuitCommand; +import seedu.duke.flashutils.commands.SearchCommand; +import seedu.duke.flashutils.commands.ViewAllCommand; +import seedu.duke.flashutils.commands.ViewCommand; + + +import seedu.duke.flashutils.exceptions.FlashCardSetDoesNotExistException; +import seedu.duke.flashutils.types.Card; +import seedu.duke.flashutils.types.FlashBook; +import seedu.duke.flashutils.types.FlashCardSet; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Parser { + private enum CommandType { Add, Delete, DeleteAll, Edit, View, FlashBang, Quit, Invalid, Search, Help } + + private static CommandType parseCommandType(String input) { + String commandKeyword = "^(\\badd\\b|\\bdelete\\b|\\bdeleteall\\b|\\bedit\\b|\\bview\\b|\\bflashbang\\b" + + "|\\bquit\\b|\\bsearch\\b|\\bhelp\\b)"; + Pattern commandPattern = Pattern.compile(commandKeyword); + Matcher matcher = commandPattern.matcher(input); + if (matcher.find()) { + return switch (matcher.group(1).toLowerCase()) { + case "add" -> CommandType.Add; + case "delete" -> CommandType.Delete; + case "edit" -> CommandType.Edit; + case "view" -> CommandType.View; + case "flashbang" -> CommandType.FlashBang; + case "search" -> CommandType.Search; + case "quit" -> CommandType.Quit; + case "help" -> CommandType.Help; + default -> CommandType.Invalid; + }; + } + return CommandType.Invalid; + } + + public static Command parseCommand(String input) { + CommandType commandType = parseCommandType(input); + return switch (commandType) { + case Add -> createAddCommand(input); + case Delete -> createDeleteCommand(input); + case Edit -> createEditCommand(input); + case View -> createViewCommand(input); + case FlashBang -> createFlashbangCommand(input); + case Search -> createSearchCommand(input); + case Quit -> createQuitCommand(); + case Help -> createHelpCommand(); + default -> new InvalidCommand(); + }; + } + + public static Command createHelpCommand(){ + return new HelpCommand(); + } + + public static Command createAddCommand(String input) { + Pattern addPattern = Pattern.compile("--m\\s+(.+?)(?:\\s+--t\\s+(.+))?\\s+--q\\s+(.+?)\\s+--a\\s+(.+)"); + Matcher matcher = addPattern.matcher(input); + if (matcher.find()) { + String moduleName = matcher.group(1); + String topic = matcher.group(2); + if (!(moduleName.contains("--m") || moduleName.trim().isEmpty())) { + FlashCardSet module = FlashBook.getInstance().getFlashCardSet(moduleName); + String question = matcher.group(3); + String answer = matcher.group(4); + if (question.contains("|") && answer.contains("|")) { + throw new IllegalArgumentException("Please enter another pair of question and answer." + + " Valid question and answer cannot include '|' "); + } + if (question.contains("|")) { + throw new IllegalArgumentException("Please enter another question." + + " A valid question cannot include '|' "); + } + if (answer.contains("|")) { + throw new IllegalArgumentException("Please enter another answer." + + " A valid answer cannot include '|' "); + } + + if (topic == null) { + topic = ""; + } + assert !(module == null); + return new AddCommand(module, new Card(question, answer, topic)); + } else { + throw new IllegalArgumentException("Please enter a valid module name"); + } + } else { + return new InvalidCommand(); + } + } + + public static Command createDeleteCommand(String input) { + try { + Pattern deletePattern = Pattern.compile("-m\\s+(.+?)(?=\\s+--i|$)(?:\\s+--i\\s+(\\d+))?"); + Matcher matcher = deletePattern.matcher(input); + if (matcher.find()) { + String moduleName = matcher.group(1); + + if (!FlashBook.getInstance().flashCardSetExists(moduleName)) { + throw new FlashCardSetDoesNotExistException(); + } + + FlashCardSet module = FlashBook.getInstance().getFlashCardSet(moduleName); + int index; + if (matcher.group(2) != null) { + index = Integer.parseInt(matcher.group(2)); + } else { + index = -1; + } + return new DeleteCommand(module, index); + } else { + return new InvalidCommand(); + } + + } catch (IndexOutOfBoundsException e) { + Ui.printResponse("Please enter a valid index"); + return new InvalidCommand(); + + } catch (FlashCardSetDoesNotExistException e) { + return new InvalidCommand("No such module exists"); + } + } + + + public static Command createEditCommand(String input) { + try { + Pattern editPattern = Pattern. + compile("--m\\s+(.+?)\\s+--i\\s+(\\d+)(?:\\s+--q\\s+(.+?)\\s+--a\\s+(.+))?$"); + Matcher matcher = editPattern.matcher(input); + + if (matcher.find()) { + String moduleName = matcher.group(1); + + if (!FlashBook.getInstance().flashCardSetExists(moduleName)) { + throw new FlashCardSetDoesNotExistException(); + } + + FlashCardSet module = FlashBook.getInstance().getFlashCardSet(moduleName); + int index = Integer.parseInt(matcher.group(2)); + + // Check if new question and answer are provided in the input + if (matcher.group(4) != null && matcher.group(5) != null) { + // Use the provided question and answer + String newQuestion = matcher.group(4); + String newAnswer = matcher.group(5); + return new EditCommand(module, index, newQuestion, newAnswer); + } else { + // No question and answer provided; create EditCommand with prompts + return new EditCommand(module, index); + } + } else { + return new InvalidCommand("Please enter a valid index"); + } + } catch (IndexOutOfBoundsException e) { + return new InvalidCommand(); + + } catch (FlashCardSetDoesNotExistException e) { + return new InvalidCommand("No such module exists"); + } + + } + + public static Command createViewCommand(String input) { + Pattern viewModulePattern = Pattern.compile("--m\\s+(.+)"); + Matcher matcher = viewModulePattern.matcher(input); + Pattern viewAllModulePattern = Pattern.compile("--all"); + Matcher matcherAll = viewAllModulePattern.matcher(input); + if (matcher.find()) { + String moduleName = matcher.group(1); + FlashCardSet module = FlashBook.getInstance().getFlashCardSet(moduleName); + return new ViewCommand(module); + } else if (matcherAll.find()) { + return new ViewAllCommand(); + } else { + return new InvalidCommand(); + } + } + + public static Command createFlashbangCommand(String input) { + Pattern flashbangPattern = Pattern + .compile("--m\\s+(\\S+)(?:\\s+--t\\s+(\\d+)\\s+(second|seconds|minute|minutes))?$"); + Matcher matcher = flashbangPattern.matcher(input); + if (matcher.find()) { + String moduleName = matcher.group(1); + String timer = matcher.group(2) != null ? input.substring(input.indexOf("--t")+3).trim() : ""; + FlashCardSet module = FlashBook.getInstance().getFlashCardSet(moduleName); + if (!timer.isEmpty()) { + try{ + long milliseconds = parseTimer(timer); + return new FlashbangCommand(module, milliseconds); + } catch (IllegalArgumentException e){ + Ui.printResponse(e.getMessage()); + } + } + return new FlashbangCommand(module); + } else { + return new InvalidCommand(); + } + } + public static Command createSearchCommand(String input) { + Pattern searchPattern = Pattern.compile("--m\\s+(.+?)(?:\\s+(/t))?\\s+--s\\s+(.+)"); + Matcher searchMatcher = searchPattern.matcher(input); + if (searchMatcher.find()) { + String module = searchMatcher.group(1); + boolean byTopic = searchMatcher.group(2) != null && searchMatcher.group(2).equals("/t"); + String searchTerm = searchMatcher.group(3); + assert (!(module == null || searchTerm == null)); + return new SearchCommand(searchTerm, byTopic, FlashBook.getInstance().getFlashCardSet(module)); + } else { + return new InvalidCommand("Invalid format for search =.="); + } + } + + private static long parseTimer(String timer) { + timer = timer.trim().toLowerCase(); + + String[] parts = timer.split(" "); + if (parts.length != 2) { + throw new IllegalArgumentException("Invalid timer format. Expected format: ' '"); + } + + // Parse the number part + double value; + try { + value = Double.parseDouble(parts[0]); + + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Invalid number format: " + parts[0]); + } + + // Determine the unit part + String unit = parts[1]; + + return switch (unit) { + case "s","second", "seconds" -> (long) (value * 1000); + case "min","minute", "minutes" -> (long) (value * 1000 * 60); + default -> throw new IllegalArgumentException("Unsupported time unit: " + + unit + "supported time units are second,seconds,minute,minutes"); + }; + } + + public static Command createQuitCommand() { + return new QuitCommand(); + } +} diff --git a/src/main/java/seedu/duke/flashutils/utils/Storage.java b/src/main/java/seedu/duke/flashutils/utils/Storage.java new file mode 100644 index 0000000000..ee72a1c331 --- /dev/null +++ b/src/main/java/seedu/duke/flashutils/utils/Storage.java @@ -0,0 +1,153 @@ +package seedu.duke.flashutils.utils; + +import seedu.duke.flashutils.types.Card; +import seedu.duke.flashutils.types.FlashBook; +import seedu.duke.flashutils.types.FlashCardSet; + +import javax.xml.namespace.QName; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Scanner; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Storage class is responsible for storing and reading data inputted from Flashbang + */ +public class Storage { + private final File directory; + + public Storage(String directoryPath) { + this.directory = new File(directoryPath); + createDir(); + } + + /** + * This function creates a directory at the file path of the class instance + */ + private void createDir() { + try { + if(!directory.exists()){ + if(!directory.mkdirs()){ + throw new IOException("Could not create directory"); + } + } + } catch (IOException e) { + System.out.println(e.getMessage()); + } + } + + /** + * This function formats lines from the txt file into appropriate strings for flashcards + * @param line represents a line from the txt file as a String + * @return a {@code Card} formatted from the file + */ + private Card cardFormatter(String line) { + Pattern cardPattern = Pattern.compile( + "(.+?)\\s*\\|\\s*(.+?)\\s*\\|\\s*(.+?)?"); + Matcher cardMatcher = cardPattern.matcher(line); + boolean isMatch = cardMatcher.matches(); + assert isMatch: "Text Format Problem in File"; + if (cardMatcher.matches()) { + String question = cardMatcher.group(1); + String answer = cardMatcher.group(2); + String topic = cardMatcher.group(3); + return new Card(question, answer, topic); + } + return null; + } + + /** + * Reads the txt file with the matching name as the module + * @param module represents the name of the module of the {@code FlashCardSet} + * @param flashCardSetFile represents the file containing the {@code Card} in the set + * @return a {@code FlashCardSet} read from the file + * @throws IOException when an input/output error occurs + */ + private FlashCardSet readFlashCardSetFromFile(String module, File flashCardSetFile) throws IOException { + + ArrayList cards = new ArrayList<>(); + + try { + Scanner scanner = new Scanner(flashCardSetFile); + while (scanner.hasNext()) { + Card card = cardFormatter(scanner.nextLine()); + if (card != null) { + cards.add(card); + } + } + scanner.close(); + } catch (IOException e) { + throw new IOException("An error occurred while reading from the file."); + } + return new FlashCardSet(module,cards); + } + + private void createFile(File file) { + try { + if(!file.exists()){ + if(!file.createNewFile()){ + throw new IOException("Could not create file"); + } + } + } catch (IOException e) { + System.out.println(e.getMessage()); + } + } + + /** + * Writes the {@code FlashBook} into a txt file + * @param flashBook represents the {@code FlashBook} instance to be written + */ + public void writeFlashBookToFile(FlashBook flashBook){ + flashBook.getAllFlashCardSets().forEach((module, flashCardSet)-> { + File flashCardSetFile = new File(directory, module+".txt"); + createFile(flashCardSetFile); + try { + FileWriter fileWriter = new FileWriter(flashCardSetFile.getPath()); + for (Card card : flashCardSet.getFlashCardSet()) { + fileWriter.write(card.toWritableString()+"\n"); + } + fileWriter.close(); + } catch (IOException e) { + System.out.println("An error occurred while writing to the file. "); + } + }); + } + + /** + * Reads the all txt files within the directory + * @return a {@code HashMap}. The key is a {@code String} which is the name of the txt file + * and the value stored is the {@code FlashCardSet} read from the file. + * @throws IOException when an input/output error occurs + */ + public HashMap readFlashCardsFromFile() throws IOException { + HashMap flashCard = new HashMap<>(); + File[] files = directory.listFiles((dir, name) -> name.endsWith(".txt")); + + if (files != null && files.length > 0) { + for (File file : files) { + String module = file.getName().split("\\.")[0]; + flashCard.put(module,readFlashCardSetFromFile(module, file)); + } + } else { + System.out.println("No text files found in the directory."); + } + + return flashCard; + } + public void deleteFlashCardSetFile(String module) throws IOException { + File[] fileList = directory.listFiles((dir, name) -> name.equals(module + ".txt")); + assert fileList != null; + if (fileList[0].exists() && fileList[0].delete()) { + System.out.println("Successfully deleted file"); + } else { + throw new IOException(); + } + } +} + + diff --git a/src/main/java/seedu/duke/flashutils/utils/StubStorage.java b/src/main/java/seedu/duke/flashutils/utils/StubStorage.java new file mode 100644 index 0000000000..4a12fbf187 --- /dev/null +++ b/src/main/java/seedu/duke/flashutils/utils/StubStorage.java @@ -0,0 +1,22 @@ +package seedu.duke.flashutils.utils; + +import seedu.duke.flashutils.types.FlashBook; + +public class StubStorage extends Storage { + + private boolean isWriteFlashBookToFileCalled = false; + + public StubStorage() { + super("./data"); + } + + @Override + public void writeFlashBookToFile(FlashBook flashBook) { + super.writeFlashBookToFile(flashBook); + isWriteFlashBookToFileCalled = true; + } + + public boolean isWriteFlashBookToFileCalled() { + return isWriteFlashBookToFileCalled; + } +} diff --git a/src/main/java/seedu/duke/flashutils/utils/Ui.java b/src/main/java/seedu/duke/flashutils/utils/Ui.java new file mode 100644 index 0000000000..e66e831be2 --- /dev/null +++ b/src/main/java/seedu/duke/flashutils/utils/Ui.java @@ -0,0 +1,85 @@ +package seedu.duke.flashutils.utils; + +import java.util.Scanner; + +/** + * A class responsible for handling user interaction and displaying information to the console. + * It provides methods to display responses, get user input, and format the output. + */ +public class Ui { + private static Scanner scanner; + private static final String LINE_SEPARATOR = "_".repeat(50); + + /** + * Initializes the scanner object for reading user input. + */ + public Ui() { + scanner = new Scanner(System.in); + } + + /** + * Displays all available commands for users + */ + public static void displayCommands() { + String availableCommands = "Type help to view all the available commands"; + System.out.println(availableCommands); + System.out.println(LINE_SEPARATOR); + } + + /** + * Prints welcome message when users enter the app + * The welcome message includes the app's logo and introduction + */ + public static void welcomeMessage() { + String logo = "FlashBang"; + String intro = "Welcome to the FlashBang app - learning your modules through engaging flashcards"; + System.out.println(logo + "\n" + intro); + System.out.println(LINE_SEPARATOR); + } + + /** + * Prints a formatted response to the console with line separators. + * + * @param text The message to be printed. + */ + public static void printResponse(String text) { + text = LINE_SEPARATOR + "\n" + text + "\n" + LINE_SEPARATOR + "\n"; + System.out.print(text); + } + + /** + * Retrieves a user's input from the console. + * + * @return The raw string input from the user. + */ + public static String getRequest() { + System.out.print("> "); + return scanner.nextLine(); + } + + /** + * Prints the announcement to get the prompt from user + * @param prompt + */ + public static void displayGetNewPromptFromUser(String prompt) { + System.out.println("Enter new "+prompt+" :"); + } + + /** + * Prints the confirmation question + * @param prompt + */ + public static void displayConfirmationQuestion(String prompt) { + System.out.println("Do you want to change "+prompt+" (y/n):"); + } + + /** + * Prints the old value + * @param prompt + * @param value + */ + public static void displayOldStoredValue(String prompt, String value) { + System.out.println("Old "+prompt+" : "+value); + } + +} diff --git a/src/test/java/seedu/duke/DukeTest.java b/src/test/java/seedu/duke/FlashbangTest.java similarity index 74% rename from src/test/java/seedu/duke/DukeTest.java rename to src/test/java/seedu/duke/FlashbangTest.java index 2dda5fd651..2e0d73d3b5 100644 --- a/src/test/java/seedu/duke/DukeTest.java +++ b/src/test/java/seedu/duke/FlashbangTest.java @@ -4,9 +4,9 @@ import org.junit.jupiter.api.Test; -class DukeTest { +class FlashbangTest { @Test - public void sampleTest() { + public void flashbangTest() { assertTrue(true); } } diff --git a/src/test/java/seedu/duke/flashutils/commands/AddCommandTest.java b/src/test/java/seedu/duke/flashutils/commands/AddCommandTest.java new file mode 100644 index 0000000000..51ee798f63 --- /dev/null +++ b/src/test/java/seedu/duke/flashutils/commands/AddCommandTest.java @@ -0,0 +1,53 @@ +package seedu.duke.flashutils.commands; + +import org.junit.jupiter.api.Test; +import seedu.duke.flashutils.types.Card; +import seedu.duke.flashutils.types.FlashCardSet; +import seedu.duke.flashutils.utils.Parser; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class AddCommandTest { + @Test + public void testAddCommandConstructor() { + String testQuestion = "Some question"; + String testAnswer = "Some answer"; + FlashCardSet testModule = new FlashCardSet("Some module"); + AddCommand command = new AddCommand(testModule, new Card(testQuestion, testAnswer)); + + assertEquals(testQuestion, command.getCardToAdd().getQuestion()); + assertEquals(testAnswer, command.getCardToAdd().getAnswer()); + assertEquals(testModule, command.getTargetSet()); + } + + @Test public void testFlashcardActuallyAdded() { + FlashCardSet testModule = new FlashCardSet("Some module"); + Card testCard = new Card("Some question", "Some answer"); + AddCommand command = new AddCommand(testModule, testCard); + command.execute(); + assertTrue(testModule.getFlashCardSet().contains(testCard)); + } + + @Test public void testAddMultipleFlashcards() { + FlashCardSet testModule = new FlashCardSet("Some module"); + Card testCard1 = new Card("Question 1", "Answer 1"); + Card testCard2 = new Card("Question 2", "Answer 2"); + new AddCommand(testModule, testCard1).execute(); + new AddCommand(testModule, testCard2).execute(); + assertEquals(2, testModule.getFlashCardSet().size()); + } + + @Test + public void testInvalidAddCommand() { + String testString = "add --m --t --q --a "; + assertThrows(IllegalArgumentException.class, () -> Parser.parseCommand(testString).execute()); + } + + @Test public void testAddCommandNullFields() { + FlashCardSet testModule = new FlashCardSet("Some module"); + assertThrows(NullPointerException.class, () -> new AddCommand(testModule, null, "Answer")); + assertThrows(NullPointerException.class, () -> new AddCommand(testModule, "Question", null)); + } +} diff --git a/src/test/java/seedu/duke/flashutils/commands/DeleteCommandTest.java b/src/test/java/seedu/duke/flashutils/commands/DeleteCommandTest.java new file mode 100644 index 0000000000..d220924cdb --- /dev/null +++ b/src/test/java/seedu/duke/flashutils/commands/DeleteCommandTest.java @@ -0,0 +1,53 @@ +package seedu.duke.flashutils.commands; + +import org.junit.jupiter.api.Test; +import seedu.duke.flashutils.types.Card; +import seedu.duke.flashutils.types.FlashCardSet; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class DeleteCommandTest { + @Test + public void testDeleteCommandConstructor() { + String testQuestion = "Some question"; + String testAnswer = "Some answer"; + FlashCardSet testModule = new FlashCardSet("Some module"); + Card testCard = new Card(testQuestion, testAnswer); + testModule.addCard(testCard); + DeleteCommand command = new DeleteCommand(testModule, 1); + + assertEquals(testCard, command.getTargetCard()); + assertEquals(testModule, command.getTargetSet()); + } + + @Test + public void testSuccessfulDeleteCommand() { + String testQuestion = "Some question"; + String testAnswer = "Some answer"; + FlashCardSet testModule = new FlashCardSet("Some module"); + Card testCard = new Card(testQuestion, testAnswer); + + testModule.addCard(testCard); + DeleteCommand command = new DeleteCommand(testModule, 1); + command.execute(); + + assertFalse(testModule.getFlashCardSet().contains(testCard)); + } + + @Test + public void testInvalidIndex() { + FlashCardSet testModule = new FlashCardSet("Some module"); + Card testCard1 = new Card("Question 1", "Answer 1"); + Card testCard2 = new Card("Question 2", "Answer 2"); + new AddCommand(testModule, testCard1).execute(); + new AddCommand(testModule, testCard2).execute(); + + assertThrows(IndexOutOfBoundsException.class, () -> new DeleteCommand(testModule, -1) + .execute()); + assertThrows(IndexOutOfBoundsException.class, () -> new DeleteCommand(testModule, 3) + .execute()); + } + +} diff --git a/src/test/java/seedu/duke/flashutils/commands/EditCommandTest.java b/src/test/java/seedu/duke/flashutils/commands/EditCommandTest.java new file mode 100644 index 0000000000..0dc23b5313 --- /dev/null +++ b/src/test/java/seedu/duke/flashutils/commands/EditCommandTest.java @@ -0,0 +1,48 @@ +package seedu.duke.flashutils.commands; + +import org.junit.jupiter.api.Test; +import seedu.duke.flashutils.types.Card; +import seedu.duke.flashutils.types.FlashCardSet; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class EditCommandTest { + @Test + public void testEditCommandConstructor() { + FlashCardSet testModule = new FlashCardSet("Some module"); + testModule.addCard(new Card("Some Question", "Some Answer")); + + EditCommand command = new EditCommand(testModule, 0, "New Question", "New Answer"); + + assertEquals("New Question", command.getNewCard().getQuestion()); + assertEquals("New Answer", command.getNewCard().getAnswer()); + assertEquals(testModule, command.getTargetSet()); + } + + @Test + public void testSuccessfulCardEditCommand() { + String testQuestion = "Some question"; + String testAnswer = "Some answer"; + String newQuestion = "New Question"; + String newAnswer = "New Answer"; + FlashCardSet testModule = new FlashCardSet("Some module"); + Card testCard = new Card(testQuestion, testAnswer); + + testModule.addCard(testCard); + EditCommand command = new EditCommand(testModule, 0, newQuestion, newAnswer); + command.execute(); + + Card editedCard = testModule.getCard(0); + assertEquals(newQuestion, editedCard.getQuestion()); + assertEquals(newAnswer, editedCard.getAnswer()); + } + + @Test + public void testEditInvalidIndex() { + FlashCardSet testModule = new FlashCardSet("Some module"); + assertThrows(IndexOutOfBoundsException.class, () -> + new EditCommand(testModule, -1, "New Question", "New Answer")); + assertThrows(IndexOutOfBoundsException.class, () -> + new EditCommand(testModule, 1, "New Question", "New Answer")); + } +} diff --git a/src/test/java/seedu/duke/flashutils/commands/FlashbangCommandTest.java b/src/test/java/seedu/duke/flashutils/commands/FlashbangCommandTest.java new file mode 100644 index 0000000000..e0c63f1b49 --- /dev/null +++ b/src/test/java/seedu/duke/flashutils/commands/FlashbangCommandTest.java @@ -0,0 +1,25 @@ +package seedu.duke.flashutils.commands; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import seedu.duke.flashutils.types.FlashCardSet; +import seedu.duke.flashutils.utils.Ui; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class FlashbangCommandTest { + + @BeforeEach + public void setUp() { + String simulatedUserInput = "Some user input\n"; + new Ui(); // Initialize the Ui instance and scanner + } + + @Test + public void testFlashbangCommandConstructor() { + FlashCardSet testModule = new FlashCardSet("Some module"); + FlashbangCommand command = new FlashbangCommand(testModule); + assertEquals(testModule, command.getTargetSet()); + } + +} diff --git a/src/test/java/seedu/duke/flashutils/commands/QuitCommandTest.java b/src/test/java/seedu/duke/flashutils/commands/QuitCommandTest.java new file mode 100644 index 0000000000..efef798acf --- /dev/null +++ b/src/test/java/seedu/duke/flashutils/commands/QuitCommandTest.java @@ -0,0 +1,16 @@ +package seedu.duke.flashutils.commands; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class QuitCommandTest { + + @Test + public void testQuitCommandSuccessMessage() { + QuitCommand command = new QuitCommand(); + CommandResult result = command.execute(); + + assertEquals("Quit Flash Session", result.getFeedbackToUser()); + } + +} diff --git a/src/test/java/seedu/duke/flashutils/commands/ViewAllCommandTest.java b/src/test/java/seedu/duke/flashutils/commands/ViewAllCommandTest.java new file mode 100644 index 0000000000..4b0a1d6a2f --- /dev/null +++ b/src/test/java/seedu/duke/flashutils/commands/ViewAllCommandTest.java @@ -0,0 +1,96 @@ +package seedu.duke.flashutils.commands; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import seedu.duke.flashutils.types.Card; +import seedu.duke.flashutils.types.FlashBook; +import seedu.duke.flashutils.types.FlashCardSet; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class ViewAllCommandTest { + + private FlashCardSet module1; + private FlashCardSet module2; + private Card card1; + private Card card2; + private Card card3; + private FlashBook flashBook; + + @BeforeEach + public void setUp() { + // Set up the flashcards and sets for testing + card1 = new Card("What is Java?", "A programming language."); + card2 = new Card("What is OOP?", "Object-Oriented Programming."); + card3 = new Card("What is Python?","A programming language."); + + flashBook = FlashBook.getInstance(); + flashBook.addFlashCardSet("CS1010"); + flashBook.getFlashCardSet("CS1010").addCard(card1); + flashBook.getFlashCardSet("CS1010").addCard(card3); + flashBook.addFlashCardSet("CS2113"); + flashBook.getFlashCardSet("CS2113").addCard(card2); + } + + @Test + public void viewAllCommandTestExecute() { + // Test the execute method for ViewAllCommand, which should view all flashcards from all modules + ViewAllCommand command = new ViewAllCommand(); + CommandResult result = command.execute(); + + // Check that the command result contains flashcards for both modules + String expectedMessage = "MODULE NAME: CS1010\n#1 -> What is Java? : A programming language.\n" + + "#2 -> What is Python? : A programming language.\n\n" + + "MODULE NAME: CS2113\n#1 -> What is OOP? : Object-Oriented Programming."; + + assertEquals(expectedMessage, result.getFeedbackToUser()); + } + + @Test + public void viewAllCommandTestLongCardContent() { + // Test handling of flashcards with lengthy content + Card longCard = new Card("Describe the OSI model", + "The OSI model is a conceptual framework used to understand network interactions. " + + "It has 7 layers: Physical, Data Link, Network, Transport, Session, Presentation, and Application."); + flashBook.getFlashCardSet("CS1010").addCard(longCard); + + ViewAllCommand command = new ViewAllCommand(); + CommandResult result = command.execute(); + + String expectedMessage = "MODULE NAME: CS1010\n" + + "#1 -> What is Java? : A programming language.\n" + + "#2 -> What is Python? : A programming language.\n" + + "#3 -> Describe the OSI model : The OSI model is a conceptual framework used to understand network interactions. " + + "It has 7 layers: Physical, Data Link, Network, Transport, Session, Presentation, and Application.\n\n" + + "MODULE NAME: CS2113\n" + + "#1 -> What is OOP? : Object-Oriented Programming."; + + assertEquals(expectedMessage, result.getFeedbackToUser()); + } + + @Test + public void viewAllCommandTestEmptyModule() { + // Test the handling of a module with no flashcards + flashBook.deleteFlashCardSet("CS1010"); + ViewAllCommand command = new ViewAllCommand(); + CommandResult result = command.execute(); + + String expectedMessage = + "MODULE NAME: CS2113\n" + + "#1 -> What is OOP? : Object-Oriented Programming."; + + assertEquals(expectedMessage, result.getFeedbackToUser()); + } + + @Test + public void viewAllCommandTestEmptyBook() { + // Test the behavior when there are no flashcards in FlashBook + flashBook.deleteFlashCardSet("CS1010"); + flashBook.deleteFlashCardSet("CS2113");// Empty the FlashBook + ViewAllCommand command = new ViewAllCommand(); + CommandResult result = command.execute(); + + // Expect an empty result message as there are no flashcards + assertEquals("", result.getFeedbackToUser()); + } +} diff --git a/src/test/java/seedu/duke/flashutils/commands/ViewCommandTest.java b/src/test/java/seedu/duke/flashutils/commands/ViewCommandTest.java new file mode 100644 index 0000000000..526c108a27 --- /dev/null +++ b/src/test/java/seedu/duke/flashutils/commands/ViewCommandTest.java @@ -0,0 +1,23 @@ +package seedu.duke.flashutils.commands; + +import org.junit.jupiter.api.Test; +import seedu.duke.flashutils.types.FlashCardSet; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class ViewCommandTest { + + @Test + public void viewCommandTestEmpty() { + FlashCardSet module1 = new FlashCardSet("Module 1"); + assertTrue(module1.getFlashCardSet().isEmpty()); + } + + @Test + public void viewCommandTestConstructor() { + FlashCardSet module2 = new FlashCardSet("Module 2"); + ViewCommand command = new ViewCommand(module2); + assertEquals(module2, command.getTargetSet()); + } +} diff --git a/src/test/java/seedu/duke/flashutils/utils/ParserTest.java b/src/test/java/seedu/duke/flashutils/utils/ParserTest.java new file mode 100644 index 0000000000..78a0bd3ec2 --- /dev/null +++ b/src/test/java/seedu/duke/flashutils/utils/ParserTest.java @@ -0,0 +1,70 @@ +package seedu.duke.flashutils.utils; + +import org.junit.jupiter.api.Test; +import seedu.duke.flashutils.commands.AddCommand; +import seedu.duke.flashutils.commands.Command; +import seedu.duke.flashutils.commands.FlashbangCommand; +import seedu.duke.flashutils.commands.InvalidCommand; +import seedu.duke.flashutils.commands.QuitCommand; +import seedu.duke.flashutils.commands.SearchCommand; +import seedu.duke.flashutils.commands.ViewCommand; + +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class ParserTest { + + @Test + public void testParseAddCommand() { + String input = "add --m SampleModule --q What is Java? --a A programming language."; + Command command = Parser.parseCommand(input); + assertInstanceOf(AddCommand.class, command); + + AddCommand addCommand = (AddCommand) command; + assertEquals("SampleModule", addCommand.getTargetSet().getModuleName()); + assertEquals("What is Java?", addCommand.getCardToAdd().getQuestion()); + assertEquals("A programming language.", addCommand.getCardToAdd().getAnswer()); + } + + @Test + public void testParseViewCommand() { + String input = "view --m SampleModule"; + Command command = Parser.parseCommand(input); + assertInstanceOf(ViewCommand.class, command); + + ViewCommand viewCommand = (ViewCommand) command; + assertEquals("SampleModule", viewCommand.getTargetSet().getModuleName()); + } + + @Test + public void testParseFlashbangCommand() { + String input = "flashbang --m SampleModule --t 100"; + Command command = Parser.parseCommand(input); + assertInstanceOf(FlashbangCommand.class, command); + + FlashbangCommand flashbangCommand = (FlashbangCommand) command; + assertEquals("SampleModule", flashbangCommand.getTargetSet().getModuleName()); + } + + @Test + public void testParseQuitCommand() { + String input = "quit"; + Command command = Parser.parseCommand(input); + assertInstanceOf(QuitCommand.class, command); + } + + @Test + public void testParseInvalidCommand() { + String input = "invalid --m SampleModule"; + Command command = Parser.parseCommand(input); + assertInstanceOf(InvalidCommand.class, command); + } + + @Test + public void testParseSearchCommand() { + String input = "search --m SampleModule --s SearchTerm"; + Command command = Parser.parseCommand(input); + assertInstanceOf(SearchCommand.class, command); + + } +} diff --git a/src/test/java/seedu/duke/flashutils/utils/StorageTest.java b/src/test/java/seedu/duke/flashutils/utils/StorageTest.java new file mode 100644 index 0000000000..e83d9b6219 --- /dev/null +++ b/src/test/java/seedu/duke/flashutils/utils/StorageTest.java @@ -0,0 +1,99 @@ +package seedu.duke.flashutils.utils; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import seedu.duke.flashutils.types.Card; +import seedu.duke.flashutils.types.FlashBook; +import seedu.duke.flashutils.types.FlashCardSet; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Scanner; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class StorageTest { + private final String directoryPath = "./data"; + private File testFile; + private Storage storage; + + @BeforeEach + public void createFile() { + this.storage = new Storage(directoryPath); + } + + @Test + void writeAndReadFlashCardsTest() { + FlashBook flashBook = FlashBook.getInstance(); + flashBook.addFlashCardSet("FunModule"); + FlashCardSet testSet = flashBook.getFlashCardSet("FunModule"); + testSet.addCard(new Card("is water wet?", "no?", "wetness")); + storage.writeFlashBookToFile(flashBook); + testFile = new File(directoryPath +"/FunModule.txt"); + assertTrue(testFile.exists()); + Scanner scanner; + try { + scanner = new Scanner(testFile); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } + assertEquals("is water wet? | no? | wetness", scanner.nextLine()); + HashMap testBook; + try { + testBook = storage.readFlashCardsFromFile(); + } catch (IOException e) { + throw new RuntimeException(e); + } + FlashCardSet testSet2 = testBook.get("FunModule"); + FlashCardSet actualSet = flashBook.getFlashCardSet("FunModule"); + assertTrue(testSet2.getCard(0).getAnswer().equals(actualSet.getCard(0).getAnswer()) + && testSet2.getCard(0).getQuestion().equals(actualSet.getCard(0).getQuestion())); + scanner.close(); + } + + private class StubCard extends Card { + public StubCard(Card card) { + super(card.getQuestion(), card.getAnswer(), card.getTopic()); + } + public boolean isEqual(Card card) { + return this.getQuestion().equals(card.getQuestion()) + && this.getAnswer().equals(card.getAnswer()) + && this.getTopic().equals(card.getTopic()); + } + } + @Test + public void readAndWriteMultipleCards() throws IOException { + FlashCardSet set1 = new FlashCardSet("CS2113"); + set1.addCard(new Card("question1", "answer1", "topic1")); + set1.addCard(new Card("question2", "answer2", "topic2")); + set1.addCard(new Card("question3", "answer3", "topic3")); + set1.addCard(new Card("question4", "answer4", "topic4")); + ArrayList testList = new ArrayList<>(set1.getFlashCardSet()); + FlashBook testBook = FlashBook.getInstance(); + testBook.getAllFlashCardSets().put(set1.getModuleName(), set1); + storage.writeFlashBookToFile(testBook); + testFile = new File(directoryPath + "/CS2113.txt"); + assertTrue(testFile.exists()); + HashMap actualMap = storage.readFlashCardsFromFile(); + FlashCardSet actualList = actualMap.get("CS2113"); + for (int i = 0; i < testList.size(); i++) { + StubCard actualCard = new StubCard(actualList.getCard(i)); + StubCard expectedCard = new StubCard(testList.get(i)); + assertTrue(actualCard.isEqual(expectedCard)); + } + } + @AfterEach + public void cleanFile() throws IOException { + if (Files.deleteIfExists(testFile.toPath())) { + System.out.println("file deleted successfully"); + } else { + System.out.println("error"); + } + } +} diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 892cb6cae7..e69de29bb2 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,9 +0,0 @@ -Hello from - ____ _ -| _ \ _ _| | _____ -| | | | | | | |/ / _ \ -| |_| | |_| | < __/ -|____/ \__,_|_|\_\___| - -What is your name? -Hello James Gosling