diff --git a/.gitignore b/.gitignore index f69985ef1f..0cd2a4d61f 100644 --- a/.gitignore +++ b/.gitignore @@ -13,5 +13,6 @@ src/main/resources/docs/ *.iml bin/ -/text-ui-test/ACTUAL.txt -text-ui-test/EXPECTED-UNIX.TXT +ACTUAL.TXT +log + diff --git a/README.md b/README.md index 698b938529..61e96f096d 100644 --- a/README.md +++ b/README.md @@ -1,64 +1,14 @@ -# Duke project template - -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. - -## Setting up in Intellij - -Prerequisites: JDK 11 (use the exact version), update Intellij to the most recent version. - -1. **Configure Intellij for JDK 11**, as described [here](https://se-education.org/guides/tutorials/intellijJdk.html). -1. **Import the project _as a Gradle project_**, as described [here](https://se-education.org/guides/tutorials/intellijImportGradleProject.html). -1. **Verify the set up**: After the importing is complete, locate the `src/main/java/seedu/duke/Duke.java` file, right-click it, and choose `Run Duke.main()`. If the setup is correct, you should see something like the below: - ``` - > Task :compileJava - > Task :processResources NO-SOURCE - > Task :classes - - > Task :Duke.main() - Hello from - ____ _ - | _ \ _ _| | _____ - | | | | | | | |/ / _ \ - | |_| | |_| | < __/ - |____/ \__,_|_|\_\___| - - What is your name? - ``` - Type some word and press enter to let the execution proceed to the end. - -## Build automation using Gradle - -* This project uses Gradle for build automation and dependency management. It includes a basic build script as well (i.e. the `build.gradle` file). -* If you are new to Gradle, refer to the [Gradle Tutorial at se-education.org/guides](https://se-education.org/guides/tutorials/gradle.html). - -## Testing - -### I/O redirection tests - -* To run _I/O redirection_ tests (aka _Text UI tests_), navigate to the `text-ui-test` and run the `runtest(.bat/.sh)` script. - -### JUnit tests - -* A skeleton JUnit test (`src/test/java/seedu/duke/DukeTest.java`) is provided with this project template. -* If you are new to JUnit, refer to the [JUnit Tutorial at se-education.org/guides](https://se-education.org/guides/tutorials/junit.html). - -## Checkstyle - -* A sample CheckStyle rule configuration is provided in this project. -* If you are new to Checkstyle, refer to the [Checkstyle Tutorial at se-education.org/guides](https://se-education.org/guides/tutorials/checkstyle.html). - -## CI using GitHub Actions - -The project uses [GitHub actions](https://github.com/features/actions) for CI. When you push a commit to this repo or PR against it, GitHub actions will run automatically to build and verify the code as updated by the commit/PR. - -## Documentation - -`/docs` folder contains a skeleton version of the project documentation. - -Steps for publishing documentation to the public: -1. If you are using this project template for an individual project, go your fork on GitHub.
- If you are using this project template for a team project, go to the team fork on GitHub. -1. Click on the `settings` tab. -1. Scroll down to the `GitHub Pages` section. -1. Set the `source` as `master branch /docs folder`. -1. Optionally, use the `choose a theme` button to choose a theme for your documentation. +# eCardnomics + ___ _ _ + ___ / __\__ _ _ __ __| |_ __ ___ _ __ ___ (_) ___ ___ + / _ \/ / / _` | '__/ _` | '_ \ / _ \| '_ ` _ \| |/ __/ __| + | __/ /__| (_| | | | (_| | | | | (_) | | | | | | | (__\__ \ + \___\____/\__,_|_| \__,_|_| |_|\___/|_| |_| |_|_|\___|___/ + +eCardnomics is a **desktop flashcard application to quickly create, manage, and access new flashcards via a Command + Line Interface (CLI)**. + +Useful links: +* [User Guide](https://ay2021s1-cs2113-t14-2.github.io/tp/UserGuide.html) +* [Developer Guide](https://ay2021s1-cs2113-t14-2.github.io/tp/DeveloperGuide.html) +* [About Us](https://ay2021s1-cs2113-t14-2.github.io/tp/AboutUs.html) diff --git a/build.gradle b/build.gradle index b0c5528fb5..2fa00375fc 100644 --- a/build.gradle +++ b/build.gradle @@ -12,8 +12,14 @@ repositories { dependencies { testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.5.0' testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.5.0' + + compile group: 'org.apache.poi', name: 'poi', version: '4.1.2' + compile group: 'org.apache.poi', name: 'poi-ooxml', version: '4.1.2' + + compile 'org.beryx:awt-color-factory:1.0.1' } + test { useJUnitPlatform() @@ -29,11 +35,11 @@ test { } application { - mainClassName = "seedu.duke.Duke" + mainClassName = "seedu.ecardnomics.Main" } shadowJar { - archiveBaseName = "duke" + archiveBaseName = "ecardnomics" archiveClassifier = null } @@ -43,4 +49,6 @@ checkstyle { run{ standardInput = System.in + enableAssertions = true + } diff --git a/data/deckList.txt b/data/deckList.txt new file mode 100644 index 0000000000..2dc7ba8a62 --- /dev/null +++ b/data/deckList.txt @@ -0,0 +1,64 @@ +================================================================================ +deck | Micro-Economics +tags | Economics | EC1301 +Q | What is the Law of demand? +A | When the price of a good rises, the quantity of the good demanded will fall, ceteris paribus. +Q | What is the Law of supply? +A | When the price of a good increases, the quantity supplied increases, ceteris paribus. +Q | What is price elasticity of demand? +A | Percentage change in quantity demanded caused by a 1 percent change in price. +Q | What is price elasticity of supply? +A | Percentage change in quantity supplied caused by a 1 percent change in price. +Q | What is Market Failure? +A | Market failure is the economic situation defined by an inefficient distribution of goods and services in the free market. +================================================================================ +deck | Macro-Economics +tags | Economics | EC1301 +Q | What are ways GDP can be calculated? +A | Three ways: Expenditure approach, Value-addedd approach and Factor payments approach. +Q | What is the GDP formula using the Expenditure approach? +A | GDP = C + I + G + NX +Q | Define frictional unemployment. +A | Unemployment related to time taken for people to find new jobs. +Q | Define seasonal unemployment. +A | Unemployment related to changes in weather, tourist patterns, or other seasonal factors. +Q | Define structural unemployment. +A | Unemployment related to skill mismatch between workers' skills and employers' requirements. +Q | Define cyclical unemployment. +A | Unemployment arising from changes in production over the business cycle. +================================================================================ +deck | Object-oriented Programming +tags | Computer | Science | CS2113 +Q | What are the programming paradigms based on OODP? +A | Abstraction, Polymorphism, Inheritence, Encapsulation +Q | What are access modifiers? +A | Access level modifiers determine whether other classes can use a particular field or invoke a particular method. +Q | How do you represent for and while loops in a sequence diagram? +A | Using the "loop" notation. +================================================================================ +deck | Test Deck +tags | TESTING | DECK | TAGS +Q | TEST 1: Empty Answer +A | +Q | TEST 2: Empty Answer with whitespaces +A | +Q | +A | TEST 3: Empty Question +Q | +A | TEST 4: Empty Question with whitespaces +Q | TEST 5: Both question and answer empty +A | Following QNA are empty. +Q | +A | +Q | TEST 6: Non-alphanumeric question and answer +A | ./!@$#*&# +Q | ./*-+"" +A | TEST 7: Non-alphanumeric question and answer +Q | CORRECT QUESTION +A | CORRECT ANSWER +================================================================================ +deck | Test Deck +tags | Duplicate | Deck | Name | Test +Q | TEST WHETHER EXISTING DECK NAME +A | SHOULD BE NOT ALLOWED? +================================================================================ diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 0f072953ea..f713ea3e71 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -2,8 +2,8 @@ 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) +![](./team/liewws.png) | Liew Wei Siew | [Github](https://github.com/LiewWS) | [Portfolio](./team/liewws.md) +![](./team/zhixiangteoh.png) | Zhixiang Teoh | [Github](https://github.com/zhixiangteoh) | [Portfolio](./team/zhixiangteoh.md) +![](https://via.placeholder.com/100.png?text=Photo) | Trang | [Github](https://github.com/alwaysnacy/) | [Portfolio](./team/alwaysnacy.md) +![](./team/kaijiel24.png) | Kai Jie | [Github](https://github.com/kaijiel24) | [Portfolio](./team/kaijiel24.md) +![](./team/wangwaynesg.png) | Wang Wayne | [Github](https://github.com/wangwaynesg/) | [Portfolio](./team/wangwaynesg.md) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 0ec3db103d..3c03440e5c 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -1,34 +1,702 @@ # Developer Guide -## Design & implementation +## Introduction -{Describe the design and implementation of the product. Use UML diagrams and short code snippets where applicable.} +Flash Card manager for Economics students on Command Line. +See also: [User Guide](./UserGuide.md) | [Releases](https://github.com/AY2021S1-CS2113-T14-2/tp/releases) | [Team Project Portfolio Page](./AboutUs.md) + +## Contents + +- [Design](#design) + * [Application Architecture](#application-architecture) + + [How the components interact with one another](#how-the-components-interact-with-one-another) + * [User Interface](#user-interface) + * [Logic](#logic) + + [Overall Logic](#overall-logic) + + [Commands](#commands) + - [Overview](#overview) + - [Mode-specific commands](#mode-specific-commands) + - [Command sequence](#command-sequence) + * [Deck Model](#deck-model) + * [Storage](#storage) + + [Loading the deckList data](#loading-the-decklist-data) + + [Writing the deckList data](#writing-the-decklist-data) + * [Exceptions](#exceptions) +- [Implementation - Features](#implementation---features) + * [Print to PowerPoint SlideShow](#print-to-powerpoint-slideshow) + + [Color Selection](#color-selection) + + [Default](#default) + + [Color Scheme](#color-scheme) + + [Original Color](#original-color) + * [Pretty Printing](#pretty-printing) + * [Tags for grouping and searching decks](#tags-for-grouping-and-searching-decks) + * [Game Mode](#game-mode) + + [General Architecture](#general-architecture) + + [Example Use Case](#example-use-case) + + [Sequential Flow](#sequential-flow) +- [Product scope](#product-scope) + * [Target user profile](#target-user-profile) + * [Value proposition](#value-proposition) +- [User Stories](#user-stories) +- [Non-Functional Requirements](#non-functional-requirements) +- [Glossary](#glossary) +- [Instructions for manual testing](#instructions-for-manual-testing) + +## Design + +### Application Architecture +![Architecture](images-dg/Architecture.png) + +The **Architecture Diagram** given above explains the high-level design of the Flash Card Manager Application. + +`Main` is responsible for initializing the other components in the program and linking them up correctly. + +* `Ui` Takes in instructions from user and displays the output to the user +* `Logic` Consists of the `Parsers` and the `Commands`. The `Parser` decipher the user input and executes the specific `Command` that affects the change the user wishes. +* `Model` Holds the data that is in memory as the program runs. It consists of the 3 components: + * `Flash Card` : A single question and answer pair. + * `Deck` : A list of `Flash Card`s under a common topic. + * `Deck List` : A complete list of all the `Deck`s in memory. +* `Storage` Reads and writes data from and to a text file. + +#### How the components interact with one another +The following **Sequence Diagram** shows how the components interact for a basic `create ` command where a new deck is created and added in to the `Deck List`. + +![Sequence Diagram](images-dg/Sequence%20Diagram.png) +The sequence shown is as follows: +* The **`Main`** instance runs and calls the *`readUserInput()`* of **`Ui`**. The function waits for the user to key in one line of input +and then returns that input as a String to **`Main`**. **`Main`** calls *`parse`* of which creates a new **`CreateCommand`** and this is returned to **`Main`**. +This section will be explained in details in the `Parser` section later on. +**`Main`** then calls for *`execute()`* of the *`CreateCommand`* that calls *`addDeck()`* of **`DeckList`** and subsequently *`printNewDeck()`* +of **`Ui`** which prints the output to the user. +Finally, **`Main`** calls *`write()`* of **`Storage`** to write the updated Deck List to the text file. + + +### User Interface + +**API**: [seedu/ecardnomics/Ui.java](https://github.com/AY2021S1-CS2113-T14-2/tp/tree/master/src/main/java/seedu/ecardnomics/Ui.java) + +The **`Ui`** contains String constants that represent the outputs that the application is defined to produce. + +The **`Ui`** component has two main purposes: +* Reading user input from the console. +* Printing program output to the console. + +Reading of user input is done using the method *`readUserInput()`* +which reads one line of user input. The other methods within **`Ui`** are +called when a specific output needs to be printed. + +The **`Ui`** component passes the user input to the **`NormalParser`**, **`DeckParser`** and +**`GameParser`** components that will extract the relevant information. +The **`Ui`** component provides its printing methods to **`NormalParser`**, **`DeckParser`** +and **`GameParser`** for printing the appropriate output when required. + +### Logic + +#### Overall Logic +This is an overview of interactions between objects in eCardnomics program. + +![DG-Overall Logic](./images-dg/Logic-DG-copy.png?raw=true "Overall Logic Diagram") + +1. The overall logic component consists of the **`Parser`** class and **`Command`** class. +2. The **`Parser`** parses the user input and creates the respective **`Command`** object. +3. This command will be executed by the **`Main`** class. +4. The command execution then can affect the Model (e.g. creating a new deck) + +#### Parsers +##### Overview +![DG-Parser UML](./images-dg/DG-Parser-UML.png?raw=true "Parsers UML Class Diagram") + +There are three types of parsers: **`NormalParser`**, **`DeckParser`** and **`GameParser`**. They will be executed +corresponding to the mode users are in. Users input in Normal Mode, Deck Mode or Game Mode will be parsed using the +respective parser. All are children classes of abstract class Parser, where there are three abstract methods: +* parse(String arguments) splits command word and arguments, passes them into parseCommand, and returns parseCommand + ouput which is a Command +* parseCommand(String arguments) gets input from parse() method, creates the right command and returns Command object +* getIndex(String arguments) returns a valid index of the deck or flashcard in the deck +Theses three methods will be reused in the children classes based on the current mode. + + Three Parsers will parse inputs from user and turns them to valid arguments for Command object creation. + NormalParser will return NormalCommand, DeckParser will return DeckCommand and GameParser will return GameCommand. + And all three can create everywhere Command such as ExitCommand. + + +#### Commands + +##### Overview + +![DG-Design Commands UML](./images-dg/DG-Design-Commands-UML.png?raw=true "Commands UML Class Diagram") + +**API**: [seedu.ecardnomics/command](https://github.com/AY2021S1-CS2113-T14-2/tp/tree/master/src/main/java/seedu/ecardnomics/command) + +Commands are primarily classified into three categories, **`NormalCommand`**, **`DeckCommand`**, and **`GameCommand`**, + corresponding to the application's Normal, Deck, and Game Modes, respectively. All three are abstract children + derived from the overarching abstract class **`Command`**. The basis **`Command`** class is defined as such: + +```java +public abstract class Command { + public abstract void execute(); +} +``` + +It only requires that all derived children implement the *`execute()`* method. The only two classes not belonging to + individual modes are **`ExitCommand`** and **`VoidCommand`**. The former is so that users can call the command `exit` + from anywhere in the application, while the latter is a catch-all "command" for all erroneous commands a user + enters. + +##### Mode-specific commands + +The specific commands defined within the different Modes are shown below; one can simply substitute the `Normal Mode + Commands`, `Game Mode Commands` and `Deck Mode Commands` components in the above UML class diagram with the + corresponding `Command` classes, with all of the classes inheriting from the corresponding abstract classes, and + being associated (with arrows pointing towards) with the corresponding **`Parser`** classes. + +![DG-Design Commands Breakdown](./images-dg/DG-Design-Commands-Breakdown.png?raw=true "Commands Components Breakdown") + +Notice that the same **`StartCommand`** class above is indicated as being in both Normal Mode and Deck Mode. While the + diagram does not explain this phenomenon fully, the idea is there: that `start` is a command that can be run from + within Deck Mode, but that its implementation is passed to **`NormalParser`** to be handled as a Normal Mode command + . More specifically, within the specification of **`DeckParser`**'s *`parseCommand()`* method, the case of command + word being parsed as `start` will in turn call **`NormalParser`**'s *`parseCommand()`* method, supplementing it with + **`DeckParser`**'s Deck class field object as the `arguments` String. + +##### Parser and Command sequence + +The **`Parser`** classes play important roles in execution of specific commands, e.g. **`CreateCommand`**, because + they define methods that check and ensure the conformity of user input to the commands' expected input. Below is a + sequence diagram showcasing this interaction, for execution of a **`CreateCommand`**, e.g. `create + microeconomics`: + +![DG-Design CreateCommand Sequence UML](./images-dg/DG-Design-Sequence-Diagram.png?raw=true "CreateCommand UML + Sequence Diagram") + +Here, *`parse()`* first splits the user input `create microeconomics` into two strings, "create" and "microeconomics", + the command word and command arguments respectively. Then within the *`parseCommand()`* call in **`NormalParser`**, a + dedicated method to create a new deck based on the argument string "microeconomics", *`prepareNewDeck()`*, is called + . A new **`Deck`** object is returned to the same *`parseCommand()`* call and used to create the new + **`CreateCommand`** object, which is then propagated back to `Main` (not shown here) that called `parse()`. + +> Note that the **`CreateCommand`** object is not marked as deleted in the above diagram because its lifeline does not +> really end until its *`execute()`* method has been called from **`Main`**, using **`Main`**'s *`executeCommand()`*. + +### Deck Model + +![DG-Design Model UML](./images-dg/DG-Design-Model.png?raw=true "Model UML Class Diagram") + +**API**: [seedu/ecardnomics/deck](https://github.com/AY2021S1-CS2113-T14-2/tp/tree/master/src/main/java/seedu/ecardnomics/deck) + +The Deck Model component is made up of three parts: +* **`DeckList`** +* **`Deck`** +* **`FlashCard`** + +The **`FlashCard`** component represents a flashcard, storing question +and answer data. The **`Deck`** represents a collection of flashcards +related by a common topic. The **`DeckList`** represents the collection +of all the **`Deck`** objects that the user has. + +Only the **`Command`** components can modify the **`DeckList`**, **`Deck`** and +**`FlashCard`** components. However, **`Ui`**, **`DeckParser`** and **`NormalParser`** +are able to read data from the **`DeckList`**, **`Deck`** and **`FlashCard`** components. + +### Storage + +#### Loading the deckList data + +![Storage Sequence Diagram](./images-dg/Storage.png?raw=true "load Storage sequence diagram") + +**API**: [seedu/ecardnomics/storage](https://github.com/AY2021S1-CS2113-T14-2/tp/blob/master/src/main/java/seedu/ecardnomics/storage) + +Storage of this application uses basic `.txt` read and write functions. +Upon start of the program, the application checks whether there is a `./data` folder and creates one if there isn't. +Then, it reads from the storage file `deckList.txt` line by line to create: +* new **`Deck`** +* new **`FlashCard`** + +and adds them to the current `deckList` passed into the *`load`* method call. + +#### Writing the deckList data + +Similarly, for writing the data into `.txt` file, the Storage will loop through all the current `Decks` and their +current `FlashCards` and write them in a specific format in the text file in the `./data` folder. + +### Exceptions + +![DG-Design Exceptions Architecture](./images-dg/DG-Exceptions-Architecture.png?raw=true "Exceptions Architecture + Overview") + +**API**: [seedu/ecardnomics/exceptions](https://github.com/AY2021S1-CS2113-T14-2/tp/blob/master/src/main/java/seedu/ecardnomics/exceptions) + +How to read the diagram above: +- The font colour of the methods correspond to the fill colour of the Exception classes that they throw; e.g +., **`NormalParser`**'s *`prepareNewDeck()`* method throws **`EmptyInputException`** +- Additionally, methods that throw more than one exception will have their colours corresponding to one of the + exception classes' fill colours, with the other associations denoted by explicit textual annotation on the + association arrows; e.g., **`NormalParser`**'s *`getIndex()`* and *`prepareDeck()`* methods additionally throw + **`DeckRangeException`**, on top of throwing **`IndexFormatException`** +- Each Exception class only has one String field unique to the class that holds the Exception message which is + printed to the user on encountering the associated erroneous feedback + +## Implementation - Features + +### Print to PowerPoint SlideShow + +An additional feature targeted at students who wish to add more style to their flash cards outside of the command +line option to keep things interesting when they are revising. + +The `PowerPointCommand` is parsed by `NormalParser` but the "Print to PowerPoint" command can be called from both Normal +and Deck Mode. + +The following diagram shows how the `PowerPointCommand`'s *`execute()`* calls the `createNewPowerPoint()` method of + `PowerPoint`. *`execute()`* first checks if the whether `isPptxCreated` is `true` and only creates the PowerPoint if + so. This is necessary as the user might have input the command `pptx` but when prompted for confirmation, they input + `n` which means no, but the parser will still output a `PowerPointCommand` except with the element `isPptxCreated + ` as `false` and thus, when executed, nothing happens. + +![PPTX Sequence Diagram](images-dg/PPTX-Sequence-Diagram.png) + +The *`newIntroSlide()`*, *`newSlide()`* and *`exportSlide()`* method of `PowerPoint` uses a third party library - +[Apache POI](https://poi.apache.org/index.html) +to create new slides, populate them with the questions and answers from the deck and finally print them out to a new + PowerPoint file in the `pptx` folder under the name `.pptx`. + +The following are the Classes/ Enum of the third party package `org.apache.poi.xslf.usermodel` which are used: +* `SlideLayout` - Enum representing the Slide Layouts available +* `XMLSlideShow` - Class representing an entire Slide Show +* `XSLFSlide` - Class representing a single Slide +* `XSLFSlideLayout` - Class representing the layout of a slide +* `XSLFSlideMaster` - Class representing the default slides layouts +* `XSLFTextShape` - Class representing a shape within a slide +* `XSLFTextParagraph` - Class representing a paragraph of text within a shape +* `XSLFTextRun` - Class representing the properties of the text within a paragraph + + +#### Color Selection +The 3 modes of Color Selection, `DEFAULT`, `COLOR_SCHEME` and `ORIGINAL_COLOR` are stored in the enum `ColorOption`. + +The `java.awt.Color` class itself has no methods to generate colors based on a given string. Thus, I used the +[`ColorFactory API`](https://github.com/beryx/awt-color-factory) which has a public static method, *`valueOf()`* which +takes in a string and outputs a `Color` object if the String matches any of the available colors documented +[here](https://www.javadoc.io/doc/org.beryx/awt-color-factory/1.0.1/org.beryx.awt.color/org/beryx/awt/color/ColorFactory.html). +This is done in the constructor of `PowerPoint` where `bgColor = ColorFactory.valueOf(bgColorString)` (same for + `txtColor`). + +Each instance of `PowerPoint` has an element of the enum `ColorOption`, `colorOpt`, which decides which of the outputs +to print back to the user. `NormalParser`'s `preparePptxCommand` create a `PowerPointCommand` instance using different +constructors depending on the mode of Color Selection. The `PowerPointCommand` then creates a `PowerPoint` instance with +the respective constructor the assigns the respective value of `colorOpt`. Below are the `PowerPointCommand` and +`PowerPoint` constructors used in each mode. + +![PPTX Color Options Sequence Diagram](images-dg/PPTX-Color-Options-Sequence-Diagram.png) + +#### Default +* Prints PowerPoint Slides with default white background and black text. +* Instantiated using `PowerPointCommand(deck, deckList, isPptxCreated)` and `PowerPoint(deck)`. + +#### Color Scheme +* Prints PowerPoint Slides with one of the 10 available color schemes that are pre-designed. +* Instantiated using`PowerPointCommand(deck, deckList, isPptxCreated, csIndex)` and `PowerPoint(deck, csIndex)` + +#### Original Color +* Prints PowerPoint Slides with an Original Color combination that is chosen by the user. +* Instantiated using `PowerPointCommand(deck, deckList, isPptxCreated, bgColorString, txtColorString, bgColor, txtColor)` + and `PowerPoint(deck, bgColorString, txtColorString, bgColor, txtColor)` + +### Exceptions Thrown by Parsers for `pptx` command +Normal Parser: +* `getCsIndex()` - for `-cs` option + * There are exceptions thrown if the index is either not within the range [1,10], `CsIndexRangeException` or + not in the correct format, `CsIndexFormatException`. + +* `preparePptxCommand()` - for the rest of the options + * There is an exception thrown, `ColorsNotAvailException` when the at least one of the colors chosen is not + a valid color. (`-cs` option only) + * In each command, only either of the options can be used to select the colors so if both options are included + at the same time, there will be an exception thrown, `BothCsAndOcException`. + * Any other options entered starting with `-` will trigger the exception, `InvalidOptionsException`. + +Deck Parser: + * `checkForValidPptxArguments()` + * If any other arguments other than the 3 options are present for `pptx` command in Deck mode, the exception, + `InvalidPptxArgumentException` will be thrown. + * Just like `preparePptxCommand()` from `NormalParser`, the function also checks for invalid options and throw + `InvalidOptions` when there are any. + +### Pretty Printing + +The purpose of this feature is to improve the readability of the command line text output for the user, in particular, +the question and answer fields of a flashcard . Without this feature, long text outputs would follow the default +wrapping style of the console. When words are truncated unnecessarily, it is going to be distracting and annoying +for students trying to study. We illustrate the problem with the following example: +``` +This is a long question (or maybe answer) field. Suppose tha +t our console is 60 characters wide, we see that the word "t +hat" was truncated in the first line and again in the second +line. +``` +In this section, we define the following terms: +* `lineLength` is the maximum number of characters on a line, set to be equal to `Ui.DASH_LINES.length()`. This is +also the number of characters between the start of line and end of line. +* `offset` is the number of characters after the start of the line before the target string will be printed. +* `usableLength` is the number of characters that can be used for printing the output. +This is `Ui.DASH_LINES.length() - offset`. + +The *`prettyPrintFormatter(String target, int offset)`* static method of the **`Ui`** class takes as argument the +target string to be formatted for printing as well as the offset. The formatted String is returned to the caller for +printing. This is illustrated in the following sequence diagram: + +![DG-Implementation-Features-PP-Sequence](./images-dg/PP-Sequence.png?raw=true) + +>Note: +> +> The lifeline on the left represents the calling method that requires a formatted string. +> The *`printMethod()`* is a placeholder for any of the printing methods of **`Ui`** class. +> The call to *`System.out.println`* is omitted. +> Minimal notation is used for the return of control to the calling method. + +The `offset` parameter specifies the number of characters already +printed on the line before the target string will be printed. + +*`prettyPrintFormatter()`* places as many words as possible on each line until +the next word does not fit within the `usableLength` of the current +line. This word is therefore placed on the next line and the process +repeats until all the words have been formatted into the response. If +the length of a single word exceeds the `usableLength`, the word is +split across multiple lines to prevent the program from looping +infinitely as it would never be able to fit the word on any line. + +Take note that infinite loops can still occur if *`prettyPrintFormatter()`* is called with offset >= `lineLength` + +### Tags for grouping and searching decks +The purpose of this feature is to provide a means to group the decks based on their subjects +and search for relevant decks related to one or more relevant subjects in a robust way. Each created deck will be +tagged to their respective field. + +![DG-Implementation-Features-TagArchitecture](./images-dg/Tag-feature.png?raw=true) + +The user can also modify the tags of the decks by using `tag` or `untag` command, and use `search` by tag to find +a group of decks he/she is interested in. + +![DG-Implementation-Features-TagSequence](./images-dg/Tag.png?raw=true) + +### Game Mode + +eCardnomics' quintessential mode. Game Mode can be started from either Normal Mode or Deck Mode. The `start` command + is parsed by **`NormalParser`** (see [Commands](#commands)). + +#### General Architecture + +Game Mode contains two main components: a storage component, **`GameStorage`**, and a logic component, **`GameEngine`**. The + former handles all data structures used by Game Mode, and stores the original deck (`originalDeck`), question pool + ([`deque`](#glossary)), and retest question pool (`retestStore`). The latter executes the main game loop + (*`runGameLoop + ()`*), and + interacts with **`GameStorage`** on package-private basis; i.e., **`GameEngine`** and **`GameStorage`** have full mutual + access as if they were a single class. This is one of the main intentional design decisions. + +![DG-Implementation-Features-Game-Mode-Architecture](./images-dg/DG-Game-Mode-Architecture-Overview.png?raw=true "Game Mode + Architecture Overview") + +The schematic below describes the individual responsibilities of the **`GameStorage`** and **`GameEngine`** classes (or + components) of Game Mode as introduced above, and also two key interactions between the two classes, namely via + **`GameEngine`**'s *`update(isResponseY:boolean, flashCard:FlashCard)`* and *`poseQuestion()`* method calls. For + context, *`poseQuestion()`* pops the top flash card off **`GameStorage`**'s question pool **`deque`** to display to the user + , while *`update()`* is the **`GameEngine`** method that adds to the retest question pool `retestStore` + when the user chooses to do so (via `isResponseY == true`). This essentially describes one iteration of + *`runGameLoop()`*; more explanation and a full-blown illustration and sequence are given further below. + +![DG-Implementation-Features-Game-Storage-Game-Engine](./images-dg/DG-Game-Storage-Game-Engine.png?raw=true "Game + Mode Game Storage Game Engine") + +**See also**: [Gameplay description](./UserGuide.md#gameplay) + +The actual "game" aspect of eCardnomics is essentially summarised in the *`runGameLoop()`* high-level overview above +. For a textual gameplay description, check out the "See also" link. + +#### Example Use Case + +For a more contextual use case, consider the following scenario of Econs Wiz attempting the Game Mode for the first + deck, `Demand-Supply`, in his deck list. + +> Note: Focus on the biggest box in the diagram! + +![DG-Implementation-Features-Game-Use-Case](./images-dg/DG-Game-Use-Case.png?raw=true "Game Mode Use Case") + +**API**: [seedu/ecardnomics/game](https://github.com/AY2021S1-CS2113-T14-2/tp/tree/master/src/main/java/seedu/ecardnomics/game) + +Since there are quite a few things going on in this diagram, here are the key takeaways (the last of which arguably + the most important): +- the retest question pool (`retestStore`) is updated upon the user's attempt at each question and response to the + prompt to include or exclude the flash card to be displayed again—if `y` then the flash card is added to the + `retestStore` +- whenever the current question pool (`deque`) is emptied, i.e. all flash cards have been popped off and displayed to + the user, the retest question pool (`retestStore`) is consulted from which to create a new question pool (`deque`) +- after a specific question is displayed, it does not appear again (even if the user has chosen to re-encounter the + question) until after all other questions in the current question pool (`deque`) have been exhausted + +#### Sequential Flow + +For a more formal sequential flow of the inner workings of Game Mode, the following elaborates the execution + sequence of Game Mode, from after a `start` command has been parsed in Normal Mode: + +![DG-Implementation-Features-Game-Mode-Sequence](./images-dg/DG-Game-Mode-Sequence.png?raw=true "Game Mode UML Sequence + Diagram") + + In the above diagram the key takeaway is the existence of an *intermediary* `game:Game` object that holds + **`GameEngine`** and **`GameStorage`** together. In fact, this is the sole purpose of the **`Game`** class: to hold the + current game instance, in a Single-Responsibility-Principle (SRP) and Object-Oriented Programming (OOP) manner. + This intermediary role of the **`Game`** class is also illustrated in the upper part of the earlier [use case + diagram](#example-use-case). Note how it is from this `game` object that the main game loop, run and managed by + **`GameEngine`**, is started. + +![DG-Implementation-Features-Game-Mode-Sequence-run-game-loop-sd](./images-dg/DG-run-game-loop-sd.png?raw=true "Game + Mode UML Sequence Diagram run game loop sd") + +The main game loop. As with all simple games, this flash card game mode is fundamentally built on the concept of a + possibly never-ending game loop with specific end conditions. In this case, the main end condition is explicitly + that the `command` object that is parsed and returned upon the *`getAttempt()`* call (that prompts the user for an + answer attempt) is either a **`DoneGameCommand`** or **`ExitCommand`**. + +> This is not the **only** end condition, though, because the other important but implicit end scenario is when the +> question pool is exhausted (i.e., `storage.deque` is empty) **and** the retest question pool (`storage.retestStore +>`) is empty. + +Naturally, the other sequence of special note here is the whole *`update()`* sequence, and even more specifically the + *`updateDeque()`* call within the *`update()`* sequence. Notice how *`updateDeque()`* calls + *`createRandomisedStack(storage.retestStore)`* with the `retestStore` as argument. This essentially creates a new + randomised question pool from the retest question pool. + +Notice how this *`updateDeque()`* sequence is only called exactly when the `storage.deque` is empty (i.e., when all + questions have been popped off the question pool). This is important because it ensures that the user encounters all + available questions in the `deque` at least once before the retest questions are later displayed. Cross-check this + with the detailed descriptions of the inner workings of the game loop implementation shown in the earlier + [architecture](#general-architecture) and [use case](#example-use-case) diagrams. + +Lastly, notice how *`refreshRetestStore()`* is called at the end of *`updateDeque()`* to, as its name suggests, clear + the retest question pool to get ready to store the next wave of retest questions. This is also covered in the bottom + few lines of the **`GameEngine`** portion of the use case diagram. ## Product scope + ### Target user profile -{Describe the target user profile} +Junior College Economics Students. + +Anybody > Students > Students in courses with high amount of content > Economics students > **Junior College + Economics students** (focus on theory than calculations) ### Value proposition -{Describe the value proposition: what problem does it solve?} +Flashcard application that allows students to quickly create new flashcards and access flashcards quickly on the + command line to enhance their studying experience, and ultimately be an aid for [active recall](https://getatomi.com/blog/what-is-active-recall-and-how-effective-is-it). ## User Stories -|Version| As a ... | I want to ... | So that I can ...| +|Version| As a(n) ... | 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| +|v1.0|poor student|have small computer programs|run it on my old computer| +|v1.0|fast typist|have an easily-navigable interface to type up notes and store them|create and manage notes quickly and efficiently| +|v1.0|smart student|be able to use the system effectively and efficiently|save time and maximise my productivity| +|v1.0|JC econs student|quickly create short notes of key concepts|keep up during lectures and tutorials| +|v1.0|tech-savvy student|have a software tool to store my notes|stop needing to worry about losing my hardcopy notes| +|v1.0|lazy student|create flashcards to keep my notes concise|learn at a comfortable, incrementing pace| +|v2.0|organised student|have my notes be stored in a systematic way|retrieve them quickly and easily| +|v2.0|student|have a system that can categorise material into different topics|quickly revise all the content for a topic when studying for an exam| +|v2.0|hardworking student|have a studying system that can help me memorise content in a non-traditional manner|remember all the facts during an exam through active recall| +|v2.0|busy student|have my notes available outside of CLI|study while on the go and not waste any time ## Non-Functional Requirements -{Give non-functional requirements} +* The program should not attempt to save user data to disk if it is not given permission by the user. +* The program should work on a machine that has Java 11 installed. +* The error messages should be easy to understand even if the reader does not have knowledge about programming. +* The program should not terminate unexpectedly. +* The program is not expected to guarantee that modifications to data file will be during execution will be retained. ## Glossary - -* *glossary item* - Definition +* *[Color Selection](#color-selection)* - The options available to select the color for printing to PowerPoint. +* *[Color Schemes](#color-scheme)* - An option to select color for printing to PowerPoint by selecting one of the + pre-designed color schemes available. +* *[Deck](#deck-model)* - A collection of flash cards that are related by a common topic. +* *[DeckList](#deck-model)* - A collection of all the decks owned by the user. +* *[Tag](#tags-for-grouping-and-searching-decks)* -An attribute of Deck that helps user to categorise all the available +deck +* *[Deck Mode](#commands)* - A state of the program that allows the user to make changes to the flashcards within the + deck +* *[Flashcard](#deck-model)* - An object that contains a non-empty question and a non-empty answer. +* *[Deck Mode](#commands)* - A state of the program that allows the user to make changes to the flashcards within the +deck +* *[Game Mode](#commands)* - A state of the program used for testing if the user recalls the answer on flashcards. +* *[Normal Mode](#command)* - A state of the program that allows the user to modify the list of decks. +* *[Original Colors](#original-color)* - An option to select color for printing to PowerPoint by selecting two of the + available colors for background and text. +* *[Pretty Printing](#pretty-printing)* - Printing text output that span more than one line in a way that minimizes + truncating words. +* *[Print to PowerPoint](#print-to-powerpoint-slideshow)* - Prints an entire deck to a PowerPoint slide (.pptx), using + `pptx` command. +* *[deque](#general-architecture)* - Pronounced "deck", short for "double-ended queue". In eCardnomics, *deque* is + implemented as an [`ArrayDeque`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/ArrayDeque.html) + and functions as a stack of shuffled flash cards, from which questions are popped off during Game Mode. ## 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} + + +Starting the program: +1. Download the `ecardnomics.jar` file and copy it into an empty folder where read, write and execute permissions +are allowed. +2. Run the command `java -jar ecardnomics.jar` in a command line terminal to start the program. + +Creating a deck: +1. Prerequisite: Listing all decks with `decks` does not show any entry 'test deck' or 'tag deck'. +2. Test case: `create test deck`
+ Expected: Listing all decks with `decks` shows the entry 'test deck'. +3. Test case: `create test deck` after running the above test
+ Expected: Error message shown. Listing all decks with `decks` shows only one entry 'test deck'. +4. Test case: `create tag deck /tag test`
+ Expected: Listing all decks with `decks` shows the entry 'tag deck' with tag 'test'. + +Deleting a deck: +1. Prerequisite: Listing all decks with `decks` shows at least two decks. +2. Test case: `delete 1` followed by `y` when prompted
+ Expected: First deck deleted from the list. +3. Test case: `delete 1` followed by `n` when prompted
+ Expected: First deck not deleted from the list. +4. Test case: `delete 1` followed by any input that is not 'y/n' surrounded with spaces when prompted
+ Expected: First deck not deleted from the list. Error message shown. +5. Test case: `delete 1 -y`
+ Expected: First deck deleted from the list. +6. Test case: `delete 0 -y`
+ Expected: No deck deleted from the list. Error message shown. + +Tagging and untagging a deck: +1. Prerequisite: Listing all decks with `decks` shows at least one deck. +2. Test case: `tag 1 /tag testing` followed by `y` when prompted
+ Expected: Listing all decks with `decks` shows the first entry with tag 'testing'. +3. Test case: `tag 1 /tag testing` followed by `n` when prompted
+ Expected: Listing all decks with `decks` shows the first entry without tag 'testing'. +4. Test case: `untag 1 /tag testing` followed by `y` when prompted
+ Expected: Listing all decks with `decks` shows the first entry without tag 'testing'. +5. Test case: `tag 1 /tag`
+ Expected: Error message shown. +6. Test case: `untag 1 /tag`
+ Expected: Error message shown. + +Searching a deck by tag: +1. Prerequisite: There exists at least one deck with tag 'test'. There are no decks with tag 'LOL'. +2. Test case: `search test`
+ Expected: All decks tagged with 'test' together with their index numbers are shown. +3. Test case: `search LOL`
+ Expected: The search returns no results. + + + + + +Printing a deck to PowerPoint: +1. Prerequisite: There exist at least one deck and any PowerPoint with the 'deck name'.pptx is not in use. +1. Test case: `pptx 1` followed by `y` when prompted
+ Expected: A PowerPoint, 'deck name'.pptx is created with white background and black text. +1. Test case: `pptx 1 -y`
+ Expected: A PowerPoint, 'deck name'.pptx is created with white background and black text. +1. Test case: `pptx 1 -y -cs 1`
+ Expected: A PowerPoint, 'deck name'.pptx is created with steelblue background and silver text. +1. Test case: `pptx 1 -y -oc black red`
+ Expected: A PowerPoint, 'deck name'.pptx is created with black background and red text. +1. Test case: `pptx 1 -y -cs black red`
+ Expected: No PowerPoint is created. Error message shown. +1. Test case: `pptx 1 -y -cs 11`
+ Expected: No PowerPoint is created. Error message shown. +1. Test case: `pptx 1 -y -oc block red`
+ Expected: No PowerPoint is created. Error message shown. +1. Test case: `pptx 1 -y -cs 1 -oc black red`
+ Expected: No PowerPoint is created. Error message shown. +1. Test case: `pptx 1 -y -cc 11`
+ Expected: No PowerPoint is created. Error message shown. + + + + + +Editing a deck: +1. Prerequisite: There exists at least one deck. +2. Test case: `edit 1`
+ Expected: Program enters deck mode for the first deck. +3. Test case: `edit -1`
+ Expected: Program remains in normal mode. Error message shown. + +Adding a flashcard: +1. Prerequisite: In deck mode. +2. Test case: `add` followed by 'question' and 'answer' when prompted
+ Expected: Listing all decks with `list /ans` shows the entry for the new flashcard. +3. Test case: `add question /ans answer`
+ Expected: Listing all decks with `list /ans` shows the entry for the new flashcard. +4. Test case: `add question/answer` followed by 'answer' when prompted
+ Expected: Listing all decks with `list /ans` shows the entry for the new flashcard. +5. Test case: `add /ans`
+ Expected: No flashcard added to the deck. Error message shown. +6. Test case: `add /ans answer`
+ Expected: No flashcard added to the deck. Error message shown. + +Deleting a flashcard: +1. Prerequisite: In deck mode, listing all flashcards in current deck with `list` shows at least two flashcards. +2. Test case: `delete 1` followed by `y` when prompted
+ Expected: First flashcard deleted from the list. +3. Test case: `delete 1` followed by `n` when prompted
+ Expected: First flashcard not deleted from the list. +4. Test case: `delete 1` followed by any input that is not 'y/n' surrounded with spaces when prompted
+ Expected: First flashcard not deleted from the list. Error message shown. +5. Test case: `delete 1 -y`
+ Expected: First flashcard deleted from the list. +6. Test case: `delete 0 -y`
+ Expected: No flashcard deleted from the list. Error message shown. + +Updating a flashcard: +1. Prerequisite: In deck mode with at least one flashcard with question 'test' and answer 'testing' +2. Test case: `update 1` followed by 'question' and 'answer' when prompted
+ Expected: Listing all decks with `list /ans` shows that the entry for the first flashcard is updated. +3. Test case: `update 1` followed by ' ' and 'answer' when prompted
+ Expected: Listing all decks with `list /ans` shows that the answer for the first flashcard is updated. +4. Test case: `update 1` followed by 'question' and ' ' when prompted
+ Expected: Listing all decks with `list /ans` shows that the question for the first flashcard is updated. +5. Test case: `update 1` followed by ' ' and ' ' when prompted
+ Expected: Listing all decks with `list /ans` shows that the question for the first flashcard is not updated. + + + + + +Starting game mode: +1. Prerequisite: In normal mode or deck mode with at least one deck with at least one flashcard +2. Test case: In normal mode, `start 1`
+ Expected: Starts game mode for deck at index 1. +3. Test case: In deck mode, `start`
+ Expected: Starts game mode for current deck. +4. Test case: While in game mode and prompted for an attempt, simply `` with no attempt
+ Expected: Answer for the question displayed and score 0.00. +5. Test case: While in game mode and prompted for an attempt, enter `done` or `exit`
+ Expected: `done` returns user to normal mode, `exit` exits the program. +6. Test case: While in game mode and prompted for an attempt, enter any attempt
+ Expected: Answer for the question displayed and some score displayed calculated based on matching words. +7. Test case: While in game mode and prompted for an attempt, enter any attempt, followed by 'y' when next prompted +, then subsequently enter 'n' for all y/n prompts
+ Expected: All questions in the deck are exhausted before user re-encounters the flashcard for which they indicated + 'y'. +8. Test case: While in game mode, try to figure out the pattern for the appearance of flashcards
+ Expected: There is no such pattern. The flashcards are always randomised, even for retest questions. + + + + + +Saving data to disk: +1. After starting the program, the directory "data" should be created. + 1. After program terminates, verify that the file "data/deckList" exists and contains data that corresponds to + the commands supplied during program execution. +2. During execution, the directory "log" should be created. +3. If a pptx command is executed, the "pptx" directory should be created. + 1. After the command is executed, verfiy that "pptx/" exists for the deck that was converted to + PowerPoint format. + + diff --git a/docs/README.md b/docs/README.md index bbcc99c1e7..a183bc228d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,6 +1,12 @@ -# Duke +# eCardnomics + ___ _ _ + ___ / __\__ _ _ __ __| |_ __ ___ _ __ ___ (_) ___ ___ + / _ \/ / / _` | '__/ _` | '_ \ / _ \| '_ ` _ \| |/ __/ __| + | __/ /__| (_| | | | (_| | | | | (_) | | | | | | | (__\__ \ + \___\____/\__,_|_| \__,_|_| |_|\___/|_| |_| |_|_|\___|___/ -{Give product intro here} +eCardnomics is a **desktop flashcard application to quickly create, manage, and access new flashcards via a Command + Line Interface (CLI)**. Useful links: * [User Guide](UserGuide.md) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index abd9fbe891..e802af502f 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -2,41 +2,1476 @@ ## Introduction -{Give a product intro} +eCardnomics is a **desktop flashcard application to quickly create, manage, and access new flashcards via a Command + Line Interface (CLI)**. eCardnomics is targeted at economics students in Junior College in Singapore, and aims to + enhance students’ study experience as an efficient and handy aid for active recall. Through the ability to create + multiple decks of flashcards and tag them independently, students can segment the subject syllabus into topics when + managing flashcards, yet consolidate flashcards by topic when accessing them to study. -## Quick Start +## Contents -{Give steps to get started quickly} +- [Preliminaries](#preliminaries) + * [Installation](#installation) + * [Running the program](#running-the-program) + * [Guide format](#guide-format) +- [Features - Normal Mode](#features---normal-mode) + * [Viewing help in Normal Mode: `help`](#viewing-help-in-normal-mode---help-) + + [Examples](#examples) + * [Create a new deck: `create`](#create-a-new-deck---create-) + + [Format](#format) + + [Examples](#examples-1) + * [Tag an existing deck: `tag`](#tag-an-existing-deck---tag-) + + [Format](#format-1) + + [Examples](#examples-2) + * [Untag an existing tag: `untag`](#untag-an-existing-tag---untag-) + + [Format](#format-2) + + [Examples](#examples-3) + * [Search decks by tag: `search`](#search-decks-by-tag---search-) + + [Format](#format-3) + + [Examples:](#examples-) + * [Display all decks: `decks`](#display-all-decks---decks-) + + [Format](#format-4) + + [Examples](#examples-4) + * [Delete an existing deck: `delete`](#delete-an-existing-deck---delete-) + + [Format](#format-5) + + [Examples](#examples-5) + * [Deck Mode: `edit`](#deck-mode---edit-) + + [Format](#format-6) + + [Examples](#examples-6) + * [Game Mode: `start`](#game-mode---start-) + + [Format](#format-7) + + [Examples](#examples-7) + * [Print an Existing Deck to a PowerPoint File: `pptx`](#print-an-existing-deck-to-a-powerpoint-file---pptx-) + + [Colors for background and text options](#colors-for-background-and-text-options) + + [Format](#format-8) + + [Examples](#examples-8) +- [Features - Deck Mode](#features---deck-mode) + * [Viewing help in Deck Mode: `help`](#viewing-help-in-deck-mode---help-) + + [Examples](#examples-9) + * [Add a flashcard: `add`](#add-a-flashcard---add-) + + [Format](#format-9) + + [Examples](#examples-10) + * [List all the flashcards in the deck: `list`](#list-all-the-flashcards-in-the-deck---list-) + + [Format](#format-10) + + [Examples](#examples-11) + * [Delete an existing Flash Card: `delete`](#delete-an-existing-flash-card---delete-) + + [Format](#format-11) + + [Examples](#examples-12) + * [Update an existing Flash Card: `update`](#update-an-existing-flash-card---update-) + + [Format](#format-12) + + [Examples](#examples-13) + * [Game Mode: `start`](#game-mode---start--1) + + [Format](#format-13) + + [Examples](#examples-14) + * [Print Current Deck to a PowerPoint File: `pptx`](#print-current-deck-to-a-powerpoint-file---pptx-) + + [Colors for background and text options](#colors-for-background-and-text-options-1) + + [Format](#format-14) + + [Examples](#examples-15) + * [Exits Deck Mode: `done`](#exits-deck-mode---done-) + + [Examples](#examples-16) +- [Features - Game Mode](#features---game-mode) + * [Gameplay](#gameplay) + + [Examples](#examples-17) + * [Viewing help in Game Mode: `help`](#viewing-help-in-game-mode---help-) + + [Examples](#examples-18) + * [Exits Game Mode: `done`](#exits-game-mode---done-) + + [Examples](#examples-19) +- [Features - Print to PowerPoint](#features---print-to-powerpoint) + * [Create new PowerPoint based on the selected deck: `pptx`](#create-new-powerpoint-based-on-the-selected-deck---pptx-) + + [Example](#example) + - [Output](#output) + + [Default Color Schemes](#default-color-schemes) + + [Original Colors available](#original-colors-available) +- [Features - Anywhere](#features---anywhere) + * [Exits the program: `exit`](#exits-the-program---exit-) + + [Examples](#examples-20) + * [Shows release version: `--version`](#shows-release-version-----version-) + + [Examples](#examples-21) +- [FAQ](#faq) + * [General](#general) + * [Storage](#storage) + * [Normal Mode](#normal-mode) + * [Deck Mode](#deck-mode) + * [Game Mode](#game-mode) +- [Command Summary](#command-summary) + * [Normal Mode](#normal-mode-1) + * [Deck Mode](#deck-mode-1) + * [Game Mode](#game-mode-1) + * [Anywhere](#anywhere) + +## Preliminaries + +### Installation 1. Ensure that you have Java 11 or above installed. -1. Down the latest version of `Duke` from [here](http://link.to/duke). +2. Download the latest _jar_ release of `eCardnomics` from [here](https://github.com/AY2021S1-CS2113-T14 +-2/tp/releases). + +> Java 11 and above is highly recommended, although eCardnomics might run on a lower version. + +### Running the program + +Open your command line or terminal and navigate to the folder (e.g., `~/downloads`) where you downloaded the jar file +. Before executing the jar file, create a new folder (e.g., `/ecardnomics`) and copy the jar file to the new folder +. Terminal commands are given below; feel free to use them as appropriate. After doing this, simply run the command + `java -jar ecardnomics.jar`: + +```batch +$ cd ~/downloads +$ ls +ecardnomics.jar +$ mkdir ./ecardnomics +$ mv ecardnomics.jar ./ecardnomics +$ cd ./ecardnomics +$ ls +ecardnomics.jar +$ java -jar ecardnomics.jar +``` + +> Note: The terminal commands don't include the `$` sign! + +### Guide format + +Words in `<>` are parameters or additional input to be supplied by the user. + +> Example: `edit ` +> +> Here, `index` is a parameter supplied by the user, in this case to specify a deck index to enter edit mode for. + +Commented-out lines `//` represent system output by the program. + +> Example: +> ```java +> add +> // Enter question: +> // Enter answer: +> ``` +> Notice how `` is still commented out but still represents user input. In other words +>, `<>` can be thought of as being delimiters in output representation across this guide. + +Words in square brackets `[]` represent optional input parameters. + +> Example: list [/ans] + +## Features - Normal Mode + +> `[Normal]` is displayed at every command prompt, to indicate that the program is in Normal Mode. + +### Viewing help in Normal Mode: `help` + +Displays the list of all commands in Normal Mode. + +#### Examples + +```java +[Normal] + > help +// -------------------------------------------------------------------------------- +// eCardnomics. +// Normal Mode. +// +// Usage: +// create [/tag [ ...]] Creates a new deck of flash cards +// named . +// decks Lists all available decks. +// edit Enter Deck Mode for editing the +// deck at list index . +// start Enter Game Mode for deck at list +// index ! Do your best! +// delete [-y] Deletes the deck at list index +// from list of decks. +// pptx [-y] [-cs | -oc Creates a PowerPoint slides based +// ] on the deck at list index . +// tag /tag [ ...] Tags the deck at list index +// with 1 or more tags. +// untag /tag [ ...] Untags specified s of the +// deck at list index . +// search [ ...] Search deck list for decks tagged +// with specified s. +// exit Exits the program. +// help Show this output. +// +// Options: +// --version Show version. +// -------------------------------------------------------------------------------- +``` + +### Create a new deck: `create` + +Creates a new deck of flashcards. The `create` command expects one argument specifying the name of the deck to be + created. + +#### Format + +Create deck without tags: +```java +create +``` +> Note: Duplicate deck name will not be allowed. And deck name cannot contain "/tag". + +Create deck with tag(s): +```java +create [/tag []] +``` +> Tags' name must be a single word, spaces are used to separate different tags. +> Tags' name is case-sensitive and cannot be duplicated. + +#### Examples + +Create deck without tags: +```java +[Normal] + > create market-failure +// New deck created: market-failure +``` + +Create deck with tags: +```java +[Normal] + > create market-failure /tag beginner advanced +// New deck created: market-failure #Tag: beginner | advanced +``` + +### Tag an existing deck: `tag` + +Adds a tag to an existing deck of flashcards. The `tag` command expects one argument specifying the name of the deck + to tag. At least one additional argument after /tag specifies tags to be added to the deck. + +#### Format + +```java +tag /tag [] +``` +> Note: Do `decks` command first to obtain up-to-date index. +> Tags' name must be a single word, spaces are used to separate different tags. +> Tags' name is case-sensitive and cannot be duplicated. + +#### Examples + +```java +[Normal] + > tag 1 /tag beginner +// The deck market-failure has been tagged as: beginner +``` +```java +[Normal] + > tag 1 /tag beginner advanced +// The deck market-failure has been tagged as: beginner | advanced +``` + +### Untag an existing tag: `untag` + +Removes an existing tag from an existing deck of flashcards. The `untag` command expects one argument specifying the +name of the deck to remove a deck from. At least one additional argument after /tag specifies tags +to be removed from the deck. User is then further prompted for an input of only either `y` or `n`. + +#### Format + +```java +untag /tag [] +// Do you want to remove the tag from ? [y/n] y/n +``` +> Note: Do `decks` command first to obtain up-to-date index. +> Tags' name must be a single word, spaces are used to separate different tags. +> Tags' name is case-sensitive. + +One-line untag command: + +```java +untag /tag [] [-y] +``` +> Specify -y for confirmation directly. +> To delete an existing -y tag in a deck, type -y twice + +#### Examples + +```java +[Normal] + > untag 1 /tag beginner +// Do you want to remove the tag beginner from market-failure? [y/n] y/n +// The tag beginner has been removed from the deck market-failure. +``` +```java +[Normal] + > untag 1 /tag -y -y +// The tag -y has been removed from the deck market-failure. +``` + +### Search decks by tag: `search` + +Searches all the decks containing the specified tags. The `search` command expects at least one argument specifying one +or more similar tags related to the deck. + +#### Format + +```java +search [] +``` +> Note: tags in search command are case-insensitive and must be a single word. +> Spaces are used to separate different tags. + +#### Examples: + +```java +[Normal] + > search Economics +//The decks having tags you are searching for: +//1. Micro-Economics +// Tags: Economics | EC1301 +//2. Macro-Economics +// Tags: Economics | EC1301 +``` + +> Notice how the original deck index is displayed. + +### Display all decks: `decks` + +Displays an enumerated list of all the decks available to the user. The `decks` command does not expect any arguments. + +#### Format + +```java +decks +``` +> Note: The program will ignore any input following the command separated by spaces. + +#### Examples + +```java +[Normal] + > decks +//The following decks are available: +//1. Micro-Economics +// Tags: Economics | EC1301 +//2. Macro-Economics +// Tags: Economics | EC1301 +``` + +```java +[Normal] + > decks thisWillBeIgnored +//The following decks are available: +//1. Micro-Economics +// Tags: Economics | EC1301 +//2. Macro-Economics +// Tags: Economics | EC1301 +``` + +### Delete an existing deck: `delete` + +Deletes an existing deck of flashcards. The `delete` command expects one argument specifying the index of the deck to + be deleted. User is then further prompted for an input of only either `y` or `n`. + +#### Format + +```java +[Normal] + > delete [-y] +// Do you want to delete `name of deck`? [y/n] +// `name of deck` has been deleted. +``` + +> Note: `name of deck` is a placeholder for the actual name of the deck corresponding to the index entered. The second +> line will only be displayed if the user entered y at the prompt for . + +One-line format: +``` + > delete -y +``` +This command forces the delete of the deck at index ``. + +#### Examples + +Deciding not to delete: +```java +[Normal] + > delete 1 +// Do you want to delete market-failure? [y/n] n +[Normal] + > +``` + +Confirming a delete: +```java +[Normal] + > delete 2 +// Do you want to delete perfect competition? [y/n] y +// perfect competition has been deleted. +[Normal] + > +``` + +Entering an invalid response: +```java +[Normal] + > delete 1 +// Do you want to delete market-failure? [y/n] no way +// Response should be 'y' or 'n'! +// Do you want to delete market-failure? [y/n] y +// market-failure has been deleted. +[Normal] + > +``` + +### Deck Mode: `edit` + +Enters the Deck Mode to edit an existing deck. The `edit` command expects one argument specifying the deck index for + which to enter Deck Mode. + +#### Format + +```java +[Normal] + > edit +``` + +#### Examples + +```java +[Normal] + > edit 1 +// -------------------------------------------------------------------------------- +// You are now in Deck Mode, editing: [1] market-failure +// -------------------------------------------------------------------------------- +[Deck - market-failure] + > +``` + +> Notice how the prompt mode identifier `[Normal]` changed to `[Deck - market-failure]`. + +### Game Mode: `start` + +Starts Game Mode for an existing deck. The `start` command expects one argument specifying the deck index for which to + enter Deck Mode. + +> The `start` command can also be entered from within Deck Mode, without the need for a deck index. + +#### Format + +```java +[Normal] + > start +``` + +#### Examples + +```java +[Normal] + > start 1 +// -------------------------------------------------------------------------------- +// Welcome to Game Mode! +// +// In this mode, you test your knowledge against... +// ... +// ...Have fun! +// +// Game Mode is started for: [1] market-failure +// -------------------------------------------------------------------------------- +// Q: What is market failure? +// Enter your attempt below (or `done`, `exit`, `help`): + > +``` + +### Print an Existing Deck to a PowerPoint File: `pptx` + +Prints an existing deck to a new PowerPoint file named `.pptx` in `pptx/` folder. +The `pptx` command expects one argument specifying the deck index for which to enter Deck Mode. +You can add the option `-y` to create the PowerPoint without any further prompt. + +#### Colors for background and text options +To select the color for background and text, you can either select from one of the 10 default color schemes +using `-cs ` or choose your own background and text colors using `-oc `. +You can only choose *either* and **not both** options to select color. +More details about the colors for original colors and color schemes available can be found [below](#features---print-to-powerpoint) + + > The `pptx` command can also be entered from within Deck Mode, without the need for deck index. + +#### Format + +```java +// -------------------------------------------------------------------------------- +[Normal] + > pptx +// Do you want to print `name of deck` deck to PowerPoint? [y/n] yes +// Response should be 'y' or 'n'! +// > y +// -------------------------------------------------------------------------------- +// 'name of deck' has been created as PowerPoint with default, +// black background and white text +// -------------------------------------------------------------------------------- +``` + +```java +// -------------------------------------------------------------------------------- +[Normal] + > pptx -y +// -------------------------------------------------------------------------------- +// 'name of deck' been created as PowerPoint with default, +// black background and white text +// -------------------------------------------------------------------------------- +``` +```java +[Normal] + > pptx -y -cs +// -------------------------------------------------------------------------------- +// Micro-Economics has been created as PowerPoint using Color Scheme, +// with background and text +// -------------------------------------------------------------------------------- +``` + +```java +[Normal] + > pptx -y -oc +// -------------------------------------------------------------------------------- +// Micro-Economics has been created as PowerPoint using Original Colors, +// with background and text +// -------------------------------------------------------------------------------- +``` + +#### Examples +```java +[Normal] + > pptx 1 +// Do you want to print `name of deck` deck to PowerPoint? [y/n] yes +// Response should be 'y' or 'n'! +// > y +// -------------------------------------------------------------------------------- +// -------------------------------------------------------------------------------- +// Micro-Economics has been created as PowerPoint with default, +// black background and white text +// -------------------------------------------------------------------------------- +``` + +```java +[Normal] + > pptx 1 -y +// -------------------------------------------------------------------------------- +// Micro-Economics has been created as PowerPoint with default, +// black background and white text +// -------------------------------------------------------------------------------- +``` + +```java +[Normal] + > pptx 1 -y -cs 1 +// -------------------------------------------------------------------------------- +// Micro-Economics has been created as PowerPoint using Color Scheme, +// with steelblue background and silver text +// -------------------------------------------------------------------------------- +``` + +```java +[Normal] + > pptx 1 -y -oc lightblue darkred +// -------------------------------------------------------------------------------- +// Micro-Economics has been created as PowerPoint using Original Colors, +// with lightblue background and darkred text +// -------------------------------------------------------------------------------- +``` + +## Features - Deck Mode + +### Viewing help in Deck Mode: `help` + +Displays the list of all commands in Deck Mode. + +#### Examples + +```java +[Deck - market-failure] + > help +// -------------------------------------------------------------------------------- +// eCardnomics. +// Deck Mode. +// +// Usage: +// add [ /ans ] Adds a new flash card to the current +// deck. +// list [/ans] Lists all flash cards in the current +// deck, optionally with answers. +// delete [-y] Deletes the flash card at list index +// from the current deck. +// update Updates the flash card at list index +// from the current deck. +// pptx [-y] [-cs | -oc Creates a PowerPoint slides based on +// ] current deck. +// start Enter Game Mode for this deck! Do your +// best! +// done Exits from Deck Mode and returns to +// Normal Mode. +// exit Exits the program. +// help Show this output. +// +// Options: +// --version Show version. +// -------------------------------------------------------------------------------- +``` + +### Add a flashcard: `add` +Adds a flashcard to the end of the current deck. The `add` command expects no initial arguments. Instructions and +format of card entry is displayed. Then, the user is prompted to specify the details of the flashcard to be added. +Duplicate flashcards are allowed. Note that questions and answers must contain at least one alphanumeric character. + +#### Format +```java + > add +// Enter question: +// Enter answer: +``` + +Equivalent One-line format: +```java + > add /ans +``` +>The `/ans` option, if supplied, must be separated from the question and answer by spaces. +> +>If `/ans` option is not supplied correctly, `` is stored and the user is prompted +>for the answer. This includes the case of 'add /ans ' because the question must not be empty and we allow +>'/ans' to be part of a question. + +#### Examples + +```java +[Deck - market failure] + > add +// -------------------------------------------------------------------------------- +// You are now adding a FlashCard to: market failure +// -------------------------------------------------------------------------------- +// Enter question: + > define market failure +// Enter answer: + > Market failure is the economic situation defined by an inefficient distribution of goods +and services in the free market +// FlashCard successfully added! +// -------------------------------------------------------------------------------- +``` + +### List all the flashcards in the deck: `list` +Lists all the existing flash cards within the current deck. You can add the option `\ans` after the `list` command +to show all the questions, and their respective answers. + +#### Format +```java +[Deck - 'name of deck'] + > list +// -------------------------------------------------------------------------------- +// You are now viewing deck: `name of deck` +// -------------------------------------------------------------------------------- +// 1. Question: +// +// 2. Question: +// +// 3. Question: +// -------------------------------------------------------------------------------- +``` + +```java +[Deck - 'name of deck'] + > list /ans +// -------------------------------------------------------------------------------- +// You are now viewing deck: 'name of deck' +// -------------------------------------------------------------------------------- +// 1. Question: +// Answer: +// +// 2. Question: +// Answer: +// +// 3. Question: +// Answer: +// -------------------------------------------------------------------------------- +``` + +#### Examples +List without answers: +```java +[Deck - market-failure] + > list +// -------------------------------------------------------------------------------- +// You are now viewing deck: market-failure +// -------------------------------------------------------------------------------- +// 1. Question: define market failure +// +// 2. Question: What is a public good? +// +// 3. Question: What is a merit good? +// -------------------------------------------------------------------------------- +``` + +List with answers: +```java +[Deck - market-failure] + > list /ans +// -------------------------------------------------------------------------------- +// You are now viewing deck: market-failure +// -------------------------------------------------------------------------------- +// 1. Question: define market failure +// Answer: Market failure is the economic situation +// defined by an inefficient distribution of goods +// and services in the free market +// +// 2. Question: What is a public good? +// Answer: A good which are non-rival and non-excludable +// +// 3. Question: What is a merit good? +// Answer: A good that people underestimates the benefits +// of +// -------------------------------------------------------------------------------- +``` + +### Delete an existing Flash Card: `delete` + +Deletes an existing flashcard from deck. The `delete` command expects one argument specifying the index of the flash card to + be deleted. User is then further prompted for an input of only either `y` or `n`. + +#### Format + +```java +[Deck - 'name of deck'] + > delete 1 +// Do you want to delete the following flash card? [y/n] ? +// '' n +// -------------------------------------------------------------------------------- +``` + +```java +[Deck - 'name of deck'] + > delete 2 +// Do you want to delete the following flash card? [y/n] ? +// '' y +// -------------------------------------------------------------------------------- +// The following flash card has been deleted: +// '' +// -------------------------------------------------------------------------------- +``` -## Features +> Note: `name of deck` is a placeholder for the name of the current deck. The second +> line will only be displayed if the user entered y at the prompt for . -{Give detailed description of each feature} +One-line format: +```java + > delete -y +``` +This command forces the delete of the Flashcard at index ``. -### Adding a todo: `todo` -Adds a new item to the list of todo items. +#### Examples -Format: `todo n/TODO_NAME d/DEADLINE` +Deciding not to delete: +```java +[Deck - market-failure] + > delete 1 +// Do you want to delete the following flash card? [y/n] +// `define market failure?` n +// -------------------------------------------------------------------------------- +[Deck - market-failure] + > +``` -* The `DEADLINE` can be in a natural language format. -* The `TODO_NAME` cannot contain punctuation. +Confirming a delete: +```java +[Deck - market-failure] + > delete 2 +// Do you want to delete the following flash card? [y/n] ? +// 'What is a public good?' y +// -------------------------------------------------------------------------------- +// The following flash card has been deleted: +// 'What is a public good?' +// -------------------------------------------------------------------------------- +[Deck - market-failure] + > +``` -Example of usage: +Entering an invalid response: +```java +[Deck - market failure] + > delete 2 +// Do you want to delete the following flash card? [y/n] ? +// 'What is a public good?' definitely +// Response should be 'y' or 'n'! +// > y +// -------------------------------------------------------------------------------- +// The following flash card has been deleted: +// 'What is a public good?' +// -------------------------------------------------------------------------------- +``` -`todo n/Write the rest of the User Guide d/next week` +### Update an existing Flash Card: `update` -`todo n/Refactor the User Guide to remove passive voice d/13/04/2020` +Updates the question and answer fields of a specified flashcard in the deck. The `update` command expects no initial + arguments. The current question and answer are displayed. Then, the user is prompted to specify the new details of + the flashcard. + +#### Format +```java +[Deck - 'name of deck'] + > update +// Question: `Current Question` +// New Question: + > +// +// Answer: `Current Answer` +// New Answer: + > +``` +> Note: `name` is a placeholder for the actual name of the deck that is being edited. +> `Current Question` and `Current Answer` are placeholders for the original question +> and answer of the flashcard. + +If a blank line is supplied as the new detail, then the detail is unchanged. + +If either the new question or new answer is specified without any alphanumeric characters, then both will be rejected +amd the old question and old answer will be retained. + +#### Examples + +Updating both the question and answer: +```java +[Deck - market failure] + > update 1 +// Question: Define market failure +// New Question: + > What is the difference between free-loading and free-riding? +// +// Answer: Economic situation defined by inefficient distribution of goods and services in the free market +// New Answer: + > Free-loading gives a benefit to the free-loader but there is a cost to the people taken advantage of. + Free-riding is an advantage to the free-rider without imposing a cost on others or society. +// -------------------------------------------------------------------------------- +// Question and answer updated. +// -------------------------------------------------------------------------------- +``` + +Updating question only: +```java +[Deck - market failure] + > update 1 +// Question: Define market failure +// New Question: + > Define Market Failure +// +// Answer: Economic situation defined by inefficient distribution of goods and services in the free market +// New Answer: + > +// -------------------------------------------------------------------------------- +// Question updated. +// -------------------------------------------------------------------------------- +``` + +Updating answer only: +```java +[Deck - market failure] + > update 1 +// Question: Define market failure +// New Question: + > +// +// Answer: Economic situation defined by inefficient distribution of goods and services in the free market +// New Answer: Economic situation where distribution of goods and services in the free market is inefficient + > Economic situation where distribution of goods and services in the free market is inefficient +// -------------------------------------------------------------------------------- +// Answer updated. +// -------------------------------------------------------------------------------- +``` + +No updates: +```java +[Deck - market failure] + > update 1 +// Question: Define market failure +// New Question: + > +// +// Answer: Economic situation defined by inefficient distribution of goods and services in the free market +// New Answer: Economic situation where distribution of goods and services in the free market is inefficient + > +// -------------------------------------------------------------------------------- +// Original question and answer retained. +// -------------------------------------------------------------------------------- +``` + +### Game Mode: `start` + +Starts Game Mode for the current deck. Upon completion of Game Mode, user is returned to Normal Mode. + +> The `start` command can also be entered from within Normal Mode. +> +> Note: typing `done` in Game Mode returns user to Normal Mode, regardless of which mode Game Mode was started from +>. See [Game Mode `done`](#exits-game-mode-done). + +#### Format + +```java +[Deck - market-failure] + > start +``` + +#### Examples + +```java +// You are now in Deck Mode, editing: [1] market-failure +// -------------------------------------------------------------------------------- +[Deck - market-failure] + > ... +// ... +// -------------------------------------------------------------------------------- +[Deck - market-failure] + > start +// -------------------------------------------------------------------------------- +// Welcome to Game Mode! +// +// In this mode, you test your knowledge against... +// ... +// ...Have fun! +// +// Game Mode is started for: [1] market-failure +// -------------------------------------------------------------------------------- +// Q: What is market failure? +// Enter your attempt below (or `done`, `exit`, `help`): + > +``` + +### Print Current Deck to a PowerPoint File: `pptx` + +Prints the current deck to a new PowerPoint file named `.pptx` in `pptx/` folder. +You can add the option `-y` to create the PowerPoint without any further prompt. + +#### Colors for background and text options +To select the color for background and text, you can either select from one of the 10 default color schemes +using `-cs ` or choose your own background and text colors using `-oc `. +You can only choose *either* and **not both** options to select color. +More details about the colors for original colors and color schemes available can be found [below](#features---print-to-powerpoint) + +> The `pptx` command can also be entered from within Normal Mode. + +#### Format +```java +[Deck - 'name of deck'] + > pptx +// Do you want to print 'name of deck' deck to PowerPoint? [y/n] yes +// Response should be 'y' or 'n'! +// > y +// -------------------------------------------------------------------------------- +// 'name of deck' has been created as PowerPoint with default, +// black background and white text +// -------------------------------------------------------------------------------- +``` + +```java +[Deck - 'name of deck'] + > pptx -y +// -------------------------------------------------------------------------------- +// 'name of deck' been created as PowerPoint with default, +// black background and white text +// -------------------------------------------------------------------------------- +``` + +```java +[Deck - 'name of deck'] + > pptx -y -cs +// -------------------------------------------------------------------------------- +// Micro-Economics has been created as PowerPoint using Color Scheme, +// with background and text +// -------------------------------------------------------------------------------- +``` + +```java +[Deck - 'name of deck'] + > pptx -y -oc +// -------------------------------------------------------------------------------- +// Micro-Economics has been created as PowerPoint using Original Colors, +// with background and text +// -------------------------------------------------------------------------------- +``` + +#### Examples + +```java +[Deck - Micro-Economics] + > pptx +// Do you want to print Micro-Economics deck to PowerPoint? [y/n] y +// -------------------------------------------------------------------------------- +// Micro-Economics has been created as PowerPoint with default, +// black background and white text. +// -------------------------------------------------------------------------------- +``` + +```java +[Deck - Micro-Economics] + > pptx -y +// -------------------------------------------------------------------------------- +// Micro-Economics has been created as PowerPoint with default, +// black background and white text. +// -------------------------------------------------------------------------------- +``` + +```java +[Deck - Micro-Economics] + > pptx -y -cs 1 +// -------------------------------------------------------------------------------- +// Micro-Economics has been created as PowerPoint using Color Scheme, +// with steelblue background and silver text. +// -------------------------------------------------------------------------------- +``` + +```java +[Deck - Micro-Economics] + > pptx -y -oc lightblue darkred +// -------------------------------------------------------------------------------- +// Micro-Economics has been created as PowerPoint using Original Colors, +// with lightblue background and darkred text. +// -------------------------------------------------------------------------------- +``` + +### Exits Deck Mode: `done` + +Returns to Normal Mode. + +#### Examples + +```java +[Deck - market-failure] + > done +// -------------------------------------------------------------------------------- +// You are back in Normal Mode +// -------------------------------------------------------------------------------- +[Normal] + > +``` + +## Features - Game Mode + +### Gameplay + +Questions are displayed in a randomised order. At each question, the user will: + +1. Try to attempt an answer at the question, by typing at the prompt; then +2. Press `` (optionally with an empty attempt). + +Then, the correct answer is displayed, and our 'advanced' algorithm scores the user's attempt against the correct + answer. Finally, the user is given the option to re-attempt the question later. See below for example gamplay. + +> Upon completion of deck in Game Mode, user is always returned to **Normal Mode**. + +#### Examples + +* Start Game Mode (from within Deck Mode) + +```java +[Deck - Micro-Economics] + > list +// -------------------------------------------------------------------------------- +// You are now viewing deck: Micro-Economics +// 1. Question: What is the Law of demand? +// +// 2. Question: What is the Law of supply? +// +// 3. Question: What is price elasticity of demand? +// +// 4. Question: What is price elasticity of supply? +// -------------------------------------------------------------------------------- +[Deck - Micro-Economics] + > start +``` + +* Play! + +```java +// ... +// Game Mode is started for: [1] Micro-Economics +// -------------------------------------------------------------------------------- +// Q: What is the Law of supply? +// Enter your attempt below (or `done`, `exit`, `help`): + > Price of good increases, quantity supplied increases. +// A: When the price of a good increases, the quantity supplied increases, ceteris paribus. +// The % match between your answer and the actual answer is: 53.85 +// -------------------------------------------------------------------------------- +// Do you want to re-attempt this question later? [y/n] + > y +// -------------------------------------------------------------------------------- +// Q: What is price elasticity of demand? +// Enter your attempt below (or `done`, `exit`, `help`): + > I don't know. +// A: Percentage change in quantity demanded caused by a 1 percent change in price. +// The % match between your answer and the actual answer is: 0.00 +// -------------------------------------------------------------------------------- +// Do you want to re-attempt this question later? [y/n] + > y +// -------------------------------------------------------------------------------- +// Q: What is the Law of demand? +// Enter your attempt below (or `done`, `exit`, `help`): + > Quantity of good demanded fall when price of good rises. +// A: When the price of a good rises, the quantity of the good demanded will fall, ceteris paribus. +// The % match between your answer and the actual answer is: 58.82 +// -------------------------------------------------------------------------------- +// Do you want to re-attempt this question later? [y/n] + > n +// -------------------------------------------------------------------------------- +// Q: What is price elasticity of supply? +// Enter your attempt below (or `done`, `exit`, `help`): + > Percentage change in quantity supplied for a 1 percent price change +// A: Percentage change in quantity supplied caused by a 1 percent change in price. +// The % match between your answer and the actual answer is: 84.62 +// -------------------------------------------------------------------------------- +// Do you want to re-attempt this question later? [y/n] + > n +// -------------------------------------------------------------------------------- +// Q: What is price elasticity of demand? +// Enter your attempt below (or `done`, `exit`, `help`): + > Percentage change in quantity demanded for a 1 percent price change. +// A: Percentage change in quantity demanded caused by a 1 percent change in price. +// The % match between your answer and the actual answer is: 84.62 +// -------------------------------------------------------------------------------- +// Do you want to re-attempt this question later? [y/n] + > n +// -------------------------------------------------------------------------------- +// Q: What is the Law of supply? +// Enter your attempt below (or `done`, `exit`, `help`): + > Price of a good increases, the quantity supplied increases, ceteris paribus. +// A: When the price of a good increases, the quantity supplied increases, ceteris paribus. +// The % match between your answer and the actual answer is: 92.31 +// -------------------------------------------------------------------------------- +// Do you want to re-attempt this question later? [y/n] + > n +// -------------------------------------------------------------------------------- +// You have completed all the flash cards in this deck! +// Returning to Normal Mode... +// -------------------------------------------------------------------------------- +// You are back in Normal Mode +// -------------------------------------------------------------------------------- +[Normal] + > +``` + +### Viewing help in Game Mode: `help` + +Displays the list of all commands in Game Mode. + +#### Examples + +```java +// ... +// -------------------------------------------------------------------------------- +// Q: What is market-failure? +// Enter your attempt below (or `done`, `exit`, `help`): + > help +// -------------------------------------------------------------------------------- +// eCardnomics. +// Game Mode. +// +// Usage: +// done Exits from Game Mode and returns to Normal Mode. +// exit Exits the program. +// help Show this output. +// +// Options: +// --version Show version. +// +// Gameplay: +// Questions will be displayed in a randomised order. At each question, you can +// 1. Try to attempt an answer at the question, by typing at the prompt +// 2. Press (with an empty attempt if you want to do it in your head) +// +// Then, our 'advanced' algorithms will check your answer and score your answer (if +// any), and display the correct answer for you to check your answer against. +// Finally, we will ask if you think you got it right. If you did not, the question +// will be inserted back into the question pool, and you will get a chance to +// attempt it again! +// -------------------------------------------------------------------------------- +// Q: What is market-failure? +// Enter your attempt below (or `done`, `exit`, `help`): + > +``` + +### Exits Game Mode: `done` + +Returns to Normal Mode. + +#### Examples + +```java +// ... +// -------------------------------------------------------------------------------- +// Q: What is market-failure? +// Enter your attempt below (or `done`, `exit`, `help`): + > done +// -------------------------------------------------------------------------------- +// You are back in Normal Mode +// -------------------------------------------------------------------------------- +[Normal] + > +``` + +## Features - Print to PowerPoint + +### Create new PowerPoint based on the selected deck: `pptx` + +Can be done from both Normal Mode and Deck Mode. In Normal Mode, a deck index is required, +but in Deck Mode, there is no need to specify the deck index. +Has the option to skip confirmation prompt using `-y` suffix. +There is also the option to select both the background and text colors. There are 10 default color schemes that the +user can choose from using the option `-cs 1`. [See below for the default color schemes](#default-color-schemes) +There is also the option to choose your own colors for background and text from the provided colors using the option +`-oc `. [See below for the colors available](#original-colors-available) + +#### Example +The following is an example of the slides printed out after the following command. +```java +[Deck - Micro-Economics] + > pptx -y -cs 1 +// -------------------------------------------------------------------------------- +// Micro-Economics has been created as PowerPoint using Color Scheme, +// with steelblue background and silver text. +// -------------------------------------------------------------------------------- +``` + +##### Output +![PPTX Example](images-ug/PPTX-Example.png) + +For details, check out: + +* [Normal Mode PowerPoint](#print-an-existing-deck-to-a-powerpoint-file-pptx) +* [Deck Mode PowerPoint](#print-current-deck-to-a-powerpoint-file-pptx) + +#### Default Color Schemes +The following is the default schemes that are available and their respective index. +![Default Color Schemes](images-ug/PPTX-Default-Color-Schemes.png) + +#### Original Colors available +Some of the colors include: +* Red +* Blue +* Yellow +* Green +* Black +* White +* Purple +* Violet +* Pink +* Navy +* Gray + +The complete list of colors can be found [here](https://www.javadoc.io/doc/org.beryx/awt-color-factory/1.0.1/org.beryx.awt.color/org/beryx/awt/color/ColorFactory.html) + +## Features - Anywhere + +### Exits the program: `exit` + +Can be entered from any mode, anywhere in the program. + +> Except during Y/N prompts. + +#### Examples + +```java +[Normal] + > exit +// -------------------------------------------------------------------------------- +// Bye! Hope to see you again soon! +// -------------------------------------------------------------------------------- +``` + +```java +[Deck - market-failure] + > exit +// -------------------------------------------------------------------------------- +// Bye! Hope to see you again soon! +// -------------------------------------------------------------------------------- +``` + +```java +... +// -------------------------------------------------------------------------------- +// Q: What is market-failure? +// Enter your attempt below (or `done`, `exit`, `help`): + > exit +// -------------------------------------------------------------------------------- +// Bye! Hope to see you again soon! +// -------------------------------------------------------------------------------- +``` + +### Shows release version: `--version` + +Shows release version from anywhere in the program. + +> Except during Y/N prompts. + +#### Examples + +```java +// ... +// -------------------------------------------------------------------------------- +// Q: What is market-failure? +// Enter your attempt below (or `done`, `exit`, `help`): + > --version +// -------------------------------------------------------------------------------- +// Version: 2.0 +// -------------------------------------------------------------------------------- +// Q: What is market-failure? +// Enter your attempt below (or `done`, `exit`, `help`): + > +``` ## FAQ -**Q**: How do I transfer my data to another computer? +### General +**Q**: With none-argument command, can it be executed if I type the command with some nonsense +arguments? +**Q**: Yes you still can, with this type of command, such as exit or done, the program will only take +into account the first word you enter, the rest will be automatically discarded. + +Example: +```java +[Deck - Macro-Economics] + > done cdcns cndcn +//-------------------------------------------------------------------------------- +//You are back in Normal Mode +//-------------------------------------------------------------------------------- +``` +**A**: No. You cannot name your deck with "/tag" word. However, the tag of deck can +contain "/tag". + +**Q**: Why are some of the output (e.g. questions and answer) aligned to the dashed lines but not others (like help). + +**A**: Question and answers could possibly be very long and could take up multiple lines. These are also important +information that the economics students are trying to study. We would not want an inconsistent wrapping scheme +distracting users from focusing on the contents of the flashcards. For other output with fixed lengths that +slightly exceed the dashed lines, the readability is not really affected and one could even say that having all +the information on a single line is more readable. + +**Q**: Help command can be typed anywhere but is not found in the "Anywhere" section of this document. Why? + +**A**: Anywhere section is for generic commands that have same effect regardless of user's location within +application. help commands are unique and distinct across different modes, which explains the decision to place +it within each mode, rather than within the "Anywhere" section. + +### Storage + +**Q**: Can I edit the save file directly to add or remove flashcards and decks? -**A**: {your answer here} +**A**: Yes, you can! However, there are some constraints when it comes to the format of the txt data file. + +Each deck of flashcards should be written in the following format: + +```java +================================================================================ +deck | +tags | | | +Q | +A | +Q | +A | +================================================================================ +``` + +Note: +* Each **`Deck`** should be separated by a line of '=' shown above. +* Each item must be specified with the delimiter " | " with whitespaces on each side. +* More `tags` are separated by the delimiter " | " as well. +* **`Decks`** with duplicate deck names will be removed when starting the program. +* `Questions` and `Answers` cannot be blank and must contain alphanumeric characters. +* **Should there be any deviance from the specified format, +the entire deck with deviant formatting will be lost when starting the program.** + + +### Normal Mode + +**Q**: Can the deck name contain "/tag"? + +**A**: No. You cannot name your deck with "/tag" word. However, the tag of deck can +contain "/tag". + +### Deck Mode + +**Q**: Can my answer contain "/ans"? + +**A**: Yes, just type it in the same way as any other one-line add command: + +```java +> add My Question /ans /ans +``` +Alternatively, `add` and `add My Question` also work since the answer will be read separately. + +**Q**: Can my question contain "/ans"? + +**A**: Yes. If it is part of another word: +```java +> add What is sum/ans? /ans some value +``` +Otherwise, use `add` without any arguments so that the question is read separately. + +**Q**: Why is a single line update command not provided? + +**A**: When updating the information on a flashcard, it is important to view the previous contents of the flashcard +to determine the correct new information to update the flashcard. Therefore, it is unlikely that there will be a +useful scenario for a single line update command. + +### Game Mode + +**Q**: What if my flash card answer contains a single word "done" or "exit"? + +```java +// Q: What is the command to exit Game Mode or Deck Mode? +// Enter your attempt below (or `done`, `exit`, or `help`): + > done +// -------------------------------------------------------------------------------- +// You are back in Normal Mode +// -------------------------------------------------------------------------------- +[Normal] + > +``` + +**A**: Escape the command using punctuations, e.g. `\done` or `"done"`. + +```java +// Q: What is the command to exit Game Mode or Deck Mode? +// Enter your attempt below (or `done`, `exit`, or `help`): + > "done" +// A: done +// The % match between your answer and the actual answer is: 100.00 +// -------------------------------------------------------------------------------- +// ... +``` ## Command Summary -{Give a 'cheat sheet' of commands here} +### Normal Mode + +|Action|Format|Example| +|------|------|-------| +|Create deck|`create `|`create market-failure`| +|Tag deck|`tag /tag [ ...]`|`tag 1 /tag important final-exam`| +|Untag deck|`untag /tag [ ...] [-y]`|`untag 1 /tag important`| +|Search by tag(s)|`search [ ...]`|`search final-exam important`| +|Display decks|`decks`|| +|Delete deck|`delete [-y]`|`delete 1`| +|Enter Deck Mode|`edit `|`edit 1`| +|Enter Game Mode|`start `|`start 1`| +|Create PowerPoint|`pptx [-y] [-oc ] [-cs ]`|`pptx 1`| +|Help|`help`|| + +### Deck Mode + +|Action|Format|Example| +|------|------|-------| +|Add flash card|`add [ /ans ]`|| +|List flash cards|`list [/ans]`|| +|Delete flash card|`delete [-y]`|`delete 1`| +|Update flash card|`update `|`update 1`| +|Enter Game Mode|`start`|| +|Create PowerPoint|`pptx [-y]`|| +|Exit Deck Mode|`done`|| +|Help|`help`|| + +### Game Mode + +|Action|Format|Example| +|------|------|-------| +|Done|`done`|| +|Help|`help`|| + +### Anywhere -* Add todo `todo n/TODO_NAME d/DEADLINE` +|Action|Format|Example| +|------|------|-------| +|Exit program|`exit`|| +|Show version|`--version`|| diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 0000000000..cc35c1df2c --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-modernist \ No newline at end of file diff --git a/docs/images-dg/Architecture.png b/docs/images-dg/Architecture.png new file mode 100644 index 0000000000..f24174a121 Binary files /dev/null and b/docs/images-dg/Architecture.png differ diff --git a/docs/images-dg/DG-Design-Commands-Breakdown.png b/docs/images-dg/DG-Design-Commands-Breakdown.png new file mode 100644 index 0000000000..216791d7b6 Binary files /dev/null and b/docs/images-dg/DG-Design-Commands-Breakdown.png differ diff --git a/docs/images-dg/DG-Design-Commands-UML.png b/docs/images-dg/DG-Design-Commands-UML.png new file mode 100644 index 0000000000..0e0a0cb1ea Binary files /dev/null and b/docs/images-dg/DG-Design-Commands-UML.png differ diff --git a/docs/images-dg/DG-Design-Model.png b/docs/images-dg/DG-Design-Model.png new file mode 100644 index 0000000000..d0da3a63aa Binary files /dev/null and b/docs/images-dg/DG-Design-Model.png differ diff --git a/docs/images-dg/DG-Design-Sequence-Diagram.png b/docs/images-dg/DG-Design-Sequence-Diagram.png new file mode 100644 index 0000000000..2a597eb9fa Binary files /dev/null and b/docs/images-dg/DG-Design-Sequence-Diagram.png differ diff --git a/docs/images-dg/DG-Exceptions-Architecture.png b/docs/images-dg/DG-Exceptions-Architecture.png new file mode 100644 index 0000000000..1da7c65143 Binary files /dev/null and b/docs/images-dg/DG-Exceptions-Architecture.png differ diff --git a/docs/images-dg/DG-Game-Mode-Architecture-Overview.png b/docs/images-dg/DG-Game-Mode-Architecture-Overview.png new file mode 100644 index 0000000000..91b1e79d81 Binary files /dev/null and b/docs/images-dg/DG-Game-Mode-Architecture-Overview.png differ diff --git a/docs/images-dg/DG-Game-Mode-Sequence.png b/docs/images-dg/DG-Game-Mode-Sequence.png new file mode 100644 index 0000000000..cada3586a4 Binary files /dev/null and b/docs/images-dg/DG-Game-Mode-Sequence.png differ diff --git a/docs/images-dg/DG-Game-Storage-Game-Engine.png b/docs/images-dg/DG-Game-Storage-Game-Engine.png new file mode 100644 index 0000000000..8f2ee6fded Binary files /dev/null and b/docs/images-dg/DG-Game-Storage-Game-Engine.png differ diff --git a/docs/images-dg/DG-Game-Use-Case.png b/docs/images-dg/DG-Game-Use-Case.png new file mode 100644 index 0000000000..7da25af4f2 Binary files /dev/null and b/docs/images-dg/DG-Game-Use-Case.png differ diff --git a/docs/images-dg/DG-Parser-UML.png b/docs/images-dg/DG-Parser-UML.png new file mode 100644 index 0000000000..6bf3a4a21e Binary files /dev/null and b/docs/images-dg/DG-Parser-UML.png differ diff --git a/docs/images-dg/DG-run-game-loop-sd.png b/docs/images-dg/DG-run-game-loop-sd.png new file mode 100644 index 0000000000..8599339b35 Binary files /dev/null and b/docs/images-dg/DG-run-game-loop-sd.png differ diff --git a/docs/images-dg/Logic-DG-copy.png b/docs/images-dg/Logic-DG-copy.png new file mode 100644 index 0000000000..723fad60d8 Binary files /dev/null and b/docs/images-dg/Logic-DG-copy.png differ diff --git a/docs/images-dg/Logic-DG.png b/docs/images-dg/Logic-DG.png new file mode 100644 index 0000000000..acbf5155cb Binary files /dev/null and b/docs/images-dg/Logic-DG.png differ diff --git a/docs/images-dg/PP-Sequence.png b/docs/images-dg/PP-Sequence.png new file mode 100644 index 0000000000..48632a0b7f Binary files /dev/null and b/docs/images-dg/PP-Sequence.png differ diff --git a/docs/images-dg/PPTX-Color-Options-Sequence-Diagram.png b/docs/images-dg/PPTX-Color-Options-Sequence-Diagram.png new file mode 100644 index 0000000000..d63e857c43 Binary files /dev/null and b/docs/images-dg/PPTX-Color-Options-Sequence-Diagram.png differ diff --git a/docs/images-dg/PPTX-Sequence-Diagram.png b/docs/images-dg/PPTX-Sequence-Diagram.png new file mode 100644 index 0000000000..aad773df0c Binary files /dev/null and b/docs/images-dg/PPTX-Sequence-Diagram.png differ diff --git a/docs/images-dg/Sequence Diagram.png b/docs/images-dg/Sequence Diagram.png new file mode 100644 index 0000000000..338e9c829a Binary files /dev/null and b/docs/images-dg/Sequence Diagram.png differ diff --git a/docs/images-dg/Storage.png b/docs/images-dg/Storage.png new file mode 100644 index 0000000000..10fcc69ab2 Binary files /dev/null and b/docs/images-dg/Storage.png differ diff --git a/docs/images-dg/Tag-feature.png b/docs/images-dg/Tag-feature.png new file mode 100644 index 0000000000..db0232f02c Binary files /dev/null and b/docs/images-dg/Tag-feature.png differ diff --git a/docs/images-dg/Tag.png b/docs/images-dg/Tag.png new file mode 100644 index 0000000000..de68ddfe9e Binary files /dev/null and b/docs/images-dg/Tag.png differ diff --git a/docs/images-dg/TagFeature-UML.png b/docs/images-dg/TagFeature-UML.png new file mode 100644 index 0000000000..6dbe9fa934 Binary files /dev/null and b/docs/images-dg/TagFeature-UML.png differ diff --git a/docs/images-ug/PPTX-Default-Color-Schemes.png b/docs/images-ug/PPTX-Default-Color-Schemes.png new file mode 100644 index 0000000000..16d3b59af6 Binary files /dev/null and b/docs/images-ug/PPTX-Default-Color-Schemes.png differ diff --git a/docs/images-ug/PPTX-Example.png b/docs/images-ug/PPTX-Example.png new file mode 100644 index 0000000000..959b45d262 Binary files /dev/null and b/docs/images-ug/PPTX-Example.png differ diff --git a/docs/team/alwaysnacy.md b/docs/team/alwaysnacy.md new file mode 100644 index 0000000000..dac3adc9a6 --- /dev/null +++ b/docs/team/alwaysnacy.md @@ -0,0 +1,47 @@ + +# Huynh Thi Thu Trang - Project Portfolio Page + +## Overview +eCardnomics is a **single-use desktop flashcard application to create, manage, modify and interact with new flashcards + via Command +Line Interface (CLI)**. Its target user is Junior College Economics students with high volume of terms to remember, + eCardnomics can be useful to anyone who want to store and interact with the large amount of information he/she learns. + Flashcards with same field of information can be group into decks, which is a collection of similar Flashcards. + Each deck can also have tags to give a summary of the information contained +in the deck and what field of information the deck is relevant. Furthermore, eCardnomics allows user to play and +interact +with the Flashcards on a daily basis to enhance users' memory. Finally, Flashcards can be exported to PowerPoint format +which helps users find the learning process more interesting and visually satisfying. + +### Summary of Contributions +[Code contribution](https://nus-cs2113-ay2021s1.github.io/tp-dashboard/#breakdown=true&search=alwaysnacy) + +In v1.0: +* Implemented CreateCommand, DecksCommand and DeletedDeckCommand in DeckCommand +* Add more methods in NormalParser() to follow the Code Quality guidelines +* Added more JUnit tests for **`CreateCommand`** and **`DecksCommand`** + +##### Enhancements implemented + +In v2.0: +* Implemented the Tag features for Deck to make it easier to group similar decks and find them + * Add tag to the deck on creation or an existing deck + * Untag an existing tag of the decks +* Implemented SearchCommand to find decks that are relevant to interest field + * search for similar words in tag field of deck +* Add more JUnit tests for all children of DeckCommand + +In v2.1: +* Make deck only accept unique name +* Make Tag feature more reliable and efficient by eliminating duplicates and spaces + +#### Contributions to documentation + +* Wrote guidelines for all the Command I have implemented. +* Review other section. + +#### Contributions to the DG + +* Wrote the section for the **`Tag`** in Enhancement Features. +* Draw diagram for Overall Logic of the diagram +* Add terms to glossary. \ No newline at end of file diff --git a/docs/team/alwaysnacy.png b/docs/team/alwaysnacy.png new file mode 100644 index 0000000000..da2166215d Binary files /dev/null and b/docs/team/alwaysnacy.png differ 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/kaijiel24.md b/docs/team/kaijiel24.md new file mode 100644 index 0000000000..761e4f0ced --- /dev/null +++ b/docs/team/kaijiel24.md @@ -0,0 +1,127 @@ + +# Liau Kai Jie - Project Portfolio Page + +## Overview +eCardnomics is a complete Command Line Interface Flash Card manager program for fast-typing, Junior College Economics +students to create flash cards. The manager can store multiple decks of flash cards, which consists of a question and an +answer, and has 3 distinct modes of operation. + +In Normal mode, it can manage decks of flash card performing operations such as `create`, `delete`, `tag`, `search` and +`ppt` on a particular deck. + +There is also Deck mode which can accessed from Normal mode via the operation `edit` which allows the user to edit the +flash cards within a deck by performing operations such as `add`, `delete` and `update`. + +The last mode is Game mode which is the main tool that the users will be using after they have created a deck to help them +with their studying by practicing active recall. + +### Summary of Contributions +#### Code Contributed +My code contribution can be found [here](https://nus-cs2113-ay2021s1.github.io/tp-dashboard/#breakdown=true&search=kaijiel24&sort=groupTitle&sortWithin=title&since=2020-09-27&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other) +#### Basic features implemented: +##### v1.0: +* Run Normal mode and run Deck mode in Main +* Normal Parser and Deck Parser +* Edit command to enter Deck mode from Normal mode +* Exit command from both Normal and Deck mode +* Done Edit command to return to Normal mode from Deck mode +* Getting index from user input to be used in commands which require an index (such as `delete`, `edit`,`tag`) + * Handling Exceptions for user input index + * If the argument given is not a positive integer (-1, micro-economics, or random etc...), `IndexFormatException` + will be thrown. + * If the number given is too big such that it is greater than MAX_INT and cannot be stored in an integer variable, + `NumberTooBigException` will be thrown. + * If the number given is not within the range of number of decks/ flash cards (either 0 or greater than number + of decks/ flash cards), `DeckRangeException`/ `FlashCardRangeException` will be thrown. + +#### Enhancements implemented: +The following are the enhancements I have implemented categorised by the versions of the program. + +##### v2.0 +* Powerpoint command + * This feature allows the user to print any deck to a PowerPoint Slide (.pptx file) within the pptx folder of the + current working directory. + * The target for this could be students who wish to use the flash cards they have created on other platforms such + their mobile phones or test themselves outside of the CLI. + * This command can be called from both Normal mode (need to specify which deck to use) and Deck mode (do not + need to specify, automatically uses the current deck). + * The PowerPoint Slide is created using the [`Apache POI API`](https://poi.apache.org/index.html). + + * Added [JUnit Test](https://github.com/AY2021S1-CS2113-T14-2/tp/commit/5c398c4b666efb7719c83f83513d797c8cbccdd6) + for these functionalities + +* 'Force Yes' option for commands that have prompt for confirmation (such as `delete` and `pptx`) + * This feature allows fast-typist to directly enter a one-line command by adding the option `-y` after the commands + to by pass the prompt, and to be more efficient in their use of the program. + +##### v2.1 +* Powerpoint command choose text and background color + + ![Example of PowerPoint Slide](../images-ug/PPTX-Example.png) + + *Example of PowerPoint Slide with steelblue background and silver text created using the `-cs` option* + + * Added two options to `pptx` command that allows user to choose their preferred background and text color for the + PowerPoint slides that are created. + * The options use [`Color Factory API`](https://github.com/beryx/awt-color-factory) which converts a + String that has the value of a valid string to a `Color` object. + * The description of the option can be found in the [User Guide](../UserGuide.md#features---print-to-powerpoint). + * The description of the implementation can be found in the [Developer Guide](../DeveloperGuide.md#print-to-powerpoint-slideshow) + * Default *Color Schemes* `-cs` + * There are exceptions thrown if the index is either not within the range [1,10], `CsIndexRangeException` or + not in the correct format, `CsIndexFormatException`. + * *Original Color* selection `-oc` + * There is an exception thrown, `ColorsNotAvailException` when the at least one of the colors chosen is not + a valid color. + + * In each command, only either of the options can be used to select the colors so if both options are included + at the same time, there will be an exception thrown, `BothCsAndOcException`. + * Any other options entered starting with `-` will trigger the exception, `InvalidOptionException`. + * In Deck Mode if any other arguments other than the 3 options are present, the exception, `InvalidPptxArgumentException` + will be thrown. + + * Added [JUnit Test](https://github.com/AY2021S1-CS2113-T14-2/tp/commit/1b123da1bad272b58964e89d6fefb2062b08d7d4) + for these functionalities + +#### Contributions to documentation: +In the User Guide, I wrote the following sections +* [Normal Mode - Print to PowerPoint](../UserGuide.md#print-an-existing-deck-to-a-powerpoint-file-pptx) +* [Deck Mode - Print to PowerPoint](../UserGuide.md#print-current-deck-to-a-powerpoint-file-pptx) +* [Features - Print to PowerPoint](../UserGuide.md#features---print-to-powerpoint) + * [Print to PowerPoint - Default Color Schemes](../UserGuide.md#default-color-schemes) + * [Print to PowerPoint - Original Colors available](../UserGuide.md#original-colors-available) +* [Command Summary - `pptx`](../UserGuide.md#command-summary) + +#### Contributions to the DG: +* [Architecture](../DeveloperGuide.md#application-architecture) +* [Exceptions](../DeveloperGuide.md#exceptions) + * Added the `NumberTooBigException` and all the exceptions for `getCsIndex()` and `preparePptxIndex()` +* [Print to PowerPoint SlideShow](../DeveloperGuide.md#print-to-powerpoint-slideshow) + * Added the [sequence diagram](../images-dg/PPTX-Sequence-Diagram.png) and explanation on how the command works + * Added a section for [Color Selection](../DeveloperGuide.md#color-selection) + * Includes [sequence diagram for the different options to select color](../images-dg/PPTX-Color-Options-Sequence-Diagram.png) + * Includes 3 sub-sections to explain what each of the option does + * [Default](../DeveloperGuide.md#default) + * [Color Scheme](../DeveloperGuide.md#color-scheme) + * [Original Color](../DeveloperGuide.md#original-color) + + +#### Contributions to team-based tasks : +Set up Team Repository. + +Released version 2.0. + +PR Reviews: +* [Review of PR 227](https://github.com/AY2021S1-CS2113-T14-2/tp/pull/227) + +Helping teammates +Explaining use of Regular Expressions to group mates to help them with reading and validating data from text file +for `Storage` based on what I have done in my IP. +* [Team mates code for Storage](https://github.com/AY2021S1-CS2113-T14-2/tp/blob/master/src/main/java/seedu/ecardnomics/storage/Storage.java) +* [My code for IP storage](https://github.com/kaijiel24/ip/blob/master/src/main/java/duke/storage/Storage.java) + +Provided exception when getting index from user inputs and refactor them into `getIndex()` method to allow group mates to +use the method if their command require getting index from user inputs. +* [Basic Exceptions](https://github.com/kaijiel24/tp/commit/d4d4f77821b3d2371d69aef55c12597af5b1f654) +* [Number Too Big Exception](https://github.com/kaijiel24/tp/commit/0f689efb9ae14233ea0ba86315229f02dcba7736) +* [Refactor `getIndex()`](https://github.com/kaijiel24/tp/commit/cf596c87119fdd5933c0a1cb702508f4846c2463) diff --git a/docs/team/kaijiel24.png b/docs/team/kaijiel24.png new file mode 100644 index 0000000000..8b25f4d941 Binary files /dev/null and b/docs/team/kaijiel24.png differ diff --git a/docs/team/liewws.md b/docs/team/liewws.md new file mode 100644 index 0000000000..0206e833cf --- /dev/null +++ b/docs/team/liewws.md @@ -0,0 +1,61 @@ + +# Liew Wei Siew - Project Portfolio Page + +## Overview + +eCardnomics is a **desktop flashcard application to quickly create, manage, and access new flashcards via a Command +Line Interface (CLI)**. While it is mainly targeted at Junior College economics students, eCardnomics can be used +by anyone who would need to keep track of large amounts of *text-based* information. This is facilitated by the +capabilities to group similar flashcards into decks and tag decks to provide a summary of the information contained +in the deck. Furthermore, the game mode and the feature of exporting decks into powerpoint allow review of the +help theinformation to user to memorize the text data. + +### Summary of Contributions + +[Code contribution as detected by RepoSense.](https://nus-cs2113-ay2021s1.github.io/tp-dashboard/#breakdown=true&search=liewws) + +In v1.0: +* Implemented Help command in Normal Mode +* Refactored code in **`NormalParser`** +* Added JUnit tests for **`CreateCommand`** and **`DeleteDeckCommand`** +* Updated behaviour of Delete Command in Normal Mode to the specification in the User Guide +* Added assertions to methods in **`NormalParser`**, **`Deck`**, **`DeckList`** and **`FlashCard`** +* Added logging for **`NormalParser`** methods. + +##### Enhancements implemented + +In v2.0: +* Implemented pretty printing for question and answer when printing flashcards and added JUnit tests +* Implemneted feature to update flashcards in existing decks and added Junit tests +* Implemented feature to add and delete flashcards in a single line command + +In v2.1: +* Refactor the pretty printing method to **`Ui`** so that it can be used for formatting other output. +* Implement logging to file to replace the printing of log to console output. + +##### Contributions to User Guide + +* Wrote the section for the Update Command in Deck Mode +* Added the one-line versions of the commands to add flashcard, delete flashcard and delete deck. + +##### Contributions to Developer Guide + +* Described the User Interface and Deck Model under the Design section. +* Explained the Pretty Printing feature under the Implementation-Features section. +* Documented some non-functional requirements and manual testing steps. + +##### Contributions to team-based tasks + +* Set-up the GitHub team organisation. +* Compiled some FAQs into the User Guide. + +##### Review/mentoring contributions + +Reviewed and provided recommendations for [this](https://github.com/AY2021S1-CS2113-T14-2/tp/pull/94) +major pull request by Zhixiang. + +##### Contributions beyond the project team + +* Reviewed the Developer Guide for [ModTracker](https://github.com/nus-cs2113-AY2021S1/tp/pull/62) and provided +suggestions for improving UML diagrams. +* Tested v2.0 of PlanNUS and reported bugs including [this](https://github.com/AY2021S1-CS2113T-F12-1/tp/issues/185). diff --git a/docs/team/liewws.png b/docs/team/liewws.png new file mode 100644 index 0000000000..8182e28e80 Binary files /dev/null and b/docs/team/liewws.png differ diff --git a/docs/team/wangwaynesg.md b/docs/team/wangwaynesg.md new file mode 100644 index 0000000000..3cd30cf1ab --- /dev/null +++ b/docs/team/wangwaynesg.md @@ -0,0 +1,55 @@ + +# Wang Wayne - Project Portfolio Page + +## Overview + +eCardnomics is a **desktop flashcard application to quickly create, manage, and access new flashcards via a Command +Line Interface (CLI)**. The main target audience of this application is Junior College students studying economics. +The main functionality stores text based flash cards in decks and displays them to the user upon request. +There are also added features such as a gamde mode and exporting to powerpoint slides. +The goal of this application is to help the user memorize their flash cards to study better. + +### Summary of Contributions + +#### Code contributed + +Summary: [tP Code Dashboard](https://nus-cs2113-ay2021s1.github.io/tp-dashboard/#breakdown=true&search=wangwaynesg&sort=groupTitle&sortWithin=title&since=2020-09-27&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other) + +**In v1.0:** +* Wrote some basic commands such as [Add Command](https://github.com/AY2021S1-CS2113-T14-2/tp/pull/21) and [List Command](https://github.com/AY2021S1-CS2113-T14-2/tp/pull/22) +* [JUnit tests for commands written](https://github.com/AY2021S1-CS2113-T14-2/tp/pull/69) +* [Exception Handling for commands written](https://github.com/AY2021S1-CS2113-T14-2/tp/pull/51) + +**In v2.0:** +* Implement [Storage feature](https://github.com/AY2021S1-CS2113-T14-2/tp/pull/98) +* [Developer's Guide for Storage](https://github.com/AY2021S1-CS2113-T14-2/tp/pull/121) + +**In v2.1:** +* [Enhanced storage to check for corrupted file](https://github.com/AY2021S1-CS2113-T14-2/tp/pull/155) + +#### Enhancements implemented + +* Wrote the **`Storage`** Class to implement storage features for the program. +* Enhanced the **`Storage`** Class to be able to handle corrupted data. + +#### Contributions to documentation + +* Wrote the section for the **`Add Command`** and **`List Command`** for Deck Mode. + +#### Contributions to the DG + +* Wrote the section for the **`Storage`** Class and constructed its sequence diagram. +* Wrote several users stories. + +#### Contributions to team-based tasks + +* Helped raise questions and suggestions during discussions. +* Conformed to timeliness of internal deadlines. + +#### Review/mentoring contributions + +* Helped look through some teammates' pull requests before their merge. + +#### Contributions beyond the project team + +* Answered coursemates' question during lecture in zoom chat. \ No newline at end of file diff --git a/docs/team/wangwaynesg.png b/docs/team/wangwaynesg.png new file mode 100644 index 0000000000..5b9086bd48 Binary files /dev/null and b/docs/team/wangwaynesg.png differ diff --git a/docs/team/zhixiangteoh.md b/docs/team/zhixiangteoh.md new file mode 100644 index 0000000000..620c2958b8 --- /dev/null +++ b/docs/team/zhixiangteoh.md @@ -0,0 +1,233 @@ +# Zhixiang Teoh - Project Portfolio Page + +## Overview + +eCardnomics is a **desktop flashcard application to quickly create, manage, and access new flashcards via a Command + Line Interface (CLI)**. eCardnomics is targeted at economics students in Junior College in Singapore, and aims to + enhance students’ study experience as an efficient and handy aid for active recall. + +The main goals of this application are to help students store and segment their economics subject syllabus into + different decks, consolidate bite-sized information within each topic by way of flash cards, and offer a fun + way for students to study and revise. + +## Summary of Contributions + +### Code contributed + +Summary: [tP Code Dashboard](https://nus-cs2113-ay2021s1.github.io/tp-dashboard/#breakdown=true&search=zhixiangteoh) + +In short, I did the following: + +**v1.0** +* [Set up UG, write Introduction, Preliminaries](https://github.com/AY2021S1-CS2113-T14-2/tp/commit/a4eb7de173daddef27e3c76c7a58b62939063e0b), [Command Summary](https://github.com/AY2021S1-CS2113-T14-2/tp/commit/c9d16d286de7d1338ae8ec4dcaf224f45f8f0b78) +* UG for [Normal Mode, half of Deck Mode](https://github.com/AY2021S1-CS2113-T14-2/tp/commit/afd37e97d5f1fe4d585538d3b8973cf45dd75b51) +* [Half of Deck Mode commands, for v1.0](https://github.com/AY2021S1-CS2113-T14-2/tp/commit/4f52a8d8aa29b3dbe61442f4eaf3117bf3f1c9ac) +* [JUnit Tests for DeckParser, NormalParser](https://github.com/AY2021S1-CS2113-T14-2/tp/commit/768192db9fdb8e2064ca44348375dd7664faf855), Ui + +**v2.0** +* UG for [Game Mode](https://github.com/AY2021S1-CS2113-T14-2/tp/commit/207103b41966691a768502f899e1b6d4c23a3950), [Help commands](https://github.com/AY2021S1-CS2113-T14-2/tp/commit/15b51255ecde6085d941e0825180668e5d1a149d), [Command Summary](https://github.com/AY2021S1-CS2113-T14-2/tp/commit/4129639e2d6e3c6fc421e1e0ebec79148b967d96), FAQ +* DG for [Commands](https://github.com/AY2021S1-CS2113-T14-2/tp/commit/072d00d0e703b9d90d486f0c8ab35bc9f6bf7261) and [Game Mode](https://github.com/AY2021S1-CS2113-T14-2/tp/commit/ba39ebbb46ce52e55001857c0c8a65e57af053a3) sections, [Product Scope, User Stories](https://github.com/AY2021S1-CS2113-T14-2/tp/commit/b22e88ee875479db0b77ee021487ae6253afd3b0) +* Implementation of [Game Mode](https://github.com/AY2021S1-CS2113-T14-2/tp/commit/f3975adca5c43c6172d4cc7d7d5a6452493368d5) +* Refine Help commands +* JUnit Tests for Game Mode + +### Enhancements implemented + +In v1.0, I split the work with Wayne in implementing the Deck Mode commands. Specifically, I implemented: + +* [DeleteCommand](https://github.com/AY2021S1-CS2113-T14-2/tp/commit/4f52a8d8aa29b3dbe61442f4eaf3117bf3f1c9ac) +* [HelpCommand](https://github.com/AY2021S1-CS2113-T14-2/tp/commit/622e584ff2847c15877375fd05a3c7db60bc9858) + +`DeleteCommand` required me to implement a `y/n` prompt method for the `Ui` class, to be used by `DeckParser`, which + was later used by all commands in the application that utilise `y/n` prompts. [Example of the user-interface of the + `y/n` prompt](https://ay2021s1-cs2113-t14-2.github.io/tp/UserGuide.html#examples-4). + +In v2.0, I implemented the [Game Mode feature](https://github.com/AY2021S1-CS2113-T14-2/tp/pull/94). For this, our + team originally thought it would be just another command implemented as part of Normal Mode or Deck Mode, but I felt + it deserved its own mode. I created a separate package, `game`, and parser class `GameParser` for Game Mode + , with the `game` package abstracting away the entire Game Mode implementation from the rest of the application. + +So the resulting `game` package is structured as such: + +``` +game + | Game + | GameEngine + | runGameLoop(), update() + | GameStorage + | originalDeck, deque, retestStore +``` + +Here, I tried as much as possible to incorporate the Single Responsibility Principle, both within the `game` classes + and the SRP-ness of the existing classes. + +### Contributions to team-based tasks + +Some examples: + +* Re-organised packages a few times +* [Released JAR v1.0 and v1.1](https://github.com/AY2021S1-CS2113-T14-2/tp/releases) +* Maintained issue tracker - authored over 37 issues, closed and opened relevant issues, wrapped up Milestones +* [Created and started several Test classes](https://github.com/AY2021S1-CS2113-T14-2/tp/pull/55), e.g. `UiTest`, `NormalParserTest`, `DeckParserTest` +* [Started and set up User Guide](https://github.com/AY2021S1-CS2113-T14-2/tp/pull/57) +* Wrote some trivial aspects of User Guide (e.g. Introduction, Preliminaries, Command Summary) and Developer Guide + (e.g. Product Scope, User Stories) +* Formatted and arranged [team Google Docs](https://docs.google.com/document/d/1e6HD8JaxAlITihBmqEDQnyKyVa52FLcAWi3Im_7wS6g/edit) + +### Review/mentoring contributions + +Some examples: + +* Reviewed and merged about 5-10 Pull Requests by other teammates +* [Authored](https://github.com/AY2021S1-CS2113-T14-2/tp/pulls?q=is%3Apr+is%3Aclosed+author%3Azhixiangteoh+) over 30 + Pull Requests + +### Contributions beyond the project team + +Notable posts/responses posted in forum, and peer software testing: + +* [Generating markdown table of contents](https://github.com/nus-cs2113-AY2021S1/forum/issues/108) +* [Comments on others' forum posts](https://github.com/nus-cs2113-AY2021S1/forum/issues?q=is%3Aissue+is%3Aclosed+zhixiangteoh) +* [Peer Evaluation-Dry Run on `Scheduler--;`](https://github.com/zhixiangteoh/ped/issues) + +### Contributions to the UG + +Mainly [Introduction](https://ay2021s1-cs2113-t14-2.github.io/tp/UserGuide.html#introduction), v1.0 Normal Mode + commands, [Features - Game Mode](https://ay2021s1-cs2113-t14-2.github.io/tp/UserGuide.html#features---game-mode), [Command Summary](https://ay2021s1-cs2113-t14-2.github.io/tp/UserGuide.html#command-summary). + +* Introduction +* Contents +* [v1.0 Normal Mode commands](https://github.com/AY2021S1-CS2113-T14-2/tp/pull/57) +* some v1.0 Deck Mode commands +* [`help` commands](https://github.com/AY2021S1-CS2113-T14-2/tp/pull/156) +* `start` commands +* [Game Mode commands and gameplay description](https://github.com/AY2021S1-CS2113-T14-2/tp/pull/100) +* Anywhere Mode +* FAQ +* Command Summary + +### UG Extracts + +#### Features - Game Mode + +##### Gameplay + +Questions are displayed in a randomised order. At each question, the user will: + +1. Try to attempt an answer at the question, by typing at the prompt; then +2. Press `` (optionally with an empty attempt). + +Then, the correct answer is displayed, and our 'advanced' algorithm scores the user's attempt against the correct + answer. Finally, the user is given the option to re-attempt the question later. See below for example gamplay. + +##### Examples + +* Start Game Mode (from within Deck Mode) + +```java +[Deck - Micro-Economics] + > list +// ------------------------------------------------------------ +// You are now viewing deck: Micro-Economics +// 1. Question: What is the Law of demand? +// +// 2. Question: What is the Law of supply? +// +// 3. Question: What is price elasticity of demand? +// +// 4. Question: What is price elasticity of supply? +// ------------------------------------------------------------ +[Deck - Micro-Economics] + > start +``` + +### Contributions to the DG + +Mainly [Commands section](https://ay2021s1-cs2113-t14-2.github.io/tp/DeveloperGuide.html#commands) and +[Exceptions section](https://ay2021s1-cs2113-t14-2.github.io/tp/DeveloperGuide.html#exceptions) under Design, +and [Game Mode section](https://ay2021s1-cs2113-t14-2.github.io/tp/DeveloperGuide.html#game-mode) under Features. + +Relevant Pull Requests for DG: + +* Commands [v2.0](https://github.com/AY2021S1-CS2113-T14-2/tp/pull/89), [v2.1](https://github.com/AY2021S1-CS2113-T14-2/tp/pull/166) +* [Exceptions](https://github.com/AY2021S1-CS2113-T14-2/tp/pull/166) +* Game Mode [v2.0](https://github.com/AY2021S1-CS2113-T14-2/tp/pull/104), [v2.1](https://github.com/AY2021S1-CS2113-T14-2/tp/pull/166) +* [Trivial](https://github.com/AY2021S1-CS2113-T14-2/tp/pull/105) + +### DG Extracts + +#### Design - Exceptions section + +#### Feature Implementation - Game Mode + +#### General Architecture + +[DG-Implementation-Features-Game-Mode-Architecture](../images-dg/DG-Game-Mode-Architecture-Overview.png?raw=true + "Game Mode + Architecture Overview") + +[DG-Implementation-Features-Game-Storage-Game-Engine](../images-dg/DG-Game-Storage-Game-Engine.png?raw=true "Game + Mode Game Storage Game Engine") + +#### Example Use Case + +For a more contextual use case, consider the following scenario of Econs Wiz attempting the Game Mode for the first + deck, `Demand-Supply`, in his deck list. + +> Note: Focus on the biggest box in the diagram! + +![DG-Implementation-Features-Game-Use-Case](../images-dg/DG-Game-Use-Case.png?raw=true "Game Mode Use Case") + +**API**: [seedu/ecardnomics/game](https://github.com/AY2021S1-CS2113-T14-2/tp/tree/master/src/main/java/seedu/ecardnomics/game) + +Since there are quite a few things going on in this diagram, here are the key takeaways (the last of which arguably + the most important): +- the retest question pool (`retestStore`) is updated upon the user's attempt at each question and response to the + prompt to include or exclude the flash card to be displayed again—if `y` then the flash card is added to the + `retestStore` +- whenever the current question pool (`deque`) is emptied, i.e. all flash cards have been popped off and displayed to + the user, the retest question pool (`retestStore`) is consulted from which to create a new question pool (`deque`) +- after a specific question is displayed, it does not appear again (even if the user has chosen to re-encounter the + question) until after all other questions in the current question pool (`deque`) have been exhausted + +#### Sequential Flow + +For a more formal sequential flow of the inner workings of Game Mode, the following elaborates the execution + sequence of Game Mode, from after a `start` command has been parsed in Normal Mode: + +![DG-Implementation-Features-Game-Mode-Sequence](../images-dg/DG-Game-Mode-Sequence.png?raw=true "Game Mode UML Sequence + Diagram") + + In the above diagram the key takeaway is the existence of an *intermediary* `game:Game` object that holds + `GameEngine` and `GameStorage` together. In fact, this is the sole purpose of the `Game` class: to hold the + current game instance, in a Single-Responsibility-Principle (SRP) and Object-Oriented Programming (OOP) manner. + This intermediary role of the `Game` class is also illustrated in the upper part of the earlier [use case + diagram](#example-use-case). Note how it is from this `game` object that the main game loop, run and managed by + `GameEngine`, is started. + +[DG-Implementation-Features-Game-Mode-Sequence-run-game-loop-sd](../images-dg/DG-run-game-loop-sd.png?raw=true "Game + Mode UML Sequence Diagram run game loop sd") + +The main game loop. As with all simple games, this flash card game mode is fundamentally built on the concept of a + possibly never-ending game loop with specific end conditions. In this case, the main end condition is explicitly + that the `command` object that is parsed and returned upon the `getAttempt()` call (that prompts the user for an + answer attempt) is either a `DoneGameCommand` or `ExitCommand`. + +> This is not the *only* end condition, though, because the other important but implicit end scenario is when the +> question pool is exhausted (i.e., `storage.deque` is empty) **and** the retest question pool (`storage.retestStore +>`) is empty. + +Naturally, the other sequence of special note here is the whole `update()` sequence, and even more specifically the + `updateDeque()` call within the `update()` sequence. Notice how `updateDeque()` calls `createRandomisedStack(storage + .retestStore)` with the `retestStore` as argument. This essentially creates a new randomised question pool from the + retest question pool. + +Notice how this `updateDeque()` sequence is only called exactly when the `storage.deque` is empty (i.e., when all + questions have been popped off the question pool). This is important because it ensures that the user encounters all + available questions in the `deque` at least once before the retest questions are later displayed. Cross-check this + with the detailed descriptions of the inner workings of the game loop implementation shown in the earlier + [architecture](#general-architecture) and [use case](#example-use-case) diagrams. + +Lastly, notice how `refreshRetestStore()` is called at the end of `updateDeque()` to, as its name suggests, clear the + retest question pool to get ready to store the next wave of retest questions. This is also covered in the bottom few + lines of the `GameEngine` portion of the use case diagram. diff --git a/docs/team/zhixiangteoh.png b/docs/team/zhixiangteoh.png new file mode 100644 index 0000000000..30f71d03d0 Binary files /dev/null and b/docs/team/zhixiangteoh.png differ diff --git a/pptx/Micro-Economics.pptx b/pptx/Micro-Economics.pptx new file mode 100644 index 0000000000..509bfdc915 Binary files /dev/null and b/pptx/Micro-Economics.pptx 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/ecardnomics/Main.java b/src/main/java/seedu/ecardnomics/Main.java new file mode 100644 index 0000000000..eafce5bc45 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/Main.java @@ -0,0 +1,137 @@ +package seedu.ecardnomics; + +import seedu.ecardnomics.command.Command; +import seedu.ecardnomics.command.deck.DoneEditCommand; +import seedu.ecardnomics.command.game.DoneGameCommand; +import seedu.ecardnomics.command.game.GameStartCommand; +import seedu.ecardnomics.command.normal.EditCommand; +import seedu.ecardnomics.command.ExitCommand; +import seedu.ecardnomics.command.normal.StartCommand; +import seedu.ecardnomics.deck.Deck; +import seedu.ecardnomics.deck.DeckList; +import seedu.ecardnomics.game.Game; +import seedu.ecardnomics.parser.DeckParser; +import seedu.ecardnomics.parser.NormalParser; +import seedu.ecardnomics.storage.LogStorage; +import seedu.ecardnomics.storage.Storage; + +import java.io.IOException; + +/** + * Main Class for eCardnomics - Flash Card Manager Command Line Program. + */ +public class Main { + + public static final double VERSION_NUMBER = 2.1; + public static DeckList deckList = new DeckList(); + public static NormalParser normalParser = new NormalParser(deckList); + public static Storage storage = new Storage(); + + /** + * Executes the command. + * + * @param command command from parser + */ + private static void executeCommand(Command command) { + command.execute(); + } + + /** + * Runs Deck Mode to edit a deck. + * + * @param deck Deck to edit + * @return Command used to exit Deck Mode (either done or exit) + */ + public static Command runDeckMode(Deck deck) { + DeckParser deckParser = new DeckParser(deckList, deck); + + String userInput; + Command command; + + do { + Ui.printDeckPrompt(deck); + userInput = Ui.readUserInput(); + + command = deckParser.parse(userInput); + + executeCommand(command); + + } while (!DoneEditCommand.isDoneEdit(command) + && !ExitCommand.isExit(command) + && !StartCommand.isStart(command)); + + return command; + } + + /** + * Runs Game Mode for specified deck. + * + * @param deck to run Game Mode on + * @return Command used to exit Game Mode (either done or exit) + */ + public static Command runGameMode(Deck deck) { + GameStartCommand gameStartCommand = new GameStartCommand(deck); + executeCommand(gameStartCommand); + + Game game = gameStartCommand.getGameInstance(); + return game.run(); + } + + /** + * Runs Normal Mode in a loop until exit in input. + * Enters Deck Mode when edit is input. + */ + public static void runNormalMode() { + Ui.printGreeting(); + + String userInput; + Command command; + + do { + Ui.printNormalPrompt(); + userInput = Ui.readUserInput(); + + command = normalParser.parse(userInput); + + executeCommand(command); + + if (EditCommand.isEdit(command)) { + EditCommand editCommand = (EditCommand) command; + command = runDeckMode(editCommand.getDeck()); + + if (command instanceof DoneEditCommand) { + Ui.printNormalWelcome(); + } + } + + if (StartCommand.isStart(command)) { + StartCommand startCommand = (StartCommand) command; + command = runGameMode(startCommand.getDeck()); + + if (command instanceof DoneGameCommand) { + Ui.printNormalWelcome(); + } + } + + } while (!ExitCommand.isExit(command)); + + Ui.printExitLine(); + } + + /** + * Main method. + * + * @param args Arguments from command line when user starts the program + */ + public static void main(String[] args) { + deckList = storage.load(deckList); + + runNormalMode(); + + try { + storage.write(Main.deckList); + } catch (IOException e) { + Ui.printMessage("Unable to write file..."); + } + } +} diff --git a/src/main/java/seedu/ecardnomics/Mode.java b/src/main/java/seedu/ecardnomics/Mode.java new file mode 100644 index 0000000000..2826cf675a --- /dev/null +++ b/src/main/java/seedu/ecardnomics/Mode.java @@ -0,0 +1,17 @@ +package seedu.ecardnomics; + +public enum Mode { + NORMAL("Normal"), DECK("Deck"); + + private String name; + + private Mode(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } + +} diff --git a/src/main/java/seedu/ecardnomics/Ui.java b/src/main/java/seedu/ecardnomics/Ui.java new file mode 100644 index 0000000000..4120c0c9b2 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/Ui.java @@ -0,0 +1,709 @@ +package seedu.ecardnomics; + +import org.apache.commons.math3.analysis.function.Log; +import seedu.ecardnomics.deck.Deck; +import seedu.ecardnomics.deck.DeckList; +import seedu.ecardnomics.deck.FlashCard; +import seedu.ecardnomics.game.Game; +import seedu.ecardnomics.powerpoint.ColorOption; +import seedu.ecardnomics.storage.LogStorage; + +import java.util.ArrayList; +import java.util.Scanner; +import java.util.logging.Level; + +import static seedu.ecardnomics.Main.VERSION_NUMBER; + +public class Ui { + + // Output lines to users + public static final String GREETING_LINES = + "Hello! Welcome to eCardnomics,\n" + + "your one stop Flash Cards solution"; + public static final String NORMAL_WELCOME_LINE = + "You are back in Normal Mode"; + public static final String DECK_WELCOME_LINE = + "You are now in Deck Mode, editing: "; + public static final String BYE_LINE = + "Bye. Hope to see you again soon!"; + public static final String NOT_RECOGNISED_LINE = + "Command not recognised"; + public static final String YN_LINE = "[y/n]"; + public static final String ADD_FLASHCARD_LINE = + "You are now adding a FlashCard to: "; + public static final String ENTER_QUESTION_LINE = + "Enter question:"; + public static final String ENTER_ANSWER_LINE = + "Enter answer:"; + public static final String FLASHCARD_ADDED_LINE = + "FlashCard successfully added!"; + public static final String LIST_FLASHCARDS_LINE = + "You are now viewing deck: "; + public static final String DELETE_FLASHCARD_LINE = + "Do you want to delete the following flash card? [y/n] "; + public static final String FLASHCARD_DELETED_LINE = + "The following flash card has been deleted:\n '"; + public static final String NEW_DECK_CREATED_LINE = + "New deck created: "; + public static final String DECKS_AVAILABLE_LINE = + "The following decks are available:\n"; + public static final String DELETED_DECK_QUESTION_LINE = + "Do you want to delete %1$s deck? [y/n] "; + public static final String DELETED_DECK_LINE = + "%1$s has been deleted."; + public static final String PPTX_DECK_QUESTION_LINE = + "Do you want to print %1$s deck to PowerPoint? [y/n] "; + public static final String PPTX_DECK_DEFAULT_LINE = + "%s has been created as PowerPoint with default,\n black background and white text."; + public static final String PPTX_DECK_CS_LINE = + "%s has been created as PowerPoint using Color Scheme,\n with %s background and %s text."; + public static final String PPTX_DECK_OC_LINE = + "%s has been created as PowerPoint using Original Colors,\n with %s background and %s text."; + public static final String INVALID_YN_RESPONSE_LINE = + "Response should be 'y' or 'n'\n > "; + private static final String EMPTY_DECK_LINE = + "Deck is currently empty!"; + private static final String NEW_TAGS_LINE = + "The deck %1$s has been tagged as: %2$s"; + private static final String REMOVED_TAGS_QUESTION_LINE = + "Do you want to remove the tag(s) %1$s from %2$s? [y/n] "; + private static final String REMOVED_TAGS_LINE = + "The tag(s) %1$s have been removed from the deck %2$s."; + private static final String NEW_QUESTION_LINE = + "New Question: "; + private static final String NEW_ANSWER_LINE = + "New Answer: "; + private static final String QUESTION_UPDATED_LINE = + "Question updated."; + private static final String ANSWER_UPDATED_LINE = + "Answer updated."; + private static final String QNA_UPDATED_LINE = + "Question and answer updated."; + private static final String NO_UPDATE_LINE = + "Original question and answer retained"; + private static final String INCLUDE_EXCLUDE_LINE = + "Do you want to re-attempt this question later? [y/n] "; + private static final String ATTEMPT_FEEDBACK_LINE = + "The score for your answer is:"; + private static final String ENTER_ATTEMPT_LINE = + " Enter your attempt below (or `done`, `exit`, `help`):"; + private static final String DONE_GAME_LINE = + "You have completed all the flash cards in this deck!\n" + + "Returning to Normal Mode..."; + private static final String GAME_EMPTY_DECK_LINE = + EMPTY_DECK_LINE + " Please add some flash cards first."; + private static final String INVALID_TAGS_LINE = + "Tag %s is not in the deck already!"; + private static final String DECKS_HAVING_TAGS_LINE = + "The decks having tags you are searching for:"; + private static final String NO_DECKS_WITH_TAGS_LINE = + "There is no decks having the tags you are looking for."; + + public static final String QUESTION = "Question "; + public static final String ANSWER = "Answer"; + public static final String CREATE_NEW_FILE_ERROR_LINE = "Error creating new file - "; + public static final String WRITE_TO_FILE_ERROR_LINE = "Error writing for file"; + public static final String FILE_NOT_FOUND_ERROR_LINE = " not found or file currently open"; + + // Commands + public static final String EXIT = "exit"; + public static final String EDIT = "edit"; + public static final String START = "start"; + public static final String DONE = "done"; + public static final String ADD = "add"; + public static final String CREATE = "create"; + public static final String LIST = "list"; + public static final String DECKS = "decks"; + public static final String DELETE = "delete"; + public static final String UPDATE = "update"; + public static final String HELP = "help"; + public static final String TAG = "tag"; + public static final String UNTAG = "untag"; + public static final String PPTX = "pptx"; + public static final String SEARCH = "search"; + + public static final String VERSION_CMD = "--version"; + + // Options + public static final String FORCE_YES_OPT = "-y"; + public static final String COLOR_SCHEME_OPT = "-cs"; + public static final String ORIGINAL_COLOR_OPT = "-oc"; + + //Regex + public static final String DIGITS_REGEX = "\\d+"; + public static final String PUNC_REGEX = "\\p{Punct}"; + public static final String ORIGINAL_COLOR_REGEX = "-oc\\s+(\\w+)\\s+(\\w+)\\s*(-|$)"; + public static final String COLOR_SCHEME_REGEX = "-cs\\s(\\S+)\\s*(-|$)"; + + public static final String Y = "y"; + public static final String N = "n"; + public static final String DASH_LINES = "------------------------------------------------------------" + + "--------------------"; + public static final String MIDDLE_SEPARATOR = " ---------------- " + + " "; + + + public static final String LOGO1 = " ___ _ _"; + public static final String LOGO2 = " ___ / __\\__ _ _ __ __| |_ __ ___ _ __ ___ (_) ___ ___"; + public static final String LOGO3 = " / _ \\/ / / _` | '__/ _` | '_ \\ / _ \\| '_ ` _ \\| |/ __/ __|"; + public static final String LOGO4 = "| __/ /__| (_| | | | (_| | | | | (_) | | | | | | | (__\\__ \\"; + public static final String LOGO5 = " \\___\\____/\\__,_|_| \\__,_|_| |_|\\___/|_| |_| |_|_|\\___|___/"; + + public static Scanner in = new Scanner(System.in); + private static LogStorage logger = new LogStorage("UiLogger"); + + /** + * Reads user input from command line. + * + * @return String containing userInput + */ + public static String readUserInput() { + return in.nextLine(); + } + + /** + * Displays dash line. + */ + public static void printDashLines() { + System.out.println(DASH_LINES); + } + + public static void printMiddleSeparator() { + System.out.println(MIDDLE_SEPARATOR); + } + + /** + * Displays the message given. + * + * @param message message to be displayed to user + */ + public static void printMessage(String message) { + printDashLines(); + System.out.println(message); + printDashLines(); + } + + /** + * Prints logo when user opens the program. + */ + public static void printLogo() { + System.out.println(LOGO1); + System.out.println(LOGO2); + System.out.println(LOGO3); + System.out.println(LOGO4); + System.out.println(LOGO5); + System.out.println(""); + } + + /** + * Displays the prompt for user input in Normal Mode. + */ + public static void printNormalPrompt() { + System.out.println("[Normal]"); + System.out.print(" > "); + } + + /** + * Displays the prompt for user input in Deck Mode. + */ + public static void printDeckPrompt(Deck deck) { + System.out.println("[Deck - " + deck.getName() + "]"); + System.out.print(" > "); + } + + /** + * Displays the prompt for user input in Game Mode. + */ + public static void printGamePrompt(Deck deck) { + System.out.println("[Game - " + deck.getName() + "]"); + System.out.print(" > "); + } + + /** + * Displays the prompt for user input without specifying current mode. + */ + public static void printPrompt() { + System.out.print(" > "); + } + + /** + * Displays the welcome message from Deck Mode to Normal Mode. + */ + public static void printNormalWelcome() { + printMessage(NORMAL_WELCOME_LINE); + } + + /** + * Displays the welcome message from Normal Mode to Deck Mode. + */ + public static void printDeckWelcome(int index, Deck deck) { + printMessage(DECK_WELCOME_LINE + "[" + index + "] " + deck.getName()); + } + + /** + * Displays the welcome message for Game Mode. + */ + public static void printGameWelcome(int index, Deck deck) { + printMessage(Game.WELCOME_MESSAGE + "[" + index + "] " + deck.getName()); + } + + /** + * Displays the greeting message. + */ + public static void printGreeting() { + printDashLines(); + printLogo(); + System.out.println(GREETING_LINES); + printDashLines(); + } + + /** + * Displays the exit message. + */ + public static void printExitLine() { + printMessage(BYE_LINE); + } + + /** + * Displays the add FlashCard line. + * + * @param deck current deck + */ + public static void printAddFlashCardLine(Deck deck) { + printMessage(ADD_FLASHCARD_LINE + deck.getName()); + } + + /** + * Displays the enter question line. + */ + public static void printEnterQuestionLine() { + System.out.println(ENTER_QUESTION_LINE); + printPrompt(); + } + + /** + * Displays the enter answer line. + */ + public static void printEnterAnswerLine() { + System.out.println(ENTER_ANSWER_LINE); + printPrompt(); + } + + /** + * Displays the FlashCard added line. + */ + public static void printFlashCardAddedLine() { + System.out.println(FLASHCARD_ADDED_LINE); + } + + /** + * Displays an index list of FlashCards in the deck. + * + * @param deck deck to display + * @param isQuestionOnly print question only if true, question and answer otherwise + */ + public static void printDeck(Deck deck, boolean isQuestionOnly) { + String deckMessage = ""; + if (deck.toString(isQuestionOnly).trim().equals("")) { + deckMessage += EMPTY_DECK_LINE; + } else { + deckMessage += LIST_FLASHCARDS_LINE + deck.getName() + System.lineSeparator() + + deck.toString(isQuestionOnly); + } + printMessage(deckMessage); + } + + /** + * Displays the delete FlashCard line. + * + * @param question The question of the FlashCard to delete + */ + public static void printDeleteFlashCardLine(String question) { + System.out.print(DELETE_FLASHCARD_LINE + "?\n '" + question + "' "); + } + + /** + * Displays the FlashCard deleted line. + * + * @param question The question of the FlashCard to delete + */ + public static void printFlashCardDeletedLine(String question) { + System.out.println(FLASHCARD_DELETED_LINE + question + "'"); + } + + /** + * Displays the help page of commands. + * + *

Displays a different help page for Normal Mode and Deck Mode.

+ * + * @param helpDisplay help text to display + */ + public static void printHelp(String helpDisplay) { + printMessage(helpDisplay); + } + + /** + * Prints new deck added in the Normal Mode. + * + * @param deck in new Deck added + */ + public static void printNewDeck(Deck deck) { + String tagsOfNewDeck = deck.getTagString(); + if (!tagsOfNewDeck.isEmpty()) { + tagsOfNewDeck = " #Tag: " + tagsOfNewDeck; + } + printMessage(NEW_DECK_CREATED_LINE + deck.getName() + tagsOfNewDeck); + } + + /** + * Prints all decks available in the Normal Mode. + * + * @param decks all decks in the list + */ + public static void printDeckList(DeckList decks) { + printMessage(DECKS_AVAILABLE_LINE + decks.toString()); + } + + /** + * Confirms the deck the user wants to delete. + * + * @param deletedDeckName name of the deleted deck + */ + public static void printDeletedDeckQuestion(String deletedDeckName) { + System.out.print(String.format(DELETED_DECK_QUESTION_LINE, deletedDeckName)); + } + + /** + * Prints the name of the deleted deck. + * + * @param deletedDeckName name of the deleted deck + */ + public static void printDeckDeletedLine(String deletedDeckName) { + System.out.println(String.format(DELETED_DECK_LINE, deletedDeckName)); + } + + /** + * Confirms the deck the user wants to print to PowerPoint. + * + * @param pptxDeckName name of the pptx deck + */ + public static void printPptxDeckQuestion(String pptxDeckName) { + System.out.print(String.format(PPTX_DECK_QUESTION_LINE, pptxDeckName)); + } + + /** + * Prints the name of the deck to be printed to PowerPoint. + * + * @param pptxDeckName name of the pptx deck + */ + public static void printDeckPptxLine(String pptxDeckName, String bgColor, String txtColor, ColorOption colorOpt) { + switch (colorOpt) { + case DEFAULT: + printMessage(String.format(PPTX_DECK_DEFAULT_LINE, pptxDeckName)); + break; + case COLOR_SCHEME: + printMessage(String.format(PPTX_DECK_CS_LINE, pptxDeckName, bgColor, txtColor)); + break; + case ORGINAL_COLOR: + printMessage(String.format(PPTX_DECK_OC_LINE, pptxDeckName, bgColor, txtColor)); + break; + default: + // + break; + } + } + + /** + * Prints the update question lines for a flashcard. + * + * @param flashCard for which the question should be updated. + */ + public static void printUpdateQuestionLine(FlashCard flashCard) { + // No offset since printing from start of line. + System.out.println(flashCard.toString(true, 0)); + System.out.println(NEW_QUESTION_LINE); + printPrompt(); + } + + /** + * Prints the update answer lines for a flashcard. + * + * @param flashCard for which the answer should be updated. + */ + public static void printUpdateAnswerLine(FlashCard flashCard) { + // No offset since printing from start of line. + System.out.println(System.lineSeparator() + flashCard.toString(false, 0)); + System.out.println(NEW_ANSWER_LINE); + printPrompt(); + } + + /** + * Prints line informing updated flashcards. + * + * @param hasNewQ boolean parameter indicating new Question provided + * @param hasNewA boolean parameter indicating new Answer providid + */ + public static void printFlashCardUpdatedLine(boolean hasNewQ, boolean hasNewA) { + if (hasNewQ && hasNewA) { + System.out.println(QNA_UPDATED_LINE); + } else if (hasNewQ) { + // !hasNewA + System.out.println(QUESTION_UPDATED_LINE); + } else if (hasNewA) { + // !hasNewQ + System.out.println(ANSWER_UPDATED_LINE); + } else { + System.out.println(NO_UPDATE_LINE); + } + } + + /** + * Prints a line prompting user to enter only 'y' or 'n'. + */ + public static void printInvalidYorNResponse() { + System.out.print(INVALID_YN_RESPONSE_LINE); + } + + public static void printVersionNumber() { + printMessage("Version: " + VERSION_NUMBER); + } + + public static void printIncludeExcludeLine() { + System.out.print(INCLUDE_EXCLUDE_LINE); + } + + public static void printAttemptFeedback(double matchPercentage) { + System.out.println(String.format("%s %.2f", ATTEMPT_FEEDBACK_LINE, matchPercentage)); + } + + public static boolean getInclExclConfirmation() { + logger.log(Level.INFO, "Logging method getInclExclConfirmation() in Ui."); + Ui.printIncludeExcludeLine(); + return checkYorNResponse(); + } + + /** + * Displays question in the flashcard. + * + * @param question String representing the question + */ + public static void printGameQuestion(String question) { + final String label = "Q: "; + System.out.println(label + prettyPrintFormatter(question, label.length())); + System.out.println(ENTER_ATTEMPT_LINE); + printPrompt(); + } + + public static void printGameEmptyDeckLine() { + System.out.println(GAME_EMPTY_DECK_LINE); + } + + public static void printAnswerGameMode(String answer) { + final String label = "A: "; + System.out.println(label + prettyPrintFormatter(answer, label.length())); + } + + public static void printDoneGameMessage() { + System.out.println(DONE_GAME_LINE); + } + + /** + * Prints the new tags added to the deck. + * + * @param name the name of the deck + * @param newTags the tag(s) will be added to the deck + */ + public static void printNewTags(String name, ArrayList newTags) { + String tagString = formStringOfTags(newTags); + printMessage(String.format(NEW_TAGS_LINE, name, tagString)); + } + + /** + * Prints out warning about invalid tags provided. + */ + public static void printInvalidTagsLine(String tags) { + logger.log(Level.WARNING, "User did not supply valid tags."); + printMessage(String.format((INVALID_TAGS_LINE), tags)); + } + + /** + * Prints confirmation question before tag removal. + * + * @param deckName the name of the deck having tags being removed + * @param tags the tags will be removed + */ + public static void printRemovedTagsQuestion(String deckName, ArrayList tags) { + String removedTags = formStringOfTags(tags); + System.out.print(String.format(REMOVED_TAGS_QUESTION_LINE, removedTags, deckName)); + } + + /** + * Prints the tags that are removed from the specified deck. + * + * @param deckName the name of the deck having removed tags + * @param tags tags were removed + */ + public static void printTagsRemovedLine(String deckName, ArrayList tags) { + String removedTags = formStringOfTags(tags); + System.out.println(String.format(REMOVED_TAGS_LINE, removedTags, deckName)); + } + + /** + * Forms a String of provided tags. + * + * @param tags tags to be formed to String + * @return a String of tags + */ + public static String formStringOfTags(ArrayList tags) { + String stringOfTags = ""; + + for (int i = 0; i < tags.size(); i++) { + stringOfTags += tags.get(i); + if (i < tags.size() - 1) { + stringOfTags += " | "; + } + } + return stringOfTags; + } + + /** + * Checks y or n response from user. + * + * @return true if user enters confirms, otherwise false + */ + private static boolean checkYorNResponse() { + logger.log(Level.INFO, "Logging method checkYorNResponse() in Ui."); + String response; + do { + response = readUserInput(); + assert response != null : "response should not be null"; + switch (response.trim()) { + case Y: + response = Y; + break; + case N: + response = N; + break; + default: + logger.log(Level.INFO, "User entered response other than 'y' or 'n'"); + logger.log(Level.INFO, "Re-prompting..."); + printInvalidYorNResponse(); + } + } while (!response.trim().equals(Y) && !response.trim().equals(N)); + assert (response.equals(Y) || response.equals(N)) : "Response should be y/n"; + return response.equals(Y); + } + + /** + * Get confirmation for deleting a deck. + * + * @param deckName String representing the index of the deck in deckList + * @return true if delete is confirmed, otherwise false + */ + public static boolean getDeletedDeckConfirmation(String deckName) { + logger.log(Level.INFO, "Logging method getDeletedDeckConfirmation() in Ui."); + printDashLines(); + printDeletedDeckQuestion(deckName); + + return checkYorNResponse(); + } + + public static boolean getDeletedFlashCardConfirmation(String question) { + logger.log(Level.INFO, "Logging method getDeletedFlashCardConfirmation() in Ui."); + printDashLines(); + Ui.printDeleteFlashCardLine(question); + + return checkYorNResponse(); + } + + /** + * Checks whether the user want to remove the tags specified + * from the deck specified. + * + * @param tags String[] representing the tags be removed + * @param deckName String representing the index of the deck of the tags + * @return true if the removal is confirmed, otherwise false + */ + public static boolean getRemovedTagsConfirmation(ArrayList tags, String deckName) { + printDashLines(); + printRemovedTagsQuestion(deckName, tags); + boolean isConfirmed = checkYorNResponse(); + + if (isConfirmed) { + printTagsRemovedLine(deckName, tags); + printDashLines(); + } + + return isConfirmed; + } + + public static void printSearchDecksLine(String decksHavingTags) { + if (decksHavingTags.isEmpty()) { + printMessage(NO_DECKS_WITH_TAGS_LINE); + } else { + printMessage(DECKS_HAVING_TAGS_LINE + decksHavingTags); + } + } + + /** + * Get confirmation from user on whether to print deck to PowerPoint. + * + * @param deckName name of deck to be printed to PowerPoint + * @return true if delete is confirmed, otherwise false + */ + public static boolean getPptxDeckConfirmation(String deckName) { + logger.log(Level.INFO, "Logging method getPptxDeckConfirmation() in Ui."); + Ui.printPptxDeckQuestion(deckName); + return checkYorNResponse(); + } + + /** + * Format the target string to properly wrap around the end + * of each line. The response will occupy the area between offset and + * Ui.DASH_LINES.length(). + * + * @param target String to be formatted + * @param offset Number of characters from the start of the line + * @return String that stores the formatted question or answer + */ + public static String prettyPrintFormatter(String target, int offset) { + String result = ""; + String[] words = target.split(" "); + int lineLength = Ui.DASH_LINES.length(); + int usableLength = lineLength - offset; + assert usableLength > 0 : "Otherwise we cannot print anything."; + + int currentLength = 0; + for (String word : words) { + // Handle the case where a word is too long to print on one line + if (word.length() > usableLength) { + // Find number of characters that can be printed on current line + int remainLength = usableLength - currentLength; + result += word.substring(0, remainLength); + String leftover = word.substring(remainLength); + // Separate the word into parts that can fit in a line + while (leftover.length() > usableLength) { + result += System.lineSeparator() + " ".repeat(offset) + + leftover.substring(0, usableLength); + leftover = leftover.substring(usableLength); + } + // Place remainder of word into line and continue + result += System.lineSeparator() + " ".repeat(offset) + leftover + " "; + currentLength = leftover.length() + 1; + continue; + } + currentLength += word.length(); + if (currentLength > usableLength) { + // Repeat enough spaces so that text is aligned to usable area. + result += System.lineSeparator() + " ".repeat(offset) + word; + currentLength = word.length(); + } else { + result += word; + } + result += " "; + // Account for the " " after the word. + ++currentLength; + } + return result.trim(); + } +} diff --git a/src/main/java/seedu/ecardnomics/command/Command.java b/src/main/java/seedu/ecardnomics/command/Command.java new file mode 100644 index 0000000000..1b8e342721 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/command/Command.java @@ -0,0 +1,5 @@ +package seedu.ecardnomics.command; + +public abstract class Command { + public abstract void execute(); +} diff --git a/src/main/java/seedu/ecardnomics/command/ExitCommand.java b/src/main/java/seedu/ecardnomics/command/ExitCommand.java new file mode 100644 index 0000000000..0c0fc10751 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/command/ExitCommand.java @@ -0,0 +1,15 @@ +package seedu.ecardnomics.command; + +public class ExitCommand extends Command { + public ExitCommand() { + } + + @Override + public void execute() { + } + + /** Returns if command given is an instance of ExitCommand. */ + public static boolean isExit(Command command) { + return command instanceof ExitCommand; + } +} diff --git a/src/main/java/seedu/ecardnomics/command/VersionCommand.java b/src/main/java/seedu/ecardnomics/command/VersionCommand.java new file mode 100644 index 0000000000..0f49171379 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/command/VersionCommand.java @@ -0,0 +1,14 @@ +package seedu.ecardnomics.command; + +import seedu.ecardnomics.Ui; + +public class VersionCommand extends Command { + + public VersionCommand() { + } + + @Override + public void execute() { + Ui.printVersionNumber(); + } +} diff --git a/src/main/java/seedu/ecardnomics/command/VoidCommand.java b/src/main/java/seedu/ecardnomics/command/VoidCommand.java new file mode 100644 index 0000000000..549c05901f --- /dev/null +++ b/src/main/java/seedu/ecardnomics/command/VoidCommand.java @@ -0,0 +1,23 @@ +package seedu.ecardnomics.command; + +import seedu.ecardnomics.Ui; + +public class VoidCommand extends Command { + private String errorMessage; + + /** Constructor without errorMessage. */ + public VoidCommand() { + errorMessage = Ui.NOT_RECOGNISED_LINE; + } + + /** Constructor with errorMessage. */ + public VoidCommand(String errorMessage) { + assert (errorMessage != null && !errorMessage.isEmpty()) : "Must specify error message"; + this.errorMessage = errorMessage; + } + + @Override + public void execute() { + Ui.printMessage(errorMessage); + } +} diff --git a/src/main/java/seedu/ecardnomics/command/deck/AddCommand.java b/src/main/java/seedu/ecardnomics/command/deck/AddCommand.java new file mode 100644 index 0000000000..d5ed60da2b --- /dev/null +++ b/src/main/java/seedu/ecardnomics/command/deck/AddCommand.java @@ -0,0 +1,23 @@ +package seedu.ecardnomics.command.deck; + +import seedu.ecardnomics.deck.Deck; +import seedu.ecardnomics.deck.FlashCard; + +public class AddCommand extends DeckCommand { + private final String question; + private final String answer; + + /** Constructor. */ + public AddCommand(Deck deck, String question, String answer) { + super(deck); + assert (question != null && !question.isEmpty()) : "Flashcard must have one question."; + this.question = question; + assert (answer != null && !answer.isEmpty()) : "Flashcard must have one question."; + this.answer = answer; + } + + @Override + public void execute() { + this.currentDeck.add(new FlashCard(this.question, this.answer)); + } +} diff --git a/src/main/java/seedu/ecardnomics/command/deck/DeckCommand.java b/src/main/java/seedu/ecardnomics/command/deck/DeckCommand.java new file mode 100644 index 0000000000..64ffd36fb9 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/command/deck/DeckCommand.java @@ -0,0 +1,19 @@ +package seedu.ecardnomics.command.deck; + +import seedu.ecardnomics.command.Command; +import seedu.ecardnomics.deck.Deck; + +public abstract class DeckCommand extends Command { + protected Deck currentDeck; + + public DeckCommand() { + currentDeck = null; + } + + public DeckCommand(Deck currentDeck) { + assert currentDeck != null : "Command must operate on a deck."; + this.currentDeck = currentDeck; + } + + public abstract void execute(); +} diff --git a/src/main/java/seedu/ecardnomics/command/deck/DeleteCommand.java b/src/main/java/seedu/ecardnomics/command/deck/DeleteCommand.java new file mode 100644 index 0000000000..6a054c3a2c --- /dev/null +++ b/src/main/java/seedu/ecardnomics/command/deck/DeleteCommand.java @@ -0,0 +1,23 @@ +package seedu.ecardnomics.command.deck; + +import seedu.ecardnomics.Ui; +import seedu.ecardnomics.deck.Deck; + +public class DeleteCommand extends DeckCommand { + private int flashCardID; + private boolean isFlashCardDeleted; + + public DeleteCommand(Deck deck, int flashCardID, boolean isFlashCardDeleted) { + super(deck); + assert (flashCardID >= 0 && flashCardID < deck.size()) : "Index must be within range."; + this.flashCardID = flashCardID; + this.isFlashCardDeleted = isFlashCardDeleted; + } + + @Override + public void execute() { + if (isFlashCardDeleted) { + currentDeck.delete(flashCardID); + } + } +} diff --git a/src/main/java/seedu/ecardnomics/command/deck/DoneEditCommand.java b/src/main/java/seedu/ecardnomics/command/deck/DoneEditCommand.java new file mode 100644 index 0000000000..69fbf55f63 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/command/deck/DoneEditCommand.java @@ -0,0 +1,21 @@ +package seedu.ecardnomics.command.deck; + +import seedu.ecardnomics.command.Command; +import seedu.ecardnomics.deck.Deck; + +public class DoneEditCommand extends DeckCommand { + + /** Constructor. */ + public DoneEditCommand(Deck deck) { + super(deck); + } + + @Override + public void execute() { + } + + /** Returns if command given is an instance of DoneEditCommand. */ + public static boolean isDoneEdit(Command command) { + return command instanceof DoneEditCommand; + } +} diff --git a/src/main/java/seedu/ecardnomics/command/deck/HelpCommand.java b/src/main/java/seedu/ecardnomics/command/deck/HelpCommand.java new file mode 100644 index 0000000000..e3320647f0 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/command/deck/HelpCommand.java @@ -0,0 +1,40 @@ +package seedu.ecardnomics.command.deck; + +import seedu.ecardnomics.Ui; + +public class HelpCommand extends DeckCommand { + public static final String DECK_HELP = "" + // -------------------------------------------------------------------------------- + + "eCardnomics.\n" + + "Deck Mode.\n" + + "\n" + + "Usage:\n" + + " add [ /ans ] Adds a new flash card to the current \n" + + " deck. \n" + + " list [/ans] Lists all flash cards in the current \n" + + " deck, optionally with answers. \n" + + " delete [-y] Deletes the flash card at list index \n" + + " update Updates the flash card at list index \n" + + " from the current deck. \n" + + " pptx [-y] [-cs | -oc Creates a PowerPoint slides based on \n" + + " ] current deck. \n" + + " start Enter Game Mode for this deck! Do your\n" + + " best! \n" + + " done Exits from Deck Mode and returns to \n" + + " Normal Mode. \n" + + " exit Exits the program. \n" + + " help Show this output. \n" + + "\n" + + "Options:\n" + + " --version Show version."; + // -------------------------------------------------------------------------------- + + public HelpCommand() { + super(); + } + + @Override + public void execute() { + Ui.printHelp(DECK_HELP); + } +} diff --git a/src/main/java/seedu/ecardnomics/command/deck/ListCommand.java b/src/main/java/seedu/ecardnomics/command/deck/ListCommand.java new file mode 100644 index 0000000000..3c249e3bb0 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/command/deck/ListCommand.java @@ -0,0 +1,20 @@ +package seedu.ecardnomics.command.deck; + +import seedu.ecardnomics.Ui; +import seedu.ecardnomics.deck.Deck; + +public class ListCommand extends DeckCommand { + private final String arguments; + + /** Constructor. */ + public ListCommand(Deck deck, String arguments) { + super(deck); + assert arguments != null : "Arguments cannot be a null String."; + this.arguments = arguments; + } + + @Override + public void execute() { + Ui.printDeck(currentDeck, !arguments.contains("/ans")); + } +} diff --git a/src/main/java/seedu/ecardnomics/command/deck/UpdateCommand.java b/src/main/java/seedu/ecardnomics/command/deck/UpdateCommand.java new file mode 100644 index 0000000000..795b81a573 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/command/deck/UpdateCommand.java @@ -0,0 +1,24 @@ +package seedu.ecardnomics.command.deck; + +import seedu.ecardnomics.deck.Deck; + +public class UpdateCommand extends DeckCommand { + private int flashCardID; + private String newQuestion; + private String newAnswer; + + public UpdateCommand(Deck deck, int flashCardID, String question, String answer) { + super(deck); + this.flashCardID = flashCardID; + newQuestion = question; + newAnswer = answer; + } + + @Override + public void execute() { + // Update question + currentDeck.get(flashCardID).setQuestion(newQuestion); + // Update answer + currentDeck.get(flashCardID).setAnswer(newAnswer); + } +} diff --git a/src/main/java/seedu/ecardnomics/command/game/DoneGameCommand.java b/src/main/java/seedu/ecardnomics/command/game/DoneGameCommand.java new file mode 100644 index 0000000000..640d42de12 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/command/game/DoneGameCommand.java @@ -0,0 +1,20 @@ +package seedu.ecardnomics.command.game; + +import seedu.ecardnomics.command.Command; +import seedu.ecardnomics.deck.Deck; + +public class DoneGameCommand extends GameCommand { + /** Constructor. */ + public DoneGameCommand(Deck deck) { + super(deck); + } + + @Override + public void execute() { + } + + /** Returns if command given is an instance of DoneEditCommand. */ + public static boolean isDoneGame(Command command) { + return command instanceof DoneGameCommand; + } +} diff --git a/src/main/java/seedu/ecardnomics/command/game/GameCommand.java b/src/main/java/seedu/ecardnomics/command/game/GameCommand.java new file mode 100644 index 0000000000..bf0b47f5f9 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/command/game/GameCommand.java @@ -0,0 +1,19 @@ +package seedu.ecardnomics.command.game; + +import seedu.ecardnomics.command.Command; +import seedu.ecardnomics.deck.Deck; + +public abstract class GameCommand extends Command { + protected Deck currentDeck; + + public GameCommand() { + currentDeck = null; + } + + public GameCommand(Deck currentDeck) { + assert currentDeck != null : "Command must operate on a deck."; + this.currentDeck = currentDeck; + } + + public abstract void execute(); +} diff --git a/src/main/java/seedu/ecardnomics/command/game/GameResponseCommand.java b/src/main/java/seedu/ecardnomics/command/game/GameResponseCommand.java new file mode 100644 index 0000000000..4d6bb93d04 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/command/game/GameResponseCommand.java @@ -0,0 +1,19 @@ +package seedu.ecardnomics.command.game; + +public class GameResponseCommand extends GameCommand { + String attempt; + + public GameResponseCommand(String userInput) { + super(); + attempt = userInput; + } + + @Override + public void execute() { + // + } + + public String getAttempt() { + return attempt; + } +} diff --git a/src/main/java/seedu/ecardnomics/command/game/GameStartCommand.java b/src/main/java/seedu/ecardnomics/command/game/GameStartCommand.java new file mode 100644 index 0000000000..23dc2896eb --- /dev/null +++ b/src/main/java/seedu/ecardnomics/command/game/GameStartCommand.java @@ -0,0 +1,22 @@ +package seedu.ecardnomics.command.game; + +import seedu.ecardnomics.deck.Deck; +import seedu.ecardnomics.game.Game; + +public class GameStartCommand extends GameCommand { + Game game; + + /** Constructor. */ + public GameStartCommand(Deck deck) { + super(deck); + } + + @Override + public void execute() { + game = new Game(currentDeck); + } + + public Game getGameInstance() { + return game; + } +} diff --git a/src/main/java/seedu/ecardnomics/command/game/HelpCommand.java b/src/main/java/seedu/ecardnomics/command/game/HelpCommand.java new file mode 100644 index 0000000000..4a7135cf8a --- /dev/null +++ b/src/main/java/seedu/ecardnomics/command/game/HelpCommand.java @@ -0,0 +1,32 @@ +package seedu.ecardnomics.command.game; + +import seedu.ecardnomics.Ui; +import seedu.ecardnomics.game.Game; + +public class HelpCommand extends GameCommand { + public static final String GAME_HELP = "" + // -------------------------------------------------------------------------------- + + "eCardnomics.\n" + + "Game Mode.\n" + + "\n" + + "Usage:\n" + + " done Exits from Game Mode and returns to Normal Mode.\n" + + " exit Exits the program.\n" + + " help Show this output.\n" + + "\n" + + "Options:\n" + + " --version Show version.\n" + + "\n" + + "Gameplay:\n" + + Game.GAMEPLAY_MESSAGE; + // -------------------------------------------------------------------------------- + + public HelpCommand() { + super(); + } + + @Override + public void execute() { + Ui.printHelp(GAME_HELP); + } +} diff --git a/src/main/java/seedu/ecardnomics/command/normal/CreateCommand.java b/src/main/java/seedu/ecardnomics/command/normal/CreateCommand.java new file mode 100644 index 0000000000..9990b3a40c --- /dev/null +++ b/src/main/java/seedu/ecardnomics/command/normal/CreateCommand.java @@ -0,0 +1,30 @@ +package seedu.ecardnomics.command.normal; + +import seedu.ecardnomics.Ui; +import seedu.ecardnomics.deck.Deck; +import seedu.ecardnomics.deck.DeckList; + +/** + * Creates a deck adds it to the existing deck list. + */ +public class CreateCommand extends NormalCommand { + Deck newDeck; + + /** + * Constructor. + * + * @param deckList reference to the existing list of decks + * @param deck the new deck created + */ + public CreateCommand(DeckList deckList, Deck deck) { + super(deckList); + assert deck != null : "Do not add null objects to the list."; + newDeck = deck; + } + + @Override + public void execute() { + deckList.addDeck(newDeck); + Ui.printNewDeck(newDeck); + } +} diff --git a/src/main/java/seedu/ecardnomics/command/normal/DecksCommand.java b/src/main/java/seedu/ecardnomics/command/normal/DecksCommand.java new file mode 100644 index 0000000000..3c22dd54df --- /dev/null +++ b/src/main/java/seedu/ecardnomics/command/normal/DecksCommand.java @@ -0,0 +1,24 @@ +package seedu.ecardnomics.command.normal; + +import seedu.ecardnomics.Ui; +import seedu.ecardnomics.deck.DeckList; + +/** + * Lists all the existing deck. + */ +public class DecksCommand extends NormalCommand { + + /** Constructor. */ + public DecksCommand(DeckList deckList) { + super(deckList); + } + + @Override + public void execute() { + if (deckList.size() == 0) { + Ui.printMessage("There are no decks to list."); + } else { + Ui.printDeckList(deckList); + } + } +} diff --git a/src/main/java/seedu/ecardnomics/command/normal/DeleteDeckCommand.java b/src/main/java/seedu/ecardnomics/command/normal/DeleteDeckCommand.java new file mode 100644 index 0000000000..d8d0c6a7a4 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/command/normal/DeleteDeckCommand.java @@ -0,0 +1,22 @@ +package seedu.ecardnomics.command.normal; + +import seedu.ecardnomics.deck.DeckList; + +public class DeleteDeckCommand extends NormalCommand { + private int index; + private boolean isDeckDeleted; + + public DeleteDeckCommand(DeckList decks, int index, boolean isDeckDeleted) { + super(decks); + assert (index >= 0 && index < decks.size()) : "Index must be within range."; + this.index = index; + this.isDeckDeleted = isDeckDeleted; + } + + @Override + public void execute() { + if (isDeckDeleted) { + deckList.removeDeck(index); + } + } +} diff --git a/src/main/java/seedu/ecardnomics/command/normal/EditCommand.java b/src/main/java/seedu/ecardnomics/command/normal/EditCommand.java new file mode 100644 index 0000000000..9486b3df66 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/command/normal/EditCommand.java @@ -0,0 +1,32 @@ +package seedu.ecardnomics.command.normal; + +import seedu.ecardnomics.Ui; +import seedu.ecardnomics.command.Command; +import seedu.ecardnomics.deck.Deck; +import seedu.ecardnomics.deck.DeckList; + +public class EditCommand extends NormalCommand { + Deck deck; + + /** Constructor. */ + public EditCommand(DeckList deckList, Deck deck) { + super(deckList); + assert deck != null : "Do not operate on a null reference."; + this.deck = deck; + } + + @Override + public void execute() { + Ui.printDeckWelcome(deckList.getIndexOf(deck) + 1, deck); + } + + /** Returns Deck which is to be edited. */ + public Deck getDeck() { + return deck; + } + + /** Returns if command given is an instance of EditCommand. */ + public static boolean isEdit(Command command) { + return command instanceof EditCommand; + } +} diff --git a/src/main/java/seedu/ecardnomics/command/normal/HelpCommand.java b/src/main/java/seedu/ecardnomics/command/normal/HelpCommand.java new file mode 100644 index 0000000000..0315db2a61 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/command/normal/HelpCommand.java @@ -0,0 +1,45 @@ +package seedu.ecardnomics.command.normal; + +import seedu.ecardnomics.Ui; +import seedu.ecardnomics.deck.DeckList; + +public class HelpCommand extends NormalCommand { + public static final String NORMAL_HELP = "" + // -------------------------------------------------------------------------------- + + "eCardnomics.\n" + + "Normal Mode.\n" + + "\n" + + "Usage:\n" + + " create [/tag [ ...]] Creates a new deck of flash cards\n" + + " named . \n" + + " decks Lists all available decks. \n" + + " edit Enter Deck Mode for editing the \n" + + " deck at list index . \n" + + " start Enter Game Mode for deck at list \n" + + " index ! Do your best! \n" + + " delete [-y] Deletes the deck at list index \n" + + " from list of decks. \n" + + " pptx [-y] [-cs | -oc Creates a PowerPoint slides based\n" + + " ] on the deck at list index .\n" + + " tag /tag [ ...] Tags the deck at list index \n" + + " with 1 or more tags. \n" + + " untag /tag [ ...] Untags specified s of the \n" + + " deck at list index . \n" + + " search [ ...] Search deck list for decks tagged\n" + + " with specified s. \n" + + " exit Exits the program.\n" + + " help Show this output.\n" + + "\n" + + "Options:\n" + + " --version Show version."; + // -------------------------------------------------------------------------------- + + public HelpCommand() { + super(); + } + + @Override + public void execute() { + Ui.printHelp(NORMAL_HELP); + } +} diff --git a/src/main/java/seedu/ecardnomics/command/normal/NormalCommand.java b/src/main/java/seedu/ecardnomics/command/normal/NormalCommand.java new file mode 100644 index 0000000000..43ddf26ef0 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/command/normal/NormalCommand.java @@ -0,0 +1,22 @@ +package seedu.ecardnomics.command.normal; + +import seedu.ecardnomics.command.Command; +import seedu.ecardnomics.deck.DeckList; + +/** + * Parent Command in Normal Mode. + */ +public abstract class NormalCommand extends Command { + protected DeckList deckList; + + public NormalCommand() { + deckList = null; + } + + public NormalCommand(DeckList deckList) { + assert deckList != null : "Command must operate on a deck list"; + this.deckList = deckList; + } + + public abstract void execute(); +} diff --git a/src/main/java/seedu/ecardnomics/command/normal/PowerPointCommand.java b/src/main/java/seedu/ecardnomics/command/normal/PowerPointCommand.java new file mode 100644 index 0000000000..5990b4c5f5 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/command/normal/PowerPointCommand.java @@ -0,0 +1,48 @@ +package seedu.ecardnomics.command.normal; + +import seedu.ecardnomics.deck.Deck; +import seedu.ecardnomics.deck.DeckList; +import seedu.ecardnomics.powerpoint.PowerPoint; + +import java.awt.Color; + +public class PowerPointCommand extends NormalCommand { + Deck deck; + PowerPoint pptx; + boolean isPpptxCreated; + + /** Constructor for default printing. */ + public PowerPointCommand(DeckList deckList, Deck deck, boolean isPpptxCreated) { + super(deckList); + assert deck != null : "Do not operate on a null reference."; + this.deck = deck; + pptx = new PowerPoint(deck); + this.isPpptxCreated = isPpptxCreated; + } + + /** Constructor for -cs option. */ + public PowerPointCommand(DeckList deckList, Deck deck, boolean isPpptxCreated, int csIndex) { + super(deckList); + assert deck != null : "Do not operate on a null reference."; + this.deck = deck; + pptx = new PowerPoint(deck, csIndex); + this.isPpptxCreated = isPpptxCreated; + } + + /** Constructor for -oc option. */ + public PowerPointCommand(DeckList deckList, Deck deck, boolean isPpptxCreated, String bgColorString, + String txtColorString, Color bgColor, Color txtColor) { + super(deckList); + assert deck != null : "Do not operate on a null reference."; + this.deck = deck; + pptx = new PowerPoint(deck, bgColorString, txtColorString, bgColor, txtColor); + this.isPpptxCreated = isPpptxCreated; + } + + @Override + public void execute() { + if (isPpptxCreated) { + pptx.createNewPowerPoint(); + } + } +} diff --git a/src/main/java/seedu/ecardnomics/command/normal/SearchCommand.java b/src/main/java/seedu/ecardnomics/command/normal/SearchCommand.java new file mode 100644 index 0000000000..a571d290f7 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/command/normal/SearchCommand.java @@ -0,0 +1,39 @@ +package seedu.ecardnomics.command.normal; + +import seedu.ecardnomics.Ui; +import seedu.ecardnomics.deck.Deck; +import seedu.ecardnomics.deck.DeckList; + +import java.util.ArrayList; + +public class SearchCommand extends NormalCommand { + private String[] relevantTags; + + /** Constructor. */ + public SearchCommand(DeckList decks, String[] relevantTags) { + super(decks); + assert (relevantTags.length != 0) : "Relevant tags must be provided."; + this.relevantTags = relevantTags; + } + + @Override + public void execute() { + String decksHavingTags = ""; + ArrayList uniqueTagList = new ArrayList<>(); + for (String tag: relevantTags) { + String lowerCaseTrimmedTag = tag.toLowerCase(); + if (!uniqueTagList.contains(lowerCaseTrimmedTag) & !lowerCaseTrimmedTag.isEmpty()) { + uniqueTagList.add(tag); + } + } + for (String tag: uniqueTagList) { + for (int i = 0; i < deckList.size(); i++) { + String tagLowerCase = deckList.getDeck(i).getTagString().toLowerCase(); + if (tagLowerCase.contains(tag.toLowerCase())) { + decksHavingTags += "\n" + deckList.getNameWithTags(i); + } + } + } + Ui.printSearchDecksLine(decksHavingTags); + } +} diff --git a/src/main/java/seedu/ecardnomics/command/normal/StartCommand.java b/src/main/java/seedu/ecardnomics/command/normal/StartCommand.java new file mode 100644 index 0000000000..9a6aecfaea --- /dev/null +++ b/src/main/java/seedu/ecardnomics/command/normal/StartCommand.java @@ -0,0 +1,34 @@ +package seedu.ecardnomics.command.normal; + +import seedu.ecardnomics.Ui; +import seedu.ecardnomics.command.Command; +import seedu.ecardnomics.command.game.GameStartCommand; +import seedu.ecardnomics.deck.Deck; +import seedu.ecardnomics.deck.DeckList; + +public class StartCommand extends NormalCommand { + Deck deck; + + /** Constructor. */ + public StartCommand(DeckList deckList, Deck deck) { + super(deckList); + assert deck != null : "Do not operate on a null reference."; + this.deck = deck; + } + + @Override + public void execute() { + Ui.printGameWelcome(deckList.getIndexOf(deck) + 1, deck); + new GameStartCommand(deck); + } + + /** Returns Deck whose Game Mode is in progress. */ + public Deck getDeck() { + return deck; + } + + /** Returns if command given is an instance of StartCommand. */ + public static boolean isStart(Command command) { + return command instanceof StartCommand; + } +} diff --git a/src/main/java/seedu/ecardnomics/command/normal/TagCommand.java b/src/main/java/seedu/ecardnomics/command/normal/TagCommand.java new file mode 100644 index 0000000000..149ee2ed3d --- /dev/null +++ b/src/main/java/seedu/ecardnomics/command/normal/TagCommand.java @@ -0,0 +1,29 @@ +package seedu.ecardnomics.command.normal; + +import seedu.ecardnomics.Ui; +import seedu.ecardnomics.deck.DeckList; + +import java.util.ArrayList; + +/** + * Adds tags to existing deck. + */ +public class TagCommand extends NormalCommand { + private int index; + private ArrayList newTags; + + /** Constructor. */ + public TagCommand(DeckList decks, int index, ArrayList newTags) { + super(decks); + assert (index >= 0 && index < decks.size()) : "Index must be within range."; + this.index = index; + assert (newTags.size() != 0) : "Tags must be provided."; + this.newTags = newTags; + } + + @Override + public void execute() { + deckList.getDeck(index).addTag(newTags); + Ui.printNewTags(deckList.getDeck(index).getName(), newTags); + } +} diff --git a/src/main/java/seedu/ecardnomics/command/normal/UntagCommand.java b/src/main/java/seedu/ecardnomics/command/normal/UntagCommand.java new file mode 100644 index 0000000000..7d0f99af32 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/command/normal/UntagCommand.java @@ -0,0 +1,64 @@ +package seedu.ecardnomics.command.normal; + +import seedu.ecardnomics.Ui; +import seedu.ecardnomics.deck.DeckList; + +import java.util.ArrayList; + +/** + * Removes tags from the existing deck. + */ +public class UntagCommand extends NormalCommand { + private int index; + private ArrayList removedTags; + private boolean isYes = false; + + /** Constructor. */ + public UntagCommand(DeckList decks, int index, ArrayList removedTags, boolean isYes) { + super(decks); + assert (index >= 0 && index < decks.size()) : "Index must be within range."; + this.index = index; + assert (removedTags.size() != 0) : "Remove tags must be provided."; + this.removedTags = removedTags; + this.isYes = isYes; + } + + @Override + public void execute() { + String deckName = deckList.getDeck(index).getName(); + boolean isTagsValid = checkTagsExist(removedTags); + + if (isTagsValid) { + if (isYes) { + deckList.getDeck(index).removeTag(removedTags); + Ui.printTagsRemovedLine(deckName, removedTags); + } else if (!isYes) { + boolean isTagsRemoved = Ui.getRemovedTagsConfirmation(removedTags, deckName); + if (isTagsRemoved) { + deckList.getDeck(index).removeTag(removedTags); + } + } + } + } + + /** + * Checks if the deck specified contains the removed tags. + * + * @param removedTags String[] list tags to be removed + * @return a boolean value indicating if all the tags exist + */ + public boolean checkTagsExist(ArrayList removedTags) { + boolean isExist = true; + ArrayList availableTagList = deckList.getDeck(index).getTag(); + outerLoop: + for (String removedTag : removedTags) { + if (!availableTagList.contains(removedTag)) { + isExist = false; + Ui.printInvalidTagsLine(removedTag); + break outerLoop; + } + } + return isExist; + } + +} diff --git a/src/main/java/seedu/ecardnomics/deck/Deck.java b/src/main/java/seedu/ecardnomics/deck/Deck.java new file mode 100644 index 0000000000..154ee1d8a0 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/deck/Deck.java @@ -0,0 +1,164 @@ +package seedu.ecardnomics.deck; + +import java.util.ArrayList; + +/** + * Deck of flashcards. + */ +public class Deck { + private String name; + private ArrayList tags; + private ArrayList deck; + + /** Constructor. */ + public Deck(String name) { + assert (name != null && !name.isEmpty()) : "A deck requires a name."; + this.name = name; + deck = new ArrayList<>(); + tags = new ArrayList<>(); + } + + /** + * Constructors with tags parameter. + * + * @param name the name of the new deck + * @param tags tags of the new deck + */ + public Deck(String name, ArrayList tags) { + assert (name != null && !name.isEmpty()) : "A deck requires a name."; + this.name = name; + deck = new ArrayList<>(); + this.tags = new ArrayList<>(); + this.tags = tags; + } + + /** + * Gets the name of the deck. + * + * @return name of deck + */ + public String getName() { + return name; + } + + public String getTagString() { + String tagString = ""; + for (int j = 0; j < tags.size(); j++) { + tagString += tags.get(j); + if (j < tags.size() - 1) { + tagString += " | "; + } + } + return tagString; + } + + public ArrayList getTag() { + return tags; + } + + /** + * Adds tag to the deck. + * + * @param newTags an unique ArraList to be added to the deck + */ + public void addTag(ArrayList newTags) { + for (String tag: newTags) { + if (!tags.contains(tag)) { + tags.add(tag); + } + } + } + + public void removeTag(ArrayList deletedTags) { + for (String tag: deletedTags) { + tags.remove(tag); + } + } + + /** + * Sets the name of the deck. + * + * @param name new name of the deck + */ + public void setName(String name) { + assert (name != null && !name.isEmpty()) : "A deck requires a name."; + this.name = name; + } + + /** + * Retrieves the inherent ArrayList data structure of the deck. + * + * @return ArrayList of FlashCards + */ + public ArrayList getDeck() { + return deck; + } + + /** + * Retrieves the flashcard at specified index. + * + * @param index Index of flashcard to be found + * @return FlashCard at index + */ + public FlashCard get(int index) { + assert (index >= 0 && index < deck.size()) : "Index should be within range"; + return deck.get(index); + } + + /** + * Adds a flashcard to the deck. + * + * @param flashCard Reference to the flashcard to be added + */ + public void add(FlashCard flashCard) { + assert flashCard != null : "Do not add null objects into deck"; + deck.add(flashCard); + } + + /** + * Returns size of current deck. + * + * @return size of current deck + */ + public int size() { + return deck.size(); + } + + /** + * Deletes the flashcard at specified index from the deck. + * + * @param index Index of flashcard to be deleted + */ + public void delete(int index) { + assert (index >= 0 && index < deck.size()) : "Index should be within range"; + deck.remove(index); + } + + @Override + public String toString() { + String output = name + ":" + System.lineSeparator(); + output += this.toString(false); + return output; + } + + public String toString(boolean isQuestionOnly) { + String output = ""; + if (this.deck.size() == 0) { + return output; + } + + for (int i = 0; i < deck.size(); i++) { + String serialNumber = (i + 1) + ". "; + // Always print the question. + output += serialNumber + deck.get(i).toString(true, serialNumber.length()); + if (!isQuestionOnly) { + // Add in the answer + output += System.lineSeparator() + deck.get(i).toString(false, serialNumber.length()); + } + if (i != deck.size() - 1) { + output += "\n\n"; + } + } + return output; + } +} diff --git a/src/main/java/seedu/ecardnomics/deck/DeckList.java b/src/main/java/seedu/ecardnomics/deck/DeckList.java new file mode 100644 index 0000000000..cd7da3926a --- /dev/null +++ b/src/main/java/seedu/ecardnomics/deck/DeckList.java @@ -0,0 +1,135 @@ +package seedu.ecardnomics.deck; + +import java.util.ArrayList; + +/** + * List for storing decks. + */ +public class DeckList { + private ArrayList deckList; + + /** Constructor. */ + public DeckList() { + deckList = new ArrayList<>(); + } + + /** + * Adds a deck to the list. + * + * @param deck reference to Deck to be added + */ + public void addDeck(Deck deck) { + assert deck != null : "Do not add null object to list."; + assert !deckList.contains(deck.getName()) : "The deck list has duplicate decks."; + deckList.add(deck); + } + + /** + * Removes a deck from the list. + * + * @param index int representing index of deck to to be removed + */ + public void removeDeck(int index) { + assert (index >= 0 && index < deckList.size()) : "Index should be within range."; + deckList.remove(index); + } + + /** + * Checks whether a deck with the same deck name is already in the deck. + * @param deck reference to deck to be checked + * @return boolean + */ + public boolean contains(Deck deck) { + for (Deck thisDeck : deckList) { + if (thisDeck.getName().equals(deck.getName())) { + return true; + } + } + return false; + } + + /** + * Returns size of current list. + * + * @return size of list + */ + public int size() { + return deckList.size(); + } + + /** + * Returns a deck from deck list based on given index. + * + * @param index Index of deck to be found + * @return Deck found at index + */ + public Deck getDeck(int index) { + assert (index >= 0 && index < deckList.size()) : "Index should be within range."; + return deckList.get(index); + } + + /** + * Returns the names of all decks in the deck list in ArrayList. + * + * @return ArrayList + */ + public ArrayList getAllNames() { + ArrayList deckNameOfDecks = new ArrayList<>(); + for (Deck deck: deckList) { + deckNameOfDecks.add(deck.getName()); + } + + return deckNameOfDecks; + } + + /** + * Returns index of deck in deck list. + * + * @param deck Deck which index is to be found + * @return Index found of deck + */ + public int getIndexOf(Deck deck) { + assert deck != null : "Do not try to get index of a null reference."; + return deckList.indexOf(deck); + } + + /** + * Returns a deck name with its tag. + * + * @param index the index of that deck + * @return a String including of the deck name and tags. + */ + public String getNameWithTags(int index) { + String nameWithTags = ""; + String offset = ""; + while (offset.length() <= (index / 10 + 2)) { + offset += " "; + } + nameWithTags += (index + 1) + ". " + deckList.get(index).getName(); + if (deckList.get(index).getTag().size() != 0) { + nameWithTags += "\n"; + nameWithTags += offset + "Tags: " + deckList.get(index).getTagString(); + } + + return nameWithTags; + } + + /** + * Returns a string including index and name, tag of all the deck available. + * + * @return A String representing index, name and tag + */ + @Override + public String toString() { + String output = ""; + for (int i = 0; i < deckList.size(); i++) { + output += getNameWithTags(i); + if (i != deckList.size() - 1) { + output += "\n"; + } + } + + return output; + } + +} diff --git a/src/main/java/seedu/ecardnomics/deck/FlashCard.java b/src/main/java/seedu/ecardnomics/deck/FlashCard.java new file mode 100644 index 0000000000..be40a8afd5 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/deck/FlashCard.java @@ -0,0 +1,85 @@ +package seedu.ecardnomics.deck; + +import seedu.ecardnomics.Ui; + +/** + * Flashcards that contain a question and asnwer. + */ +public class FlashCard { + private String question; + private String answer; + + private static final String QN_LABEL = "Question: "; + private static final String ANS_LABEL = "Answer: "; + + /** Constructor. */ + public FlashCard(String question, String answer) { + assert (question != null && !question.isEmpty()) : "Flashcard must have one question."; + this.question = question; + assert (answer != null && !answer.isEmpty()) : "Flashcard must have one answer"; + this.answer = answer; + } + + /** + * Get the answer of the flashcard. + * + * @return String representing the answer on the flashcard + */ + public String getAnswer() { + return answer; + } + + /** + * Get the question of the flashcard. + * + * @return String representing the question on the flashcard + */ + public String getQuestion() { + return question; + } + + /** + * Set the answer of the flashcard. + * + * @param answer new answer on the flashcard + */ + public void setAnswer(String answer) { + assert (answer != null && !answer.isEmpty()) : "Flashcard must have one answer"; + this.answer = answer; + } + + /** + * Set the question of the flashcard. + * + * @param question new answer on the flashcard + */ + public void setQuestion(String question) { + assert (question != null && !question.isEmpty()) : "Flashcard must have one question."; + this.question = question; + } + + /* Would not recommend using this method unless there is no offset to be printed before QN_LABEL */ + public String toString() { + return QN_LABEL + Ui.prettyPrintFormatter(question, QN_LABEL.length()) + System.lineSeparator() + + ANS_LABEL + Ui.prettyPrintFormatter(answer, ANS_LABEL.length()); + } + + /** + * Returns a String that contains the requested details (question or answer) + * for the flashcard for printing. The String is formatted such that the + * details will fit between offset characters from the start of the line and + * Ui.DASH_LINES.length(). + * + * @param isQuestion indicates whether the required detail is question or answer + * @param offset Number of characters from the start of the line + * @return String for displaying question if isQuestion, answer otherwise + */ + public String toString(boolean isQuestion, int offset) { + if (isQuestion) { + return QN_LABEL + Ui.prettyPrintFormatter(question, offset + QN_LABEL.length()); + } else { + return " ".repeat(offset) + ANS_LABEL + + Ui.prettyPrintFormatter(answer, offset + ANS_LABEL.length()); + } + } +} diff --git a/src/main/java/seedu/ecardnomics/exceptions/BothOcAndCsException.java b/src/main/java/seedu/ecardnomics/exceptions/BothOcAndCsException.java new file mode 100644 index 0000000000..997459b311 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/exceptions/BothOcAndCsException.java @@ -0,0 +1,11 @@ +package seedu.ecardnomics.exceptions; + +public class BothOcAndCsException extends Exception { + public static final String BOTH_OC_AND_CS_LINE = + "Input should not have both -oc and -cs option."; + + @Override + public String getMessage() { + return BOTH_OC_AND_CS_LINE; + } +} diff --git a/src/main/java/seedu/ecardnomics/exceptions/ColorsNotAvailException.java b/src/main/java/seedu/ecardnomics/exceptions/ColorsNotAvailException.java new file mode 100644 index 0000000000..932aca127e --- /dev/null +++ b/src/main/java/seedu/ecardnomics/exceptions/ColorsNotAvailException.java @@ -0,0 +1,11 @@ +package seedu.ecardnomics.exceptions; + +public class ColorsNotAvailException extends Exception { + public static final String COLORS_NOT_AVAIL_LINE = + "Colors option is not available"; + + @Override + public String getMessage() { + return COLORS_NOT_AVAIL_LINE; + } +} diff --git a/src/main/java/seedu/ecardnomics/exceptions/CsIndexFormatException.java b/src/main/java/seedu/ecardnomics/exceptions/CsIndexFormatException.java new file mode 100644 index 0000000000..d0ec24f2fa --- /dev/null +++ b/src/main/java/seedu/ecardnomics/exceptions/CsIndexFormatException.java @@ -0,0 +1,12 @@ +package seedu.ecardnomics.exceptions; + +public class CsIndexFormatException extends Exception { + public static final String CS_INDEX_FORMAT_LINE = + "-cs needs to be followed by a integer between 1 and 10 (inclusive)"; + + @Override + public String getMessage() { + return CS_INDEX_FORMAT_LINE; + + } +} diff --git a/src/main/java/seedu/ecardnomics/exceptions/CsIndexRangeException.java b/src/main/java/seedu/ecardnomics/exceptions/CsIndexRangeException.java new file mode 100644 index 0000000000..fd3c342c28 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/exceptions/CsIndexRangeException.java @@ -0,0 +1,11 @@ +package seedu.ecardnomics.exceptions; + +public class CsIndexRangeException extends Exception { + public static final String CS_INDEX_RANGE_LINE = + "-cs index not within the correct range, [1,10]"; + + @Override + public String getMessage() { + return CS_INDEX_RANGE_LINE; + } +} diff --git a/src/main/java/seedu/ecardnomics/exceptions/DeckRangeException.java b/src/main/java/seedu/ecardnomics/exceptions/DeckRangeException.java new file mode 100644 index 0000000000..f43b8b440f --- /dev/null +++ b/src/main/java/seedu/ecardnomics/exceptions/DeckRangeException.java @@ -0,0 +1,11 @@ +package seedu.ecardnomics.exceptions; + +public class DeckRangeException extends Exception { + public static final String DECK_RANGE_LINE = + "Index should be within range of number of decks!"; + + @Override + public String getMessage() { + return DECK_RANGE_LINE; + } +} diff --git a/src/main/java/seedu/ecardnomics/exceptions/DuplicateDeckException.java b/src/main/java/seedu/ecardnomics/exceptions/DuplicateDeckException.java new file mode 100644 index 0000000000..c19fff7457 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/exceptions/DuplicateDeckException.java @@ -0,0 +1,11 @@ +package seedu.ecardnomics.exceptions; + +public class DuplicateDeckException extends Exception { + public static final String DUPLICATE_DECK_LINE = + "This deck name has already existed, please choose another name."; + + @Override + public String getMessage() { + return DUPLICATE_DECK_LINE; + } +} diff --git a/src/main/java/seedu/ecardnomics/exceptions/EmptyInputException.java b/src/main/java/seedu/ecardnomics/exceptions/EmptyInputException.java new file mode 100644 index 0000000000..acf3b0fd95 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/exceptions/EmptyInputException.java @@ -0,0 +1,11 @@ +package seedu.ecardnomics.exceptions; + +public class EmptyInputException extends Exception { + public static final String EMPTY_INPUT_LINE = + "Input shouldn't be empty! Returning..."; + + @Override + public String getMessage() { + return EMPTY_INPUT_LINE; + } +} diff --git a/src/main/java/seedu/ecardnomics/exceptions/EmptyQnAException.java b/src/main/java/seedu/ecardnomics/exceptions/EmptyQnAException.java new file mode 100644 index 0000000000..0f6d856283 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/exceptions/EmptyQnAException.java @@ -0,0 +1,11 @@ +package seedu.ecardnomics.exceptions; + +public class EmptyQnAException extends Exception { + public static final String EMPTY_QNA_LINE = + "A flash card must have a question and answer."; + + @Override + public String getMessage() { + return EMPTY_QNA_LINE; + } +} diff --git a/src/main/java/seedu/ecardnomics/exceptions/FlashCardRangeException.java b/src/main/java/seedu/ecardnomics/exceptions/FlashCardRangeException.java new file mode 100644 index 0000000000..4043359182 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/exceptions/FlashCardRangeException.java @@ -0,0 +1,11 @@ +package seedu.ecardnomics.exceptions; + +public class FlashCardRangeException extends Exception { + public static final String FLASHCARD_RANGE_LINE = + "Index should be within range of number of flash cards!"; + + @Override + public String getMessage() { + return FLASHCARD_RANGE_LINE; + } +} diff --git a/src/main/java/seedu/ecardnomics/exceptions/IndexFormatException.java b/src/main/java/seedu/ecardnomics/exceptions/IndexFormatException.java new file mode 100644 index 0000000000..0b5c86d648 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/exceptions/IndexFormatException.java @@ -0,0 +1,11 @@ +package seedu.ecardnomics.exceptions; + +public class IndexFormatException extends Exception { + public static final String INDEX_FORMAT_LINE = + "Command should be followed by a valid positive !"; + + @Override + public String getMessage() { + return INDEX_FORMAT_LINE; + } +} diff --git a/src/main/java/seedu/ecardnomics/exceptions/InvalidListCommandException.java b/src/main/java/seedu/ecardnomics/exceptions/InvalidListCommandException.java new file mode 100644 index 0000000000..ebaa3822a5 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/exceptions/InvalidListCommandException.java @@ -0,0 +1,11 @@ +package seedu.ecardnomics.exceptions; + +public class InvalidListCommandException extends Exception { + public static final String INVALID_LIST_COMMAND_LINE = + "Invalid argument for list command"; + + @Override + public String getMessage() { + return INVALID_LIST_COMMAND_LINE; + } +} diff --git a/src/main/java/seedu/ecardnomics/exceptions/InvalidOptionsException.java b/src/main/java/seedu/ecardnomics/exceptions/InvalidOptionsException.java new file mode 100644 index 0000000000..9834b6e3ac --- /dev/null +++ b/src/main/java/seedu/ecardnomics/exceptions/InvalidOptionsException.java @@ -0,0 +1,11 @@ +package seedu.ecardnomics.exceptions; + +public class InvalidOptionsException extends Exception { + public static final String INVALID_OPTIONS_LINE = + "Invalid options in command"; + + @Override + public String getMessage() { + return INVALID_OPTIONS_LINE; + } +} diff --git a/src/main/java/seedu/ecardnomics/exceptions/InvalidPptxArgumentException.java b/src/main/java/seedu/ecardnomics/exceptions/InvalidPptxArgumentException.java new file mode 100644 index 0000000000..07feba7875 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/exceptions/InvalidPptxArgumentException.java @@ -0,0 +1,11 @@ +package seedu.ecardnomics.exceptions; + +public class InvalidPptxArgumentException extends Exception { + public static final String INVALID_PPTX_ARGUMENT_LINE = + "Invalid arguments for PPTX command"; + + @Override + public String getMessage() { + return INVALID_PPTX_ARGUMENT_LINE; + } +} diff --git a/src/main/java/seedu/ecardnomics/exceptions/NoAlphaNumericInputException.java b/src/main/java/seedu/ecardnomics/exceptions/NoAlphaNumericInputException.java new file mode 100644 index 0000000000..54b0f33f10 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/exceptions/NoAlphaNumericInputException.java @@ -0,0 +1,11 @@ +package seedu.ecardnomics.exceptions; + +public class NoAlphaNumericInputException extends Exception { + public static final String NO_ALPHANUMERIC_LINE = + "Input should contain at least one alphanumeric character! Returning..."; + + @Override + public String getMessage() { + return NO_ALPHANUMERIC_LINE; + } +} diff --git a/src/main/java/seedu/ecardnomics/exceptions/NoSeparatorException.java b/src/main/java/seedu/ecardnomics/exceptions/NoSeparatorException.java new file mode 100644 index 0000000000..f911488c2f --- /dev/null +++ b/src/main/java/seedu/ecardnomics/exceptions/NoSeparatorException.java @@ -0,0 +1,11 @@ +package seedu.ecardnomics.exceptions; + +public class NoSeparatorException extends Exception { + public static final String NO_SEPARATOR_LINE = + " separator should be added before tags!"; + + @Override + public String getMessage() { + return NO_SEPARATOR_LINE; + } +} diff --git a/src/main/java/seedu/ecardnomics/exceptions/NumberTooBigException.java b/src/main/java/seedu/ecardnomics/exceptions/NumberTooBigException.java new file mode 100644 index 0000000000..74568da418 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/exceptions/NumberTooBigException.java @@ -0,0 +1,10 @@ +package seedu.ecardnomics.exceptions; + +public class NumberTooBigException extends Exception { + public static final String NUMBER_TOO_BIG_LINE = "Index is too large."; + + @Override + public String getMessage() { + return NUMBER_TOO_BIG_LINE; + } +} diff --git a/src/main/java/seedu/ecardnomics/game/Game.java b/src/main/java/seedu/ecardnomics/game/Game.java new file mode 100644 index 0000000000..940cedad4d --- /dev/null +++ b/src/main/java/seedu/ecardnomics/game/Game.java @@ -0,0 +1,41 @@ +package seedu.ecardnomics.game; + +import seedu.ecardnomics.command.Command; +import seedu.ecardnomics.deck.Deck; + +public class Game { + GameStorage storage; + GameEngine engine; + public static final String GAMEPLAY_MESSAGE = "" + + "Questions will be displayed in a randomised order. At each question, you can\n" + + " 1. Try to attempt an answer at the question, by typing at the prompt\n" + + " 2. Press (with an empty attempt if you want to do it in your head)\n" + + System.lineSeparator() + + "Then, our 'advanced' algorithms will check your answer and score your answer (if\n" + + "any), and display the correct answer for you to check your answer against.\n" + + "Finally, we will ask if you think you got it right. If you did not, the question\n" + + "will be inserted back into the question pool, and you will get a chance to\n" + + "attempt it again!"; + public static final String WELCOME_MESSAGE = "" + + "Welcome to Game Mode!" + + "\n" + + System.lineSeparator() + + "In this mode, you test your knowledge against the flash cards in the deck.\n" + + System.lineSeparator() + + GAMEPLAY_MESSAGE + + "\n" + + System.lineSeparator() + + "Type `done` to return to Normal Mode, `exit` to exit application.\n" + + "Type `help` to see a list of commands. Have fun!\n" + + System.lineSeparator() + + "Game Mode is started for: "; + + public Game(Deck deck) { + storage = new GameStorage(deck); + engine = new GameEngine(storage); + } + + public Command run() { + return engine.runGameLoop(); + } +} diff --git a/src/main/java/seedu/ecardnomics/game/GameEngine.java b/src/main/java/seedu/ecardnomics/game/GameEngine.java new file mode 100644 index 0000000000..ee9605b288 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/game/GameEngine.java @@ -0,0 +1,158 @@ +package seedu.ecardnomics.game; + +import seedu.ecardnomics.Ui; +import seedu.ecardnomics.command.Command; +import seedu.ecardnomics.command.ExitCommand; +import seedu.ecardnomics.command.VersionCommand; +import seedu.ecardnomics.command.game.DoneGameCommand; +import seedu.ecardnomics.command.game.GameResponseCommand; +import seedu.ecardnomics.command.game.HelpCommand; +import seedu.ecardnomics.deck.FlashCard; +import seedu.ecardnomics.parser.GameParser; + +public class GameEngine { + GameStorage storage; + GameParser gameParser; + + GameEngine(GameStorage storage) { + gameParser = new GameParser(storage.originalDeck); + this.storage = storage; + } + + Command runGameLoop() { + Command command; + + do { + if (isDeckInitiallyEmpty()) { + Ui.printGameEmptyDeckLine(); + return forceDoneGame(); + } + FlashCard flashCard = getQuestion(); + do { + poseQuestion(flashCard); + command = getAttempt(); + if (isHelpCommand(command) || isVersionCommand(command)) { + command.execute(); + } + } while (isHelpCommand(command) || isVersionCommand(command)); + if (isTerminate(command)) { + break; + } + Ui.printAnswerGameMode(flashCard.getAnswer()); + try { + Ui.printAttemptFeedback(checkAttempt(command, flashCard)); + } catch (Exception e) { + e.getMessage(); + } + command = update(Ui.getInclExclConfirmation(), flashCard); + + } while (!isTerminate(command) && !isNoMoreCards()); + + return command; + } + + Command update(boolean isResponseY, FlashCard flashCard) { + updateRetestStore(isResponseY, flashCard); + + if (storage.deque.isEmpty()) { + if (storage.retestStore.isEmpty()) { + Ui.printMiddleSeparator(); + Ui.printDoneGameMessage(); + return forceDoneGame(); + } else { + updateDeque(); + } + } + + Ui.printMiddleSeparator(); + return null; + } + + void updateDeque() { + storage.deque = storage.createRandomisedStack(storage.retestStore); + storage.refreshRetestStore(); + } + + void updateRetestStore(boolean response, FlashCard flashCard) { + if (response) { + storage.retestStore.add(flashCard); + } + } + + FlashCard getQuestion() { + return storage.deque.pop(); + } + + void poseQuestion(FlashCard flashCard) { + Ui.printGameQuestion(flashCard.getQuestion()); + } + + Command getAttempt() { + String userInput = Ui.readUserInput(); + Command command = gameParser.parse(userInput); + return command; + } + + double checkAttempt(Command command, FlashCard flashCard) { + assert command instanceof GameResponseCommand : "command not instance of GameResponseCommand!"; + + String attempt = ((GameResponseCommand) command).getAttempt(); + String answer = flashCard.getAnswer(); + + return getMatchPercentage(attempt, answer); + } + + double getMatchPercentage(String attempt, String answer) { + String[] attemptArray = attempt.replaceAll(Ui.PUNC_REGEX,"").split(" "); + String[] answerArray = answer.replaceAll(Ui.PUNC_REGEX,"").split(" "); + int answerLength = answerArray.length; + if (answerLength <= 1 && !isValidAnswer(answerArray)) { + return 0; + } + int matchCount = 0; + for (String word1 : answerArray) { + for (String word2: attemptArray) { + if (word2.trim().equalsIgnoreCase(word1.trim())) { + matchCount++; + break; + } + } + } + + assert matchCount <= answerLength : "matchCount > answerLength!"; + return (double) matchCount / answerLength * 100; + } + + boolean isValidAnswer(String[] answerArray) { + for (String word : answerArray) { + if (!word.isBlank()) { + return true; + } + } + return false; + } + + Command forceDoneGame() { + return gameParser.parse("done"); + } + + boolean isDeckInitiallyEmpty() { + return storage.originalDeck.size() == 0; + } + + boolean isTerminate(Command command) { + return DoneGameCommand.isDoneGame(command) || ExitCommand.isExit(command); + } + + boolean isNoMoreCards() { + return storage.deque.isEmpty() && storage.retestStore.isEmpty(); + } + + boolean isHelpCommand(Command command) { + return command instanceof HelpCommand; + } + + boolean isVersionCommand(Command command) { + return command instanceof VersionCommand; + } +} diff --git a/src/main/java/seedu/ecardnomics/game/GameStorage.java b/src/main/java/seedu/ecardnomics/game/GameStorage.java new file mode 100644 index 0000000000..1fb26d942b --- /dev/null +++ b/src/main/java/seedu/ecardnomics/game/GameStorage.java @@ -0,0 +1,35 @@ +package seedu.ecardnomics.game; + +import seedu.ecardnomics.deck.Deck; +import seedu.ecardnomics.deck.FlashCard; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collections; + +public class GameStorage { + Deck originalDeck; + ArrayDeque deque; + ArrayList retestStore; + + GameStorage(Deck deck) { + originalDeck = deck; + deque = createRandomisedStack(deck); + retestStore = new ArrayList<>(); + } + + ArrayDeque createRandomisedStack(Deck deck) { + ArrayList randomisedDeck = new ArrayList<>(deck.getDeck()); + Collections.shuffle(randomisedDeck); + return new ArrayDeque<>(randomisedDeck); + } + + ArrayDeque createRandomisedStack(ArrayList retestStore) { + Collections.shuffle(retestStore); + return new ArrayDeque<>(retestStore); + } + + void refreshRetestStore() { + retestStore = new ArrayList<>(); + } +} diff --git a/src/main/java/seedu/ecardnomics/parser/DeckParser.java b/src/main/java/seedu/ecardnomics/parser/DeckParser.java new file mode 100644 index 0000000000..9786378512 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/parser/DeckParser.java @@ -0,0 +1,320 @@ +package seedu.ecardnomics.parser; + +import seedu.ecardnomics.Main; +import seedu.ecardnomics.Ui; +import seedu.ecardnomics.command.Command; +import seedu.ecardnomics.command.VersionCommand; +import seedu.ecardnomics.command.deck.AddCommand; +import seedu.ecardnomics.command.deck.DeleteCommand; +import seedu.ecardnomics.command.deck.DoneEditCommand; +import seedu.ecardnomics.command.deck.HelpCommand; +import seedu.ecardnomics.command.deck.ListCommand; +import seedu.ecardnomics.command.deck.UpdateCommand; +import seedu.ecardnomics.command.ExitCommand; +import seedu.ecardnomics.command.VoidCommand; +import seedu.ecardnomics.deck.Deck; +import seedu.ecardnomics.deck.DeckList; +import seedu.ecardnomics.exceptions.EmptyQnAException; +import seedu.ecardnomics.exceptions.FlashCardRangeException; +import seedu.ecardnomics.exceptions.IndexFormatException; +import seedu.ecardnomics.exceptions.InvalidListCommandException; +import seedu.ecardnomics.exceptions.InvalidOptionsException; +import seedu.ecardnomics.exceptions.InvalidPptxArgumentException; +import seedu.ecardnomics.exceptions.NoAlphaNumericInputException; +import seedu.ecardnomics.exceptions.NumberTooBigException; +import seedu.ecardnomics.storage.LogStorage; + +import java.util.logging.Level; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Parser for commands supplied in Deck Mode. + */ +public class DeckParser extends Parser { + public Deck deck; + public DeckList deckList; + private static LogStorage logger = new LogStorage("DeckParserLogger"); + + /** + * Constructor. + */ + public DeckParser(DeckList deckList, Deck deck) { + this.deckList = deckList; + this.deck = deck; + } + + protected Command prepareDeletedFlashCard(String arguments) throws Exception { + int flashCardID; + boolean isFlashCardDeleted; + + if (arguments.contains("-y")) { + arguments = arguments.replaceAll(Ui.FORCE_YES_OPT, ""); + flashCardID = getIndex(arguments); + isFlashCardDeleted = true; + } else { + flashCardID = getIndex(arguments); + isFlashCardDeleted = Ui.getDeletedFlashCardConfirmation(deck.get(flashCardID).getQuestion()); + } + logger.log(Level.INFO, "returning DeleteCommand object"); + + if (isFlashCardDeleted) { + Ui.printFlashCardDeletedLine(deck.get(flashCardID).getQuestion()); + } + + Ui.printDashLines(); + + return new DeleteCommand(deck, flashCardID, isFlashCardDeleted); + } + + public static boolean containsNoAlphaNumerics(String field) { + String puncRemovedField = field.replaceAll(Ui.PUNC_REGEX, ""); + return puncRemovedField.isBlank(); + } + + /** + * Verify that a String is contains meaningful contents. + * + * @param field String to be verified + * @throws EmptyQnAException if string is empty after trim + */ + private void verifyStringField(String field) throws EmptyQnAException, NoAlphaNumericInputException { + if (field.trim().length() == 0) { + logger.log(Level.WARNING, "User entered nothing or a series of blank spaces for field"); + throw new EmptyQnAException(); + } + if (containsNoAlphaNumerics(field.trim())) { + logger.log(Level.WARNING, "User entered no alphanumeric characters as field!"); + throw new NoAlphaNumericInputException(); + } + } + + protected Command prepareFlashCard(String arguments) throws EmptyQnAException, NoAlphaNumericInputException { + String[] questionAndAnswer = new String[2]; + + if (arguments.isEmpty()) { + // Ask for both question and answer + Ui.printAddFlashCardLine(deck); + Ui.printEnterQuestionLine(); + questionAndAnswer[0] = Ui.readUserInput(); + logger.log(Level.INFO, "Reading user input for question"); + verifyStringField(questionAndAnswer[0]); + + Ui.printEnterAnswerLine(); + questionAndAnswer[1] = Ui.readUserInput(); + logger.log(Level.INFO, "Reading user input for answer"); + } else if (arguments.contains(" /ans ")) { + // Split by the first /ans only + questionAndAnswer = arguments.split("/ans", 2); + // Expect a qn and ans + if (questionAndAnswer.length != 2) { + throw new EmptyQnAException(); + } + verifyStringField(questionAndAnswer[0]); + } else if (arguments.trim().equals("/ans")) { + throw new EmptyQnAException(); + } else { + questionAndAnswer[0] = arguments; + // Valid question provided but not answer + Ui.printAddFlashCardLine(deck); + Ui.printEnterAnswerLine(); + questionAndAnswer[1] = Ui.readUserInput(); + logger.log(Level.INFO, "Reading user input for answer"); + } + verifyStringField(questionAndAnswer[1]); + + assert questionAndAnswer[0].length() > 0 : "question field empty!"; + assert questionAndAnswer[1].length() > 0 : "answer field empty!"; + assert !containsNoAlphaNumerics(questionAndAnswer[0]) : "question field nonsensical!"; + assert !containsNoAlphaNumerics(questionAndAnswer[1]) : "question field nonsensical!"; + + Ui.printDashLines(); + Ui.printFlashCardAddedLine(); + Ui.printDashLines(); + + logger.log(Level.INFO, "returning AddCommand object"); + return new AddCommand(deck, questionAndAnswer[0].trim(), questionAndAnswer[1].trim()); + } + + protected Command prepareUpdate(int flashCardID) throws EmptyQnAException, NoAlphaNumericInputException { + String[] newQnA = new String[2]; + Ui.printUpdateQuestionLine(deck.get(flashCardID)); + newQnA[0] = Ui.readUserInput(); + logger.log(Level.INFO, "Reading user input for question"); + boolean hasNewQ = true; + boolean hasNewA = true; + if (newQnA[0].trim().length() == 0) { + logger.log(Level.INFO, "User entered nothing or a series of blank spaces for question. " + + "Keep current question."); + newQnA[0] = deck.get(flashCardID).getQuestion(); + hasNewQ = false; + } + Ui.printUpdateAnswerLine(deck.get(flashCardID)); + newQnA[1] = Ui.readUserInput(); + logger.log(Level.INFO, "Reading user input for answer"); + if (newQnA[1].trim().length() == 0) { + logger.log(Level.INFO, "User entered nothing or a series of blank spaces for answer. " + + "Keep current answer."); + newQnA[1] = deck.get(flashCardID).getAnswer(); + hasNewA = false; + } + verifyStringField(newQnA[0]); + verifyStringField(newQnA[1]); + + assert newQnA[0].length() > 0 : "question field empty!"; + assert newQnA[1].length() > 0 : "answer field empty!"; + Ui.printDashLines(); + Ui.printFlashCardUpdatedLine(hasNewQ, hasNewA); + Ui.printDashLines(); + return new UpdateCommand(deck, flashCardID, newQnA[0], newQnA[1]); + } + + protected Command prepareListCommand(String arguments) throws InvalidListCommandException { + if (arguments.trim().equals("/ans") || arguments.trim().isBlank()) { + return new ListCommand(deck, arguments); + } + throw new InvalidListCommandException(); + } + + protected void checkForValidPptxCommand(String arguments) throws Exception { + String validArg = arguments; + + String dashOrEnd = ""; + if (validArg.contains(Ui.ORIGINAL_COLOR_OPT)) { + Pattern p1 = Pattern.compile(Ui.ORIGINAL_COLOR_REGEX); + Matcher m1 = p1.matcher(arguments); + if (m1.find()) { + dashOrEnd = m1.group(3); + } + validArg = validArg.replaceAll(Ui.ORIGINAL_COLOR_REGEX, dashOrEnd); + } + + if (validArg.contains(Ui.COLOR_SCHEME_OPT)) { + Pattern p2 = Pattern.compile(Ui.COLOR_SCHEME_REGEX); + Matcher m2 = p2.matcher(arguments); + if (m2.find()) { + dashOrEnd = m2.group(2); + } + validArg = validArg.replaceAll(Ui.COLOR_SCHEME_REGEX, dashOrEnd); + } + + if (validArg.contains(Ui.FORCE_YES_OPT)) { + validArg = validArg.replaceAll(Ui.FORCE_YES_OPT, ""); + } + + Pattern p = Pattern.compile("-\\w+"); + Matcher m = p.matcher(validArg); + if (m.find()) { + throw new InvalidOptionsException(); + } + + System.out.println(validArg); + + if (!validArg.isBlank()) { + throw new InvalidPptxArgumentException(); + } + } + + @Override + protected int getIndex(String arguments) throws IndexFormatException, + FlashCardRangeException, NumberTooBigException { + + arguments = arguments.trim(); + + if (!arguments.matches(Ui.DIGITS_REGEX)) { + logger.log(Level.WARNING, "User did not enter valid integer"); + throw new IndexFormatException(); + } + + assert arguments.length() > 0 : "arguments empty!"; + + int index; + try { + index = Integer.parseInt(arguments) - INDEX_OFFSET; + } catch (NumberFormatException e) { + throw new NumberTooBigException(); + } + + if (index >= deck.size() || index < LOWEST_POSSIBLE_INDEX) { + logger.log(Level.WARNING, "Flash card index larger than total number of cards"); + throw new FlashCardRangeException(); + } + return index; + } + + @Override + protected Command parseCommand(String commandWord, String arguments) + throws Exception { + + switch (commandWord) { + // Version + case Ui.VERSION_CMD: + return new VersionCommand(); + // Exit + case Ui.EXIT: + logger.log(Level.INFO, "returning ExitCommand object"); + return new ExitCommand(); + // Done with Edit Mode + case Ui.DONE: + logger.log(Level.INFO, "returning DoneEditCommand object"); + return new DoneEditCommand(deck); + // Help + case Ui.HELP: + logger.log(Level.INFO, "returning HelpCommand object"); + return new HelpCommand(); + // Start + case Ui.START: + logger.log(Level.INFO, "Starting Game Mode"); + return new NormalParser(Main.deckList).parseCommand(commandWord, + String.valueOf(deckList.getIndexOf(deck) + 1)); + // Add a FlashCard + case Ui.ADD: + logger.log(Level.INFO, "Preparing FlashCard to add"); + return prepareFlashCard(arguments); + // List all FlashCards + case Ui.LIST: + logger.log(Level.INFO, "returning ListCommand object"); + return prepareListCommand(arguments); + // Delete a FlashCard + case Ui.DELETE: + logger.log(Level.INFO, "Preparing FlashCard to delete"); + return prepareDeletedFlashCard(arguments); + // Update a FlashCard + case Ui.UPDATE: + logger.log(Level.INFO, "Preparing FlashCard to update"); + int flashCardID = getIndex(arguments); + assert flashCardID >= LOWEST_POSSIBLE_INDEX : "flash card ID less than lowest possible flash card index!"; + return prepareUpdate(flashCardID); + // Create PowerPoint + case Ui.PPTX: + logger.log(Level.INFO, "Printing to PowerPoint"); + checkForValidPptxCommand(arguments); + return new NormalParser(Main.deckList).parseCommand(commandWord, + (deckList.getIndexOf(deck) + 1) + " " + arguments); + default: + logger.log(Level.WARNING, "returning VoidCommand object"); + return new VoidCommand(); + } + } + + @Override + public Command parse(String userInput) { + logger.log(Level.INFO, "Parsing user input string"); + String[] splitString = userInput.split(" ", 2); + String commandWord = splitString[0]; + logger.log(Level.INFO, "Parsed commandWord"); + String arguments = ""; + boolean argumentsExist = splitString.length > 1; + if (argumentsExist) { + arguments = splitString[1]; + logger.log(Level.INFO, "Parsed arguments"); + } + try { + logger.log(Level.INFO, "Parsing command"); + return parseCommand(commandWord, arguments); + } catch (Exception e) { + logger.log(Level.WARNING, "Parsed void or invalid command"); + return new VoidCommand(e.getMessage()); + } + } +} diff --git a/src/main/java/seedu/ecardnomics/parser/GameParser.java b/src/main/java/seedu/ecardnomics/parser/GameParser.java new file mode 100644 index 0000000000..6bf4b98294 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/parser/GameParser.java @@ -0,0 +1,89 @@ +package seedu.ecardnomics.parser; + +import seedu.ecardnomics.Ui; +import seedu.ecardnomics.command.Command; +import seedu.ecardnomics.command.ExitCommand; +import seedu.ecardnomics.command.VersionCommand; +import seedu.ecardnomics.command.VoidCommand; +import seedu.ecardnomics.command.game.HelpCommand; +import seedu.ecardnomics.command.game.DoneGameCommand; +import seedu.ecardnomics.command.game.GameResponseCommand; +import seedu.ecardnomics.deck.Deck; +import seedu.ecardnomics.exceptions.FlashCardRangeException; +import seedu.ecardnomics.exceptions.IndexFormatException; +import seedu.ecardnomics.exceptions.NumberTooBigException; +import seedu.ecardnomics.storage.LogStorage; + +import java.util.logging.Level; + +public class GameParser extends Parser { + public Deck deck; + private static LogStorage logger = new LogStorage("GameParserLogger"); + + /** + * Constructor. + */ + public GameParser(Deck deck) { + this.deck = deck; + } + + @Override + protected int getIndex(String arguments) throws IndexFormatException, + NumberTooBigException, FlashCardRangeException { + arguments = arguments.trim(); + + if (!arguments.matches(Ui.DIGITS_REGEX)) { + logger.log(Level.WARNING, "User did not enter valid integer"); + throw new IndexFormatException(); + } + + assert arguments.length() > 0 : "arguments empty!"; + + int index; + try { + index = Integer.parseInt(arguments) - INDEX_OFFSET; + } catch (NumberFormatException e) { + throw new NumberTooBigException(); + } + + if (index >= deck.size() || index < LOWEST_POSSIBLE_INDEX) { + logger.log(Level.WARNING, "Flash card index larger than total number of cards"); + throw new FlashCardRangeException(); + } + return index; + } + + @Override + protected Command parseCommand(String commandWord, String arguments) { + + switch (commandWord) { + // Version + case Ui.VERSION_CMD: + return new VersionCommand(); + // Exit + case Ui.EXIT: + logger.log(Level.INFO, "returning ExitCommand object"); + return new ExitCommand(); + // Done with Game Mode + case Ui.DONE: + logger.log(Level.INFO, "returning DoneGameCommand object"); + return new DoneGameCommand(deck); + // Help + case Ui.HELP: + logger.log(Level.INFO, "returning HelpCommand object"); + return new HelpCommand(); + default: + return new VoidCommand(); + } + } + + @Override + public Command parse(String userInput) { + Command command = parseCommand(userInput.trim(), ""); + if (command instanceof VoidCommand) { + return new GameResponseCommand(userInput.trim()); + } + + return command; + } +} diff --git a/src/main/java/seedu/ecardnomics/parser/NormalParser.java b/src/main/java/seedu/ecardnomics/parser/NormalParser.java new file mode 100644 index 0000000000..0453443bb5 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/parser/NormalParser.java @@ -0,0 +1,546 @@ +package seedu.ecardnomics.parser; + +import seedu.ecardnomics.Ui; +import seedu.ecardnomics.command.Command; +import seedu.ecardnomics.command.ExitCommand; +import seedu.ecardnomics.command.VersionCommand; +import seedu.ecardnomics.command.VoidCommand; +import seedu.ecardnomics.command.normal.CreateCommand; +import seedu.ecardnomics.command.normal.DecksCommand; +import seedu.ecardnomics.command.normal.DeleteDeckCommand; +import seedu.ecardnomics.command.normal.EditCommand; +import seedu.ecardnomics.command.normal.HelpCommand; +import seedu.ecardnomics.command.normal.PowerPointCommand; +import seedu.ecardnomics.command.normal.StartCommand; +import seedu.ecardnomics.command.normal.TagCommand; +import seedu.ecardnomics.command.normal.UntagCommand; +import seedu.ecardnomics.command.normal.SearchCommand; +import seedu.ecardnomics.deck.Deck; +import seedu.ecardnomics.deck.DeckList; +import seedu.ecardnomics.exceptions.BothOcAndCsException; +import seedu.ecardnomics.exceptions.ColorsNotAvailException; +import seedu.ecardnomics.exceptions.CsIndexFormatException; +import seedu.ecardnomics.exceptions.CsIndexRangeException; +import seedu.ecardnomics.exceptions.DeckRangeException; +import seedu.ecardnomics.exceptions.EmptyInputException; +import seedu.ecardnomics.exceptions.IndexFormatException; +import seedu.ecardnomics.exceptions.InvalidOptionsException; +import seedu.ecardnomics.exceptions.NoSeparatorException; +import seedu.ecardnomics.exceptions.NumberTooBigException; +import seedu.ecardnomics.powerpoint.ColorOption; +import seedu.ecardnomics.exceptions.DuplicateDeckException; +import seedu.ecardnomics.storage.LogStorage; + +import java.util.ArrayList; +import java.util.logging.Level; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import java.awt.Color; +import org.beryx.awt.color.ColorFactory; + +import static seedu.ecardnomics.Ui.FORCE_YES_OPT; + +/** + * Parser for commands supplied in Normal Mode. + */ +public class NormalParser extends Parser { + DeckList deckList; + private static LogStorage logger = new LogStorage("NormalParserLogger"); + public static final int HIGHEST_CS_INDEX = 9; + + /** Constructor. */ + public NormalParser(DeckList deckList) { + this.deckList = deckList; + } + + /** + * Check whether the index input from users is valid. + * + * @param arguments Argument from user input which is index + * @return the index from user if it is valid + * @throws IndexFormatException if index input is not a number + * @throws DeckRangeException if index is out of range + * @throws NumberTooBigException if the index is too big + */ + @Override + protected int getIndex(String arguments) throws IndexFormatException, + DeckRangeException, NumberTooBigException { + + arguments = arguments.trim(); + + logger.log(Level.INFO, "Logging method getIndex() in NormalParser."); + + if (!arguments.matches(Ui.DIGITS_REGEX)) { + logger.log(Level.WARNING, "User did not enter a valid integer index. string = " + arguments); + throw new IndexFormatException(); + } + + int index; + try { + index = Integer.parseInt(arguments) - INDEX_OFFSET; + } catch (NumberFormatException e) { + throw new NumberTooBigException(); + } + + if ((index >= deckList.size()) || (index < LOWEST_POSSIBLE_INDEX)) { + logger.log(Level.WARNING, "User did not enter an index in the valid range."); + throw new DeckRangeException(); + } + + return index; + } + + /** + * Retrieves deck at index specified in arguments from deckList. + * getIndex() is used to convert arguments from String to an int index. + * + * @param arguments String that contains the ID number of the deck requested + * @return Reference to requested deck + * @throws IndexFormatException if arguments is not a digit + * @throws DeckRangeException if index obtained from arguments is not in range + */ + private Deck prepareDeck(String arguments) throws Exception { + return deckList.getDeck(getIndex(arguments)); + } + + /** + * Prepares an instance of DeleteDeckCommand from given arguments. + * + * @param arguments String that contains the user arguments for the delete command + * @return DeleteDeckCommand that can be executed to delete the deck. + * @throws Exception if index is invalid or no index is supplied. + */ + private Command prepareDeleteDeck(String arguments) throws Exception { + boolean isDeckDeleted; + int deckID; + if (arguments.contains(FORCE_YES_OPT)) { + arguments = arguments.replaceAll(FORCE_YES_OPT, ""); + deckID = getIndex(arguments); + isDeckDeleted = true; + } else { + deckID = getIndex(arguments); + isDeckDeleted = Ui.getDeletedDeckConfirmation(deckList.getDeck(deckID).getName()); + } + + if (isDeckDeleted) { + Ui.printDeckDeletedLine(deckList.getDeck(deckID).getName()); + } + + Ui.printDashLines(); + + return new DeleteDeckCommand(deckList, deckID, isDeckDeleted); + } + + /** + * Prepares new Tag Command from given arguments. + * + * @param arguments arguments input from user + * @return a Tag Command + * @throws Exception if index is invalid or empty arguments + */ + private Command prepareTagCommand(String arguments) throws Exception { + String argumentsWithSpace = arguments + " "; + String[] idAndNewTags = argumentsWithSpace.split("/tag", 2); + + if (idAndNewTags.length < 2) { + logger.log(Level.WARNING, "User did not provide /tag when adding tag."); + throw new NoSeparatorException(); + } + assert (arguments.contains("/tag")) : + "Tags to be added are after /tag label."; + int deckID = getIndex(idAndNewTags[0]); + + if (idAndNewTags[1].trim().isEmpty()) { + logger.log(Level.WARNING, "User did not supply tags when adding tag."); + throw new EmptyInputException(); + } + + String[] newTags = idAndNewTags[1].trim().split(" "); + + return new TagCommand(deckList, deckID, getUniqueValues(newTags)); + } + + /** + * Prepares new Tag Command from given arguments. + * + * @param arguments arguments input from user + * @return a Tag Command + * @throws Exception if index is invalid or empty arguments + */ + private Command prepareUntagCommand(String arguments) throws Exception { + String argumentsWithSpace = arguments + " "; + String[] idAndRemovedTags = argumentsWithSpace.split("/tag ", 2); + boolean isYes = false; + if (idAndRemovedTags.length < 2) { + logger.log(Level.WARNING, "User did not provide /tag when removing tags."); + throw new NoSeparatorException(); + } + assert (arguments.contains("/tag")) : + "tags to be removed are after /tag label"; + + if (idAndRemovedTags[1].trim().isEmpty()) { + logger.log(Level.WARNING, "User did not supply tags when removing tags."); + throw new EmptyInputException(); + } + String[] removedTags = idAndRemovedTags[1].trim().split(" "); + ArrayList uniqueTags = getUniqueValues(removedTags); + if (uniqueTags.get(uniqueTags.size() - 1).equals(FORCE_YES_OPT)) { + isYes = true; + uniqueTags.remove(uniqueTags.size() - 1); + } + + if (uniqueTags.isEmpty()) { + logger.log(Level.WARNING, "User did not supply tags when removing tags."); + throw new EmptyInputException(); + } + + int deckID = getIndex(idAndRemovedTags[0]); + return new UntagCommand(deckList, deckID, uniqueTags, isYes); + } + + /** + * Returns an array list of unique values. + * + * @param tags a String array of tags + * @return a unique array list + */ + private ArrayList getUniqueValues(String[] tags) { + ArrayList uniqueTags = new ArrayList<>(); + for (String tag: tags) { + String trimmedTag = tag.trim(); + if (!uniqueTags.contains(trimmedTag) & !trimmedTag.isEmpty()) { + uniqueTags.add(tag.trim()); + } + } + return uniqueTags; + } + + /** + * Creates a new deck for adding to deckList. + * + * @param arguments String that represents the name of deck to be created + * @return Reference to the deck created + * @throws EmptyInputException if no name is supplied for the deck + * @throws DuplicateDeckException if duplicated name is entered + */ + private Deck prepareNewDeck(String arguments) throws Exception { + logger.log(Level.INFO, "Logging method prepareNewDeck() in NormalParser."); + if (arguments.trim().isEmpty()) { + logger.log(Level.WARNING, "User did not supply name when creating a new deck."); + throw new EmptyInputException(); + } + + if (deckList.getAllNames().contains(arguments.trim())) { + throw new DuplicateDeckException(); + } + + String argumentsWithSpace = arguments + " "; + if (argumentsWithSpace.contains("/tag ")) { + return prepareNewDeckWithTags(argumentsWithSpace); + } else { + return new Deck(arguments); + } + } + + /** + * Prepare a CreateCommand with tag argument. + * + * @param arguments input from user after command word + * @return a create command + * @throws Exception if input is empty or if duplicate name presents + */ + private Deck prepareNewDeckWithTags(String arguments) throws Exception { + assert (arguments.contains("/tag ")) : "User did enter tag label."; + String[] nameAndTags = arguments.split("/tag ", 2); + + if (nameAndTags.length != 2) { + throw new EmptyInputException(); + } + + assert (nameAndTags.length == 2) : "Input should contain name and tags."; + + String name = nameAndTags[0].trim(); + if (name.isEmpty()) { + throw new EmptyInputException(); + } + if (deckList.getAllNames().contains(name)) { + throw new DuplicateDeckException(); + } + if (nameAndTags[1].trim().isEmpty()) { + throw new EmptyInputException(); + } + + String[] tags = nameAndTags[1].trim().split(" "); + ArrayList tagsList = getUniqueValues(tags); + return new Deck(name, tagsList); + } + + /** + * Gets the index for the -cs option in pptx command. + * @param arguments String representing the user input for the index + * @return int representing the index for the the color scheme chosen + * @throws CsIndexFormatException when format of index is not an integer + * @throws CsIndexRangeException when the index integer is not within the range [1,10], number of cs available + */ + protected int getCsIndex(String arguments) throws CsIndexFormatException, + CsIndexRangeException { + + arguments = arguments.trim(); + + logger.log(Level.INFO, "Logging method getIndex() in NormalParser."); + + if (!arguments.matches(Ui.DIGITS_REGEX)) { + logger.log(Level.WARNING, "User did not enter a valid integer index. string = " + arguments); + throw new CsIndexFormatException(); + } + + int index; + try { + index = Integer.parseInt(arguments) - INDEX_OFFSET; + } catch (NumberFormatException e) { + throw new CsIndexFormatException(); + } + + if (index > HIGHEST_CS_INDEX || index < LOWEST_POSSIBLE_INDEX) { + logger.log(Level.WARNING, "User did not enter an index in the valid range for -cs."); + throw new CsIndexRangeException(); + } + + return index; + } + + /** + * Prepares the PowerPoint command when the user input is pptx. + * @param arguments String representing the arguments of user input + * @return PowerPointCommand to be executed in the Main + * @throws Exception when arguments (index and options) are not valid + */ + protected PowerPointCommand preparePptxCommand(String arguments) throws Exception { + Color bgColor = null; + Color txtColor = null; + String bgColorString = ""; + String txtColorString = ""; + ColorOption colorOpt = ColorOption.DEFAULT; + int colorSchemeIndex = 0; + + boolean isPptxCreated = false; + boolean bothOCandCS = false; + boolean colorInvalid = false; + boolean colorSchemeInvalid = false; + + Exception csException = null; + + if (arguments.contains(FORCE_YES_OPT)) { + arguments = arguments.replaceAll(FORCE_YES_OPT, "").trim(); + isPptxCreated = true; + } + + if (arguments.contains(Ui.ORIGINAL_COLOR_OPT) && arguments.contains(Ui.COLOR_SCHEME_OPT)) { + bothOCandCS = true; + } + + if (arguments.contains(Ui.ORIGINAL_COLOR_OPT)) { + String dashOrEnd = ""; + + Pattern pattern = Pattern.compile(Ui.ORIGINAL_COLOR_REGEX); + Matcher matcher = pattern.matcher(arguments); + + + if (matcher.find()) { + bgColorString = matcher.group(1); + txtColorString = matcher.group(2); + dashOrEnd = matcher.group(3); + + try { + bgColor = ColorFactory.valueOf(bgColorString); + txtColor = ColorFactory.valueOf(txtColorString); + + } catch (IllegalArgumentException e) { + colorInvalid = true; + } + } + + colorOpt = ColorOption.ORGINAL_COLOR; + arguments = arguments.replaceAll(Ui.ORIGINAL_COLOR_REGEX, dashOrEnd).trim(); + } + + if (arguments.contains(Ui.COLOR_SCHEME_OPT)) { + String dashOrEnd = ""; + Pattern pattern = Pattern.compile(Ui.COLOR_SCHEME_REGEX); + Matcher matcher = pattern.matcher(arguments); + if (matcher.find()) { + String numArg = matcher.group(1); + dashOrEnd = matcher.group(2); + + + try { + colorSchemeIndex = getCsIndex(numArg); + } catch (Exception e) { + csException = e; + colorSchemeInvalid = true; + } + + colorOpt = ColorOption.COLOR_SCHEME; + arguments = arguments.replaceAll(Ui.COLOR_SCHEME_REGEX, dashOrEnd).trim(); + } + } + + if (arguments.contains("-")) { + throw new InvalidOptionsException(); + } + + if (bothOCandCS) { + throw new BothOcAndCsException(); + } + + if (colorInvalid) { + throw new ColorsNotAvailException(); + } + + if (colorSchemeInvalid) { + throw csException; + } + + int deckID = getIndex(arguments); + Deck deck = deckList.getDeck(deckID); + + if (!isPptxCreated) { + isPptxCreated = Ui.getPptxDeckConfirmation(deck.getName()); + } + + if (colorOpt == ColorOption.ORGINAL_COLOR) { + return new PowerPointCommand(deckList, deck, isPptxCreated, bgColorString, txtColorString, + bgColor, txtColor); + } + + if (colorOpt == ColorOption.COLOR_SCHEME) { + return new PowerPointCommand(deckList, deck, isPptxCreated, colorSchemeIndex); + } + + return new PowerPointCommand(deckList, deck, isPptxCreated); + } + + /** + * Gets input from users and prepares a search command. + * + * @param arguments input from users after command word + * @return a search command + * @throws EmptyInputException if input is empty + */ + private Command prepareSearchCommand(String arguments) throws EmptyInputException { + logger.log(Level.INFO, "Logging method prepareSearchCommand() in NormalParser."); + + if (arguments.trim().isEmpty()) { + logger.log(Level.WARNING, "User did not supply tags when searching for decks."); + throw new EmptyInputException(); + } + + String[] relevantTags = arguments.trim().split(" "); + return new SearchCommand(deckList, relevantTags); + } + + /** + * Prepare Command for execution in Main. + * + * @param commandWord String that corresponds to a command + * @param arguments String that lists the arguments for the command + * @return respective Command object + * @throws Exception when something wrong with the argument + */ + @Override + protected Command parseCommand(String commandWord, String arguments) + throws Exception { + + assert (commandWord != null && arguments != null) : + "commandWord and arguments should not be null"; + + logger.log(Level.INFO, "Logging method parseCommand() in NormalParser."); + + switch (commandWord) { + // Version + case Ui.VERSION_CMD: + return new VersionCommand(); + // Exit + case Ui.EXIT: + logger.log(Level.INFO, "User issued command to terminate program."); + return new ExitCommand(); + // Help + case Ui.HELP: + logger.log(Level.INFO, "User issued command to view help."); + return new HelpCommand(); + // Edit + case Ui.EDIT: + Deck deck = prepareDeck(arguments); + logger.log(Level.INFO, "User issued command to edit deck " + deck.getName() + "."); + return new EditCommand(deckList, deck); + // Start + case Ui.START: + Deck startDeck = prepareDeck(arguments); + logger.log(Level.INFO, "User issued command to start deck " + startDeck.getName() + "."); + return new StartCommand(deckList, startDeck); + // Create + case Ui.CREATE: + Deck newDeck = prepareNewDeck(arguments); + logger.log(Level.INFO, "User issued command to create deck " + newDeck.getName() + "."); + return new CreateCommand(deckList, newDeck); + // Decks + case Ui.DECKS: + logger.log(Level.INFO, "User issued command to list decks."); + return new DecksCommand(deckList); + // Delete + case Ui.DELETE: + logger.log(Level.INFO, "User issued command to delete deck"); + return prepareDeleteDeck(arguments); + // Tag + case Ui.TAG: + logger.log(Level.INFO, "User issued command to tag a deck."); + return prepareTagCommand(arguments); + // Untag + case Ui.UNTAG: + logger.log(Level.INFO, "User issued command to untag a deck."); + return prepareUntagCommand(arguments); + // Create new PowerPoint + case Ui.PPTX: + logger.log(Level.INFO, "User issued command to create a PowerPoint."); + return preparePptxCommand(arguments); + // Search + case Ui.SEARCH: + logger.log(Level.INFO, "User issued command to search for decks."); + return prepareSearchCommand(arguments); + default: + logger.log(Level.INFO, "User issued an invalid command."); + return new VoidCommand(); + } + } + + /** + * Parses User Input from Main. + * + * @param userInput Input from user, passed through Main + * @return Command to be executed + */ + @Override + public Command parse(String userInput) { + logger.log(Level.INFO, "Logging method parse() in NormalParser."); + String[] splitString = userInput.split(" ", 2); + String commandWord = splitString[0]; + logger.log(Level.INFO, "Parsed commandWord"); + boolean argumentsExist = splitString.length > 1; + String arguments = ""; + + if (argumentsExist) { + arguments = splitString[1]; + logger.log(Level.INFO, "Parsed arguments"); + } + + try { + logger.log(Level.INFO, "Parsing command"); + return parseCommand(commandWord, arguments); + } catch (Exception e) { + logger.log(Level.WARNING, "Parsed void or invalid command"); + return new VoidCommand(e.getMessage()); + } + } + +} diff --git a/src/main/java/seedu/ecardnomics/parser/Parser.java b/src/main/java/seedu/ecardnomics/parser/Parser.java new file mode 100644 index 0000000000..95bbe3d289 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/parser/Parser.java @@ -0,0 +1,39 @@ +package seedu.ecardnomics.parser; + +import seedu.ecardnomics.command.Command; + +public abstract class Parser { + + public static final int LOWEST_POSSIBLE_INDEX = 0; + public static final int INDEX_OFFSET = 1; + + /** + * Checks whether argument from user is a number and whether the index given is a within the correct range of + * decks/ flash cards. + * + * @param arguments Argument from user input + * @return int representing the index from the argument given + */ + protected abstract int getIndex(String arguments) + throws Exception; + + /** + * Determines the Command subclass to return based on the commandWord + * and arguments provided. + * + * @param commandWord String that corresponds to a command + * @param arguments String that lists the arguments for the command + * @return Command representing the command to be executed + * @throws Exception if arguments are inappropriate + */ + protected abstract Command parseCommand(String commandWord, String arguments) + throws Exception; + + /** + * Parse user input to determine which Command to output. + * + * @param userInput Input from user + * @return Command representing command to be executed + */ + public abstract Command parse(String userInput); +} diff --git a/src/main/java/seedu/ecardnomics/powerpoint/ColorOption.java b/src/main/java/seedu/ecardnomics/powerpoint/ColorOption.java new file mode 100644 index 0000000000..100c41bb83 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/powerpoint/ColorOption.java @@ -0,0 +1,7 @@ +package seedu.ecardnomics.powerpoint; + +public enum ColorOption { + DEFAULT, + ORGINAL_COLOR, + COLOR_SCHEME +} diff --git a/src/main/java/seedu/ecardnomics/powerpoint/PowerPoint.java b/src/main/java/seedu/ecardnomics/powerpoint/PowerPoint.java new file mode 100644 index 0000000000..5d5c5fc642 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/powerpoint/PowerPoint.java @@ -0,0 +1,176 @@ +package seedu.ecardnomics.powerpoint; + +import org.apache.poi.xslf.usermodel.SlideLayout; +import org.apache.poi.xslf.usermodel.XMLSlideShow; +import org.apache.poi.xslf.usermodel.XSLFSlide; +import org.apache.poi.xslf.usermodel.XSLFSlideLayout; +import org.apache.poi.xslf.usermodel.XSLFSlideMaster; +import org.apache.poi.xslf.usermodel.XSLFTextParagraph; +import org.apache.poi.xslf.usermodel.XSLFTextRun; +import org.apache.poi.xslf.usermodel.XSLFTextShape; +import org.beryx.awt.color.ColorFactory; +import seedu.ecardnomics.Ui; +import seedu.ecardnomics.deck.Deck; +import seedu.ecardnomics.deck.FlashCard; + +import java.awt.Color; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; + +public class PowerPoint { + private Deck deck; + private XMLSlideShow pptx = new XMLSlideShow(); + private XSLFSlideMaster defaultMaster = pptx.getSlideMasters().get(0); + private String bgColorString; + private String txtColorString; + private Color bgColor; + private Color txtColor; + private ColorOption colorOpt; + + public static final String[][] COLOR_SCHEMES = { + {"steelblue", "silver"}, + {"goldenrod", "crimson"}, + {"navy", "turquoise"}, + {"black", "orange"}, + {"black", "deeppink"}, + {"grey", "pink"}, + {"pink", "navy"}, + {"lightyellow", "seagreen"}, + {"maroon", "peachpuff"}, + {"azure", "darkgreen"} + }; + + public static final String PPTX_DIR = "pptx/"; + public static final String PPTX_EXT = ".pptx"; + + /** Constructor for default printing. */ + public PowerPoint(Deck deck) { + this.deck = deck; + bgColorString = "white"; + txtColorString = "black"; + bgColor = ColorFactory.valueOf("white"); + txtColor = ColorFactory.valueOf("black"); + colorOpt = ColorOption.DEFAULT; + } + + /** Constructor for -cs option. */ + public PowerPoint(Deck deck, int colorScheme) { + this.deck = deck; + bgColorString = COLOR_SCHEMES[colorScheme][0]; + txtColorString = COLOR_SCHEMES[colorScheme][1]; + bgColor = ColorFactory.valueOf(COLOR_SCHEMES[colorScheme][0]); + txtColor = ColorFactory.valueOf(COLOR_SCHEMES[colorScheme][1]); + colorOpt = ColorOption.COLOR_SCHEME; + } + + /** Constructor for -oc option. */ + public PowerPoint(Deck deck, String bgColorString, String txtColorSting, + Color bgColor, Color txtColor) { + this.deck = deck; + this.bgColorString = bgColorString; + this.txtColorString = txtColorSting; + this.bgColor = bgColor; + this.txtColor = txtColor; + colorOpt = ColorOption.ORGINAL_COLOR; + } + + private void newIntroSlide() { + XSLFSlideLayout layout = defaultMaster.getLayout(SlideLayout.TITLE); + XSLFSlide slide = pptx.createSlide(layout); + + slide.getBackground().setFillColor(bgColor); + + XSLFTextShape title = slide.getPlaceholder(0); + XSLFTextShape subtitle = slide.getPlaceholder(1); + slide.removeShape(subtitle); + + title.clearText(); + + XSLFTextParagraph p = title.addNewTextParagraph(); + XSLFTextRun r = p.addNewTextRun(); + + r.setText(deck.getName()); + r.setFontColor(txtColor); + r.setFontSize(60.); + } + + private void newSlide(String titleText, String text) { + XSLFSlideLayout layout = defaultMaster.getLayout(SlideLayout.TITLE_AND_CONTENT); + XSLFSlide slide = pptx.createSlide(layout); + + XSLFTextShape title = slide.getPlaceholder(0); + XSLFTextShape object = slide.getPlaceholder(1); + + title.clearText(); + object.clearText(); + + XSLFTextParagraph p = title.addNewTextParagraph(); + XSLFTextRun r = p.addNewTextRun(); + + r.setText(titleText); + r.setFontColor(txtColor); + r.setFontSize(50.); + + p = object.addNewTextParagraph(); + r = p.addNewTextRun(); + + r.setText(text); + r.setFontColor(txtColor); + r.setFontSize(40.); + } + + private void createPowerPoint() { + newIntroSlide(); + + for (int i = 0; i < deck.size(); ++i) { + FlashCard card = deck.get(i); + newSlide(Ui.QUESTION + (i + 1), card.getQuestion()); + newSlide(Ui.ANSWER, card.getAnswer()); + } + } + + private void exportPowerPoint() { + String fileName = PPTX_DIR + deck.getName() + PPTX_EXT; + + File folder = new File(PPTX_DIR); + if (!folder.exists()) { + folder.mkdir(); + } + + assert (folder.exists()); + + File file = new File(fileName); + try { + file.createNewFile(); + } catch (Exception e) { + Ui.printMessage(Ui.CREATE_NEW_FILE_ERROR_LINE + fileName); + } + + assert (file.exists()); + + try { + FileOutputStream out = new FileOutputStream(fileName); + try { + pptx.write(out); + out.close(); + pptx.close(); + Ui.printDeckPptxLine(deck.getName(), bgColorString, txtColorString, colorOpt); + } catch (IOException e) { + Ui.printMessage(Ui.WRITE_TO_FILE_ERROR_LINE); + } + } catch (FileNotFoundException e) { + Ui.printMessage(fileName + Ui.FILE_NOT_FOUND_ERROR_LINE); + } + } + + /** + * Creates a new PowerPoint Slide based on the current deck + * and saves it as pptx/deckName.pptx + */ + public void createNewPowerPoint() { + createPowerPoint(); + exportPowerPoint(); + } +} diff --git a/src/main/java/seedu/ecardnomics/storage/LogStorage.java b/src/main/java/seedu/ecardnomics/storage/LogStorage.java new file mode 100644 index 0000000000..68d30dd30f --- /dev/null +++ b/src/main/java/seedu/ecardnomics/storage/LogStorage.java @@ -0,0 +1,64 @@ +package seedu.ecardnomics.storage; + +import seedu.ecardnomics.Ui; + +import java.io.File; +import java.io.IOException; +import java.util.logging.FileHandler; +import java.util.logging.Logger; +import java.util.logging.Level; +import java.util.logging.SimpleFormatter; + +/** + * Class for instantiating loggers that log to files instead of stdout. + */ +public class LogStorage { + private boolean willLog; + private String name; + private String filePath; + private Logger logger; + private FileHandler handler; + private static final String FOLDER_PATH = "./log/"; + private static final File DIRECTORY = new File(FOLDER_PATH); + + /** + * Constructor to initialize a logger for logging to file. + * + * @param name of class being logged by logger + */ + public LogStorage(String name) { + this.name = name; + willLog = true; + filePath = FOLDER_PATH + this.name + ".log"; + logger = Logger.getLogger(name); + logger.setUseParentHandlers(false); + + if (!DIRECTORY.exists()) { + DIRECTORY.mkdir(); + } + + try { + handler = new FileHandler(filePath); + logger.addHandler(handler); + handler.setFormatter(new SimpleFormatter()); + } catch (SecurityException se) { + Ui.printMessage("No permission to create file, log file will not be created."); + willLog = false; + } catch (IOException ioe) { + Ui.printMessage("Unable to open log file. Will not be logging."); + willLog = false; + } + } + + /** + * Create a log entry in the log file. + * + * @param level java.util.logging.Level object to indicate log level + * @param message to be logged + */ + public void log(Level level, String message) { + if (willLog) { + logger.log(level, message); + } + } +} diff --git a/src/main/java/seedu/ecardnomics/storage/Storage.java b/src/main/java/seedu/ecardnomics/storage/Storage.java new file mode 100644 index 0000000000..ab966f11a5 --- /dev/null +++ b/src/main/java/seedu/ecardnomics/storage/Storage.java @@ -0,0 +1,126 @@ +package seedu.ecardnomics.storage; + +import seedu.ecardnomics.deck.Deck; +import seedu.ecardnomics.deck.DeckList; +import seedu.ecardnomics.deck.FlashCard; +import seedu.ecardnomics.parser.DeckParser; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Scanner; + +public class Storage { + public static final String FILE_PATH = "./data/deckList.txt"; + public static final String FOLDER_PATH = "./data"; + public static final String DIVIDER = + "================================================================================"; + public static final String DECK_NAME_REGEX = "deck\\s\\|\\s.+"; + public static final String TAGS_REGEX = "tags\\s\\|.+"; + public static final String NO_TAGS_REGEX = "tags\\s\\|"; + public static final String QUESTION_REGEX = "Q\\s\\|\\s.+"; + public static final String EMPTY_QUESTION_REGEX = "Q\\s\\|\\s+"; + public static final String ANSWER_REGEX = "A\\s\\|\\s.+"; + public static final String EMPTY_ANSWER_REGEX = "A\\s\\|\\s+"; + public static final String DIVIDER_REGEX = "={10,}"; + + public DeckList load(DeckList deckList) { + File folder = new File(FOLDER_PATH); + if (!folder.exists()) { + folder.mkdir(); + } + + File file = new File(FILE_PATH); + Scanner scanner = null; + try { + scanner = new Scanner(file); + } catch (FileNotFoundException e) { + return deckList; + } + + String line; + + while (scanner.hasNext()) { + line = scanner.nextLine(); + String deckName = null; + Deck deck = null; + if (line.matches(DECK_NAME_REGEX)) { + deckName = line.substring(7); + deck = new Deck(deckName); + } else { + continue; + } + line = scanner.nextLine(); + if (line.matches(TAGS_REGEX)) { + line = line.substring(line.indexOf("|") + 2).trim(); + ArrayList arrayOfTags = new ArrayList<>(); + if (!line.isBlank()) { + String[] tags = line.split("\\|"); + for (String tag: tags) { + arrayOfTags.add(tag.trim()); + } + deck.addTag(arrayOfTags); + } + } else if (!line.matches(NO_TAGS_REGEX)) { + continue; + } + while (scanner.hasNext()) { + line = scanner.nextLine(); + String question = null; + if (line.matches(QUESTION_REGEX) && !line.matches(EMPTY_QUESTION_REGEX)) { + question = line.substring(4).trim(); + if (DeckParser.containsNoAlphaNumerics(question)) { + continue; + } + } else if (line.matches(DIVIDER_REGEX)) { + break; + } else { + continue; + } + line = scanner.nextLine(); + String answer = null; + if (line.matches(ANSWER_REGEX) && !line.matches(EMPTY_ANSWER_REGEX)) { + answer = line.substring(4).trim(); + if (DeckParser.containsNoAlphaNumerics(answer)) { + continue; + } + } else if (line.matches(DIVIDER_REGEX)) { + break; + } else { + continue; + } + FlashCard flashCard = new FlashCard(question, answer); + deck.add(flashCard); + } + if (!deckList.contains(deck)) { + deckList.addDeck(deck); + } + } + return deckList; + } + + public void write(DeckList deckList) throws IOException { + FileWriter fw = new FileWriter(FILE_PATH); + fw.write(DIVIDER); + fw.write(System.lineSeparator()); + + for (int i = 0; i < deckList.size(); i++) { + Deck deck = deckList.getDeck(i); + fw.write("deck | " + deck.getName()); + fw.write(System.lineSeparator()); + fw.write("tags | " + deck.getTagString()); + fw.write(System.lineSeparator()); + for (int j = 0; j < deck.size(); j++) { + fw.write("Q | " + deck.get(j).getQuestion()); + fw.write(System.lineSeparator()); + fw.write("A | " + deck.get(j).getAnswer()); + fw.write(System.lineSeparator()); + } + fw.write(DIVIDER); + fw.write(System.lineSeparator()); + } + fw.close(); + } +} diff --git a/src/test/java/seedu/duke/DukeTest.java b/src/test/java/seedu/ecardnomics/MainTest.java similarity index 79% rename from src/test/java/seedu/duke/DukeTest.java rename to src/test/java/seedu/ecardnomics/MainTest.java index 2dda5fd651..1c64425e71 100644 --- a/src/test/java/seedu/duke/DukeTest.java +++ b/src/test/java/seedu/ecardnomics/MainTest.java @@ -1,10 +1,10 @@ -package seedu.duke; +package seedu.ecardnomics; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; -class DukeTest { +class MainTest { @Test public void sampleTest() { assertTrue(true); diff --git a/src/test/java/seedu/ecardnomics/UiTest.java b/src/test/java/seedu/ecardnomics/UiTest.java new file mode 100644 index 0000000000..3aa2728ffa --- /dev/null +++ b/src/test/java/seedu/ecardnomics/UiTest.java @@ -0,0 +1,250 @@ +package seedu.ecardnomics; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import seedu.ecardnomics.deck.Deck; +import seedu.ecardnomics.deck.FlashCard; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.util.ArrayList; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static seedu.ecardnomics.Ui.printTagsRemovedLine; +import static seedu.ecardnomics.Ui.printNewDeck; +import static seedu.ecardnomics.Ui.printNewTags; +import static seedu.ecardnomics.Ui.printDeletedDeckQuestion; +import static seedu.ecardnomics.Ui.printDeckDeletedLine; +import static seedu.ecardnomics.Ui.printInvalidTagsLine; +import static seedu.ecardnomics.Ui.printRemovedTagsQuestion; +import static seedu.ecardnomics.Ui.printUpdateAnswerLine; +import static seedu.ecardnomics.Ui.printUpdateQuestionLine; +import static seedu.ecardnomics.Ui.printSearchDecksLine; +import static seedu.ecardnomics.Ui.formStringOfTags; + + + +public class UiTest { + private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); + private final ByteArrayOutputStream errContent = new ByteArrayOutputStream(); + private final PrintStream originalOut = System.out; + private final PrintStream originalErr = System.err; + + @BeforeEach + public void setUpStreams() { + System.setOut(new PrintStream(outContent)); + System.setErr(new PrintStream(errContent)); + } + + @AfterEach + public void restoreStreams() { + System.setOut(originalOut); + System.setErr(originalErr); + } + + @Test + public void printMessage_singleLine_goodFormat() { + String singleLine = "Hello! Welcome to eCardnomics!\t\t\tFlashCards Application."; + String message = Ui.DASH_LINES + System.lineSeparator() + singleLine + System.lineSeparator() + + Ui.DASH_LINES + System.lineSeparator(); + Ui.printMessage(singleLine); + assertEquals(message, outContent.toString()); + } + + @Test + void printNormalPrompt() { + } + + @Test + void printDeckPrompt() { + } + + @Test + void printPrompt() { + } + + @Test + void printNormalWelcome() { + } + + @Test + void printDeckWelcome() { + } + + @Test + void printGreeting() { + } + + @Test + void printExitLine() { + } + + @Test + void printNotRecognisedLine() { + } + + @Test + void printAddFlashCardLine() { + } + + @Test + void printEnterQuestionLine() { + } + + @Test + void printEnterAnswerLine() { + } + + @Test + void printFlashCardAddedLine() { + } + + @Test + void printDeck() { + } + + @Test + void printDeleteFlashCardLine() { + } + + @Test + void printFlashCardDeletedLine() { + } + + @Test + void printHelp() { + } + + @Test + void printNewDeck_newDeckName_deckName() { + + String expectedOutput = "------------------------------------------------------------" + + "--------------------" + System.lineSeparator() + + "New deck created: Pokemon" + System.lineSeparator() + "--------------------" + + "------------------------------------------------------------" + System.lineSeparator(); + printNewDeck(new Deck("Pokemon")); + assertEquals(expectedOutput, outContent.toString()); + } + + @Test + void printDeckList() { + } + + @Test + void printDeletedDeckQuestion_deletedDeckName_questionLine() { + String expectedOutput = "Do you want to delete Pokemon deck? [y/n] "; + Ui.printDeletedDeckQuestion("Pokemon"); + assertEquals(expectedOutput, outContent.toString()); + } + + @Test + void printDeletedDeck_deletedDeckName_confirmation() { + String expectedOutput = "Pokemon has been deleted." + System.lineSeparator(); + printDeckDeletedLine("Pokemon"); + assertEquals(expectedOutput, outContent.toString()); + } + + @Test + void printNewTags_none_newTagsLine() { + ArrayList newTagsArray = new ArrayList<>(); + newTagsArray.add("Anime"); + newTagsArray.add("Unreal"); + printNewTags("Pokemon", newTagsArray); + String expectedOutput = "------------------------------------------------------------" + + "--------------------" + System.lineSeparator() + + "The deck Pokemon has been tagged as: Anime | Unreal" + System.lineSeparator() + + "--------------------------------------------------------------------------------" + + System.lineSeparator(); + assertEquals(expectedOutput, outContent.toString()); + } + + @Test + void printInvalidTagsLine_none_warning() { + String expectedOutput = "------------------------------------------------------------" + + "--------------------" + System.lineSeparator() + + "Tag Cons is not in the deck already!" + System.lineSeparator() + "--------------------" + + "------------------------------------------------------------" + System.lineSeparator(); + printInvalidTagsLine("Cons"); + assertEquals(expectedOutput, outContent.toString()); + } + + @Test + void printRemovedTagsQuestion_StringArrayOfTagsAndDeckName_question() { + ArrayList removedTagsArray = new ArrayList<>(); + removedTagsArray.add("Beginner"); + removedTagsArray.add("Year2"); + printRemovedTagsQuestion("Micro-Economics", removedTagsArray); + String expectedOutput = "Do you want to remove the tag(s) Beginner | Year2 from Micro-Economics? [y/n] "; + assertEquals(expectedOutput, outContent.toString()); + } + + @Test + void printTagsRemovedLine_removedTagsAndDeckName_confirmation() { + ArrayList removedTagsArray = new ArrayList<>(); + removedTagsArray.add("Beginner"); + removedTagsArray.add("Year2"); + printTagsRemovedLine("Micro-Economics", removedTagsArray); + String expectedOutput = "The tag(s) Beginner | Year2 have been removed from the deck Micro-Economics." + + System.lineSeparator(); + assertEquals(expectedOutput, outContent.toString()); + } + + @Test + void formStringOfTags_StringArrayOfTags_aStringOfTags() { + ArrayList arrayStringOfTags = new ArrayList<>(); + arrayStringOfTags.add("Advanced"); + arrayStringOfTags.add("Year4"); + arrayStringOfTags.add("Finance"); + String expectedOutput = "Advanced | Year4 | Finance"; + + String actualOutput = formStringOfTags(arrayStringOfTags); + assertEquals(expectedOutput, actualOutput); + } + + @Test + void printUpdateQuestionLine_existingFlashCard_updateQuestionLine() { + String question = "Old question"; + String answer = "Old answer"; + FlashCard existingCard = new FlashCard(question, answer); + String expectedOutput = existingCard.toString(true, 0) + System.lineSeparator() + + "New Question: " + System.lineSeparator() + " > "; + printUpdateQuestionLine(existingCard); + assertEquals(expectedOutput, outContent.toString()); + } + + @Test + void printUpdateAnswerLine_existingFlashCard_updateAnswerLine() { + String question = "Old question"; + String answer = "Old answer"; + FlashCard existingCard = new FlashCard(question, answer); + String expectedOutput = System.lineSeparator() + existingCard.toString(false, 0) + + System.lineSeparator() + "New Answer: " + System.lineSeparator() + " > "; + printUpdateAnswerLine(existingCard); + assertEquals(expectedOutput, outContent.toString()); + } + + @Test + void printSearchDecksLine_emptyString_NoDecksLine() { + String expectedOutput = "------------------------------------------------------------" + + "--------------------" + System.lineSeparator() + + "There is no decks having the tags you are looking for." + + System.lineSeparator() + "--------------------" + + "------------------------------------------------------------" + System.lineSeparator(); + printSearchDecksLine(""); + assertEquals(expectedOutput, outContent.toString()); + } + + @Test + void printSearchDecksLine_stringOfMatchedDecks_decksListings() { + String expectedOutput = "------------------------------------------------------------" + + "--------------------" + System.lineSeparator() + + "The decks having tags you are searching for:" + + "\n1. Micro-Economics" + + "\n3. Object-oriented Programming" + System.lineSeparator() + "--------------------" + + "------------------------------------------------------------" + System.lineSeparator(); + String input = "\n1. Micro-Economics\n3. Object-oriented Programming"; + printSearchDecksLine(input); + assertEquals(expectedOutput, outContent.toString()); + } +} diff --git a/src/test/java/seedu/ecardnomics/command/deck/AddCommandTest.java b/src/test/java/seedu/ecardnomics/command/deck/AddCommandTest.java new file mode 100644 index 0000000000..b2c312b726 --- /dev/null +++ b/src/test/java/seedu/ecardnomics/command/deck/AddCommandTest.java @@ -0,0 +1,17 @@ +package seedu.ecardnomics.command.deck; + +import org.junit.jupiter.api.Test; +import seedu.ecardnomics.deck.Deck; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class AddCommandTest { + + @Test + void execute_addNewFlashCard() { + Deck deck = new Deck("Test"); + AddCommand cmd = new AddCommand(deck, "Test Question", "Test Amswer"); + cmd.execute(); + assertEquals(1, deck.size()); + } +} diff --git a/src/test/java/seedu/ecardnomics/command/deck/ListCommandTest.java b/src/test/java/seedu/ecardnomics/command/deck/ListCommandTest.java new file mode 100644 index 0000000000..296401ac4c --- /dev/null +++ b/src/test/java/seedu/ecardnomics/command/deck/ListCommandTest.java @@ -0,0 +1,79 @@ +package seedu.ecardnomics.command.deck; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import seedu.ecardnomics.deck.Deck; +import seedu.ecardnomics.deck.FlashCard; +import seedu.ecardnomics.storage.LogStorage; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class ListCommandTest { + private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); + private final ByteArrayOutputStream errContent = new ByteArrayOutputStream(); + private final PrintStream originalOut = System.out; + private final PrintStream originalErr = System.err; + + @BeforeEach + public void setUpStreams() { + System.setOut(new PrintStream(outContent)); + System.setErr(new PrintStream(errContent)); + } + + @AfterEach + public void restoreStreams() { + System.setOut(originalOut); + System.setErr(originalErr); + } + + @Test + void execute_emptyDeck_listCommand() { + String expectedOutput = + "------------------------------------------------------------" + + "--------------------" + System.lineSeparator() + + "Deck is currently empty!" + System.lineSeparator() + "--------------------" + + "------------------------------------------------------------" + System.lineSeparator(); + + Deck deck = new Deck("Test"); + ListCommand cmd = new ListCommand(deck, "/ans"); + cmd.execute(); + assertEquals(expectedOutput, outContent.toString()); + } + + @Test + void execute_nonEmptyDeck_ansArgument_listCommand() { + String expectedOutput = + "------------------------------------------------------------" + + "--------------------" + System.lineSeparator() + + "You are now viewing deck: Test" + System.lineSeparator() + + "1. Question: Test Question" + System.lineSeparator() + + " Answer: Test Answer" + System.lineSeparator() + "--------------------" + + "------------------------------------------------------------" + System.lineSeparator(); + + Deck deck = new Deck("Test"); + deck.add(new FlashCard("Test Question", "Test Answer")); + ListCommand cmd = new ListCommand(deck, "/ans"); + cmd.execute(); + assertEquals(expectedOutput, outContent.toString()); + } + + @Test + void execute_nonEmptyDeck_noAnsArgument_listCommand() { + String expectedOutput = + "------------------------------------------------------------" + + "--------------------" + System.lineSeparator() + + "You are now viewing deck: Test" + System.lineSeparator() + + "1. Question: Test Question" + System.lineSeparator() + "--------------------" + + "------------------------------------------------------------" + System.lineSeparator(); + + Deck deck = new Deck("Test"); + deck.add(new FlashCard("Test Question", "Test Answer")); + ListCommand cmd = new ListCommand(deck, ""); + cmd.execute(); + assertEquals(expectedOutput, outContent.toString()); + } +} diff --git a/src/test/java/seedu/ecardnomics/command/deck/UpdateCommandTest.java b/src/test/java/seedu/ecardnomics/command/deck/UpdateCommandTest.java new file mode 100644 index 0000000000..01021ae669 --- /dev/null +++ b/src/test/java/seedu/ecardnomics/command/deck/UpdateCommandTest.java @@ -0,0 +1,32 @@ +package seedu.ecardnomics.command.deck; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import seedu.ecardnomics.deck.Deck; +import seedu.ecardnomics.deck.FlashCard; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class UpdateCommandTest { + Deck currentDeck; + int flashCardID; + static final String OLD_Q = "Knock knock"; + static final String OLD_A = "No joke"; + + @Test + void execute_update() { + String newQ = "newQ"; + String newA = "newA"; + UpdateCommand cmd = new UpdateCommand(currentDeck, flashCardID, newQ, newA); + cmd.execute(); + assertEquals(newQ, currentDeck.get(flashCardID).getQuestion()); + assertEquals(newA, currentDeck.get(flashCardID).getAnswer()); + } + + @BeforeEach + void initializeDeck() { + currentDeck = new Deck("test deck"); + currentDeck.add(new FlashCard(OLD_Q, OLD_A)); + flashCardID = 0; + } +} \ No newline at end of file diff --git a/src/test/java/seedu/ecardnomics/command/normal/CreateCommandTest.java b/src/test/java/seedu/ecardnomics/command/normal/CreateCommandTest.java new file mode 100644 index 0000000000..c91393c0e6 --- /dev/null +++ b/src/test/java/seedu/ecardnomics/command/normal/CreateCommandTest.java @@ -0,0 +1,23 @@ +package seedu.ecardnomics.command.normal; + +import org.junit.jupiter.api.Test; +import seedu.ecardnomics.deck.Deck; +import seedu.ecardnomics.deck.DeckList; + +import java.util.ArrayList; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class CreateCommandTest { + + @Test + void execute_createNewDeck() { + DeckList deckList = new DeckList(); + assertEquals(0, deckList.size()); + CreateCommand cmd = new CreateCommand(deckList, new Deck("Test")); + cmd.execute(); + assertEquals(1, deckList.size()); + assertEquals("Test", deckList.getDeck(0).getName()); + } + +} \ No newline at end of file diff --git a/src/test/java/seedu/ecardnomics/command/normal/DeleteDeckCommandTest.java b/src/test/java/seedu/ecardnomics/command/normal/DeleteDeckCommandTest.java new file mode 100644 index 0000000000..dc58553906 --- /dev/null +++ b/src/test/java/seedu/ecardnomics/command/normal/DeleteDeckCommandTest.java @@ -0,0 +1,30 @@ +package seedu.ecardnomics.command.normal; + +import org.junit.jupiter.api.Test; +import seedu.ecardnomics.deck.Deck; +import seedu.ecardnomics.deck.DeckList; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class DeleteDeckCommandTest { + + @Test + void execute_confirmDeleteDeck() { + DeckList deckList = new DeckList(); + deckList.addDeck(new Deck("Test")); + assertEquals(1, deckList.size()); + DeleteDeckCommand cmd = new DeleteDeckCommand(deckList, 0, true); + cmd.execute(); + assertEquals(0, deckList.size()); + } + + @Test + void execute_notConfirmedDeleteDeck() { + DeckList deckList = new DeckList(); + deckList.addDeck(new Deck("Test")); + assertEquals(1, deckList.size()); + DeleteDeckCommand cmd = new DeleteDeckCommand(deckList, 0, false); + cmd.execute(); + assertEquals(1, deckList.size()); + } +} \ No newline at end of file diff --git a/src/test/java/seedu/ecardnomics/command/normal/TagCommandTest.java b/src/test/java/seedu/ecardnomics/command/normal/TagCommandTest.java new file mode 100644 index 0000000000..eed662a338 --- /dev/null +++ b/src/test/java/seedu/ecardnomics/command/normal/TagCommandTest.java @@ -0,0 +1,25 @@ +package seedu.ecardnomics.command.normal; + +import org.junit.jupiter.api.Test; +import seedu.ecardnomics.deck.Deck; +import seedu.ecardnomics.deck.DeckList; + +import java.util.ArrayList; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class TagCommandTest { + + @Test + void execute_addNewTagsToExistingDeck() { + DeckList deckList = new DeckList(); + Deck newDeck = new Deck("Pokemon"); + deckList.addDeck(newDeck); + ArrayList newTags = new ArrayList<>(); + newTags.add("anime"); + newTags.add("unreal"); + TagCommand cmd = new TagCommand(deckList, 0, newTags); + cmd.execute(); + assertEquals(2, deckList.getDeck(0).getTag().size()); + } +} diff --git a/src/test/java/seedu/ecardnomics/command/normal/UntagCommandTest.java b/src/test/java/seedu/ecardnomics/command/normal/UntagCommandTest.java new file mode 100644 index 0000000000..c7d700135a --- /dev/null +++ b/src/test/java/seedu/ecardnomics/command/normal/UntagCommandTest.java @@ -0,0 +1,27 @@ +package seedu.ecardnomics.command.normal; + +import org.junit.jupiter.api.Test; +import seedu.ecardnomics.deck.Deck; +import seedu.ecardnomics.deck.DeckList; + +import java.util.ArrayList; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class UntagCommandTest { + + @Test + void execute_removeTagsFromDeck() { + DeckList deckList = new DeckList(); + ArrayList tags = new ArrayList<>(); + tags.add("anime"); + tags.add("unreal"); + Deck newDeck = new Deck("Pokemon", tags); + deckList.addDeck(newDeck); + ArrayList removedTags = new ArrayList<>(); + removedTags.add("unreal"); + UntagCommand cmd = new UntagCommand(deckList, 0, removedTags, true); + cmd.execute(); + assertEquals(1, deckList.getDeck(0).getTag().size()); + } +} diff --git a/src/test/java/seedu/ecardnomics/deck/DeckListTest.java b/src/test/java/seedu/ecardnomics/deck/DeckListTest.java new file mode 100644 index 0000000000..3450d28afd --- /dev/null +++ b/src/test/java/seedu/ecardnomics/deck/DeckListTest.java @@ -0,0 +1,35 @@ +package seedu.ecardnomics.deck; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class DeckListTest { + @Test + void size() { + DeckList deckList = initialiseDeckList(2); + assertEquals(2, deckList.size()); + deckList.removeDeck(1); + assertEquals(1, deckList.size()); + deckList.removeDeck(0); + assertEquals(0, deckList.size()); + deckList.addDeck(new Deck("deck 1")); + assertEquals(1, deckList.size()); + } + + @Test + void testToString() { + DeckList deckList = initialiseDeckList(2); + String deckListString = "1. deck 1\n2. deck 2"; + assertEquals(deckListString, deckList.toString()); + } + + DeckList initialiseDeckList(int size) { + DeckList deckList = new DeckList(); + for (int i = 1; i <= size; i++) { + Deck deck = new Deck(String.format("deck %d", i)); + deckList.addDeck(deck); + } + return deckList; + } +} \ No newline at end of file diff --git a/src/test/java/seedu/ecardnomics/deck/DeckTest.java b/src/test/java/seedu/ecardnomics/deck/DeckTest.java new file mode 100644 index 0000000000..d8f3086c7a --- /dev/null +++ b/src/test/java/seedu/ecardnomics/deck/DeckTest.java @@ -0,0 +1,176 @@ +package seedu.ecardnomics.deck; + +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class DeckTest { + @Test + void getName() { + Deck deck = new Deck("Pokemon"); + assertEquals("Pokemon", deck.getName()); + } + + @Test + void setName() { + Deck deck = new Deck("Pokemon"); + deck.setName("Pokemon Go"); + assertEquals("Pokemon Go", deck.getName()); + } + + // @Test + // void get_emptyDeck_exceptionThrown() { + // Deck deck = new Deck("Pokemon"); + // try { + // assertEquals(0, deck.get(0)); + // fail(); + // } catch (Exception e) { + // NullPointerException npe = new NullPointerException(); + // assertEquals(npe.getMessage(), e.getMessage()); + // } + // } + + // @Test + // void get_invalidIndex_exceptionThrown() { + // Deck deck = initialiseDeck(2); + // try { + // assertEquals(0, deck.get(-1)); + // assertEquals(0, deck.get(2)); + // assertEquals(0, deck.get(3)); + // fail(); // test should not reach this line + // } catch (Exception e) { + // NullPointerException npe = new NullPointerException(); + // assertEquals(npe.getMessage(), e.getMessage()); + // } + // } + + @Test + void get_validIndex_success() { + Deck deck = new Deck("Pokemon"); + FlashCard q1 = new FlashCard("q 1", "a 1"); + FlashCard q2 = new FlashCard("q 2", "a 2"); + deck.add(q1); + deck.add(q2); + assertEquals(q1, deck.get(0)); + assertEquals(q2, deck.get(1)); + } + + @Test + void size() { + Deck deck = initialiseDeck(2, new ArrayList<>()); + assertEquals(2, deck.size()); + deck.delete(1); + assertEquals(1, deck.size()); + deck.delete(0); + assertEquals(0, deck.size()); + deck.add(new FlashCard("q 1", "a 1")); + assertEquals(1, deck.size()); + } + + @Test + void constructor_newDeckWithTags() { + ArrayList tags = new ArrayList<>(); + tags.add("anime"); + tags.add("unreal"); + Deck deck = initialiseDeck(2, tags); + assertEquals(2, deck.getTag().size()); + } + + @Test + void getTagString_StringOfTags() { + ArrayList tags = new ArrayList<>(); + tags.add("anime"); + tags.add("unreal"); + Deck deck = initialiseDeck(2, tags); + String expectedOutput = "anime | unreal"; + assertEquals(expectedOutput, deck.getTagString()); + } + + @Test + void addTag_newTags_void() { + ArrayList tags = new ArrayList<>(); + tags.add("anime"); + tags.add("unreal"); + Deck deck = initialiseDeck(2, tags); + ArrayList newTags = new ArrayList<>(); + newTags.add("for-kids"); + deck.addTag(newTags); + assertEquals(3, deck.getTag().size()); + } + + @Test + void removeTag_newTags_void() { + ArrayList tags = new ArrayList<>(); + tags.add("anime"); + tags.add("unreal"); + tags.add("for-kids"); + Deck deck = initialiseDeck(2, tags); + ArrayList removedTags = new ArrayList<>(); + removedTags.add("for-kids"); + deck.removeTag(removedTags); + assertEquals(2, deck.getTag().size()); + } + + // @Test + // void delete_emptyDeck_exceptionThrown() { + // Deck deck = initialiseDeck(0); + // try { + // deck.delete(0); + // fail(); // test should not reach this line + // } catch (Exception e) { + // NullPointerException npe = new NullPointerException(); + // assertEquals(npe.getMessage(), e.getMessage()); + // } + // } + + // @Test + // void delete_invalidIndex_exceptionThrown() { + // Deck deck = initialiseDeck(2); + // try { + // deck.delete(-1); + // deck.delete(2); + // deck.delete(3); + // fail(); // test should not reach this line + // } catch (Exception e) { + // NullPointerException npe = new NullPointerException(); + // assertEquals(npe.getMessage(), e.getMessage()); + // } + // } + + @Test + void delete_validIndex_success() { + Deck deck = initialiseDeck(2, new ArrayList<>()); + deck.delete(1); + assertEquals(1, deck.size()); + } + + @Test + void testToString_default_goodFormat() { + Deck deck = initialiseDeck(2, new ArrayList<>()); + String deckString = "Pokemon:" + System.lineSeparator() + "1. Question: q 1" + + System.lineSeparator() + " Answer: a 1\n\n2. Question: q 2" + + System.lineSeparator() + " Answer: a 2"; + assertEquals(deckString, deck.toString()); + } + + @Test + void testToString_withType_goodFormat() { + Deck deck = initialiseDeck(2, new ArrayList<>()); + String deckString = "1. Question: q 1\n\n" + "2. Question: q 2"; + String deckStringAns = "1. Question: q 1" + System.lineSeparator() + " Answer: a 1\n\n" + + "2. Question: q 2" + System.lineSeparator() + " Answer: a 2"; + assertEquals(deckString, deck.toString(true)); + assertEquals(deckStringAns, deck.toString(false)); + } + + Deck initialiseDeck(int size, ArrayList tags) { + Deck deck = new Deck("Pokemon", tags); + for (int i = 1; i <= size; i++) { + FlashCard flashCard = new FlashCard(String.format("q %d", i), String.format("a %d", i)); + deck.add(flashCard); + } + return deck; + } +} \ No newline at end of file diff --git a/src/test/java/seedu/ecardnomics/deck/FlashCardTest.java b/src/test/java/seedu/ecardnomics/deck/FlashCardTest.java new file mode 100644 index 0000000000..f7595c636a --- /dev/null +++ b/src/test/java/seedu/ecardnomics/deck/FlashCardTest.java @@ -0,0 +1,108 @@ +package seedu.ecardnomics.deck; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class FlashCardTest { + static FlashCard flashCard; + static FlashCard longFlashCard; + static FlashCard longWordFlashCard; + static final String LABEL_PAD = " ".repeat("Question: ".length()); + + @Test + void testToString_default_goodFormat() { + String flashCardString = "Question: Who's that Pokemon?" + System.lineSeparator() + + "Answer: It's Pikachu!"; + assertEquals(flashCardString, flashCard.toString()); + } + + @Test + void testToString_default_wrappedLine() { + String flashCardString = "Question: " + + "Ok, long question let's goooooooo! Ahhhhhhhhhhhhh. Make this question " + System.lineSeparator() + + LABEL_PAD + "veryyyyyy longggg!" + System.lineSeparator() + + "Answer: " + "Ahhhhhhhhhhhhhhhhhhhhhhhh!!!!!!!!!!!!!! Get an even longer answer. How " + + System.lineSeparator() + LABEL_PAD + + "many lines will this answer span? I do not know. Maybe we can get it " + + System.lineSeparator() + LABEL_PAD + + "to three lines? Perhaps. Anyway span reminds me of linear algebra."; + assertEquals(flashCardString, longFlashCard.toString()); + } + + @Test + void testToString_default_wrapLongWord() { + String flashCardString = "Question: Do you have Pneumonoultramicroscopicsilicovolcanoconiosis?" + + System.lineSeparator() + "Answer: Try to enter the loop so create an answer with " + + "more than 2 lines: Aaaa" + System.lineSeparator() + LABEL_PAD + + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahhhhhhhhhhhhhhhhhhh" + + System.lineSeparator() + LABEL_PAD + "hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh" + + "hhhhhhhhhhhhh" + System.lineSeparator() + LABEL_PAD + + "hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhSaveMeeeeeeeeeeeeeee" + + System.lineSeparator() + LABEL_PAD + "e"; + assertEquals(flashCardString, longWordFlashCard.toString()); + } + + @Test + void testToString_withType_goodFormat() { + String flashCardQn = "Question: Who's that Pokemon?"; + String flashCardAns = "Answer: It's Pikachu!"; + assertEquals(flashCardQn, flashCard.toString(true, 0)); + assertEquals(flashCardAns, flashCard.toString(false, 0)); + } + + @Test + void testToString_withType_wrappedLine() { + String flashCardQn = "Question: " + + "Ok, long question let's goooooooo! Ahhhhhhhhhhhhh. Make this question " + System.lineSeparator() + + LABEL_PAD + "veryyyyyy longggg!"; + String flashCardAns = "Answer: " + + "Ahhhhhhhhhhhhhhhhhhhhhhhh!!!!!!!!!!!!!! Get an even longer answer. How " + System.lineSeparator() + + LABEL_PAD + "many lines will this answer span? I do not know. Maybe we can get it " + + System.lineSeparator() + + LABEL_PAD + "to three lines? Perhaps. Anyway span reminds me of linear algebra."; + assertEquals(flashCardQn, longFlashCard.toString(true, 0)); + assertEquals(flashCardAns, longFlashCard.toString(false, 0)); + } + + @Test + void testToString_withType_wrapLongWord() { + String largeSerialNumber = Integer.MAX_VALUE + ". "; + String indexPad = " ".repeat(largeSerialNumber.length()); + String flashCardQn = largeSerialNumber + "Question: Do you have " + + "Pneumonoultramicroscopicsilicovolcanoconiosis?"; + String flashCardAns = indexPad + "Answer: Try to enter the loop so create an answer with more than 2 " + + System.lineSeparator() + indexPad + LABEL_PAD + "lines: Aaaaaaa" + + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + + System.lineSeparator() + indexPad + LABEL_PAD + "aaaahhhhhhhhhhhhhhhhhhhhhhhhhhhhh" + + "hhhhhhhhhhhhhhhhhhhhhhhhh" + + System.lineSeparator() + indexPad + LABEL_PAD + "hhhhhhhhhhhhhhhhhhhhhhhhhh" + + "hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh" + + System.lineSeparator() + indexPad + LABEL_PAD + "hhhhhhhhhhhhhhhhhhhhhhhhhhhSav" + + "eMeeeeeeeeeeeeeeee"; + assertEquals(flashCardQn, largeSerialNumber + + longWordFlashCard.toString(true, largeSerialNumber.length())); + assertEquals(flashCardAns, longWordFlashCard.toString(false, largeSerialNumber.length())); + } + + @BeforeAll + public static void createFlashCard() { + flashCard = new FlashCard("Who's that Pokemon?", "It's Pikachu!"); + + String longQuestion = "Ok, long question let's goooooooo! Ahhhhhhhhhhhhh." + + " Make this question veryyyyyy longggg!"; + String longAnswer = "Ahhhhhhhhhhhhhhhhhhhhhhhh!!!!!!!!!!!!!! " + + "Get an even longer answer. How many lines will this answer span? " + + "I do not know. Maybe we can get it to three lines? Perhaps. " + + "Anyway span reminds me of linear algebra."; + longFlashCard = new FlashCard(longQuestion, longAnswer); + + String longWordQn = "Do you have Pneumonoultramicroscopicsilicovolcanoconiosis?"; + String longWordAns = "Try to enter the loop so create an answer with more than 2 lines: " + + "Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaahhhhhhhhhhhhhhhhhhhhhhhhhh" + + "hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh" + + "hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhSaveMeeeeeeeeeeeeeeee"; + longWordFlashCard = new FlashCard(longWordQn, longWordAns); + } +} \ No newline at end of file diff --git a/src/test/java/seedu/ecardnomics/game/GameEngineTest.java b/src/test/java/seedu/ecardnomics/game/GameEngineTest.java new file mode 100644 index 0000000000..e36201141b --- /dev/null +++ b/src/test/java/seedu/ecardnomics/game/GameEngineTest.java @@ -0,0 +1,166 @@ +package seedu.ecardnomics.game; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import seedu.ecardnomics.command.Command; +import seedu.ecardnomics.command.game.DoneGameCommand; +import seedu.ecardnomics.deck.Deck; +import seedu.ecardnomics.deck.DeckList; +import seedu.ecardnomics.deck.FlashCard; +import seedu.ecardnomics.parser.GameParser; + +import java.util.ArrayDeque; +import java.util.ArrayList; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class GameEngineTest { + Deck deck; + DeckList deckList; + GameStorage storage; + GameParser gameParser; + GameEngine engine; + + FlashCard[] createfcStubs(int number) { + FlashCard[] fcStubs = new FlashCard[number]; + for (int i = 0; i < number; i++) { + fcStubs[i] = new FlashCard(String.format("ques-stub%d", i), String.format("ans-stub%d", i)); + } + return fcStubs; + } + + /** + * Checks if storage.deque is randomised enough by sequentially popping and ensuring there is some degree of + * randomness. + * + * @return true if random "enough", otherwise false + */ + boolean checkRandomnessOfDequeFromReference(ArrayDeque dequeFromRetestStubs, + ArrayList referenceList) { + int mismatchCount = 0; + while (!dequeFromRetestStubs.isEmpty()) { + // flash cards follow strict format + int index = 0; + FlashCard fc = dequeFromRetestStubs.pop(); + if (!fc.getQuestion().equals(referenceList.get(index))) { + mismatchCount++; + } + index++; + } + + double mismatchRatio = (double) referenceList.size() / mismatchCount; + double threshold = 0.25; + if (mismatchRatio >= threshold) { + return true; + } else { + return false; + } + } + + @Test + void update_storageDequeAndRetestStoreEmptyUserEntersN_returnsDoneGameCommand() { + storage.deque.clear(); + storage.retestStore.clear(); + FlashCard fcStub = createfcStubs(1)[0]; + assertTrue(engine.update(false, fcStub) instanceof DoneGameCommand); + } + + @Test + void update_storageDequeNotEmpty_returnsNull() { + FlashCard fcStub = createfcStubs(1)[0]; + assertTrue(engine.update(true, fcStub) == null); + assertTrue(engine.update(false, fcStub) == null); + + } + + @Test + void updateDeque_storageDequeEmptyAndUpdateFromRetestStore_success() { + storage.deque.clear(); + FlashCard[] fcStubs = createfcStubs(5); + for (FlashCard fc : fcStubs) { + storage.retestStore.add(fc); + } + ArrayList retestStoreRef = storage.retestStore; + storage.createRandomisedStack(storage.retestStore); + engine.updateDeque(); + assertTrue(checkRandomnessOfDequeFromReference(storage.deque, retestStoreRef)); + assertEquals(0, storage.retestStore.size()); + } + + @Test + void updateRetestStore_responseY_addsFlashCard() { + storage.retestStore.clear(); + FlashCard fcStub = createfcStubs(1)[0]; + engine.updateRetestStore(true, fcStub); + assertTrue(storage.retestStore.contains(fcStub)); + } + + @Test + void updateRetestStore_responseN_doNothing() { + storage.retestStore.clear(); + FlashCard fcStub = createfcStubs(1)[0]; + engine.updateRetestStore(false, fcStub); + assertFalse(storage.retestStore.contains(fcStub)); + } + + @Test + void checkAttempt_validCommand_success() { + Command attempt = gameParser.parse("ans-stub0"); + FlashCard fcStub = createfcStubs(1)[0]; + assertEquals(100.0, engine.checkAttempt(attempt, fcStub)); + } + + @Test + void getMatchPercentage_completeMatchSpacePunctuationPadded_success() { + double match = engine.getMatchPercentage("this is an answer.!` -_+=)(*&^%$#@!", "this is an answer ."); + assertEquals(100.0, match); + } + + @Test + void getMatchPercentage_completeMatchSpacePaddedMatchedWords_success() { + double match = engine.getMatchPercentage("this \t\t is an answer.", "this is an answer"); + assertEquals(100.0, match); + } + + @Test + void getMatchPercentage_zeroMatchSpacePunctuationPadded_success() { + double match = engine.getMatchPercentage("completely wrong.!` -_+=)(*&^%$#@!", "this is an answer ."); + assertEquals(0.0, match); + } + + @Test + void getMatchPercentage_punctuationAnswerZeroMatch_success() { + double match = engine.getMatchPercentage("!@#//... . %^*()", "!@#//... . %^*()"); + assertEquals(0.0, match); + } + + @BeforeEach + void preliminaries() { + deckList = initialiseDeckList(2); + deck = initialiseDeck(deckList, 2); + storage = new GameStorage(deck); + gameParser = new GameParser(deck); + engine = new GameEngine(storage); + } + + DeckList initialiseDeckList(int size) { + DeckList deckList = new DeckList(); + for (int i = 1; i <= size; i++) { + Deck deck = new Deck(String.format("deck %d", i)); + deckList.addDeck(deck); + } + return deckList; + } + + Deck initialiseDeck(DeckList deckList, int size) { + deckList.addDeck(new Deck("Pokemon")); + Deck deck = deckList.getDeck(2); + for (int i = 1; i <= size; i++) { + FlashCard flashCard = new FlashCard(String.format("q %d", i), String.format("a %d", i)); + deck.add(flashCard); + } + return deck; + } +} \ No newline at end of file diff --git a/src/test/java/seedu/ecardnomics/game/GameStorageTest.java b/src/test/java/seedu/ecardnomics/game/GameStorageTest.java new file mode 100644 index 0000000000..d745e087c7 --- /dev/null +++ b/src/test/java/seedu/ecardnomics/game/GameStorageTest.java @@ -0,0 +1,66 @@ +package seedu.ecardnomics.game; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import seedu.ecardnomics.deck.Deck; +import seedu.ecardnomics.deck.DeckList; +import seedu.ecardnomics.deck.FlashCard; + +class GameStorageTest { + GameStorage storage; + Deck originalDeck; + + @Test + void createRandomisedStack_takesDeck_originalDeckUnchanged() throws Exception { + Deck deckCopy = new Deck("Pokemon"); + copyDeck(originalDeck, deckCopy); + storage.createRandomisedStack(originalDeck); + checkDeckEquality(originalDeck, deckCopy); + } + + void checkDeckEquality(Deck original, Deck copy) throws Exception { + boolean isEqual = true; + for (int i = 0; i < original.size(); i++) { + if (!original.get(i).equals(copy.get(i))) { + isEqual = false; + break; + } + } + + if (!isEqual) { + throw new Exception("Decks not equal!"); + } + } + + void copyDeck(Deck originalDeck, Deck deckCopy) { + for (FlashCard fc : originalDeck.getDeck()) { + deckCopy.add(fc); + } + } + + @BeforeEach + void preliminaries() { + DeckList deckList = initialiseDeckList(2); + originalDeck = initialiseDeck(deckList, 2); + storage = new GameStorage(originalDeck); + } + + DeckList initialiseDeckList(int size) { + DeckList deckList = new DeckList(); + for (int i = 1; i <= size; i++) { + Deck deck = new Deck(String.format("deck %d", i)); + deckList.addDeck(deck); + } + return deckList; + } + + Deck initialiseDeck(DeckList deckList, int size) { + deckList.addDeck(new Deck("Pokemon")); + Deck deck = deckList.getDeck(2); + for (int i = 1; i <= size; i++) { + FlashCard flashCard = new FlashCard(String.format("q %d", i), String.format("a %d", i)); + deck.add(flashCard); + } + return deck; + } +} \ No newline at end of file diff --git a/src/test/java/seedu/ecardnomics/parser/DeckParserTest.java b/src/test/java/seedu/ecardnomics/parser/DeckParserTest.java new file mode 100644 index 0000000000..d9e82c8400 --- /dev/null +++ b/src/test/java/seedu/ecardnomics/parser/DeckParserTest.java @@ -0,0 +1,401 @@ +package seedu.ecardnomics.parser; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import seedu.ecardnomics.command.VersionCommand; +import seedu.ecardnomics.command.VoidCommand; +import seedu.ecardnomics.command.deck.DoneEditCommand; +import seedu.ecardnomics.command.deck.AddCommand; +import seedu.ecardnomics.command.deck.DeleteCommand; +import seedu.ecardnomics.command.deck.ListCommand; +import seedu.ecardnomics.command.deck.HelpCommand; +import seedu.ecardnomics.command.ExitCommand; +import seedu.ecardnomics.command.normal.PowerPointCommand; +import seedu.ecardnomics.command.normal.StartCommand; +import seedu.ecardnomics.deck.Deck; +import seedu.ecardnomics.deck.DeckList; +import seedu.ecardnomics.deck.FlashCard; +import seedu.ecardnomics.exceptions.BothOcAndCsException; +import seedu.ecardnomics.exceptions.ColorsNotAvailException; +import seedu.ecardnomics.exceptions.CsIndexFormatException; +import seedu.ecardnomics.exceptions.CsIndexRangeException; +import seedu.ecardnomics.exceptions.EmptyQnAException; +import seedu.ecardnomics.exceptions.FlashCardRangeException; +import seedu.ecardnomics.exceptions.IndexFormatException; +import seedu.ecardnomics.exceptions.InvalidOptionsException; +import seedu.ecardnomics.exceptions.InvalidPptxArgumentException; +import seedu.ecardnomics.exceptions.NoAlphaNumericInputException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +class DeckParserTest { + DeckParser deckParser; + + @Test + void getIndex_validIndex_success() throws Exception { + assertEquals(0, deckParser.getIndex("1")); + assertEquals(1, deckParser.getIndex("2")); + } + + @Test + void getIndex_validIndexSpacePadded_success() throws Exception { + assertEquals(0, deckParser.getIndex(" 1")); + assertEquals(0, deckParser.getIndex("\t1")); + assertEquals(1, deckParser.getIndex("2\t")); + assertEquals(1, deckParser.getIndex(" 2 ")); + } + + @Test + void getIndex_outOfRangeIndex_exceptionThrown() { + try { + assertEquals(1, deckParser.getIndex("3")); + assertEquals(1, deckParser.getIndex("0")); + fail(); + } catch (Exception e) { + assertEquals((new FlashCardRangeException()).getMessage(), e.getMessage()); + } + } + + @Test + void getIndex_noIndex_exceptionThrown() { + try { + assertEquals(1, deckParser.getIndex("")); + assertEquals(1, deckParser.getIndex(" ")); + assertEquals(1, deckParser.getIndex("something")); + assertEquals(1, deckParser.getIndex(" something")); + fail(); + } catch (Exception e) { + assertEquals((new IndexFormatException()).getMessage(), e.getMessage()); + } + } + + @Test + void prepareFlashCard_oneLinerEmptyInputQuestion_EmptyQnAExceptionThrown() { + try { + assertEquals(1, deckParser.prepareFlashCard(" /ans some answer")); + // question input checked first + assertEquals(1, deckParser.prepareFlashCard(" /ans !@#")); + } catch (Exception e) { + assertEquals(new EmptyQnAException().getMessage(), e.getMessage()); + } + } + + @Test + void prepareFlashCard_oneLinerEmptyInputAnswerValidQuestion_EmptyQnAExceptionThrown() { + try { + // question input checked first + assertEquals(1, deckParser.prepareFlashCard("some question /ans ")); + } catch (Exception e) { + assertEquals(new EmptyQnAException().getMessage(), e.getMessage()); + } + } + + @Test + void prepareFlashCard_oneLinerNoAlphaNumericQuestion_NoAlphaNumericExceptionThrown() { + try { + assertEquals(1, deckParser.prepareFlashCard("!@#.. /ans some answer")); + // question input checked first + assertEquals(1, deckParser.prepareFlashCard("!@#.. / ")); + } catch (Exception e) { + assertEquals(new NoAlphaNumericInputException().getMessage(), e.getMessage()); + } + } + + @Test + void prepareFlashCard_oneLinerNoAlphaNumericAnswerValidQuestion_NoAlphaNumericExceptionThrown() { + try { + // question input checked first + assertEquals(1, deckParser.prepareFlashCard("some question /ans !@#..")); + } catch (Exception e) { + assertEquals(new NoAlphaNumericInputException().getMessage(), e.getMessage()); + } + } + + @Test + void prepareFlashCard_oneLinerInValidAnswerInvalidQuestion_questionDependentExceptionsThrown() { + try { + // question input checked first + // throws EmptyInputException + assertEquals(1, deckParser.prepareFlashCard(" /ans !@#..")); + // throws NoAlphaNumericException + assertEquals(1, deckParser.prepareFlashCard("!@#. /ans ")); + } catch (EmptyQnAException eie) { + assertEquals(new EmptyQnAException().getMessage(), eie.getMessage()); + } catch (NoAlphaNumericInputException nae) { + assertEquals(new NoAlphaNumericInputException().getMessage(), nae.getMessage()); + } + } + + @Test + void prepareFlashCard_oneLinerValidQuestionContainsAns() throws EmptyQnAException, NoAlphaNumericInputException { + assertTrue(deckParser.prepareFlashCard("question with/ans /ans answer") instanceof AddCommand); + } + + @Test + void prepareFlashCard_oneLinerValidAnswerContainsAns() throws EmptyQnAException, NoAlphaNumericInputException { + assertTrue(deckParser.prepareFlashCard("question /ans answer with/ans") instanceof AddCommand); + assertTrue(deckParser.prepareFlashCard("question /ans answer /ans") instanceof AddCommand); + } + + @Test + void prepareFlashCard_oneLinerEmptyQuestionEmptyAnswer() { + try { + assertTrue(deckParser.prepareFlashCard("/ans") instanceof AddCommand); + } catch (EmptyQnAException ee) { + assertEquals(new EmptyQnAException().getMessage(), ee.getMessage()); + } catch (NoAlphaNumericInputException nae) { + assertTrue(!NoAlphaNumericInputException.NO_ALPHANUMERIC_LINE.equals(nae.getMessage())); + } + try { + assertTrue(deckParser.prepareFlashCard(" /ans ") instanceof AddCommand); + } catch (EmptyQnAException ee) { + assertEquals(new EmptyQnAException().getMessage(), ee.getMessage()); + } catch (NoAlphaNumericInputException nae) { + assertTrue(!NoAlphaNumericInputException.NO_ALPHANUMERIC_LINE.equals(nae.getMessage())); + } + } + + @Test + void parseCommand_ExitCommand_success() throws Exception { + assertTrue(deckParser.parseCommand("exit", "") instanceof ExitCommand); + } + + @Test + void parseCommand_DoneEditCommand_success() throws Exception { + assertTrue(deckParser.parseCommand("done", "") instanceof DoneEditCommand); + } + + @Test + void parseCommand_AddCommand_oneLine_success() throws Exception { + assertTrue(deckParser.parseCommand("add", "qn /ans ans") instanceof AddCommand); + } + + @Test + void parseCommand_ListCommand_success() throws Exception { + assertTrue(deckParser.parseCommand("list", "") instanceof ListCommand); + } + + @Test + void parseCommand_DeleteCommand_success() throws Exception { + assertTrue(deckParser.parseCommand("delete", "1 -y") instanceof DeleteCommand); + } + + @Test + void parseCommand_DeleteCommandNoIndex_exceptionThrown() { + try { + deckParser.parseCommand("delete", ""); + } catch (Exception e) { + assertTrue(e instanceof IndexFormatException); + } + } + + @Test + void parseCommand_DeleteCommandOutOfRangeIndex_exceptionThrown() { + try { + deckParser.parseCommand("delete", "3"); + } catch (Exception e) { + assertTrue(e instanceof FlashCardRangeException); + } + } + + @Test + void parseCommand_UpdateCommandNoIndex_exceptionThrown() { + try { + deckParser.parseCommand("update", ""); + } catch (Exception e) { + assertTrue(e instanceof IndexFormatException); + } + } + + @Test + void parseCommand_UpdateCommandOutOfRangeIndex_exceptionThrown() { + try { + deckParser.parseCommand("update", "3"); + } catch (Exception e) { + assertTrue(e instanceof FlashCardRangeException); + } + } + + @Test + void parseCommand_PptxCommandForceYes_success() { + try { + assertTrue(deckParser.parseCommand("pptx", "-y") instanceof PowerPointCommand); + } catch (Exception e) { + System.out.println(" error"); + } + } + + @Test + void parseCommand_PptxCommandForceYesOriginalColor_success() { + try { + assertTrue(deckParser.parseCommand("pptx", "-oc blue green -y") instanceof PowerPointCommand); + assertTrue(deckParser.parseCommand("pptx", "-y -oc blue green") instanceof PowerPointCommand); + } catch (Exception e) { + System.out.println(" error"); + } + } + + @Test + void parseCommand_PptxCommandForceYesColorScheme_success() { + try { + assertTrue(deckParser.parseCommand("pptx", "-cs 1 -y") instanceof PowerPointCommand); + assertTrue(deckParser.parseCommand("pptx", "-y -cs 1") instanceof PowerPointCommand); + } catch (Exception e) { + System.out.println(" error"); + } + } + + @Test + void parseCommand_PptxCommandExtraArguments_exceptionThrown() { + try { + deckParser.parseCommand("pptx", "1"); + deckParser.parseCommand("pptx", "1 -y"); + deckParser.parseCommand("pptx", "-y 1"); + fail(); + } catch (Exception e) { + assertTrue(e instanceof InvalidPptxArgumentException); + } + } + + @Test + void parseCommand_PptxCommandBothOcAndCs_exceptionThrown() { + try { + deckParser.parseCommand("pptx", "-y -oc green black -cs 1"); + deckParser.parseCommand("pptx", "-y -cs 1 -oc black green"); + fail(); + } catch (Exception e) { + System.out.println(e.getMessage()); + assertTrue(e instanceof BothOcAndCsException); + } + } + + @Test + void parseCommand_PptxCommandOriginalColorUnavailableColor_exceptionThrown() { + try { + deckParser.parseCommand("pptx", "-y -oc green block"); + deckParser.parseCommand("pptx", "-y -oc block green"); + fail(); + } catch (Exception e) { + assertTrue(e instanceof ColorsNotAvailException); + } + } + + @Test + void parseCommand_PptxCommandInvalidOptions_exceptionThrown() { + try { + deckParser.parseCommand("pptx", "-y -cc green black"); + deckParser.parseCommand("pptx", "-y -ss 1"); + fail(); + } catch (Exception e) { + System.out.println(e.getMessage()); + assertTrue(e instanceof InvalidOptionsException); + } + } + + @Test + void parseCommand_PptxCommandCsIndexOutOfRange_exceptionThrown() { + try { + deckParser.parseCommand("pptx", "-y -cs 0"); + deckParser.parseCommand("pptx", "-y -cs 11"); + fail(); + } catch (Exception e) { + assertTrue(e instanceof CsIndexRangeException); + } + } + + @Test + void parseCommand_PptxCommandCsIndexWrongFormat_exceptionThrown() { + try { + deckParser.parseCommand("pptx", "-y -cs -1"); + deckParser.parseCommand("pptx", "-y -cs blue red"); + fail(); + } catch (Exception e) { + assertTrue(e instanceof CsIndexFormatException); + } + } + + @Test + void parseCommand_HelpCommand_success() throws Exception { + assertTrue(deckParser.parseCommand("help", "") instanceof HelpCommand); + } + + @Test + void parseCommand_StartCommandArgumentsSpacePadded_success() { + try { + assertTrue(deckParser.parseCommand("start", "") instanceof StartCommand); + assertTrue(deckParser.parseCommand("start", "\t") instanceof StartCommand); + assertTrue(deckParser.parseCommand("start", "\n") instanceof StartCommand); + } catch (Exception e) { + System.out.println(" error"); + } + } + + @Test + void parseCommand_StartCommandRandomArguments_success() { + try { + assertTrue(deckParser.parseCommand("start", "1") instanceof StartCommand); + assertTrue(deckParser.parseCommand("start", "\t1") instanceof StartCommand); + assertTrue(deckParser.parseCommand("start", "\n1") instanceof StartCommand); + } catch (Exception e) { + System.out.println(" error"); + } + } + + @Test + void parseCommand_VersionCommandAnyArguments_success() { + try { + assertTrue(deckParser.parseCommand("--version", "1") instanceof VersionCommand); + assertTrue(deckParser.parseCommand("--version", "\t1") instanceof VersionCommand); + assertTrue(deckParser.parseCommand("--version", "\n hdkljfashdfs\t\t") instanceof VersionCommand); + } catch (Exception e) { + System.out.println(" error"); + } + } + + @Test + void parse_invalidCommand_returnsVoidCommand() { + assertTrue(deckParser.parse("something random") instanceof VoidCommand); + assertTrue(deckParser.parse("") instanceof VoidCommand); + assertTrue(deckParser.parse(" ") instanceof VoidCommand); + assertTrue(deckParser.parse("\t") instanceof VoidCommand); + assertTrue(deckParser.parse("blah") instanceof VoidCommand); + } + + //@BeforeAll + //public static void addUserInput() { + //String userInputs = "q1" + System.getProperty("line.separator") + "a1" + System.getProperty("line.separator") + //+ "y" + System.getProperty("line.separator") + "y"; + //ByteArrayInputStream input = new ByteArrayInputStream(userInputs.getBytes()); + //System.setIn(input); + //} + + @BeforeEach + void initialiseDeckParser() { + DeckList deckList = initialiseDeckList(2); + Deck deck = initialiseDeck(deckList, 2); + deckParser = new DeckParser(deckList, deck); + } + + DeckList initialiseDeckList(int size) { + DeckList deckList = new DeckList(); + for (int i = 1; i <= size; i++) { + Deck deck = new Deck(String.format("deck %d", i)); + deckList.addDeck(deck); + } + return deckList; + } + + Deck initialiseDeck(DeckList deckList, int size) { + deckList.addDeck(new Deck("Pokemon")); + Deck deck = deckList.getDeck(2); + for (int i = 1; i <= size; i++) { + FlashCard flashCard = new FlashCard(String.format("q %d", i), String.format("a %d", i)); + deck.add(flashCard); + } + return deck; + } + + +} \ No newline at end of file diff --git a/src/test/java/seedu/ecardnomics/parser/GameParserTest.java b/src/test/java/seedu/ecardnomics/parser/GameParserTest.java new file mode 100644 index 0000000000..3b604134fc --- /dev/null +++ b/src/test/java/seedu/ecardnomics/parser/GameParserTest.java @@ -0,0 +1,106 @@ +package seedu.ecardnomics.parser; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import seedu.ecardnomics.command.ExitCommand; +import seedu.ecardnomics.command.VersionCommand; +import seedu.ecardnomics.command.VoidCommand; +import seedu.ecardnomics.command.game.DoneGameCommand; +import seedu.ecardnomics.command.game.GameResponseCommand; +import seedu.ecardnomics.command.game.HelpCommand; +import seedu.ecardnomics.deck.Deck; +import seedu.ecardnomics.deck.DeckList; +import seedu.ecardnomics.deck.FlashCard; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class GameParserTest { + GameParser gameParser; + + @Test + void parseCommand_ExitCommand_success() { + assertTrue(gameParser.parseCommand("exit", "") instanceof ExitCommand); + } + + @Test + void parseCommand_DoneGameCommand_success() { + assertTrue(gameParser.parseCommand("done", "") instanceof DoneGameCommand); + + + } + + @Test + void parseCommand_HelpCommandAnyArguments_success() { + assertTrue(gameParser.parseCommand("help", "") instanceof HelpCommand); + assertTrue(gameParser.parseCommand("help", "gibberish\t") instanceof HelpCommand); + assertTrue(gameParser.parseCommand("help", "\n 12gibberish\t") instanceof HelpCommand); + } + + @Test + void parseCommand_VersionCommandAnyArguments_success() { + assertTrue(gameParser.parseCommand("--version", "") instanceof VersionCommand); + assertTrue(gameParser.parseCommand("--version", "\t1") instanceof VersionCommand); + assertTrue(gameParser.parseCommand("--version", "\n hdkljfashdfs\t\t") instanceof VersionCommand); + } + + @Test + void parseCommand_AnythingElse_returnsVoidCommand() { + assertTrue(gameParser.parseCommand("random", "") instanceof VoidCommand); + assertTrue(gameParser.parseCommand("lmaoo sdalkfe we", "") instanceof VoidCommand); + } + + @Test + void parse_someGameModeCommandPermissibleFormat_success() { + assertTrue(gameParser.parse("--version") instanceof VersionCommand); + assertTrue(gameParser.parse("--version ") instanceof VersionCommand); + assertTrue(gameParser.parse("done") instanceof DoneGameCommand); + assertTrue(gameParser.parse("done ") instanceof DoneGameCommand); + assertTrue(gameParser.parse("exit") instanceof ExitCommand); + assertTrue(gameParser.parse("help") instanceof HelpCommand); + } + + @Test + void parse_commandWordWithOtherWords_returnsGameResponseCommand() { + assertTrue(gameParser.parse("--version s df") instanceof GameResponseCommand); + assertTrue(gameParser.parse("--version 22 ") instanceof GameResponseCommand); + assertTrue(gameParser.parse("done sdsd") instanceof GameResponseCommand); + assertTrue(gameParser.parse("done 22") instanceof GameResponseCommand); + assertTrue(gameParser.parse("exit sd2") instanceof GameResponseCommand); + assertTrue(gameParser.parse("help sde 2") instanceof GameResponseCommand); + } + + @Test + void parse_AnythingElseGivesGameResponseCommand_responseRecordedTrimmed() { + GameResponseCommand response = (GameResponseCommand) gameParser.parse("Some answer here"); + assertEquals(response.getAttempt(), "Some answer here"); + response = (GameResponseCommand) gameParser.parse(" Some answer here "); + assertEquals(response.getAttempt(), "Some answer here"); + } + + @BeforeEach + void initialiseGameParser() { + DeckList deckList = initialiseDeckList(2); + Deck deck = initialiseDeck(deckList, 2); + gameParser = new GameParser(deck); + } + + DeckList initialiseDeckList(int size) { + DeckList deckList = new DeckList(); + for (int i = 1; i <= size; i++) { + Deck deck = new Deck(String.format("deck %d", i)); + deckList.addDeck(deck); + } + return deckList; + } + + Deck initialiseDeck(DeckList deckList, int size) { + deckList.addDeck(new Deck("Pokemon")); + Deck deck = deckList.getDeck(2); + for (int i = 1; i <= size; i++) { + FlashCard flashCard = new FlashCard(String.format("q %d", i), String.format("a %d", i)); + deck.add(flashCard); + } + return deck; + } +} \ No newline at end of file diff --git a/src/test/java/seedu/ecardnomics/parser/NormalParserTest.java b/src/test/java/seedu/ecardnomics/parser/NormalParserTest.java new file mode 100644 index 0000000000..3b081cd2d1 --- /dev/null +++ b/src/test/java/seedu/ecardnomics/parser/NormalParserTest.java @@ -0,0 +1,334 @@ +package seedu.ecardnomics.parser; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import seedu.ecardnomics.command.VersionCommand; +import seedu.ecardnomics.command.VoidCommand; +import seedu.ecardnomics.command.ExitCommand; +import seedu.ecardnomics.command.normal.DeleteDeckCommand; +import seedu.ecardnomics.command.normal.EditCommand; +import seedu.ecardnomics.command.normal.CreateCommand; +import seedu.ecardnomics.command.normal.DecksCommand; +import seedu.ecardnomics.command.normal.PowerPointCommand; +import seedu.ecardnomics.command.normal.HelpCommand; +import seedu.ecardnomics.command.normal.StartCommand; +import seedu.ecardnomics.deck.Deck; +import seedu.ecardnomics.deck.DeckList; +import seedu.ecardnomics.exceptions.BothOcAndCsException; +import seedu.ecardnomics.exceptions.ColorsNotAvailException; +import seedu.ecardnomics.exceptions.CsIndexFormatException; +import seedu.ecardnomics.exceptions.CsIndexRangeException; +import seedu.ecardnomics.exceptions.DeckRangeException; +import seedu.ecardnomics.exceptions.EmptyInputException; +import seedu.ecardnomics.exceptions.IndexFormatException; +import seedu.ecardnomics.exceptions.InvalidOptionsException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +class NormalParserTest { + NormalParser normalParser; + + @Test + void getIndex_validIndex_success() throws Exception { + assertEquals(0, normalParser.getIndex("1")); + assertEquals(1, normalParser.getIndex("2")); + } + + @Test + void getIndex_validIndexSpacePadded_success() throws Exception { + assertEquals(0, normalParser.getIndex(" 1")); + assertEquals(0, normalParser.getIndex("\t1")); + assertEquals(1, normalParser.getIndex("2\t")); + assertEquals(1, normalParser.getIndex(" 2 ")); + } + + @Test + void getIndex_outOfRangeIndex_exceptionThrown() { + try { + assertEquals(1, normalParser.getIndex("3")); + assertEquals(1, normalParser.getIndex("-1")); + assertEquals(1, normalParser.getIndex("0")); + fail(); + } catch (Exception e) { + assertEquals((new DeckRangeException()).getMessage(), e.getMessage()); + } + } + + @Test + void getIndex_noIndex_exceptionThrown() { + try { + assertEquals(1, normalParser.getIndex("")); + assertEquals(1, normalParser.getIndex(" ")); + assertEquals(1, normalParser.getIndex("something")); + assertEquals(1, normalParser.getIndex(" something")); + fail(); + } catch (Exception e) { + assertEquals((new IndexFormatException()).getMessage(), e.getMessage()); + } + } + + @Test + void getCsIndex_indexOutOfRange_exceptionThrown() { + try { + assertEquals(1, normalParser.getCsIndex("11")); + assertEquals(1, normalParser.getCsIndex("0")); + fail(); + } catch (Exception e) { + assertTrue(e instanceof CsIndexRangeException); + } + } + + @Test + void getCsCsIndex_indexWrongFormat_exceptionThrown() { + try { + assertEquals(1, normalParser.getCsIndex("-1")); + assertEquals(1, normalParser.getCsIndex("blue")); + assertEquals(1, normalParser.getIndex("")); + fail(); + } catch (Exception e) { + assertTrue(e instanceof CsIndexFormatException); + } + } + + @Test + void preparePptxCommand_forceYes_success() { + try { + assertTrue(normalParser.preparePptxCommand("1 -y") instanceof PowerPointCommand); + assertTrue(normalParser.preparePptxCommand("-y 1") instanceof PowerPointCommand); + } catch (Exception e) { + System.out.println(" error"); + } + } + + @Test + void preparePptxCommand_forceYesOriginalColor_success() { + try { + assertTrue(normalParser.preparePptxCommand("1 -oc blue green -y") instanceof PowerPointCommand); + assertTrue(normalParser.preparePptxCommand("1 -y -oc blue green") instanceof PowerPointCommand); + } catch (Exception e) { + System.out.println(" error"); + } + } + + @Test + void preparePptxCommand_forceYesColorScheme_success() { + try { + assertTrue(normalParser.preparePptxCommand("1 -cs 1 -y") instanceof PowerPointCommand); + assertTrue(normalParser.preparePptxCommand("1 -y -cs 1") instanceof PowerPointCommand); + } catch (Exception e) { + System.out.println(" error"); + } + } + + @Test + void preparePptxCommand_bothOcAndCs_exceptionThrown() { + try { + normalParser.preparePptxCommand("1 -y -oc green black -cs 1"); + normalParser.preparePptxCommand("1 -y -cs 1 -oc black green"); + fail(); + } catch (Exception e) { + assertTrue(e instanceof BothOcAndCsException); + } + } + + @Test + void preparePptxCommand_originalColorUnavailableColor_exceptionThrown() { + try { + normalParser.preparePptxCommand("1 -y -oc green block"); + normalParser.preparePptxCommand("1 -y -oc block green"); + fail(); + } catch (Exception e) { + assertTrue(e instanceof ColorsNotAvailException); + } + } + + @Test + void preparePptxCommand_invalidOptions_exceptionThrown() { + try { + normalParser.preparePptxCommand("1 -y -cc green black"); + normalParser.preparePptxCommand("1 -y -ss 1"); + fail(); + } catch (Exception e) { + assertTrue(e instanceof InvalidOptionsException); + } + } + + @Test + void parseCommand_VersionCommand_success() throws Exception { + assertTrue(normalParser.parseCommand("--version", "") instanceof VersionCommand); + } + + + @Test + void parseCommand_ExitCommand_success() throws Exception { + assertTrue(normalParser.parseCommand("exit", "") instanceof ExitCommand); + } + + @Test + void parseCommand_EditCommand_success() throws Exception { + assertTrue(normalParser.parseCommand("edit", "1") instanceof EditCommand); + assertTrue(normalParser.parseCommand("edit", "2") instanceof EditCommand); + } + + @Test + void parseCommand_EditCommandNoIndex_exceptionThrown() { + try { + assertTrue(normalParser.parseCommand("edit", "") instanceof EditCommand); + fail(); + } catch (Exception e) { + assertTrue(e instanceof IndexFormatException); + } + } + + @Test + void parseCommand_EditCommandOutOfRangeIndex_exceptionThrown() { + try { + normalParser.parseCommand("edit", "0"); + normalParser.parseCommand("edit", "3"); + fail(); + } catch (Exception e) { + assertTrue(e instanceof DeckRangeException); + } + } + + @Test + void parseCommand_CreateCommand_success() throws Exception { + assertTrue(normalParser.parseCommand("create", "Deck 3") instanceof CreateCommand); + } + + @Test + void parseCommand_CreateCommandEmptyInput_exceptionThrown() throws Exception { + try { + normalParser.parseCommand("create", ""); + fail(); + } catch (Exception e) { + assertTrue(e instanceof EmptyInputException); + } + } + + + @Test + void parseCommand_DecksCommand_success() throws Exception { + assertTrue(normalParser.parseCommand("decks", "") instanceof DecksCommand); + } + + @Test + void parseCommand_DeleteDeckCommand_force_success() throws Exception { + assertTrue(normalParser.parseCommand("delete", "1 -y") instanceof DeleteDeckCommand); + } + + @Test + void parseCommand_DeleteDeckCommandNoIndex_exceptionThrown() { + try { + normalParser.parseCommand("delete", ""); + fail(); + } catch (Exception e) { + assertTrue(e instanceof IndexFormatException); + } + } + + @Test + void parseCommand_DeleteDeckCommandOutOfRangeIndex_exceptionThrown() { + try { + normalParser.parseCommand("delete", "3"); + fail(); + } catch (Exception e) { + assertTrue(e instanceof DeckRangeException); + } + } + + + + @Test + void parseCommand_PptxCommandOutOfRangeIndex_exceptionThrown() { + try { + normalParser.parseCommand("pptx", "3"); + fail(); + } catch (Exception e) { + assertTrue(e instanceof DeckRangeException); + } + } + + @Test + void parseCommand_StartCommandValidArgumentsSpacePadded_success() { + try { + assertTrue(normalParser.parseCommand("start", "1") instanceof StartCommand); + assertTrue(normalParser.parseCommand("start", " 1 ") instanceof StartCommand); + assertTrue(normalParser.parseCommand("start", "\t1\n") instanceof StartCommand); + } catch (Exception e) { + System.out.println(" error"); + } + } + + @Test + void parseCommand_StartCommandNoIndex_exceptionThrown() { + try { + normalParser.parseCommand("start", ""); + fail(); + } catch (Exception e) { + assertTrue(e instanceof IndexFormatException); + } + } + + @Test + void parseCommand_StartCommandTooHighRangeIndex_exceptionThrown() { + try { + normalParser.parseCommand("start", "3"); + fail(); + } catch (Exception e) { + assertTrue(e instanceof DeckRangeException); + } + } + + // @Test + // void parseCommand_StartCommandTooLowRangeIndex_exceptionThrown() { + // try { + // normalParser.parseCommand("start", "-1"); + // fail(); + // } catch (Exception e) { + // assertTrue(e instanceof DeckRangeException); + // } + // } + + @Test + void parseCommand_HelpCommand_success() throws Exception { + assertTrue(normalParser.parseCommand("help", "") instanceof HelpCommand); + } + + @Test + void parseCommand_VersionCommandAnyArguments_success() { + try { + assertTrue(normalParser.parseCommand("--version", "1") instanceof VersionCommand); + assertTrue(normalParser.parseCommand("--version", "\t1") instanceof VersionCommand); + assertTrue(normalParser.parseCommand("--version", "\n hdkljfashdfs\t\t") instanceof VersionCommand); + } catch (Exception e) { + System.out.println(" error"); + } + } + + @Test + void parse_invalidCommand_returnsVoidCommand() { + assertTrue(normalParser.parse("something random") instanceof VoidCommand); + assertTrue(normalParser.parse("") instanceof VoidCommand); + assertTrue(normalParser.parse(" ") instanceof VoidCommand); + assertTrue(normalParser.parse("\t") instanceof VoidCommand); + assertTrue(normalParser.parse("blah") instanceof VoidCommand); + } + + @BeforeEach + void initialiseNormalParser() { + DeckList deckList = initialiseDeckList(2); + normalParser = new NormalParser(deckList); + } + + DeckList initialiseDeckList(int size) { + DeckList deckList = new DeckList(); + for (int i = 1; i <= size; i++) { + Deck deck = new Deck(String.format("deck %d", i)); + deckList.addDeck(deck); + } + return deckList; + } +} \ No newline at end of file diff --git a/text-ui-test/ACTUAL.TXT b/text-ui-test/ACTUAL.TXT new file mode 100644 index 0000000000..b651c6b210 --- /dev/null +++ b/text-ui-test/ACTUAL.TXT @@ -0,0 +1,14 @@ +-------------------------------------------------------------------------------- + ___ _ _ + ___ / __\__ _ _ __ __| |_ __ ___ _ __ ___ (_) ___ ___ + / _ \/ / / _` | '__/ _` | '_ \ / _ \| '_ ` _ \| |/ __/ __| +| __/ /__| (_| | | | (_| | | | | (_) | | | | | | | (__\__ \ + \___\____/\__,_|_| \__,_|_| |_|\___/|_| |_| |_|_|\___|___/ + +Hello! Welcome to eCardnomics, +your one stop Flash Cards solution +-------------------------------------------------------------------------------- +[Normal] + > -------------------------------------------------------------------------------- +Bye. Hope to see you again soon! +-------------------------------------------------------------------------------- diff --git a/text-ui-test/EXPECTED-UNIX.TXT b/text-ui-test/EXPECTED-UNIX.TXT new file mode 100644 index 0000000000..1162be95c2 --- /dev/null +++ b/text-ui-test/EXPECTED-UNIX.TXT @@ -0,0 +1,14 @@ +------------------------------------------------------------ + ___ _ _ + ___ / __\__ _ _ __ __| |_ __ ___ _ __ ___ (_) ___ ___ + / _ \/ / / _` | '__/ _` | '_ \ / _ \| '_ ` _ \| |/ __/ __| +| __/ /__| (_| | | | (_| | | | | (_) | | | | | | | (__\__ \ + \___\____/\__,_|_| \__,_|_| |_|\___/|_| |_| |_|_|\___|___/ + +Hello! Welcome to eCardnomics, +your one stop Flash Cards solution +------------------------------------------------------------ +[Normal] + > ------------------------------------------------------------ +Bye. Hope to see you again soon! +------------------------------------------------------------ diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 892cb6cae7..294a4e5490 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,9 +1,14 @@ -Hello from - ____ _ -| _ \ _ _| | _____ -| | | | | | | |/ / _ \ -| |_| | |_| | < __/ -|____/ \__,_|_|\_\___| +-------------------------------------------------------------------------------- + ___ _ _ + ___ / __\__ _ _ __ __| |_ __ ___ _ __ ___ (_) ___ ___ + / _ \/ / / _` | '__/ _` | '_ \ / _ \| '_ ` _ \| |/ __/ __| +| __/ /__| (_| | | | (_| | | | | (_) | | | | | | | (__\__ \ + \___\____/\__,_|_| \__,_|_| |_|\___/|_| |_| |_|_|\___|___/ -What is your name? -Hello James Gosling +Hello! Welcome to eCardnomics, +your one stop Flash Cards solution +-------------------------------------------------------------------------------- +[Normal] + > -------------------------------------------------------------------------------- +Bye. Hope to see you again soon! +-------------------------------------------------------------------------------- diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index f6ec2e9f95..dc1c9e520a 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -1 +1,2 @@ -James Gosling \ No newline at end of file +exit + diff --git a/text-ui-test/runtest.bat b/text-ui-test/runtest.bat index 25ac7a2989..f78bf8b2f5 100644 --- a/text-ui-test/runtest.bat +++ b/text-ui-test/runtest.bat @@ -16,4 +16,4 @@ java -jar %jarloc% < ..\..\text-ui-test\input.txt > ..\..\text-ui-test\ACTUAL.TX cd ..\..\text-ui-test -FC ACTUAL.TXT EXPECTED.TXT >NUL && ECHO Test passed! || Echo Test failed! +FC ACTUAL.TXT EXPECTED.TXT && ECHO Test passed! || Echo Test failed!