diff --git a/.gitignore b/.gitignore index 2873e189e1..8077059475 100644 --- a/.gitignore +++ b/.gitignore @@ -6,12 +6,15 @@ # Gradle build files /.gradle/ /build/ -src/main/resources/docs/ +/src/main/resources/ # MacOS custom attributes files created by Finder .DS_Store *.iml bin/ +/database/ /text-ui-test/ACTUAL.TXT text-ui-test/EXPECTED-UNIX.TXT + + diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..385f27a27c --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.configuration.updateBuildConfiguration": "interactive" +} diff --git a/META-INF/MANIFEST.MF b/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..d54b3b8812 --- /dev/null +++ b/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: seedu.meal360.Meal360 + diff --git a/Parser.puml b/Parser.puml new file mode 100644 index 0000000000..04698e5967 --- /dev/null +++ b/Parser.puml @@ -0,0 +1,24 @@ +@startuml +class seedu.meal360.Parser { +~ Ui ui ++ String combineWords(String[],int,int) ++ HashMap parseIngredientName(String[]) ++ Recipe parseAddRecipe(String[],RecipeList) ++ Recipe parseEditRecipe(String[],RecipeList) ++ String parseDeleteRecipe(String[],RecipeList) ++ String parseTagRecipe(String[],RecipeList) ++ String parseAddRecipeTag(String,RecipeList) ++ String parseRemoveRecipeTag(String,RecipeList) ++ RecipeList parseListRecipe(String[],RecipeList) ++ Recipe parseViewRecipe(String[],RecipeList) ++ Recipe parseViewRecipe(String,RecipeList) ++ Recipe parseRandomRecipe(RecipeList) ++ WeeklyPlan parseWeeklyPlan(String[],RecipeList) +- WeeklyPlan parseEditSingleWeeklyPlan(String[],RecipeList) +- WeeklyPlan parseEditMultiWeeklyPlan(String[],RecipeList) +- StringBuilder getRecipeNames(String[],ArrayList,StringBuilder,int,int) ++ LocalDate parseDate(String) ++ void parseAddUserIngredients(String[],IngredientList) ++ void parseDeleteUserIngredients(String[],IngredientList) +} +@enduml \ No newline at end of file diff --git a/README.md b/README.md index f82e2494b7..a9fc66b426 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# Duke project template +# Meal360 -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. +Given below are instructions on how to use Meal360, a simple CLI meal planner application. ## Setting up in Intellij @@ -8,23 +8,30 @@ Prerequisites: JDK 11 (use the exact version), update Intellij to the most recen 1. **Ensure Intellij JDK 11 is defined as an SDK**, as described [here](https://www.jetbrains.com/help/idea/sdk.html#set-up-jdk) -- this step is not needed if you have used JDK 11 in a previous Intellij project. 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: +1. **Verify the set up**: After the importing is complete, locate the `src/main/java/seedu/meal360/Meal360.java` file, right-click it, and choose `Run Meal360.main()`. If the setup is correct, you should see something like the below: ``` - > Task :compileJava - > Task :processResources NO-SOURCE - > Task :classes + > Task :compileJava UP-TO-DATE + > Task :processResources UP-TO-DATE + > Task :classes UP-TO-DATE + + > Task :Meal360.main() + ---------------------------------------------------------------------------------------------------- + Welcome to Meal360, your ultimate Recipe Manager! + __ __ _ ____ __ __ + | \/ |___ __ _| |__ / / / / \ + | |\/| / -_) _` | ||_ \/ _ \ () | + |_| |_\___\__,_|_|___/\___/\__/ - > Task :Duke.main() - Hello from - ____ _ - | _ \ _ _| | _____ - | | | | | | | |/ / _ \ - | |_| | |_| | < __/ - |____/ \__,_|_|\_\___| - - What is your name? + ---------------------------------------------------------------------------------------------------- + | Loading recipes... | + | Recipes loaded successfully. | + | Loading weekly plan... | + | Weekly plan loaded successfully. | + | Loading ingredients... | + | Ingredients loaded successfully. | + ---------------------------------------------------------------------------------------------------- ``` - Type some word and press enter to let the execution proceed to the end. + Type `bye` and press enter to end the program. ## Build automation using Gradle @@ -39,7 +46,7 @@ Prerequisites: JDK 11 (use the exact version), update Intellij to the most recen ### JUnit tests -* A skeleton JUnit test (`src/test/java/seedu/duke/DukeTest.java`) is provided with this project template. +* A JUnit test (`src/test/java/seedu/meal360/Meal360Test.java`) is included in this project. * 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 @@ -53,7 +60,11 @@ The project uses [GitHub actions](https://github.com/features/actions) for CI. W ## Documentation -`/docs` folder contains a skeleton version of the project documentation. +`/docs` folder contains the project documentation. + +![Developer Guide](https://github.com/AY2223S2-CS2113-F10-3/tp/blob/master/docs/DeveloperGuide.md) + +![User Guide](https://github.com/AY2223S2-CS2113-F10-3/tp/blob/master/docs/UserGuide.md) Steps for publishing documentation to the public: 1. If you are using this project template for an individual project, go your fork on GitHub.
diff --git a/build.gradle b/build.gradle index d5e548e85f..8eec0184b2 100644 --- a/build.gradle +++ b/build.gradle @@ -10,6 +10,7 @@ repositories { } dependencies { + implementation 'com.google.code.gson:gson:2.8.9' 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' } @@ -29,11 +30,11 @@ test { } application { - mainClass = "seedu.duke.Duke" + mainClass = "seedu.meal360.Meal360" } shadowJar { - archiveBaseName = "duke" + archiveBaseName = "meal360" archiveClassifier = null } @@ -41,6 +42,26 @@ checkstyle { toolVersion = '10.2' } -run{ +run { standardInput = System.in + enableAssertions = true +} + +jar { + manifest { + attributes "Main-Class": "seedu.meal360.Meal360" + } + + from { + configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } + } +} + +tasks.withType(Copy).all { duplicatesStrategy 'exclude' } + +sourceSets { + main.java.srcDirs 'src/main/java' + main.resources.srcDirs 'src/main/resources' + test.java.srcDirs 'src/test/java' + test.resources.srcDirs 'src/test/resources' } diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 0f072953ea..420cf7fea8 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -1,9 +1,10 @@ # About us -Display | Name | Github Profile | Portfolio ---------|:----:|:--------------:|:---------: -![](https://via.placeholder.com/100.png?text=Photo) | John Doe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Don Joe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Ron John | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | John Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Don Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) +| Display | Name | Github Profile | Portfolio | +|------------------------------------------------------------|:------------------:|:--------------------------------------:|:---------------------------------:| +| ![](https://avatars.githubusercontent.com/u/88262428?v=4) | Suresh Abijith Ram | [Github](https://github.com/) | [Portfolio](team/topgun2001.md) | +| ![](https://avatars.githubusercontent.com/u/101538126?v=4) | Lu Bingyuan | [Github](https://github.com/notbingsu) | [Portfolio](team/notbingsu.md) | +| ![](https://avatars.githubusercontent.com/gurmankalkat) | Gurman Kalkat | [Github](https://github.com/) | [Portfolio](team/gurmankalkat.md) | +| ![](https://avatars.githubusercontent.com/u/88151966?v=4) | Nita Chenvisuwat | [Github](https://github.com/junenita) | [Portfolio](team/junenita.md) | +| ![](https://avatars.githubusercontent.com/u/88092096?v=4) | Jared Oong | [Github](https://github.com/jaredoong) | [Portfolio](team/jaredoong.md) | + diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 64e1f0ed2b..12ee47abe1 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -1,38 +1,905 @@ # Developer Guide +- [Acknowledgements](#acknowledgements) +- [Setting up, getting started](#setting-up-getting-started) +- [Design](#design) + - [Architecture](#architecture) + - [Meal360 Component](#meal360-component) + - [UI Component](#ui-component) + - [Parser Component](#parser-component) + - [RecipeList Component](#recipelist-component) + - [WeeklyPlan Component](#weeklyplan-component) + - [Database Component](#database-component) + - [IngredientList Component](#ingredientlist-component) +- [Implementation](#implementation) + - [Add Recipes Feature](#add-recipes-feature) + - [Edit Recipes Feature](#edit-recipes-feature) + - [Categorise/Tag Recipes Feature](#categorisetag-recipes-feature) + - [List Recipes Feature](#list-recipes-feature) + - [Delete Recipes Feature](#delete-recipes-feature) + - [Add Ingredients Feature](#add-ingredients-feature) + - [Delete Ingredients Feature](#delete-ingredients-feature) + - [List Ingredients Feature](#list-ingredients-feature) + - [Edit Weekly Meal Plan Feature](#edit-weekly-meal-plan-feature) + - [List Weekly Plan Feature](#list-weekly-plan-feature) + - [Mark Recipe as Done Feature](#mark-recipe-as-done-feature) +- [Appendix: Requirements](#appendix-requirements) + - [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) + +--- + ## Acknowledgements -{list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well} +- [GSON](https://github.com/google/gson) library used for saving and loading databases easily in + JSON format. + +--- + +## Setting up, getting started + +- Refer to the + instructions [here](https://github.com/AY2223S2-CS2113-F10-3/tp/blob/master/README.md). + +--- + +## Design + +- [Architecture](#architecture) +- [Meal360 Component](#meal360-component) +- [UI Component](#ui-component) +- [Parser Component](#parser-component) +- [RecipeList Component](#recipelist-component) +- [WeeklyPlan Component](#weeklyplan-component) +- [Database Component](#database-component) +- [IngredientList Component](#ingredientlist-component) + +### Architecture + +The Architecture Diagram below shows a high-level design of the Meal360 application: +![](./UML/Architecture/ArchitectureUML.png) + +**How the architecture components interact with each other:** + +Based on the user input, only certain components will be called to execute the command. For example, +if the user inputs `view 1`, the `Parser` component will call the `RecipeList` component to retrieve +the recipe at index 0, following which the `Ui` component will then display it to the user. The +sequence diagram below shows how the +interaction described above: +![](./UML/Architecture/ArchitectureExampleUML.png) + +Depending on whether the user input is to make changes to the recipes, ingredients, or weekly plan, +the `Parser` component will call the `RecipeList`, `Ingredient`, and/or `WeeklyPlan` component +respectively. The class diagrams below shows the subsystems for recipes, ingredients and weekly +plan. For the class diagrams, `Meal360` and the `Ui` component are not shown for simplicity. +Additionally, methods irrelevant to the subsystem shown are also omitted for simplicity. + +- Recipe related + +![](./UML/Architecture/RecipeRelated.png) + +- Ingredient related + +![](./UML/Architecture/IngredientRelated.png) + +- WeeklyPlan related + +![](./UML/Architecture/WeeklyPlanRelated.png) + +### Meal360 Component + +API: `Meal360.java` + +The (partial) class diagram below shows the structure of the `Meal360` component: +![](./UML/Meal360/Meal360ClassDiagram.png) + +The `Meal360` component: + +- initializes the databases for the recipes, ingredients, and weekly plan upon program startup. +- receives the user input once initialization is complete. +- passes the user input to the `Parser` component. +- receives the output from the `Parser` component and passes it to the `Ui` component to be + displayed + to the user. +- saves the databases for the recipes, ingredients, and weekly plan upon receiving the `bye` + command. +- terminates the program once the databases have been saved. + +### UI Component + +API: `Ui.java` + +The `Ui` component: + +![](./UML/Ui/UiClassDiagram.drawio.png) + +- displays the welcome message upon startup of the program. +- formats all the output to be displayed to the user. +- provides the user with information on whether command was executed successfully or not. +- displays the error message if the command was not executed successfully. +- displays the goodbye message upon receiving the `bye` command. + +The sequence diagram below shows how the `Ui` component works together with `Parser` +when the user inputs `list`. + +![](./UML/Ui/commandList.drawio.png) + +### Parser Component + +API: `Parser.java` + +The `Parser` component: + +![](./UML/Parser/ParserClassDiagram.png) + +- receives the user input from the `Meal360` component +- checks and filters input string +- catches `exceptions` and throws error messages via `UI` to + 1. prompts user to enter valid input + 2. prevent `Meal360` from crashing upon invalid input +- interacts with the `RecipeList`, `WeeklyPlan`, and/or `Ingredient` components to execute the + commands. + +The sequence diagram below shows how `Parser` filters user input dates and parses them +as valid `LocalDate` objects. + +![](./UML/Parser/parseDate.drawio.png) + +### RecipeList Component + +API: `RecipeList.java` and `Recipe.java` + +![](./UML/RecipeList/RecipeListClassDiagram.png) + +`Recipe` is a class with the following characteristics: + +- contains `name` and `ingredients` attribute +- store the ingredients details the user has added in `ingredients` + +The `RecipeList` component: + +- extends from `ArrayList` +- stores the recipes the user has added as `Recipe` objects +- allows users to add their own recipes +- allows users to delete the existing recipes +- allows users to edit the existing recipes +- allows users to view the entire list of recipes +- allows users to view the ingredients required for a recipe +- allows users to add and remove recipes from a tag +- allows users to random a recipe from all the recipes that users have + +### WeeklyPlan Component + +API: `WeeklyPlan.java` + +![](./UML/WeeklyPlan/WeeklyPlanClassDiagram.png) + +The `WeeklyPlan` component: + +- extends from `HashMap` +- stores the names of recipes that the user plans to prepare for the week as `String` +- stores the number of times the user plans to prepare each recipe as `Integer` +- allows users to add single or multiple recipes from the weekly plan +- allows users to delete single, multiple, or all recipes from the weekly plan +- allows users to see all ingredients for the week + +How the `WeeklyPlan` component works: + +1. When the user enters an input with the first word being `weekly`, the input is passed to + the `Parser` component. +2. Based on the second argument, the `Parser` component will call the appropriate method in the + `Parser` component. +3. `Parser` component then returns a `WeeklyPlan` object to indicate the recipe(s) to be added or + deleted from the weekly plan. +4. `WeeklyPlan` component then uses either `addPlans` or `deletePlans` method to add or delete + the recipe(s) from the weekly plan, `clearPlan` to clear the entire plan for the week, + and `checkValidity` to + check the validness of the weekly plan (i.e. make sure all recipes are valid). + +The sequence diagram below shows how the `WeeklyPlan` component works when the user +inputs `weekly /add burger 1`: +![](./UML/WeeklyPlan/AddWeeklyPlanUML.png) + +### Database Component + +API: `Database.java` + +![](./UML/Database/DatabaseClassDiagram.png) + +The `Database` component: + +- stores the recipes, ingredients, and weeklyplan in a local database in json format +- loads up automatically upon startup of program +- saves automatically upon exit of program +- comes with a default database of 10 recipes for new users + +How the `Database` component works at start up for the recipes: + +1. Upon starting up the program, the `Database` component will check for the existence of a + database file in the local directory. +2. If the database file exists, the `Database` component will load the recipes from the database + file into a `RecipeList` and return this `RecipeList`. +3. If the database file does not exist, the `Database` component will check if the parent directory, + and create any missing directories and files as necessary. +4. If the database file does not exist or if the file was empty, the `Database` component will + create a new `RecipeList` + with 10 default recipes and return this `RecipeList`. + +- The loading of the ingredients and weekly plan works in a similar manner, except that the default + ingredient list and weekly plan are empty. + +The activity diagram below shows how the `Database` component works at start up: +![](./UML/Database/DatabaseStartupUML.png) + +### IngredientList Component + +API: `IngredientList.java` and `Ingredient.java` + +![](./UML/IngredientList/IngredientListClassDiagram.png) + +`Ingredient` is a class with the following characteristics: + +- contains `name`, `quantity`, and `expiryDate` attribute. +- store the ingredients details the user has added in per ingredient. + +The `IngredientList` component: + +- extends from `HashMap` +- stores the ingredients the user has added as `Ingredient` objects +- allows users to add their own ingredients +- allows users to delete the existing ingredients +- allows users to view the entire list of ingredients that they have + +--- + +## Implementation + +- [Categorise/Tag Recipes Feature](#categorisetag-recipes-feature) +- [List Recipes Feature](#list-recipes-feature) +- [Delete Recipes Feature](#delete-recipes-feature) +- [Add Recipes Feature](#add-recipes-feature) +- [Edit Recipes Feature](#edit-recipes-feature) +- [Random a Recipe Feature](#random-a-recipe-feature) +- [Add Ingredients Feature](#add-ingredients-feature) +- [Delete Ingredients Feature](#delete-ingredients-feature) +- [List Ingredients Feature](#list-ingredients-feature) +- [Edit Weekly Meal Plan Feature](#edit-weekly-meal-plan-feature) +- [Mark Recipe as Done Feature](#mark-recipe-as-done-feature) + +### Categorise/Tag Recipes Feature + +- add single or multiples recipes into a tag +- remove single or multiples recipes from a tag + +It is implemented through the following step: + +1. When the user enters an input with the first word being `tag`, the input is passed to + the `Parser` component. +2. In `Parser`, `parseTagRecipe()` is executed to identify whether user want to add recipes + to a tag (`<<`), or remove recipes from a tag(`>>`). Then, + - If `isAddTag`, user want to add recipes to a tag, `parseAddRecipeTag()` will be executed to + extract + the all the recipes to be added, separated by `&&`, and pass those recipes and tag label + to `RecipeList` + component. + - If `isRemoveTag`, user want to remove recipes from a tag, `parseRemoveRecipeTag()` will be + executed to + extract the all the recipes to be removed, separated by `&&`, and pass those recipes and tag + label to + `RecipeList` component. + - If user enter invalid command, an error message will be thrown. +3. In `RecipeList`, + - If user want to add recipes to a tag, `addRecipeToTag()` is executed to add recipes in + to the tag. + - If user want to remove recipes to a tag, `removeRecipeFromTag()` is executed to remove recipes + from the tag. + +The sequence diagram below shows how this feature works: + +![](./UML/Implementation/TagFunction/TagFunction.png) + +### List Recipes Feature + +- list all recipes +- list recipe with filters (name, ingredients, tags) + +It is implemented through the following step: + +1. When the user enters an input with the first word being `list`, the input is passed to + the `Parser` component. +2. In `Parser`, `parseListRecipe()` is executed to first identify whether user want to filter + by tag (`/t`). + + - If user filters the recipes by tag (`/t`), `isTag` is set to `true`. + - Otherwise, `isTag` is set to `false`. + + Then, it will extract all the filters separated by `&&`, if any. All the filters are + extracted out and passed to `RecipeList`component. + +3. In `RecipeList`, `listRecipes()` is executed to first identify whether user want to + filter by tag. + - If `isTag` is true, `listTagRecipes()` is called to filter all recipes that meet + all the filters by tag, and return the `recipeList` containing all relevant recipes + to `listRecipes()` + and `ParserRecipe()`, respectively. + - If user `isTag` is false, it filters all recipes that meet all the filters by name + and ingredients, and return `recipeList` containing all relevant recipes to + `ParselistRecipe()`. + +The sequence diagram below shows how this feature works: + +![](./UML/Implementation/ListFunction/ListFunction.png) + +### List Available Recipes Feature + +- list all recipes that have sufficient ingredients + +It is implemented through the following steps: + +1. When the user enters and input `available`, an instance of `RecipeList` availableRecipes is + created +2. This instance of recipe calls the `availableRecipes()` method in `RecipeList` on the current + recipeList which + returns a `RecipeList` object containing all available `Recipes` +3. `availableRecipes()` method iterates through `recipeList` and calls the `isAvailable()` method on + each `Recipe`, + appending the available recipes to `availableRecipes` +4. `isAvailable()` method + - iterates through the `ingredients` Hashmap of each `Recipe` + - compares the quantity required for each recipe to the amount of the same `Ingredient` in + the `ingredientList`, + - returns `true` if the quantity in the `ingredientList` is more than or equal to the quantity + required +5. The `listAvailableRecipes()` method is then called in `Ui` to print `availableRecipes` + +The sequence diagram below shows how this feature works: + +![](./UML/Implementation/ListAvailableFunction/availableSequenceDiagram.drawio.png) + +### Delete Recipes Feature + +The current implementation: + +- deletes a single recipe by name or recipe's index in recipe list +- deletes a range of recipes +- deletes all recipes + +It is implemented through the following step: + +1. When the user enters an input with the first word being `delete`, the input is passed to + the `Parser` component. +2. In `Parser`, `parseDeleteRecipe()` is executed to identify whether the user wants to delete all + recipes, a single recipe, or range of recipes. + + - If user inputs `delete /r {some recipe or all}`, assume user has inputted a recipe name or all. + - If user inputs `delete {index or range}`, assume user has inputted an index or range. + - Checks in place to ensure user has inputted recipe(s) in the correct format. + - Checks in place to ensure user is inputting a recipe that is currently in the list, a valid range, or a + valid index. + + `parseDeleteRecipe()` will return the name of the recipe that was just deleted. + +3. In `RecipeList`, `deleteRecipe()` is executed to delete the recipe at whatever index is passed as + a parameter, and return the `Recipe` object at that index/the one just deleted. + +![](./UML/Implementation/DeleteFunction/DeleteFunction.jpg) + +### Add Recipes Feature -## Design & implementation +The current implementation: -{Describe the design and implementation of the product. Use UML diagrams and short code snippets where applicable.} +- Add a single recipe in 1 line and followed by all the ingredients in next another line after being + prompted. +It is implemented through the following steps: + +1. When the user enters an input with the first word being `add`, the input is passed to + the `Parser` component. +2. In `Parser`, the `parseAddRecipe` is executed to identify whether the recipe is an already + existing recipe or + it's a new recipe that is being added. +3. After the user enters the ingredients in 1 line, the input is passed to `parseIngredientName` + which returns a + hashmap with the ingredient name as 'key' and quantity as 'value'. +4. After the recipe name and ingredients are accepted and processed, the input is sent + to `recipeList.addRecipe()` + to store the new recipe data. + +![](UML/Implementation/addRecipe/AddRecipe.png) + +### Edit Recipes Feature + +The current implementation: + +- There are 3 ways to edit: + - Edit all ingredients. + - Edit 1 particular ingredient. + - Add new ingredient. + +It is implemented through the following steps: + +1. When the user enters an input with the first word being `edit`, the input is passed to + the `Parser` component. +2. In `Parser`, the `parseEditRecipe` is executed to identify whether the recipe is an already + existing recipe to make edits. +3. The user will then be prompted with 3 options as mentioned above to make edits to the recipe + ingredients. +4. After the new ingredients are accepted and processed, the input is sent + to `recipeList.editRecipe()` + to update the new recipe data. + +![](UML/Implementation/editRecipe/EditRecipe.png) + +### Random a Recipe Feature + +- randomly pick a recipe and display to the user + +It is implemented through the following step: + +1. When the user enters an input with the first word being `random`, the `Parser` + component will be executed. +2. In `Parser`, `parseRandomRecipe()` is executed and call `randomRecipe()` in + `RecipeList` component. +3. In `RecipeList`, `randomRecipe()` is executed to random a recipe and return a + `recipe` to `parseRandomRecipe()`. + +The sequence diagram below shows how this feature works: + +![](./UML/Implementation/RandomFunction/RandomFunction.png) + +### Add Ingredients Feature + +The current implementation: + +- allows for adding only 1 ingredient at a time. +- ingredient needs to have its name, quantity and expiry date specified. + +It is implemented through the following steps: + +- When the user enters an input with the first word being `add_i`, the input is passed to + the `Parser` component. +- In `Parser`, the `parseAddUserIngredient` is executed to identify whether all the required + parameters are provided. +- If any of the parameters are missing/invalid, the user will be alerted with a relevant error + message. +- If all the parameters are valid, the input is sent to `IngredientList` component. +- In `IngredientList`, `addIngredient()` is executed to first check if the ingredient already + exists in the list. +- If the ingredient already exists, the quantity of the ingredient will be updated. +- If the ingredient does not exist, the ingredient will be added to the list. + +The simplified sequence diagram below shows how this feature works assuming all inputs are correct: + +![](./UML/Implementation/AddIngredientFunction/AddIngredientFunction.png) + +### Delete Ingredients Feature + +The current implementation: + +- allows for deleting only 1 ingredient at a time. +- ingredient needs to have its name and quantity specified. + +It is implemented through the following steps: + +- When the user enters an input with the first word being `del_i`, the input is passed to + the `Parser` component. +- In `Parser`, the `parseDeleteUserIngredient` is executed to identify whether all the required + parameters are provided. +- If any of the parameters are missing/invalid, the user will be alerted with a relevant error + message. +- If all the parameters are valid, the input is sent to `IngredientList` component. +- In `IngredientList`, `deleteIngredient()` is executed to first check if the ingredient exists in + the list. +- If the ingredient does not exist, the user will be alerted with a relevant error message. +- If the ingredient exists and the new quantity is more than 0, the quantity of the ingredient will + be updated. Otherwise, the ingredient would be deleted entirely from the list. + +The sequence diagram of deleting an ingredient is similar to adding an ingredient. + +### List Ingredients Feature + +The current implementation: + +- allows for listing all ingredients in the list. + +It is implemented through the following steps: + +- When the user enters an input with the first word being `view_ingredients`, the list of user + ingredients that is represented by `userIngredients` is passed to the `Ui` component. +- In `Ui`, `printUserIngredients()` first checks if the list is empty. +- If the list is empty, the user will be alerted with a relevant message and the function exits. +- If the list is not empty, the list of ingredients will be printed out, with information regarding + the names, quanntity, and expiry date of each ingredient displayed. + +The sequence diagram below shows how this feature works: + +![](./UML/Implementation/ViewIngredientFunction/ViewIngredientFunction.png) + +### Edit Weekly Meal Plan Feature + +The current implementation: + +- allows for adding single recipe to the weekly meal plan. +- allows for deleting single recipe from the weekly meal plan. +- allow for adding multiple recipes to the weekly meal plan. +- allow for deleting multiple recipes from the weekly meal plan. + +It is implemented through the following steps: + +- When the user enters an input with the first word being `weekly`, the input is passed to + the `Parser` component. +- In `Parser`, the `parseWeeklyMealPlan` is executed to identify whether the user wants to + add/delete only one or multiple recipes. +- If the user wants to add/delete only one recipe, parseEditSingleWeeklyPlan() is executed. +- If the user wants to add/delete multiple recipes, parseEditMultiWeeklyPlan() is executed. +- `parseEditSingleWeeklyPlan()` and `parseEditMultiWeeklyPlan()` checks if the command parameters + are valid first, then it returns the + name and quantity of the recipes specified by the user. If the command parameters are invalid or + the recipe is not a valid recipe, a relevant error message will be displayed. +- In `Meal360`, the second argument of the user input is then use to determine whether the user + wants to add or delete the recipes. +- If the user wants to add the recipes, the list of recipe(s) would be passed to the `WeeklyPlan` + component to execute `addPlans()`. +- If the user wants to delete the recipes, the list of recipe(s) would be passed to the `WeeklyPlan` + component to execute `deletePlans()`. + +The sequence diagram below shows how this feature works: + +![](./UML/Implementation/EditWeeklyPlan/EditWeeklyPlan.png) + +### List Weekly Plan Feature + +The current implementation: + +- allows for listing all recipes that are in the weekly plan. + +It is implemented through the following steps: + +- When the user enters an input with the first word being `weeklyplan`, the list of user + ingredients that is represented by `userIngredients` is passed to the `Ui` component. +- In `Ui`, `printUserIngredients()` first checks if the list is empty. +- If the list is empty, the user will be alerted with a relevant message and the function exits. +- If the list is not empty, the list of ingredients will be printed out, with information regarding + the names, quantity, and expiry date of each ingredient displayed. + +The sequence diagram below shows how this feature works: + +![](./UML/Implementation/ViewWeeklyPlan/ViewWeeklyPlan.png) + +### Mark Recipe as Done Feature + +The current implementation: + +- allows for marking a recipe in the weekly plan as done. +- marking a recipe as done automatically removes the recipe and one set of its ingredients from the + weekly plan. + +It is implemented through the following steps: + +- When the user enters an input with the first two word being `weekly` and `/done` respectively, the + input is passed to the `Parser` component. +- In `Parser`, the `parseMarkDone` is executed, which then send the input to `Recipe` component to + obtain the list of ingredients of the recipe using `getIngredients()`. +- The list of ingredients is then passed to `IngredientList` component to check if the user has all + the necessary ingredients using `findIngredientCount()`. +- If the user does not have all the necessary ingredients, the user will be alerted with a relevant + error message. +- If the user has all the necessary ingredients, the `IngredientList` component will then remove the + ingredients from the list using `deleteIngredient()`. +- The `WeeklyPlan` component will then remove the recipe from the weekly plan using `remove()` if no + counts of the recipe is left, otherwise it will reduce the count of the recipe by 1 using `put()`. + +The simplified sequence diagram below shows how this feature works assuming no counts of the recipe +is left after the deletion: + +![](./UML/Implementation/MarkDoneWeeklyPlan/MarkDoneWeeklyPlan.png) + +--- + +## Appendix: Requirements + +### Product scope -## Product scope ### Target user profile -{Describe the target user profile} +- has a need to manage a significant number of recipes +- has a need to manage a significant number of ingredients +- has a need to manage their meal plan on a weekly basis +- can type fast +- prefers typing to mouse input +- is reasonably comfortable using CLI apps ### Value proposition -{Describe the value proposition: what problem does it solve?} +Allows management of recipes, ingredients and weekly meal plan faster than a typical mouse/GUI +driven app. + +### User Stories + +| Version | As a ... | I want to ... | So that I can ... | +|---------|----------|---------------------------------------------------|-------------------------------------------------------------------| +| v1.0 | user | add my own recipes to the list | refer to them when next time | +| v1.0 | user | edit the existing recipe | | +| v1.0 | user | delete a recipe from the list | clear the unused recipes | +| v1.0 | user | view ingredients of the recipe | know what is needed to be prepared | +| v1.0 | user | list all recipes I have | know what I have some idea of what to cook | +| v1.0 | user | find the recipe that contain specific ingredients | find specific recipe without having to go through the entire list | +| v1.0 | user | exit from the program | | +| v2.0 | new user | list all the command that can be used | know what command I can use | +| v2.0 | user | add meals I plan to make for the week | refer to the weekly meals plan next time | +| v2.0 | user | delete meals I plan to make for the week | remove some meals from the weekly plan if I change my mind | +| v2.0 | user | categorise recipes using tags | group recipes with similar theme together | +| v2.0 | user | list the recipes by tag | list recipes that are under the specific category | +| v2.0 | user | random a recipe | have a suggestion when do not know what to cook | +| v2.0 | user | know what ingredients I have | know what ingredients I need to buy | +| v2.0 | user | add the ingredients I have | easily track the ingredients I have | +| v2.0 | user | delete the ingredients I have | easily track the ingredients I have | +| v2.0 | user | be able to cross recipes off my weekly plan | have a neater weekly plan | + +### Non-Functional Requirements + +1. Should work on any _mainstream OS_ as long as it has Java 11 or above installed. +2. A user with above average typing speed for regular English text (i.e. not code, not system + admin commands) should be able to accomplish most of the tasks faster using commands than + using the mouse, especially for the more advanced commands that allow for editing multiple items + at once. + +### Glossary + +- **Mainstream OS**: Windows, Linux, Unix, OS-X + +### Instructions for manual testing + +Given below are instructions to test the app manually. + +#### Launch and Shutdown + +1. Initial launch + + 1. Download the jar file and copy into an empty folder + 2. Run the jar file using the command `java -jar meal360.jar`. The GUI similar to the below + should appear in a few seconds. The app comes with a smaall sample data of 10 recipes to + allow for easy testing. + + Expected output: + + ``` + ---------------------------------------------------------------------------------------------------- + Welcome to Meal360, your ultimate Recipe Manager! + __ __ _ ____ __ __ + | \/ |___ __ _| |__ / / / / \ + | |\/| / -_) _` | ||_ \/ _ \ () | + |_| |_\___\__,_|_|___/\___/\__/ + + ---------------------------------------------------------------------------------------------------- + | Loading recipes... | + | Recipes loaded successfully. | + | Loading weekly plan... | + | Weekly plan loaded successfully. | + | Loading ingredients... | + | Ingredients loaded successfully. | + ---------------------------------------------------------------------------------------------------- + ``` + +2. Shutdown + + 1. A user input of `bye` allows the user to exit the app. The app will then save the data for + recipes, ingredients and weekly meal plan automatically before exiting. + + Expected output: + + ``` + ---------------------------------------------------------------------------------------------------- + | Saving recipes... | + | Recipes saved successfully. | + | Saving weekly plan... | + | Weekly plan saved successfully. | + | Saving ingredients... | + | Ingredients saved successfully. | + | Bye. Hope to see you again soon! | + ---------------------------------------------------------------------------------------------------- + ``` + +#### Ingredients + +1. Test case: `add_i /n fish /c 10 /d 10/10/2024` + Expected: The ingredient `fish` with quantity `10` and expiry date `10/10/2024` should be added + into the user ingredients list. A success message should be displayed. + + Expected output: + + ``` + ---------------------------------------------------------------------------------------------------- + | Ingredient successfully added! | + ---------------------------------------------------------------------------------------------------- + ``` + +2. Test case: `del_i /n fish /c 5` + Expected: The ingredient `fish` with quantity `5` should be deleted from the user ingredients. + + Expected output: + + ``` + ---------------------------------------------------------------------------------------------------- + | Ingredient successfully deleted! | + ---------------------------------------------------------------------------------------------------- + ``` + +3. Test case: `view_ingredients` + Expected: The list of ingredients should be displayed. + + Expected output: + + ``` + ---------------------------------------------------------------------------------------------------- + | Here is your ingredient list: | + | fish (5) [by:10/10/2024] | + ---------------------------------------------------------------------------------------------------- + ``` + +#### Weekly Plan + +1. Test case: `weekly /add avocado toast 2` + Expected: The recipe `avocado toast` should be added into the weekly plan with count `2`. + A success message should be displayed. + + Expected output: + + ``` + ---------------------------------------------------------------------------------------------------- + | I've added the recipes to your weekly plan! | + ---------------------------------------------------------------------------------------------------- + ``` + +2. Test case: `weekly /delete avocado toast 1` + Expected: The recipe `avocado toast` should be deleted from the weekly plan with count `1`. + A success message should be displayed. + + Expected output: + + ``` + ---------------------------------------------------------------------------------------------------- + | I've deleted the recipes from your weekly plan! | + ---------------------------------------------------------------------------------------------------- + ``` + +3. Test case: `weeklyplan` + Expected: The list of recipes in the weekly plan should be displayed. + + Expected output: + + ``` + ---------------------------------------------------------------------------------------------------- + | Here is your weekly plan: | + | avocado toast x1 | + ---------------------------------------------------------------------------------------------------- + ``` + +4. Test case: `weekly /multiadd /r chicken rice /q 3 /r seafood paella /q 1` + Expected: The recipes `chicken rice` and `seafood paella` should be added into the weekly plan + with count `3` and `1` respectively. A success message should be displayed. + + Expected output: + + ``` + ---------------------------------------------------------------------------------------------------- + | I've added the recipes to your weekly plan! | + ---------------------------------------------------------------------------------------------------- + ``` + +5. Test case: `weekly /multidelete /r chicken rice /q 2 /r seafood paella /q 1` + Expected: The recipes `chicken rice` and `seafood paella` should be deleted from the weekly plan + with count `2` and `1` respectively. A success message should be displayed. + + Expected output: + ``` + ---------------------------------------------------------------------------------------------------- + | I've deleted the recipes from your weekly plan! | + ---------------------------------------------------------------------------------------------------- + >>> weeklyplan + ---------------------------------------------------------------------------------------------------- + | Here is your weekly plan: | + | chicken rice x1 | + | avocado toast x1 | + ---------------------------------------------------------------------------------------------------- + ``` + +#### Tag/Categorize Recipes -## User Stories +1. Remove recipe from an unknown tag. + * Prerequisite: Make sure that `unknowntag` has never been created. + * Test case 1: `tag unknowntag >> recipe1` + * Expected: An error message is shown. + * Expected output 1: + ``` + ---------------------------------------------------------------------------------------------------- + | There is no "unknowntag" tag found. Please make sure you have entered the correct tag. | + ---------------------------------------------------------------------------------------------------- + ``` +2. Add tag command with an unknown recipe + * Prerequisite: Make sure that there is no recipe named `unknown` in the list, and + a recipe named `recipe1` using the `add` command. + * Test case 2: `tag testtag << recipe1 && unknown` + * Expected: An error message is showed. `recipe1` is added to `testtag` but `unknown` is not + added. + * Expected output 2: -|Version| As a ... | I want to ... | So that I can ...| -|--------|----------|---------------|------------------| -|v1.0|new user|see usage instructions|refer to them when I forget how to use the application| -|v2.0|user|find a to-do item by name|locate a to-do without having to go through the entire list| + ``` + ---------------------------------------------------------------------------------------------------- + | Unable to find the recipe: "unknown" in the tag. | + | All the recipe before "unknown" (if any) are successfully added from the tag. | + ---------------------------------------------------------------------------------------------------- + ``` -## Non-Functional Requirements +3. Remove tag command with an unknown recipe + * Prerequisite: Complete test case 2 + * Test case 3: `tag testtag >> recipe1 && unknown` + * Expected: An error message is showed. `recipe1` is removed `testtag` but `unknown` is not + removed. + * Expected output 3: + ``` + ---------------------------------------------------------------------------------------------------- + | Unable to find the recipe: "unknown" in the tag. | + | All the recipe before "unknown" (if any) are successfully removed from the tag. | + ---------------------------------------------------------------------------------------------------- + ``` +4. Successfully add a recipes into a tag + * Prerequisite: Add recipe named `recipe2` using the `add` command + * Test case 4: `tag testtag << recipe2` + * Expected: `recipe2` is added to `testtag`. + * Expected output 4: + ``` + ---------------------------------------------------------------------------------------------------- + | You have successfully added the recipe(s) to "testtag" tag. | + ---------------------------------------------------------------------------------------------------- + ``` -{Give non-functional requirements} +5. Successfully remove a recipe from a tag + * Prerequisite: Complete test case 4 + * Test case 5: `tag testTag >> recipe2` + * Expected: `recipe2` is removed `testtag`. + * Expected output 5: + ``` + ---------------------------------------------------------------------------------------------------- + | You have successfully removed the recipe(s) from "testtag" tag. | + ---------------------------------------------------------------------------------------------------- + ``` -## Glossary +#### List Recipes -* *glossary item* - Definition +1. Listing recipes when there is no recipe in the list. + * Prerequisite: Empty the list of recipes. + * Test case 1: `list` + * Expected output 1: + ``` + ---------------------------------------------------------------------------------------------------- + | There is nothing to list. | + ---------------------------------------------------------------------------------------------------- + ``` +2. List all recipes + * Prerequisite: Add one or more recipes using the `add` command. + * Test case 2: `list` + * Expected output 2: An ordered list of recipes is displayed. + The recipes' name that are added are listed out along with the number of ingredients needed. -## Instructions for manual testing +3. List specific recipes + * Test case 3: `list a` + * Expected output 3: + * An ordered list of recipes that contain `a` in their name or ingredients is displayed. + The recipes' name that are added are listed out along with the number of ingredients + needed. + * If there is no recipe or ingredient that contain `a` the expected output is identical + to expected output 1. -{Give instructions on how to do a manual product testing e.g., how to load sample data to be used for testing} +4. List recipes from a tag + * Prerequisite: Create a recipe named `recipefortag` using `add` command, and add the recipe to + `testtag1` using `tag` command. + * Test case 4: `list /t testtag1` + * Expected output 4: An ordered list of recipes that are in `testtag1` are listed out, + including `recipefortag`. + The recipes' name that are added are listed out along with the number of ingredients needed. diff --git a/docs/README.md b/docs/README.md index bbcc99c1e7..aa1a12e3ef 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,6 +1,9 @@ -# Duke +# Meal360 -{Give product intro here} +Meal360 is a desktop app for managing your recipes and weekly meal plans, optimized for use via a +Command Line Interface (CLI) while still having the benefits of a Graphical User Interface (GUI). If +you can type fast, Meal360 can get your recipe management tasks done faster than traditional GUI +apps. Useful links: * [User Guide](UserGuide.md) diff --git a/docs/UML/Architecture/.$ArchitectureExampleUML.drawio.bkp b/docs/UML/Architecture/.$ArchitectureExampleUML.drawio.bkp new file mode 100644 index 0000000000..ef8f47c11e --- /dev/null +++ b/docs/UML/Architecture/.$ArchitectureExampleUML.drawio.bkp @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/docs/UML/Architecture/.$ArchitectureExampleUML.png.bkp b/docs/UML/Architecture/.$ArchitectureExampleUML.png.bkp new file mode 100644 index 0000000000..b61f2136d6 Binary files /dev/null and b/docs/UML/Architecture/.$ArchitectureExampleUML.png.bkp differ diff --git a/docs/UML/Architecture/.$ArchitectureUML.drawio.bkp b/docs/UML/Architecture/.$ArchitectureUML.drawio.bkp new file mode 100644 index 0000000000..b1ede881fb --- /dev/null +++ b/docs/UML/Architecture/.$ArchitectureUML.drawio.bkp @@ -0,0 +1 @@ +ddHNEoIgEADgp+GuMJmezfLSyUNnRjZhBl0HabSePh0wY6wTy7fL30JY3k4Xw3t5RQGa0EhMhJ0IpXGWHuZhkaeTLEkdNEYJR9EGlXqBX7nqQwkYvDmyiNqqPsQauw5qGxg3Bsew7I5aBNDzBnZQ1Vzv9aaElV7jJNsSJahG+qNTenSJlq/F/iWD5ALHL2IFYblBtC5qpxz00rywL+c/2c/FDHT2x4I52PaeJ8EPseIN \ No newline at end of file diff --git a/docs/UML/Architecture/.$IngredientRelated.drawio.bkp b/docs/UML/Architecture/.$IngredientRelated.drawio.bkp new file mode 100644 index 0000000000..38e24b20ca --- /dev/null +++ b/docs/UML/Architecture/.$IngredientRelated.drawio.bkp @@ -0,0 +1 @@ +1Vpbc9o4FP41zHQfmpEsXx8D6V5m0t1s03a7j8KWQRNhubII0F9fyRewLRtIMZDkIWMdyZL8ne9ckxGaLNZ/CJzOP/KIsJEFovUI3Y0sy4a2+q0Fm0Jg2W4hmAkaFSKwEzzSH6QQwkq6pBHJSlkhkpwzSdOmMORJQkLZkGEh+Kq5LOYsaghSPCOG4DHEzJT+RyM5L6XQDXYTfxI6m5dH+5ZXTCxwtbj8kmyOI76qidCHEZoIzmXxtFhPCNPYNXH5vWd2ezFBEnnMCz++rZLFD+Q9fv3n899P9prQ8b/vLafY5hmzZfnF5W3lpoJgLhdMPcERGseUsQlnXKhxRGK8ZOrocbkFEZKsey8Ht5+sqEL4gkixUUuqF5wSpU0FcAnjage6h8o18xreKCi/AJeKnm333mGhHko4XgANNJD5SDBDLjAAUppN9eNywe5pTBhN1GicEkHVTYjGipXih51svJpTSR5THOpXV8p4lKwGtSK0xOoVsR0zhtOMTvNTgZIIEi5FRp/JJ5IVdqOlfCn1SZOtPYCW3kYWiv2QhKGSZ1LwJ1KbmfqO7YBhdIoAbOrUsw2dQtCl00o4vE7Bi+iecprI/A7OeOTctZTKhZzzGU8wq6v1BBPpp2EvxrbTgNiHHQibAMMgOBPAloHvAxaZAubN20zkED+yu2zGt6bIdYexGQe6LZtxr24z6M3ZjPUSm4GwC2ITYdc9E8Amvp9ISFNyTzP59u0mjmOrO9ZE7tR1BrIb17FeXaxx35zdoBfZDQqOspsqIR4cYPswvj3WgoWkysJwDoaaU0ROIqyudDVL0tqiquq4ZXSWKJnkeuNMnUOT2Wc9uENun8UNYD/QQU3l+mb+HVzUfI6wHqHVRqIShQOa6baVplNKeM6PQfBs575Wh7F4HVHmXHh6Bp5fDuRlt6HUqGy5eY+nhD3wjErKNUenXEq+6CXvRcpGv0Vb20yAu6rGs6EMzapRGTxR5v5Xki7lu5Hlfl/qin/8TMlKLYQ7yW/7wkMb4y34JIludZ9DyxgPn3KR+pRvpVnkg/8r59OwmOI8ErU6IBKLGdlbkoAePanL86UIySGn3avP9+AGAFQ6dkEYlgq5xgFdGiu3e9ARtBauqhZC1XnyWwlccdfyrXrjpNqoWsjjOCPS4Mb2+ifQxayXTiUBT4maGkc4m+eKhsV02Vnz9WhNpWaHBhvapSBnyE0QuAZL9Ozduj7Y1Aa12LiPUwd5sYdTB/nYw6mB+YNcq7lFcS+DP4Nxw8xvUl1Lf1WOoygQ9nqTkTXJMagqiYt7l4pmilV+g2TQ84ai1WFXhX7dVUFwFl4hx2/6paAVjHr80mC8Mlu+gnxfkkySqODV5VwQaHkf/1rep58mR0bD/SGtyvROdUmoWeWidk+ixyVdNKSZiaa60jtwNf8Db1T23eAZAvblHJB7ggPqqcdPZJHbZhFsbXFuB+S/WgfkHJsjH9Zdv96PDFqD690Dzd4y8ludmTMnNFZg6P0LNVT95hqcAPgYgK4GJwCOl8/E6vhmS1T9DFMBe1Wj8/U0PpFZA+8z6NfQ+CyoeWzn06rShUOdT/tcCL/sTzIXdpm7AH0TWHYjSN94nlMJdrE3H23qo6HyOtRnTyfkdad6YTtokMl20UW9cBX862WlUGeVJWUrEl+zamzwzHXQxXK2Pay5VtHYZo0Ru385Z1PD3f8gFct3/8iFPvwE \ No newline at end of file diff --git a/docs/UML/Architecture/.$RecipeRelated.drawio.bkp b/docs/UML/Architecture/.$RecipeRelated.drawio.bkp new file mode 100644 index 0000000000..73784a7c4a --- /dev/null +++ b/docs/UML/Architecture/.$RecipeRelated.drawio.bkp @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/docs/UML/Architecture/.$RecipeRelated.png.bkp b/docs/UML/Architecture/.$RecipeRelated.png.bkp new file mode 100644 index 0000000000..71bdc1cac9 Binary files /dev/null and b/docs/UML/Architecture/.$RecipeRelated.png.bkp differ diff --git a/docs/UML/Architecture/.$WeeklyPlanRelated.drawio.bkp b/docs/UML/Architecture/.$WeeklyPlanRelated.drawio.bkp new file mode 100644 index 0000000000..989321496d --- /dev/null +++ b/docs/UML/Architecture/.$WeeklyPlanRelated.drawio.bkp @@ -0,0 +1 @@ +7Vptc+I2EP41zKSdCWNbtnE+Brj00kmuuZDrtf2mYIE1J1uMLA64X98Vlt9lIDkz/VAyzAStpV1rn9XqWYkBmsTb3wReRY88JGzgWOF2gKYDx7GdGw/+Kckuk4z8IBMsBQ0zkVUKZvQH0SNz6ZqGJNWyTCQ5Z5Ku6sI5TxIylzUZFoJv6t0WnIU1wQovSUswm2PWln6loYy01PZvygcfCV1G2nTgjLIHMc4765mkEQ75piJCHwZoIjiX2bd4OyFMOa/ul7uOp8WLCZLIUwZE93f399HT767c3H3xdo+W4ItrW8PzHbO1nrF+W7nLXSD4OgmJ0mIN0HgTUUlmKzxXTzcAOsgiGTNo2fA1lYJ/IxPOuABJwhPoNtY2iJBk2/n2duETCCbCYyLFDrroATc6bHQYeVrBpsQkcLUsqsDheRoOrONgWWguXQVftLfMnvs8HQnnYTKj6OXb5+U/X9MR8a/tluOesEiJaLkv3dCY4b0fFjyRM/1EOQszukzg+xycACPRWLmIQvDd6geSK/fOI8rCB7zjazWrVOL5t7w1jrigP0AtLgHAQup15Pi1HjM1UsMoSAp9nnL/2w3RI97WOj7gVGrBnDOGVyl9LaYRY7GkyZhLyWPdqQNeQxB0Iu54fg3ykdOC3M0XWxVyJw/h3iF3WpBfQ3NNB+gW/n+hLehhprJrTSwoYw1RHg6MLGRnMKSw9GiyfNj3mbql5Fm7QIk4jF2wfbaJaBiSRAHJJZY4Q01BtOI0kXsXeWP4QPKYWENv4MGLT6Btl234qO5CTngCc8F0jymBkNgQFRYGtA8umeMhoCF3/BMh98+EODqeHRndY5dhnG8R9rsAjgEqRkpEXxTg02u7hTpqo44MCDP8StgTT6mkXOkXWd8G8sfArUcvTSIi6DlB905c58GZMHcNmI+zmabkNgyfyZyuyBVNVioBq5U/kwKQ0M6ExeNYYt/ngaa6x3PR/qUqGKjARXaWMAsbH0Iqz25kShiRpBczeojRzAteVm2kZzFSgALWruY8jnES1kf9tIlnEkNOPbcV1a0fd5UCs6U/KdloS6aptEzpNxFvCbFn0MpjbaWmpvq6lz2z3/TpW6elT9s/G01qU+MWyiQJb1V1VgJZqSHqBQf4R+z+UhuhNbQsJ5f8rR4PvcDPBdNtdcB0V209wZ4Fk1McOxNuqVQq7VwjtDOFAXJ1u9SnGrtKo6mtk+ymfC3m5FD5pcEiYa0KbYNbQdOzDGWOlgnCsKTf67WrCWBt4UnFc0m5PWs0RIFXo92u5w8Dt0Gwsonp0dWas6HQt6yhHwRW/mfXVTto6CK3+TQ3AoXMksiWkX1MFq75iTBt0/lfu7JRJTiP1mwFi9tXTp3JQ+UHvJY8zUq1ztrZkPN6KK68Ub2edi23lTYcdB6mbVwJbTDyreVSTvdygOI3Fh8atRFHliG32KgHpm3EvF1dqXo6wTE5SJVAJkhISZLxo4kzuLU+4jR6xCvoyxSMVUZ2D8GwVOcy/rLcd/+3hONIDHWvzbcU6cY4cns4iTO+nqlguxTp1SK9f9BH6DTQz5U6TMfWKjcAYfgE+ePqcLEFve7LJJJ1fnMaaev8tI7/WLQUq4RVr8IuCajXWAzadwHGWOzjKsDM4k/IQO8peKyh49rVekeVK8F76h1jlWGkkBnpPkCb3f+yQEHeTYPEeO8rTDwX1RUFDUX9FR/GkLnpoLvZic2F8vZCeW1/VAc5v3uuZgpkiFbgvOdKFZaR80q8PInLNh7lJZJ5f7rQ3fftNjdvprumGELnqpTtE47XLny3Z9Tzw9JjqJ+L79rt85GMdi5oEo53e87bLJwPc2Bc3GRlR/PVk3k98juHd2qOC6sXR9lQ4L0F0T18L1AYfeHqFgXSXtcNyqmvIyoXM3eCxz2pZcVNTHoFawb21s67GJq+5PZeOWcEJ8fvYJT64mrsgIXjmkT1kqXh/Evm7zUHjNo3K8YcYLtvZw/QLH8JlpHM8vd06MO/ \ No newline at end of file diff --git a/docs/UML/Architecture/ArchitectureExampleUML.drawio b/docs/UML/Architecture/ArchitectureExampleUML.drawio new file mode 100644 index 0000000000..38e24b20ca --- /dev/null +++ b/docs/UML/Architecture/ArchitectureExampleUML.drawio @@ -0,0 +1 @@ +1Vpbc9o4FP41zHQfmpEsXx8D6V5m0t1s03a7j8KWQRNhubII0F9fyRewLRtIMZDkIWMdyZL8ne9ckxGaLNZ/CJzOP/KIsJEFovUI3Y0sy4a2+q0Fm0Jg2W4hmAkaFSKwEzzSH6QQwkq6pBHJSlkhkpwzSdOmMORJQkLZkGEh+Kq5LOYsaghSPCOG4DHEzJT+RyM5L6XQDXYTfxI6m5dH+5ZXTCxwtbj8kmyOI76qidCHEZoIzmXxtFhPCNPYNXH5vWd2ezFBEnnMCz++rZLFD+Q9fv3n899P9prQ8b/vLafY5hmzZfnF5W3lpoJgLhdMPcERGseUsQlnXKhxRGK8ZOrocbkFEZKsey8Ht5+sqEL4gkixUUuqF5wSpU0FcAnjage6h8o18xreKCi/AJeKnm333mGhHko4XgANNJD5SDBDLjAAUppN9eNywe5pTBhN1GicEkHVTYjGipXih51svJpTSR5THOpXV8p4lKwGtSK0xOoVsR0zhtOMTvNTgZIIEi5FRp/JJ5IVdqOlfCn1SZOtPYCW3kYWiv2QhKGSZ1LwJ1KbmfqO7YBhdIoAbOrUsw2dQtCl00o4vE7Bi+iecprI/A7OeOTctZTKhZzzGU8wq6v1BBPpp2EvxrbTgNiHHQibAMMgOBPAloHvAxaZAubN20zkED+yu2zGt6bIdYexGQe6LZtxr24z6M3ZjPUSm4GwC2ITYdc9E8Amvp9ISFNyTzP59u0mjmOrO9ZE7tR1BrIb17FeXaxx35zdoBfZDQqOspsqIR4cYPswvj3WgoWkysJwDoaaU0ROIqyudDVL0tqiquq4ZXSWKJnkeuNMnUOT2Wc9uENun8UNYD/QQU3l+mb+HVzUfI6wHqHVRqIShQOa6baVplNKeM6PQfBs575Wh7F4HVHmXHh6Bp5fDuRlt6HUqGy5eY+nhD3wjErKNUenXEq+6CXvRcpGv0Vb20yAu6rGs6EMzapRGTxR5v5Xki7lu5Hlfl/qin/8TMlKLYQ7yW/7wkMb4y34JIludZ9DyxgPn3KR+pRvpVnkg/8r59OwmOI8ErU6IBKLGdlbkoAePanL86UIySGn3avP9+AGAFQ6dkEYlgq5xgFdGiu3e9ARtBauqhZC1XnyWwlccdfyrXrjpNqoWsjjOCPS4Mb2+ifQxayXTiUBT4maGkc4m+eKhsV02Vnz9WhNpWaHBhvapSBnyE0QuAZL9Ozduj7Y1Aa12LiPUwd5sYdTB/nYw6mB+YNcq7lFcS+DP4Nxw8xvUl1Lf1WOoygQ9nqTkTXJMagqiYt7l4pmilV+g2TQ84ai1WFXhX7dVUFwFl4hx2/6paAVjHr80mC8Mlu+gnxfkkySqODV5VwQaHkf/1rep58mR0bD/SGtyvROdUmoWeWidk+ixyVdNKSZiaa60jtwNf8Db1T23eAZAvblHJB7ggPqqcdPZJHbZhFsbXFuB+S/WgfkHJsjH9Zdv96PDFqD690Dzd4y8ludmTMnNFZg6P0LNVT95hqcAPgYgK4GJwCOl8/E6vhmS1T9DFMBe1Wj8/U0PpFZA+8z6NfQ+CyoeWzn06rShUOdT/tcCL/sTzIXdpm7AH0TWHYjSN94nlMJdrE3H23qo6HyOtRnTyfkdad6YTtokMl20UW9cBX862WlUGeVJWUrEl+zamzwzHXQxXK2Pay5VtHYZo0Ru385Z1PD3f8gFct3/8iFPvwE \ No newline at end of file diff --git a/docs/UML/Architecture/ArchitectureExampleUML.png b/docs/UML/Architecture/ArchitectureExampleUML.png new file mode 100644 index 0000000000..61b0605110 Binary files /dev/null and b/docs/UML/Architecture/ArchitectureExampleUML.png differ diff --git a/docs/UML/Architecture/ArchitectureUML.drawio b/docs/UML/Architecture/ArchitectureUML.drawio new file mode 100644 index 0000000000..2c128a3c22 --- /dev/null +++ b/docs/UML/Architecture/ArchitectureUML.drawio @@ -0,0 +1 @@ +5Vpdb5swFP01edwENhh4bNOuW9dJ1ap99NGASdw5GBmnDfv1M8UUqFlaErJQLZUqfP0R+9xzrq9NZnC+2lwInC2/8JiwGbDizQyezQDw7UD9Lw1FZXB8UBkWgsaVyWoMN/Q3qYx2bV3TmOTaVpkk50zSrGuMeJqSSHZsWAj+0G2WcBZ3DBleEMNwE2FmWn/QWC611UZBU/GR0MVSf7UPvKpihevGeiX5Esf8oWWC5zM4F5zL6mm1mRNWYtfF5cNfap8mJkgqX9MhDIvL6/PL38VSfP7+YX4Z3WWrd44e5h6ztV6xnq0sagiWcsXUkz2Dp7kU/BeZc8aFsqQ8VU1OzYnUgxIhyaZl0hO7IHxFpChUk5omqOqhSWI7sCo/NJB7nh512UIbOr52tXbz4mnoBgn1oMEYAswgXEaAwEZeFwOEDAwQck0MQGAdCgPLWLPg6zQmZSdLLZsLueQLnmJ2xXmmsbgjUhZayHgtuTK1kFIAieKn7v9YuC0L7926eLZpV54VuvRXhCUWCyK3rEJDRuKO0E0/CMKwpPdd3fdhqrtec6qm0vgP+h3/QYC6Q+R8LSKiez3zzNM0dneWKeRvORGGA1UUysrH9YqdRLKU8WnJUaoi3hUOCbvmOZWUp6pJyKXkq1aDE0YXZYUsfd12Kl9LRlMVF+oYvNVjAzRhdeMCCExNQMuUBDqUIgIDz5JWN7rYyOG8sZ7uL5kNlT8bkajSbaum0UtZqOXSyKxR1m1HWP0ySyhjdXifAZj4EYkiI/CrmtB3HXermyu+vyzMFwXsjy3gvShg28fkgN1iQMOHlzgwLNROkQN1OjgVEsDpBAJrp0Bgv0ESoElxwDU23C8EM4jMpKnlwhFhHbCN+s9SE8s20+vA3EbdQ22jYBf5RGtx/6ge+3AB1XttRN1rV40x8ZNer6PIJ2Gyl5jQayMqmJSagDNRSvxHjJhWfAXuRBkBtlNikq4NJuVaZGydZ1jiEOdkwN65D7C775225xx37/QN7L7RAagRO3aJ14dagDyI0YEyDt89csZhZmXHCia7Ze97HeGUy/3Y6XO6D0KItjr9xRhUH83eXMaBJkqJ3W52Bh7oJkKJ0felx64nQuCi1SArL13z1sjP7m6fDm11uPJAm15Ge88C29qrh2oGo17w1i+WJsDXMTLkt8nX0VPknfjqBt178fpdw0H5V4PUyjyuseh7x7AlZ9vDi6/PPmDdZSr3HfXO14LuK4loRq5oLgfAlyQJ6L8uilGI3JHgc9xuNFTh8f2R0zfbPDH8IOQXKzKG0yEAuuVf75nh8VP24Kls2avPOMC6LpocsIEB7Kd0IUhM1TIHsjO04zix+sC1LQ8GZBwQEQL/DkRVbH6gUQXS5lcu8PwP \ No newline at end of file diff --git a/docs/UML/Architecture/ArchitectureUML.png b/docs/UML/Architecture/ArchitectureUML.png new file mode 100644 index 0000000000..bfae3c75c6 Binary files /dev/null and b/docs/UML/Architecture/ArchitectureUML.png differ diff --git a/docs/UML/Architecture/IngredientRelated.drawio b/docs/UML/Architecture/IngredientRelated.drawio new file mode 100644 index 0000000000..27f388e91e --- /dev/null +++ b/docs/UML/Architecture/IngredientRelated.drawio @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/docs/UML/Architecture/IngredientRelated.png b/docs/UML/Architecture/IngredientRelated.png new file mode 100644 index 0000000000..9372e0f17e Binary files /dev/null and b/docs/UML/Architecture/IngredientRelated.png differ diff --git a/docs/UML/Architecture/RecipeRelated.drawio b/docs/UML/Architecture/RecipeRelated.drawio new file mode 100644 index 0000000000..a2c4d0b03c --- /dev/null +++ b/docs/UML/Architecture/RecipeRelated.drawio @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/docs/UML/Architecture/RecipeRelated.png b/docs/UML/Architecture/RecipeRelated.png new file mode 100644 index 0000000000..9da0021425 Binary files /dev/null and b/docs/UML/Architecture/RecipeRelated.png differ diff --git a/docs/UML/Architecture/WeeklyPlanRelated.drawio b/docs/UML/Architecture/WeeklyPlanRelated.drawio new file mode 100644 index 0000000000..1efbab1276 --- /dev/null +++ b/docs/UML/Architecture/WeeklyPlanRelated.drawio @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/docs/UML/Architecture/WeeklyPlanRelated.png b/docs/UML/Architecture/WeeklyPlanRelated.png new file mode 100644 index 0000000000..253a18dff3 Binary files /dev/null and b/docs/UML/Architecture/WeeklyPlanRelated.png differ diff --git a/docs/UML/Database/.$DatabaseClassDiagram.drawio.bkp b/docs/UML/Database/.$DatabaseClassDiagram.drawio.bkp new file mode 100644 index 0000000000..ab5a5e0134 --- /dev/null +++ b/docs/UML/Database/.$DatabaseClassDiagram.drawio.bkp @@ -0,0 +1 @@ +7Vlbb+I6EP41SN2VusoFUngs0Ju2bKtS6XTPy8olJrHq2BzHFOiv33FsE5yEQk/hrVJF4/F4bM8383nitMJBtrwSaJaOeIxpK/DiZSsctoKg3QvhVwlWWhD0Ai1IBIm1yCsFY/KGtdC30jmJcW5kWiQ5p5LMXOGEM4Yn0pEhIfjCVZtyGjuCGUpwTTCeIFqX/kNimRqpH/XKjmtMktRM3Q3OdEeGrLLZSZ6imC82ROFFKxwIzqV+ypYDTJXvXL9cbuldL0xgJvcZMBq+eTf+6HmxuMjw8D/278Pdz1Nj5RXRudmwWaxcWQ8IPmcxVka8VthfpETi8QxNVO8CIAdZKjMKLR8ecyn4Cx5wygVIGGeg1q+v1E6LhcTLDZFZ+RXmGZZiBSqmN/BMHJkw8rumvShBaUcdLUs38OgERhGZQEjWtktfwYNxV7PrulMyWt2torfRMEvR/Lr9+ylrcN0DnpAZviW5rDkxX5CMosIbU87k2PQolyFKEgbPE/AOBq/1lVcIROC56ZBcOXmSEhrfohWfq53lEk1ebKufckHewCwqYUBCmmQKIkdjrEYaMAXOQefeAuNXRCO0dBRvUS6NYMIpRbOcPK+3kSGRENbnUvLMKB0C98DFPfCiOu5BVMc9sMKD4x40pExf0RJKYKpzeLpGeTqCzAgiqtAZS0FY0goG9S4dMbrrBuBPIACCKJGF8+z/SiiB5+S2TJsSSisiG14UT+XW4MohoWGNt4XOsF1KHoxHlYjD2CktKCwlcYyZCgwukUQ6ChTkM06YLDze6cMfMNLA+9FpdYZqj52+X7bhT6kLOeAM9oJIESMYQmyBc9kYPe+m4e6QsiG0bwR1jxRA4W7OpaTATmNszx3/fwGcAVQUl4g+KsCHp34N9bCOetiAMEXPmN7znEjClX2hdSvI7wLXjV7CUizIMUHvtPcD/ViYt5tIY0pY3F/9Qhk+YfCjyUOzxTfdsPwAKw19Tal9FMdafCJ074amGfbKYTHOIBwTuXWU5h+YVYAaLlDcQWMVrto6LRSDWGJn4l/zTGtDTOyzy0f+iJITINdN9+hVfGD/AmdAX1rpUvDsEDYpHPRaJT+BvITDO3fs6TzQvs0f7WTPUL1ixJyt65KhahuG7DS/wwx6RYSq5LWGdugLxGKeGbwq6HwdQwdlpCjYj5H8qH0kTupsKWC/iteDFa+eU7y2u90GzL0GzO1L8sExj2qYn0KzevpskILqXrOI1ulrBqsolcdHQSuF5o0rc6jmcyfOFx29T0edvWP1vaq4ITTtO/bBI/Psqyr+bFX8YdB7/n6gH6sq7jZVxab6qHPSp4tUh38SLIvK+9sW4tMqJYOta6ePkFR1wnl2N20wqQLB0SX5ueVcq/NsWfeL+A6bA739cqB3tHvEPe5gMYvP1WU2tGKCMs7ix5Qoh0PHJaG2coKWrZsUQhv3su4lLl4S+aSewfe69XujZ7jcbKxsg8Fm9aB298wK1Dj/h+f5VlAOLlrO6HugM/CZqhPfL7FyPhcTvBtlKBQhq3a/duPYueSvx8IG+H5kwBeYIkle3e8ATegbc/e8SGNb/IVd3y3+zip1vN6kGbV5b18x1GnvMKS9UDNUBON6j3vFJwtGdHDFpmyOf/YGf56GLwluuOf+vo2Edn0IeIePtr5ArE/qpi8PblhvSf2GENtasrf9Sske1euiqIEdQu/D7ADN8uOPBqv8ghZe/AU= \ No newline at end of file diff --git a/docs/UML/Database/.$DatabaseStartupUML.drawio.bkp b/docs/UML/Database/.$DatabaseStartupUML.drawio.bkp new file mode 100644 index 0000000000..06b67de9a1 --- /dev/null +++ b/docs/UML/Database/.$DatabaseStartupUML.drawio.bkp @@ -0,0 +1 @@ +7Vvfc+IqFP5rfNxOCJCQx9b+2JndO7Mzfbjt0x1qULMbxUFs6/71FwyYIFFjq8ap9qVyQgj5zvkO5xxIB3ZH7w+CTob/8JTlnTBI3zvwthOGAAKo/mnJvJDEJCoEA5GlhSgoBY/ZX2butNJZlrKpkRUiyXkus4kr7PHxmPWkI6NC8De3W5/nqSOY0AHzBI89mvvSf7NUDo0UREl54TvLBkPzaBLGxYURtZ3Nm0yHNOVvFRG868Cu4FwWv0bvXZZr8Fxc7tdcXU5MsLFsdEPM58EkfHp8+PH637cfD/jl6fu3CBXDvNJ8Zt7YzFbOLQQDwWcT040Jyd7rgKcvtnvgTwwsX1fZCeMjJsVcdTEDJWYkYyEEF823Eu6EGFCHFaQjQIyajYoHy5FLFNQPA8QOoIB4OyhqGGWCqnEzlCM1+i1QP5WKJ/r6VFIhHyWV+no/y/Muz7lY3AiDxZ/uLAX/wypX7u/NlbVYO5iuV6ePtIEWhB60MPCRtbL9A0saAJsq/pkmF3LIB3xM87tSWgVcY5Qpsl7n2WCsZC9cSj5SF9g4vdbs14NM2LiQGO9C6sBfqkUZ+zhl+i2CxV3qvZ904yoIiBU8LwQIQiu4fa/2v51XW7+YyBR8TFjheyafzPz172IwbFrlSLphBypQ0tA49jDlM9FjmwCPdzIc5VupGLBNHUFSb2GC5VRmr+786gzI3PqLZ2pCSx8AEHGcQBi5IxTzMjetWOFyFp8wzMQzzO6Q9f7o+/p69aLKwVFFd7WCZLn+p9QzlZ7xlsajtfs2zCR7nNCFit7U+uga74pjSCkj/V6dbUY9wl76B3EMdhgceJ4BhDWuITqUa7AP2+QaxJCPXmbT7ci6HF7BmYEUs7gO5ySKIY0OiTMMsGPmse+QSQ3q5GCog304ZBfuss9PzidGI7+ZlHPjf+lM8pV1c603XuP4Gvuz7R4yaej4KjHbxxzf5xQVbnZQBhDlqTKhojG+mFq9j2rMnRPzURgihzsgat1nwXNnj4V7O3vCVtkT1bNnauljFvVMC9hoIudfhjUAuysOiFtnTZPs6sKa0m5bY01NtoZvDFVSzjRZxlwuFxp86+lRWbN0YacmU+spLHVK5KVwoyxNCw2zafbXlBW0WiY6+F+8I77Rz1JjKaVOC/36Gh3z8Wr2bUSHi+6S2OEaQT7VQE0lYzXZ2Z8G/bSm1OBCadOL1kIbAlqtAU9rMTqi0uB5ZEIwdFAPkb8uHTUVgmefClmL3roswVZTIWs4FUX95DTVUZzgo7pizVcJ5YhbKIfQr5QfN5SDZ58AwaahHGw1AYL+No/hTMr6dJZLhzZfhC8IQYcvGMZt8wVf+NKUL2t0eyS++AUDFb9WygNnHzWj0F2MQruBUyGX3SM/TthcU1bANyY/veis0FG0VWfxMfNTdBapTgzhCux+3HbUVAddUp2w4TKEWk11kB+2dQXTx0rqd3y+SOBGIreOBmHScuCGzj5wQ00DN9Rq4IZqA7dLfbqgB3T3gkIcebxCx4zZUG3MdtlaqHGJYbxVdckxVRf5VbjT8ojLk3ug45zaw50tZ/bM8bzySN6zHWTX43l79L6koff9dNpcfzwvjoOrBBCoYmUUJZC4h/UwWBnwwKf1kL8xuQyLvmrxNwZuEoFsu72Y6NSLv+XZ3d08wAnFUuQgbI5CchVgFQ4AHJMExW5UAG0lZQubr4Wg80o3s4hueOpKxcgc9L9v2N+Wc0ubLWawV9diz8Ee14Y/suZ8dH1rM4doesj803b/KceGm4Q2mz7/0J8a7PrxR79f+/3B/tcTu2YTv/Z31G9BMPFAbZdHFeo8O8w5OR7ZHfCtPCosuTUeJRt9qUmZ9qf0TvPvadY4zxNWeVPX2a7Ko5ryy2aWb4nH10P+4ex7Y5iN3TPmyM+yQZDgmig72R1S1Sy//iwCmPIbWnj3Pw== \ No newline at end of file diff --git a/docs/UML/Database/DatabaseClassDiagram.drawio b/docs/UML/Database/DatabaseClassDiagram.drawio new file mode 100644 index 0000000000..0648ff7f56 --- /dev/null +++ b/docs/UML/Database/DatabaseClassDiagram.drawio @@ -0,0 +1 @@ +7Vnbbts4EP0aA+2DC4m6xH6M7aTZIsEGSRftPtISLRGhRC9F3/L1O5RI6+pba7dBEcOApeHMcDjnDDWie844WX8WeB4/8JCwHrLCdc+Z9BCyLceHHyXZFBLfdgtBJGhYiKxS8ExfibHU0gUNSaZlhUhyziSd14UBT1MSyJoMC8FXdbUZZ2FNMMcRaQmeA8za0m80lLGW2v6wHLgjNIr11AN0VQwk2CjrlWQxDvmqInJues5YcC6Lq2Q9Jkwlr56X2x2j28AESeUxBkmKvvCZJe5Hkzv6tLamkn7v62CXmC30gnWwcmMyIPgiDYlyYvWc0SqmkjzPcaBGV4A5yGKZMLiz4ZLhKWEjHLxEudmYMy5yN85t/gGVGU/lLU4oU4y4I2xJJA2wHtAEsJG+rziw8g/IMyn4CzEjKU/BYtTOhk7QkghJ1hWRzs5nwhMixQZUzOhAI6WpipBT3K9K4H1P68QVzNGVJjnWZIu2vks84EJD0g3PYEYfNn9v/NeHSRLjxZ377/ekb7fgmWCJpzgjLZiyFU0YznORJ1KPKFAwo1EK1wHkhkDORionkHN2rQckVzAGMWXhPd7whVpXJgFFczeKuaCv4BYboGFYGLSQX9N4VpaaLoJkoPNoYLEboge8rine40xqQcAZw/OMTrfLSLCIaDriUvJEK50BdWQN66i7Vy3U0cDtQN0U9tlRRy3U+3ArSEDnJDMEuKWMzPM95hoGn6WgadQDJJBjq8woixUhL2zzCKw4wWiREfFXGgkSUkjqgekaHISUy10FOqOMNUSGl4zM5E5WZrDXwFz3uc7ELSVPGgol4mA7Y/nuGtMwJKliFJcq9jw2tbg5p7AeFag3gi+AN7Y+eT0PAh/DvV3ew1epC9h9UlgLpjm5CHBzRRQ/O2i3t3oPc9Fwzz+Oer59IeY5hx8HjObYFRibR6L9QwAnABUjJaJfFeCTvt1C3Wmj7nQgnD98HnlGJeXKvyh0G8gfArfOXprGRNBLgj44cr8ZXAhztwNzWKzFOA6f6nvOh49F+Rfiewr5KjePwirDS7LDaskhjqa+muVba58yJuVI50S7DXfO9U/39mbsyqGdq9tj3TlrSGZ4wWSZs64svm+kZ62poXdcTdnI+/mq6mys250bEIOpNU8ZD17+W3BJapj7SpSnpHIV6d/cEOBKO02m2367HxTEUPQS0fQD8jwFCrKqFx/3zqKax85Z1EA/yzs+5d5G83XDkTUrXgtKWdnet+b7kvFt1YLaHBpT5ONEMTWPo7bHFPJanIBKEarfFBd5aomreTeDjbKrvMicobP0BqjeWQ7sNivtDla6lyJlu7F8J+VJpKw9j/4IUjrO8DeTsqvnfCflCaRs9Sx/BjGHHX3xLyVmuzG2WzkgaXitThnLXqySFRisnmflRyZGO6Q44Wn4NaZpr36aYrtGAO/cW1dhRMyRDvRTMY94itlNKT3UA071scmofpz304dxsEYd5t7zmIwvhGb83jMPWHZE5OG+SuXjGFZBI+qat2VBGJZ0WceyizXa26Pqc0tX7tBpPM4Hn+zGwV+xTG1YPYtt+PK9w76KVLR8AYHwpqKmu/GdYfuW25hquD+0/fpwUUTQsDbh8NksI7LXrL0tMj9ejt57Of66cnSPLEf0ZsrR8W0oIWv7aTg8ujId9xS3ZypSD7UWszfKA/q/r0j99yJ9e0XqvJkidR10iSI94PZcRerW/x90m3+4nqZ/iSKF2/IP5UK9/Fveufkf \ No newline at end of file diff --git a/docs/UML/Database/DatabaseClassDiagram.png b/docs/UML/Database/DatabaseClassDiagram.png new file mode 100644 index 0000000000..7a306127c9 Binary files /dev/null and b/docs/UML/Database/DatabaseClassDiagram.png differ diff --git a/docs/UML/Database/DatabaseStartupUML.drawio b/docs/UML/Database/DatabaseStartupUML.drawio new file mode 100644 index 0000000000..ecd27131df --- /dev/null +++ b/docs/UML/Database/DatabaseStartupUML.drawio @@ -0,0 +1 @@ +5Vtdd6o4FP01PrYLSMLHY7Ufd01v13RNH+bO06woUZiLxhVjq/31k0gihFClLSq2fak5hAD7nL1zTgI9MJiu7hieJw80JlnPc+JVD1z3PM91gC/+Scs6t/guzA0Tlsa5ySkMT+kr0Wcq6zKNyULZchOnNOPp3DSO6GxGRtywYcboi9ltTLPYMMzxhFiGpxHObOvfacwTZXX9qDjwg6STRF069IL8wBTrzupJFgmO6UvJBG56YMAo5fmv6WpAMgmeicvtG0e3N8bIjDc6IaBrZ+79erq7f/734v4ODX/9uPCVe55xtlRPrO6WrzUEjC5nMZGjOD3Qf0lSTp7meCSPvginC1vCp5loueLngjP6mwxoRpmwzOhMdOuP0yzTppiM8TITd9y3H0A90zNhnKxKJvVAd4ROCWdr0UUfBQptHV2q+VK4KojUEyUlLyEfqRBR4THZDl0gKH4oEN8BqBvsB1QMI8KXVIBL8FweX3DM+BPHvApczwPO5s9CWRy5vVVH2gAVmqBGyAIVODam2tY+pGEDSGPBWtWkjCd0Qmc4uymsZaglFKmg+FWWTmbCNqSc06k4QGbxldQMOciczHKL0qSwDvatQ0yOCOzZ+pdsXDpOqA3/bAwQAG24XpX7X6/LrUfCUgEfYdq4Snk+YuiHqp0PiDzdLsaTjXWpUR3tzShZ0CUbkV3OULEhonRC+K6OSiClZ3ZGHSMZ5umzqbd1QaROfaSpuOlttHogMqLVdStD5HeqzirrY2UgV1/7rYFybKyBNjG9fZ5PhHlkhfkgIaPf8ryxnEExx0MsZEPMYmkm/wnPLvgOuXb3y3VFYGJMwvGoLtL9UUiG43YExoMm0qEtMK5XozD+oRRGX2znLJjQ6XC52A+pKQUVgIkbIxLUARz5AcB+OwCLwcxQRva8GNYAHB4MYHc/wPsl3ES26POT0rkC/z/C+VopNl5yuis5Kev3JwQxaiiIOsjaE8TPucTbLTcKC6E7KROJNd3cWr3iNCbEyRQHQfcSGZTw4MlFB3ScE6UsBhk5zJ785eNc0vjv55LXLS7ZVdSGSwtNJjVhp9JApnO+PkMOechMtDrAoCa11nnOKs2Z4HeLCTW1Guqr8I8pkQSYUb6dStC15TERjtwEGKs6bSRglCWMVcBN0zjOfUkW6SseboaSDpjLZH3zjKgvryXGEu5b5J5stFyhTC3wBwaVpEwvIZX5o8urMn+0i9t3ll1xFM7a+GfxnRwEvP0OCuAR/QO+WFkCdcqjAAaObwF81LIEfN2yRAfl3gkEdKssAXZZ8pPiWOZQjE7rlkHOL5GqTASgbiI4aiIFvmopsl01NVdMd66XfpxzoGnSpoO8K5yDb3FOb9aUaXd2fIOOOe0g5J2ab6jjfDsGB2C3OFCzEYr6pYL9GyXCyPXMCcq1Jyg9Zx0nEa4p9FFf1ZHfzTugsntS553gQGXk+vXhr/Ef6D7586KPnYfVE7t5vACn0K6PzOxFFmHkEEVK0fqCJmyqhjBsWw03p14xhtelDiqOi5Ere6B+ZTMV+F45PPb2Rx6shFN+B61ulMIvVhb7QWiCrp/nVGUx/MJlsdeUkN0qi6Gdog8YkS8J1e/UnV2SHkSOSYIotEhw3CQddj1J/2xRrKfRwxbFzafAbhUEsLYg+Ha7Fj6orNnqdomX8Ji1AKytBb731lKgVzS2TrIX1qNjOsm3V3E7qp3u+0qB4jVMVFJP91DaGTbUzvYXU+rz/dB3LyM3BCL7hX4EQjNxRVElnt549bK1QsDebN4mRee/TeCH0EAXRvDEGRFyLBiPWem/g3IfpfchSg6d15xgDeBz3m6i4bu+ZJDvzr/3O4bxuPaF+hboBCqvd6Oab0WO+lkDCi04T8uVd5YRB9k9aFqe6+DsCleincqoMr3TLnp2QApBUyls3717lkObpyaiWXyzl3cvvnwEN/8D \ No newline at end of file diff --git a/docs/UML/Database/DatabaseStartupUML.png b/docs/UML/Database/DatabaseStartupUML.png new file mode 100644 index 0000000000..62afae562d Binary files /dev/null and b/docs/UML/Database/DatabaseStartupUML.png differ diff --git a/docs/UML/Implementation/AddIngredientFunction/.$AddIngredientFunction.drawio.bkp b/docs/UML/Implementation/AddIngredientFunction/.$AddIngredientFunction.drawio.bkp new file mode 100644 index 0000000000..7630fafd14 --- /dev/null +++ b/docs/UML/Implementation/AddIngredientFunction/.$AddIngredientFunction.drawio.bkp @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/UML/Implementation/AddIngredientFunction/AddIngredientFunction.drawio b/docs/UML/Implementation/AddIngredientFunction/AddIngredientFunction.drawio new file mode 100644 index 0000000000..214cb7526d --- /dev/null +++ b/docs/UML/Implementation/AddIngredientFunction/AddIngredientFunction.drawio @@ -0,0 +1 @@ +5Vhbb9owGP01SN0DVRInQB8htCtbV1VDU9unymCTWDVx6jgF9utnE+diwiUtDE3aC7KPnc/2+Y6PbVrAny+/chiHPxjCtOVYaNkCw5bj9DpA/ipglQGe7WRAwAnKIKsExuQ3zkA7R1OCcKKxDBKMUUFiE5yyKMJTYWCQc7Ywu80YRQYQwwDXgPEU0jr6SJAI9bI8q8RvMQnCfGTb0i1zmHfWQBJCxBYVCFy3gM8ZE1lpvvQxVdyZvNzsaC0mxnEkmnxwf/s2C8eRdz+1F8OX3gjCx/u2jvIOaaoXrCcrVjkDnKURwiqI1QKDRUgEHsdwqloXMuUSC8Wcypoti4ng7BX7jDIukYhFsttAj4G5wMudk7cLSqSUMJtjwVeyi/4AXGVfaBW5vay6KFPiuXrqYSUdDtAdoZZBUEQumZIFTdZ24uDo+xv2w2+Dn4OXJ+x3bibpXdvuHmauwotaPJGq6lMSRBKbMCHYXDbgCPWVTCXGYiybBggm4ZpuO2vWe6KnaksinnQiVPlZlS+vPFk1s7SDXsfCyJD7Tr6tS8vKSeaYQkHezQ2xjVAd7YEROWqZOgAuLZkI68pb/3Y9I5WO5ZgRBeQBFjpIVc8bcT3L3R8oYSmf4j2B8o5sNkuwqEmiYOUIlXRqKoEIjaKAY0Rkbi7K4pdj5TOhbPq6huScC5WoyrOuNNRIxtueVWlLzxK1b/VeQ9EdqTAXWIYS7B5opIRTZRnUrQD0HyBPMK8lVZ4CsSqmc3pHZpiStUPGmBM5C6xMk2r4ocQOua48/ASUn/CiTimMEzJZj7pOPZ6mPJEU/8RJ5icKZalQI/nF2blXFzUD3+keTrdnJKTj1ey6OCcNu7bA7twfZdfuh9w6VkpZT8EbtLzhRoIYFyELWARpNUV12vZqpTGXrmmX+X3KoLLOpJ3vvZMz6dWYjJXS+wj9knov/Sy5GAtOokBT6PgqcNF6R5J/wfGOsjf3PO7WlhrYKoEzuVv9CDvfRact7yEeqNx2bHkvAbXEqtbhslpZVSqVXfoJGRw8DZvKIE+nulr1OkZG292T3LTajmm7dnfj9nvwanWGG9GWFwfojwxj+D8PTdfb2OaND033L1m9Xff6U5+aM0Jp/lpEeAZTKprvUPsTHJsbxK6/I7edpvkT/gMMy2r5tM82T/n/CLj+Aw== \ No newline at end of file diff --git a/docs/UML/Implementation/AddIngredientFunction/AddIngredientFunction.png b/docs/UML/Implementation/AddIngredientFunction/AddIngredientFunction.png new file mode 100644 index 0000000000..0a4785e065 Binary files /dev/null and b/docs/UML/Implementation/AddIngredientFunction/AddIngredientFunction.png differ diff --git a/docs/UML/Implementation/DeleteFunction/DeleteFunction.jpg b/docs/UML/Implementation/DeleteFunction/DeleteFunction.jpg new file mode 100644 index 0000000000..ebe41a4288 Binary files /dev/null and b/docs/UML/Implementation/DeleteFunction/DeleteFunction.jpg differ diff --git a/docs/UML/Implementation/EditWeeklyPlan/.$EditWeeklyPlan.drawio.bkp b/docs/UML/Implementation/EditWeeklyPlan/.$EditWeeklyPlan.drawio.bkp new file mode 100644 index 0000000000..7630fafd14 --- /dev/null +++ b/docs/UML/Implementation/EditWeeklyPlan/.$EditWeeklyPlan.drawio.bkp @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/UML/Implementation/EditWeeklyPlan/.$EditWeeklyPlan.png.bkp b/docs/UML/Implementation/EditWeeklyPlan/.$EditWeeklyPlan.png.bkp new file mode 100644 index 0000000000..96083cc579 Binary files /dev/null and b/docs/UML/Implementation/EditWeeklyPlan/.$EditWeeklyPlan.png.bkp differ diff --git a/docs/UML/Implementation/EditWeeklyPlan/EditWeeklyPlan.drawio b/docs/UML/Implementation/EditWeeklyPlan/EditWeeklyPlan.drawio new file mode 100644 index 0000000000..ad9b6a7151 --- /dev/null +++ b/docs/UML/Implementation/EditWeeklyPlan/EditWeeklyPlan.drawio @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/docs/UML/Implementation/EditWeeklyPlan/EditWeeklyPlan.png b/docs/UML/Implementation/EditWeeklyPlan/EditWeeklyPlan.png new file mode 100644 index 0000000000..bf3bf3d067 Binary files /dev/null and b/docs/UML/Implementation/EditWeeklyPlan/EditWeeklyPlan.png differ diff --git a/docs/UML/Implementation/ListAvailableFunction/availableSequenceDiagram.drawio b/docs/UML/Implementation/ListAvailableFunction/availableSequenceDiagram.drawio new file mode 100644 index 0000000000..55d31d32c2 --- /dev/null +++ b/docs/UML/Implementation/ListAvailableFunction/availableSequenceDiagram.drawio @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/UML/Implementation/ListAvailableFunction/availableSequenceDiagram.drawio.png b/docs/UML/Implementation/ListAvailableFunction/availableSequenceDiagram.drawio.png new file mode 100644 index 0000000000..ff745e7c5c Binary files /dev/null and b/docs/UML/Implementation/ListAvailableFunction/availableSequenceDiagram.drawio.png differ diff --git a/docs/UML/Implementation/ListFunction/ListFunction.drawio b/docs/UML/Implementation/ListFunction/ListFunction.drawio new file mode 100644 index 0000000000..f5ac2e7347 --- /dev/null +++ b/docs/UML/Implementation/ListFunction/ListFunction.drawio @@ -0,0 +1 @@ +7ZjJbtswEIafRkB7SKHFsuNjbKfpIQWCuG16KxhpLBGlRIWkvOTpS0rUSm8xEiRtczL5czhc5uOItOVNk/UVQ1n8lYZALNcO15Y3s1zXsX1b/ihlUyq+45dCxHCojRphjh+h6qnVHIfAO4aCUiJw1hUDmqYQiI6GGKOrrtmCku6oGYrAEOYBIqZ6h0MRl+p5tSylfwEcxdXIjq1bElQZa4HHKKSrluRdWt6UUSrKUrKeAlGbV+1L2e/zjtZ6YgxScUyHXwNxt/nhTp3xw9335Xj88/Fseqa9LBHJ9YIt7+IWApzBNeZCz1xsqu2Qi8hUMU/INV4AwamsTTJgOAEBTLYQLd802mQVYwHzDAWq60qCIrVYJETWHFmUsRNIdmG6HjKafUMsArUuuzAgBGUc3xfTUAqDIGccL+EWeMmMUmku1NDTmgUlZpQJqXDB5BhKBMTFCuTivEkKq8tQxrta4GhiucOHXIVkAnVDLcm9abWTe9rq3Ri5057RDgdLYAJL0nZ0lQtcQtjpa3cMGM3T0LAYzWRJ74lnF077nFRBl+PDuiVpbq6AysCxjTTRrf5IM1wdYl1dNSei5j5un4ZzLSJ9CqPadQOqLGhWn8Cta3LbR7UFWEZl5Isp+BPLn/WIlXzENKIpIm1mt7IkCirn6hBw8xT8h6CZcO1NM0cTN/A7wI22AWfy5r8Ubt62NHmDGJeovKfIv5LcZ06Rnt0l9vVT5OA9Rb4J0I5Mkd5TiRscAZzJ24vh5hu4ZSo/qltkeZ/8ENAkQWlY7JvN6jvmx31YVnG5IDhKpXZPhaCJCmYaXqjrvdIIDX53IJFcdYGogu/N6kDXQdXZUgewrO1MCKLKuQdOnZrLMUE8sz85RtyqGDEgSMgE3n3UbAmcdn+jznDj2xl0b211/qlccJqzAHSv9suh58gdH3BU7soeR5UhXSw4dGwK1up9OR2/4UH8FpjItMVL/DD/hqJ/izzn7ZDnDXvAuCeS1394GI7eAHkjg7ySNAj3vKNfH7QQ8bioOPuoO0jT88MzdLofNmd0IjwGhX1HO+B5LjDODTDYOxCnZBO/B0T/DnMsEMYHse/oZCBktfkjrzRv/g71Lv8A \ No newline at end of file diff --git a/docs/UML/Implementation/ListFunction/ListFunction.png b/docs/UML/Implementation/ListFunction/ListFunction.png new file mode 100644 index 0000000000..847b360464 Binary files /dev/null and b/docs/UML/Implementation/ListFunction/ListFunction.png differ diff --git a/docs/UML/Implementation/MarkDoneWeeklyPlan/.$AddIngredientFunction.drawio.bkp b/docs/UML/Implementation/MarkDoneWeeklyPlan/.$AddIngredientFunction.drawio.bkp new file mode 100644 index 0000000000..7630fafd14 --- /dev/null +++ b/docs/UML/Implementation/MarkDoneWeeklyPlan/.$AddIngredientFunction.drawio.bkp @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/UML/Implementation/MarkDoneWeeklyPlan/.$MarkDoneWeeklyPlan.drawio.bkp b/docs/UML/Implementation/MarkDoneWeeklyPlan/.$MarkDoneWeeklyPlan.drawio.bkp new file mode 100644 index 0000000000..89bd599e96 --- /dev/null +++ b/docs/UML/Implementation/MarkDoneWeeklyPlan/.$MarkDoneWeeklyPlan.drawio.bkp @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/docs/UML/Implementation/MarkDoneWeeklyPlan/.$ViewIngredientFunction.drawio.bkp b/docs/UML/Implementation/MarkDoneWeeklyPlan/.$ViewIngredientFunction.drawio.bkp new file mode 100644 index 0000000000..9cb80d2792 --- /dev/null +++ b/docs/UML/Implementation/MarkDoneWeeklyPlan/.$ViewIngredientFunction.drawio.bkp @@ -0,0 +1 @@ +5VhRb9owEP41SN0DlRMnkD5C2m7dOqkamtY+TQYfiVUTZ45T6H79nMROSAMUBqs27QXZn3Pny3ef7xx6OFys3kuSxp8FBd5zEV318GXPdQcB1r8F8FwBHnIrIJKMVhBqgAn7CRXoWDRnFDKDVZASgiuWtsGZSBKYqRZGpBTL9mNzwWkLSEkEHWAyI7yLfmNUxRUa+KjBPwCLYruzg8zKgtiHDZDFhIrlGoSvejiUQqhqtFiFwAvu2rxcb1mtA5OQqH0MyM2nHxDGH8dfxt/vIRxcT/Pbvg3uifDcvLGJVj1bCmK14Hrk9PB4zjgPBRdSzynMSc711uNMSfEIFk9Eog3Hxi9IBautETs1D1o/IBag5LN+xBpcVBZWOia0ZZMH3zfyitdy4Hqeyb/JfVR7bujRA8PQAWw5w4PYKl6eaSmNOIsSjU2FUmKhFyCho0KbGhMp6KUxJVkM1NjpZXMQgmK2Yupej5EZPxTj8wtfT6XIE1qaoZpxoB1B78s3OkfICyorCZwo9tR2tYlQ4+1OML1J7QpjfI5cHKALv/wd+q1UurYKWI+KyAiUcbIu4hd+feTtdpSJXM5ghyP7oJjPM1AdSdSsHKGSQUclhNKbJJJAmU7FWTN8d6x8plzMHktIx1yrpJg8mMleGqno3/VO/hYx6eBLxnfY4s2iO1JhHkYtJTi207yihFNlGXdLAR7dEZmB7CRVl/60GOYLfsvmwFlZIVOQTEcBRdHkBr5rsPEyZgomKSnJXeruqrE1eeiOp4g2kfWcc5JmbFruWqYeZrnMNMVfIKvqSYGKXBU7hXXDRKep1u4waCVk4HfKdd0cW+Ua4e25P6pcewdV67RQShmCP+75ly8SJKSKRSQSwtdTtBdthxwKK+52uRzgDVR2mXTsKT05k36HybRQ+ojSr1rvTT3LziZKsiQyFLph4bhevWXZv1PxvOMSeWR162sNbJTAG1W3bgv7+y86rzaiI1NqU1Nck4JBKzv94UluTX0Xt3va8NBr0hvcbpxNje+mdcj/zwbo+S+O7N4N8I99r3Tr9qk74OYvwt/vi85rHLcPiBPs1RntZ+4BDOtp821eHZ7mDw589Qs= \ No newline at end of file diff --git a/docs/UML/Implementation/MarkDoneWeeklyPlan/.$ViewWeeklyPlan.drawio.bkp b/docs/UML/Implementation/MarkDoneWeeklyPlan/.$ViewWeeklyPlan.drawio.bkp new file mode 100644 index 0000000000..9ab21a8d83 --- /dev/null +++ b/docs/UML/Implementation/MarkDoneWeeklyPlan/.$ViewWeeklyPlan.drawio.bkp @@ -0,0 +1 @@ +5Vnbbts4EP0aA9kHB7pa8qMvSTfbBNs26G77VDDW2CJKiSpFxfZ+/Y4s6krZsWsnKNAXQzykKOnMmZkjeWDPos07QZLwgQfABpYRbAb2fGBZY3+MvzmwLQDXtwpgJWhQQEYNPNL/oADNEs1oAKnCCkhyziRN2uCCxzEsZAsjQvB1e9mSs6AFJGQFGvC4IExH/6WBDAvUd40a/xPoKiyvbBpqJiLlYgWkIQn4ugHZNwN7JjiXxVG0mQHLuWvzcrtntroxAbE85gS2/jvcbhbW8z9y8s66+/jxPfWGo2KXZ8Iy9cDqZuW2ZCCUEcMjc2BPUyn4d5hxxgUiMY9xyVTtAELCZu+tmdUDo1CARyDFFpeUJ6jbUBqx/WK4rgn3SlrDBtm2XwZaBXlV7VzzgAeKin5ayN37HzAL/5p+mn77ArPR7VN2PzS9k3jJH56iZiaMrmLEnriUPMIJiINJLkLEeAI4NQ1IGkKgzsNppXgfR4JncbCbM3Zz+CBf8oXXhluOv+aT1964Auab5vL5tjn6AIIiHSAUuCcq+FA8Ews4RIdaCEEnWSQRK5AHznQOxt24NgxHBVsAI5I+t/fvC6za7QOn+BjVVpbpXBuoCGPs7n49tyUpc9zZsbhztUkzazr72v4LGxXkHdioXMiXyxRaa1AaZNtYkeRnpPuf0fb8dp4oVdZKL3asdV9RfkYq6CUiEXhbn1MQd/FKQEBRT+lV1h7/cW6+PDG++N7MA6OVBFq+/Ly67SO1bLq9WaCr+0wp22W6lZLzrKMkd6mI23rtsycPQJg9MrSoYldL8sMsYvd0CYzuekLSKD1MwY1yNF2HVMJjQnZxWaNxQKyhD2zmkuApohozRpKUPu2uuos9LDKRIsefIC0qaI7yTOZXmlVe4KAwtJa1vz+541ZEXEdrUGZvgyo72cUblHNSf1KFBWF3OnDnnQBxIUO+4jFhzRDptB0Uy9FcOu16OrJ7qNSZRN/4Sky6GpOoLUBl3cVJJq8G1uhHllu06TOF9TdaV7h65heodWcVNudt6trQtEe9sX+junaa172Mp4MNlXlAh2h1PE8BXwtXZ5i66cPZ2tLlg21j8LKhOySDF/vgsTIow5m7N78d0aF3ETM39LtG51T3tt90XcwXmbqc7Mln+nt2SMex2x2yr6z3dUjHeaW6buqF/dItckkZK1+GA1iSjMnjs9I8nePOm4ipvyb3tU7TvcBbcu/HAz0DGOfJoQS4FSSCl9W9h5lTPilUwlJsWYalsWWXzDTp6r7fXYytHlvtTpcoHiy5ZBHmp1fmIldgl0bkQrZp6v0c0xSlgohqYQvceSfcbm+LaBCwfXHpsx5nhcYpVVqFxtNCM+qrFcYrReatzPR5rI07gnb6XkJ01pzXYk2vryjmiMgHSFOygitsNWig1Ui3yLnNeFTDmrSbGm0qvVQwg2WeAykqFJPlfjeaW/1met/ntK617nU9bdd8SDVHRPBMN+SM22+fiLS32OObT/285Fr91/nJz0s4rD9uF8vrfwjsm/8B \ No newline at end of file diff --git a/docs/UML/Implementation/MarkDoneWeeklyPlan/.$ViewWeeklyPlan.drawio.dtmp b/docs/UML/Implementation/MarkDoneWeeklyPlan/.$ViewWeeklyPlan.drawio.dtmp new file mode 100644 index 0000000000..6a8ba1ada7 --- /dev/null +++ b/docs/UML/Implementation/MarkDoneWeeklyPlan/.$ViewWeeklyPlan.drawio.dtmp @@ -0,0 +1 @@ +5VnZcqM4FP0aV2UenAIExn70knRnktQknZruzrxMySAbVWREhIjt/vq5GLGZxbhtV3fVvCToaAGde+4iuYemq80ngQPvkbuE9QzN3fTQrGcYuolM+Bcj2wQZmgpYCuomkJYDL/QHUTNTNKIuCRWWQJJzJmlQBh3u+8SRJQwLwdflYQvO3BIQ4CWpAC8OZlX0G3Wlp3ZhaTn+mdCll75Z11TPCqeDFRB62OXrAoRuemgqOJfJ02ozJSwmr8zLbUNv9mGC+LLLhMnyn1cLvd9b8/Hn5ze0oOHXeV+t8oFZpDasPlZuUwYEj3yXxItoPTRZe1SSlwA7ce8abA6YJ1cMWjo8quWIkGTT+J16tnuQDeErIsUWhqgJKV9KMAglzXXO/mA0SDCvwDwaqXlYWXyZrZyTAg+Kl3qO8N39O5l6f06+TP79TqaD23n00NftwyQVKIg3T0FAY0aXPmBzLiVfQQfx3XGsSMB4QKBr4uLQ2zGrJ91K/kNolVkH6sT2ezzwWrPS9mvceW2PMmC2KQ6fbYutJyIo0EGEAhusApvikXBIGx2pG2KxJLJloHJ04pY8rNHu2rWmmcNkjiAMS/pR9sE6w6rVnjiFbeQS0s1rzUBDbWTt/tpWSVL6aG/FZCtqkaIL7a2LhgcWSshrWSgdyBeLkJTGgDTwtjAiiGeEzXtE9rDsJ0qVudKTFXPdZ5Sf4AqDiisEAj7rGyFvbPvEsH+1zh7/ONVL5ow7b0X1ayXpV7zk5zWNOkpatzpq+kQBo9TJUqHZRiehncvOqBrx0PiRYIYGWsWqkNiC+DFasQe6IIz60JoEhYDDFFwIQocSCeRziWGKyNqM4SCk891bd7YnTiRC4PgLCZO4GaM8kvGbplk50CqMSqJqjE66NSpZxDIraSlL/aW0hIYXSkvmUVlJhROArUnPmu0ZiAvp8SX3MSuaqEpbq1g6c2mWo+igmuH1GiYN61JMWhUmQVsElHXnB5G86hmD9yiu0iZJbAsgtuXYbxDl2qxyTPa1rLNk376OBrUG7pwl23XSOWs3J9tzxclqOjxVDYcrQ7KhMpZJH0xm2wp4TWpDTa+WjtCbF4ZxY1toHC4L28R1MK8eWQLGW9KGZfH07fOIcrhfLh1bA15eTXrNaQyN/6b/z4xrmqiccevSRF3GTW8Yzn8QrCaKc6fcBWVsyhmPR7hkgSMmu3ulfjzHe+cZfdgpFetWi/91pZit//K2G8f4+CrHn4y75+d7atfcRzDOgzYHuBV4RbpeTJwkyExYii1DMypsoZSZIl37p8SzsVVTpluTBYgHQi52vF3UdCgwZc0qFMKmZZmiUAr+RlL1+XwXVIqCVBBW6csBKnei3c9rK+q6rMkmHY9sR2hY1/bMYlfMMqiLE2e4L6q1yq8ozI9nbbQnZrPuQFNlzbwUa9XYurtZeCRhiJfkCrIM1OKqVa254wrjRTVzzm5ytCj0VMCMLGIXCEGg1F8+7Fozo746b7qP26/VG01TvFtok82lrxbMUfkgC0h5iYbq/Nj7Kcuof89P3k9BM78qT4bnPzigm/8A \ No newline at end of file diff --git a/docs/UML/Implementation/MarkDoneWeeklyPlan/MarkDoneWeeklyPlan.drawio b/docs/UML/Implementation/MarkDoneWeeklyPlan/MarkDoneWeeklyPlan.drawio new file mode 100644 index 0000000000..31d8cf8bb0 --- /dev/null +++ b/docs/UML/Implementation/MarkDoneWeeklyPlan/MarkDoneWeeklyPlan.drawio @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/docs/UML/Implementation/MarkDoneWeeklyPlan/MarkDoneWeeklyPlan.png b/docs/UML/Implementation/MarkDoneWeeklyPlan/MarkDoneWeeklyPlan.png new file mode 100644 index 0000000000..36d7dd4938 Binary files /dev/null and b/docs/UML/Implementation/MarkDoneWeeklyPlan/MarkDoneWeeklyPlan.png differ diff --git a/docs/UML/Implementation/RandomFunction/RandomFunction.drawio b/docs/UML/Implementation/RandomFunction/RandomFunction.drawio new file mode 100644 index 0000000000..0c4e525c64 --- /dev/null +++ b/docs/UML/Implementation/RandomFunction/RandomFunction.drawio @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/UML/Implementation/RandomFunction/RandomFunction.png b/docs/UML/Implementation/RandomFunction/RandomFunction.png new file mode 100644 index 0000000000..8ebd0d09db Binary files /dev/null and b/docs/UML/Implementation/RandomFunction/RandomFunction.png differ diff --git a/docs/UML/Implementation/TagFunction/TagFunction.drawio b/docs/UML/Implementation/TagFunction/TagFunction.drawio new file mode 100644 index 0000000000..7630fafd14 --- /dev/null +++ b/docs/UML/Implementation/TagFunction/TagFunction.drawio @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/UML/Implementation/TagFunction/TagFunction.png b/docs/UML/Implementation/TagFunction/TagFunction.png new file mode 100644 index 0000000000..0d8c0dc44b Binary files /dev/null and b/docs/UML/Implementation/TagFunction/TagFunction.png differ diff --git a/docs/UML/Implementation/ViewIngredientFunction/.$AddIngredientFunction.drawio.bkp b/docs/UML/Implementation/ViewIngredientFunction/.$AddIngredientFunction.drawio.bkp new file mode 100644 index 0000000000..7630fafd14 --- /dev/null +++ b/docs/UML/Implementation/ViewIngredientFunction/.$AddIngredientFunction.drawio.bkp @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/UML/Implementation/ViewIngredientFunction/.$ViewIngredientFunction.drawio.bkp b/docs/UML/Implementation/ViewIngredientFunction/.$ViewIngredientFunction.drawio.bkp new file mode 100644 index 0000000000..9cb80d2792 --- /dev/null +++ b/docs/UML/Implementation/ViewIngredientFunction/.$ViewIngredientFunction.drawio.bkp @@ -0,0 +1 @@ +5VhRb9owEP41SN0DlRMnkD5C2m7dOqkamtY+TQYfiVUTZ45T6H79nMROSAMUBqs27QXZn3Pny3ef7xx6OFys3kuSxp8FBd5zEV318GXPdQcB1r8F8FwBHnIrIJKMVhBqgAn7CRXoWDRnFDKDVZASgiuWtsGZSBKYqRZGpBTL9mNzwWkLSEkEHWAyI7yLfmNUxRUa+KjBPwCLYruzg8zKgtiHDZDFhIrlGoSvejiUQqhqtFiFwAvu2rxcb1mtA5OQqH0MyM2nHxDGH8dfxt/vIRxcT/Pbvg3uifDcvLGJVj1bCmK14Hrk9PB4zjgPBRdSzynMSc711uNMSfEIFk9Eog3Hxi9IBautETs1D1o/IBag5LN+xBpcVBZWOia0ZZMH3zfyitdy4Hqeyb/JfVR7bujRA8PQAWw5w4PYKl6eaSmNOIsSjU2FUmKhFyCho0KbGhMp6KUxJVkM1NjpZXMQgmK2Yupej5EZPxTj8wtfT6XIE1qaoZpxoB1B78s3OkfICyorCZwo9tR2tYlQ4+1OML1J7QpjfI5cHKALv/wd+q1UurYKWI+KyAiUcbIu4hd+feTtdpSJXM5ghyP7oJjPM1AdSdSsHKGSQUclhNKbJJJAmU7FWTN8d6x8plzMHktIx1yrpJg8mMleGqno3/VO/hYx6eBLxnfY4s2iO1JhHkYtJTi207yihFNlGXdLAR7dEZmB7CRVl/60GOYLfsvmwFlZIVOQTEcBRdHkBr5rsPEyZgomKSnJXeruqrE1eeiOp4g2kfWcc5JmbFruWqYeZrnMNMVfIKvqSYGKXBU7hXXDRKep1u4waCVk4HfKdd0cW+Ua4e25P6pcewdV67RQShmCP+75ly8SJKSKRSQSwtdTtBdthxwKK+52uRzgDVR2mXTsKT05k36HybRQ+ojSr1rvTT3LziZKsiQyFLph4bhevWXZv1PxvOMSeWR162sNbJTAG1W3bgv7+y86rzaiI1NqU1Nck4JBKzv94UluTX0Xt3va8NBr0hvcbpxNje+mdcj/zwbo+S+O7N4N8I99r3Tr9qk74OYvwt/vi85rHLcPiBPs1RntZ+4BDOtp821eHZ7mDw589Qs= \ No newline at end of file diff --git a/docs/UML/Implementation/ViewIngredientFunction/ViewIngredientFunction.drawio b/docs/UML/Implementation/ViewIngredientFunction/ViewIngredientFunction.drawio new file mode 100644 index 0000000000..9ab21a8d83 --- /dev/null +++ b/docs/UML/Implementation/ViewIngredientFunction/ViewIngredientFunction.drawio @@ -0,0 +1 @@ +5Vnbbts4EP0aA9kHB7pa8qMvSTfbBNs26G77VDDW2CJKiSpFxfZ+/Y4s6krZsWsnKNAXQzykKOnMmZkjeWDPos07QZLwgQfABpYRbAb2fGBZY3+MvzmwLQDXtwpgJWhQQEYNPNL/oADNEs1oAKnCCkhyziRN2uCCxzEsZAsjQvB1e9mSs6AFJGQFGvC4IExH/6WBDAvUd40a/xPoKiyvbBpqJiLlYgWkIQn4ugHZNwN7JjiXxVG0mQHLuWvzcrtntroxAbE85gS2/jvcbhbW8z9y8s66+/jxPfWGo2KXZ8Iy9cDqZuW2ZCCUEcMjc2BPUyn4d5hxxgUiMY9xyVTtAELCZu+tmdUDo1CARyDFFpeUJ6jbUBqx/WK4rgn3SlrDBtm2XwZaBXlV7VzzgAeKin5ayN37HzAL/5p+mn77ArPR7VN2PzS9k3jJH56iZiaMrmLEnriUPMIJiINJLkLEeAI4NQ1IGkKgzsNppXgfR4JncbCbM3Zz+CBf8oXXhluOv+aT1964Auab5vL5tjn6AIIiHSAUuCcq+FA8Ews4RIdaCEEnWSQRK5AHznQOxt24NgxHBVsAI5I+t/fvC6za7QOn+BjVVpbpXBuoCGPs7n49tyUpc9zZsbhztUkzazr72v4LGxXkHdioXMiXyxRaa1AaZNtYkeRnpPuf0fb8dp4oVdZKL3asdV9RfkYq6CUiEXhbn1MQd/FKQEBRT+lV1h7/cW6+PDG++N7MA6OVBFq+/Ly67SO1bLq9WaCr+0wp22W6lZLzrKMkd6mI23rtsycPQJg9MrSoYldL8sMsYvd0CYzuekLSKD1MwY1yNF2HVMJjQnZxWaNxQKyhD2zmkuApohozRpKUPu2uuos9LDKRIsefIC0qaI7yTOZXmlVe4KAwtJa1vz+541ZEXEdrUGZvgyo72cUblHNSf1KFBWF3OnDnnQBxIUO+4jFhzRDptB0Uy9FcOu16OrJ7qNSZRN/4Sky6GpOoLUBl3cVJJq8G1uhHllu06TOF9TdaV7h65heodWcVNudt6trQtEe9sX+junaa172Mp4MNlXlAh2h1PE8BXwtXZ5i66cPZ2tLlg21j8LKhOySDF/vgsTIow5m7N78d0aF3ETM39LtG51T3tt90XcwXmbqc7Mln+nt2SMex2x2yr6z3dUjHeaW6buqF/dItckkZK1+GA1iSjMnjs9I8nePOm4ipvyb3tU7TvcBbcu/HAz0DGOfJoQS4FSSCl9W9h5lTPilUwlJsWYalsWWXzDTp6r7fXYytHlvtTpcoHiy5ZBHmp1fmIldgl0bkQrZp6v0c0xSlgohqYQvceSfcbm+LaBCwfXHpsx5nhcYpVVqFxtNCM+qrFcYrReatzPR5rI07gnb6XkJ01pzXYk2vryjmiMgHSFOygitsNWig1Ui3yLnNeFTDmrSbGm0qvVQwg2WeAykqFJPlfjeaW/1met/ntK617nU9bdd8SDVHRPBMN+SM22+fiLS32OObT/285Fr91/nJz0s4rD9uF8vrfwjsm/8B \ No newline at end of file diff --git a/docs/UML/Implementation/ViewIngredientFunction/ViewIngredientFunction.png b/docs/UML/Implementation/ViewIngredientFunction/ViewIngredientFunction.png new file mode 100644 index 0000000000..874373a72a Binary files /dev/null and b/docs/UML/Implementation/ViewIngredientFunction/ViewIngredientFunction.png differ diff --git a/docs/UML/Implementation/ViewWeeklyPlan/.$AddIngredientFunction.drawio.bkp b/docs/UML/Implementation/ViewWeeklyPlan/.$AddIngredientFunction.drawio.bkp new file mode 100644 index 0000000000..7630fafd14 --- /dev/null +++ b/docs/UML/Implementation/ViewWeeklyPlan/.$AddIngredientFunction.drawio.bkp @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/UML/Implementation/ViewWeeklyPlan/.$ViewIngredientFunction.drawio.bkp b/docs/UML/Implementation/ViewWeeklyPlan/.$ViewIngredientFunction.drawio.bkp new file mode 100644 index 0000000000..9cb80d2792 --- /dev/null +++ b/docs/UML/Implementation/ViewWeeklyPlan/.$ViewIngredientFunction.drawio.bkp @@ -0,0 +1 @@ +5VhRb9owEP41SN0DlRMnkD5C2m7dOqkamtY+TQYfiVUTZ45T6H79nMROSAMUBqs27QXZn3Pny3ef7xx6OFys3kuSxp8FBd5zEV318GXPdQcB1r8F8FwBHnIrIJKMVhBqgAn7CRXoWDRnFDKDVZASgiuWtsGZSBKYqRZGpBTL9mNzwWkLSEkEHWAyI7yLfmNUxRUa+KjBPwCLYruzg8zKgtiHDZDFhIrlGoSvejiUQqhqtFiFwAvu2rxcb1mtA5OQqH0MyM2nHxDGH8dfxt/vIRxcT/Pbvg3uifDcvLGJVj1bCmK14Hrk9PB4zjgPBRdSzynMSc711uNMSfEIFk9Eog3Hxi9IBautETs1D1o/IBag5LN+xBpcVBZWOia0ZZMH3zfyitdy4Hqeyb/JfVR7bujRA8PQAWw5w4PYKl6eaSmNOIsSjU2FUmKhFyCho0KbGhMp6KUxJVkM1NjpZXMQgmK2Yupej5EZPxTj8wtfT6XIE1qaoZpxoB1B78s3OkfICyorCZwo9tR2tYlQ4+1OML1J7QpjfI5cHKALv/wd+q1UurYKWI+KyAiUcbIu4hd+feTtdpSJXM5ghyP7oJjPM1AdSdSsHKGSQUclhNKbJJJAmU7FWTN8d6x8plzMHktIx1yrpJg8mMleGqno3/VO/hYx6eBLxnfY4s2iO1JhHkYtJTi207yihFNlGXdLAR7dEZmB7CRVl/60GOYLfsvmwFlZIVOQTEcBRdHkBr5rsPEyZgomKSnJXeruqrE1eeiOp4g2kfWcc5JmbFruWqYeZrnMNMVfIKvqSYGKXBU7hXXDRKep1u4waCVk4HfKdd0cW+Ua4e25P6pcewdV67RQShmCP+75ly8SJKSKRSQSwtdTtBdthxwKK+52uRzgDVR2mXTsKT05k36HybRQ+ojSr1rvTT3LziZKsiQyFLph4bhevWXZv1PxvOMSeWR162sNbJTAG1W3bgv7+y86rzaiI1NqU1Nck4JBKzv94UluTX0Xt3va8NBr0hvcbpxNje+mdcj/zwbo+S+O7N4N8I99r3Tr9qk74OYvwt/vi85rHLcPiBPs1RntZ+4BDOtp821eHZ7mDw589Qs= \ No newline at end of file diff --git a/docs/UML/Implementation/ViewWeeklyPlan/.$ViewWeeklyPlan.drawio.bkp b/docs/UML/Implementation/ViewWeeklyPlan/.$ViewWeeklyPlan.drawio.bkp new file mode 100644 index 0000000000..9ab21a8d83 --- /dev/null +++ b/docs/UML/Implementation/ViewWeeklyPlan/.$ViewWeeklyPlan.drawio.bkp @@ -0,0 +1 @@ +5Vnbbts4EP0aA9kHB7pa8qMvSTfbBNs26G77VDDW2CJKiSpFxfZ+/Y4s6krZsWsnKNAXQzykKOnMmZkjeWDPos07QZLwgQfABpYRbAb2fGBZY3+MvzmwLQDXtwpgJWhQQEYNPNL/oADNEs1oAKnCCkhyziRN2uCCxzEsZAsjQvB1e9mSs6AFJGQFGvC4IExH/6WBDAvUd40a/xPoKiyvbBpqJiLlYgWkIQn4ugHZNwN7JjiXxVG0mQHLuWvzcrtntroxAbE85gS2/jvcbhbW8z9y8s66+/jxPfWGo2KXZ8Iy9cDqZuW2ZCCUEcMjc2BPUyn4d5hxxgUiMY9xyVTtAELCZu+tmdUDo1CARyDFFpeUJ6jbUBqx/WK4rgn3SlrDBtm2XwZaBXlV7VzzgAeKin5ayN37HzAL/5p+mn77ArPR7VN2PzS9k3jJH56iZiaMrmLEnriUPMIJiINJLkLEeAI4NQ1IGkKgzsNppXgfR4JncbCbM3Zz+CBf8oXXhluOv+aT1964Auab5vL5tjn6AIIiHSAUuCcq+FA8Ews4RIdaCEEnWSQRK5AHznQOxt24NgxHBVsAI5I+t/fvC6za7QOn+BjVVpbpXBuoCGPs7n49tyUpc9zZsbhztUkzazr72v4LGxXkHdioXMiXyxRaa1AaZNtYkeRnpPuf0fb8dp4oVdZKL3asdV9RfkYq6CUiEXhbn1MQd/FKQEBRT+lV1h7/cW6+PDG++N7MA6OVBFq+/Ly67SO1bLq9WaCr+0wp22W6lZLzrKMkd6mI23rtsycPQJg9MrSoYldL8sMsYvd0CYzuekLSKD1MwY1yNF2HVMJjQnZxWaNxQKyhD2zmkuApohozRpKUPu2uuos9LDKRIsefIC0qaI7yTOZXmlVe4KAwtJa1vz+541ZEXEdrUGZvgyo72cUblHNSf1KFBWF3OnDnnQBxIUO+4jFhzRDptB0Uy9FcOu16OrJ7qNSZRN/4Sky6GpOoLUBl3cVJJq8G1uhHllu06TOF9TdaV7h65heodWcVNudt6trQtEe9sX+junaa172Mp4MNlXlAh2h1PE8BXwtXZ5i66cPZ2tLlg21j8LKhOySDF/vgsTIow5m7N78d0aF3ETM39LtG51T3tt90XcwXmbqc7Mln+nt2SMex2x2yr6z3dUjHeaW6buqF/dItckkZK1+GA1iSjMnjs9I8nePOm4ipvyb3tU7TvcBbcu/HAz0DGOfJoQS4FSSCl9W9h5lTPilUwlJsWYalsWWXzDTp6r7fXYytHlvtTpcoHiy5ZBHmp1fmIldgl0bkQrZp6v0c0xSlgohqYQvceSfcbm+LaBCwfXHpsx5nhcYpVVqFxtNCM+qrFcYrReatzPR5rI07gnb6XkJ01pzXYk2vryjmiMgHSFOygitsNWig1Ui3yLnNeFTDmrSbGm0qvVQwg2WeAykqFJPlfjeaW/1met/ntK617nU9bdd8SDVHRPBMN+SM22+fiLS32OObT/285Fr91/nJz0s4rD9uF8vrfwjsm/8B \ No newline at end of file diff --git a/docs/UML/Implementation/ViewWeeklyPlan/.$ViewWeeklyPlan.drawio.dtmp b/docs/UML/Implementation/ViewWeeklyPlan/.$ViewWeeklyPlan.drawio.dtmp new file mode 100644 index 0000000000..6a8ba1ada7 --- /dev/null +++ b/docs/UML/Implementation/ViewWeeklyPlan/.$ViewWeeklyPlan.drawio.dtmp @@ -0,0 +1 @@ +5VnZcqM4FP0aV2UenAIExn70knRnktQknZruzrxMySAbVWREhIjt/vq5GLGZxbhtV3fVvCToaAGde+4iuYemq80ngQPvkbuE9QzN3fTQrGcYuolM+Bcj2wQZmgpYCuomkJYDL/QHUTNTNKIuCRWWQJJzJmlQBh3u+8SRJQwLwdflYQvO3BIQ4CWpAC8OZlX0G3Wlp3ZhaTn+mdCll75Z11TPCqeDFRB62OXrAoRuemgqOJfJ02ozJSwmr8zLbUNv9mGC+LLLhMnyn1cLvd9b8/Hn5ze0oOHXeV+t8oFZpDasPlZuUwYEj3yXxItoPTRZe1SSlwA7ce8abA6YJ1cMWjo8quWIkGTT+J16tnuQDeErIsUWhqgJKV9KMAglzXXO/mA0SDCvwDwaqXlYWXyZrZyTAg+Kl3qO8N39O5l6f06+TP79TqaD23n00NftwyQVKIg3T0FAY0aXPmBzLiVfQQfx3XGsSMB4QKBr4uLQ2zGrJ91K/kNolVkH6sT2ezzwWrPS9mvceW2PMmC2KQ6fbYutJyIo0EGEAhusApvikXBIGx2pG2KxJLJloHJ04pY8rNHu2rWmmcNkjiAMS/pR9sE6w6rVnjiFbeQS0s1rzUBDbWTt/tpWSVL6aG/FZCtqkaIL7a2LhgcWSshrWSgdyBeLkJTGgDTwtjAiiGeEzXtE9rDsJ0qVudKTFXPdZ5Sf4AqDiisEAj7rGyFvbPvEsH+1zh7/ONVL5ow7b0X1ayXpV7zk5zWNOkpatzpq+kQBo9TJUqHZRiehncvOqBrx0PiRYIYGWsWqkNiC+DFasQe6IIz60JoEhYDDFFwIQocSCeRziWGKyNqM4SCk891bd7YnTiRC4PgLCZO4GaM8kvGbplk50CqMSqJqjE66NSpZxDIraSlL/aW0hIYXSkvmUVlJhROArUnPmu0ZiAvp8SX3MSuaqEpbq1g6c2mWo+igmuH1GiYN61JMWhUmQVsElHXnB5G86hmD9yiu0iZJbAsgtuXYbxDl2qxyTPa1rLNk376OBrUG7pwl23XSOWs3J9tzxclqOjxVDYcrQ7KhMpZJH0xm2wp4TWpDTa+WjtCbF4ZxY1toHC4L28R1MK8eWQLGW9KGZfH07fOIcrhfLh1bA15eTXrNaQyN/6b/z4xrmqiccevSRF3GTW8Yzn8QrCaKc6fcBWVsyhmPR7hkgSMmu3ulfjzHe+cZfdgpFetWi/91pZit//K2G8f4+CrHn4y75+d7atfcRzDOgzYHuBV4RbpeTJwkyExYii1DMypsoZSZIl37p8SzsVVTpluTBYgHQi52vF3UdCgwZc0qFMKmZZmiUAr+RlL1+XwXVIqCVBBW6csBKnei3c9rK+q6rMkmHY9sR2hY1/bMYlfMMqiLE2e4L6q1yq8ozI9nbbQnZrPuQFNlzbwUa9XYurtZeCRhiJfkCrIM1OKqVa254wrjRTVzzm5ytCj0VMCMLGIXCEGg1F8+7Fozo746b7qP26/VG01TvFtok82lrxbMUfkgC0h5iYbq/Nj7Kcuof89P3k9BM78qT4bnPzigm/8A \ No newline at end of file diff --git a/docs/UML/Implementation/ViewWeeklyPlan/ViewWeeklyPlan.drawio b/docs/UML/Implementation/ViewWeeklyPlan/ViewWeeklyPlan.drawio new file mode 100644 index 0000000000..194dbdd2cb --- /dev/null +++ b/docs/UML/Implementation/ViewWeeklyPlan/ViewWeeklyPlan.drawio @@ -0,0 +1 @@ +5Vldc6M2FP01nkkfkgEEBj/6I8mmSabJZrq76UtHBhk0kRERIrb31/dihPk0xht72pm+JOhICDj3XJ0reYCmy/WtwFHwyD3CBobmrQdoNjAMXUND+JcimwwZ6mYG+IJ6GaQVwAv9SfI7FZpQj8QKyyDJOZM0qoIuD0PiygqGheCr6rAFZ14FiLBPGsCLi1kT/U49GWSoY2kF/oVQP8ifrGuqZ4nzwQqIA+zxVQlC1wM0FZzL7Gq5nhKWklfl5WZP7+7FBAllnxsm/l+vFnq/t+bjL89vaEHjb/NLNcsHZon6YPWycpMzIHgSeiSdRBugySqgkrxE2E17VxBzwAK5ZNDS4TKWgr+RKWdcABLyEIZNmm+aP5YISdYlSL35LeFLIsUGhqjenESlIoSy5qoIyXCklBaUwoFG6j6sZODvZi6YggtFVjtx+O7+nUyD3ydfJ3//INPhzTx5uNTtw8yVeEm/lIKqxoz6IWBzLiVfQgcJvXEqU8B4RKBr4uE42NKtZ90qJxxoVUMBbIrNj3TglWbl7de088oe7YDZujx8tim3noigQAcRCtwbqJgnwiVddOS5iYVPZMdAlf3Eq6Td3rhrV5pmOtk9gjAs6Uc1MdsCq2Z74hQ+o5CQbl5pBnK0kbX9a1sVSemj2ozZp6hJynlVmxc5BybKyOuYKB/IF4uYVMaANPCmNCJK74j3fyOynWqeKFUWSs9mLHS/o/wTqTBspEIk4LW+E/LGNk8Mhxer3eVvn82SOePuW1n9WkX6jSz5dU2jnpLWrZ6a/qSAUZ5kudBso5fQThVn1Fzx0PiRYIaGWiOq4HZRepks2QNdEEa3ThCVFhym4NIidMhdwOQlhlvErs0YjmI63z51G3viJiIGjr+SOFs3U5QnMn3SdFcjdAqjvyvp1qgSEcts2NKuHqjYEnLOZEvmUa6klhOArcnAmtUCxIUMuM9DzMohatLWKZbeXJrVVXTYdHi9hUnDOheTVoNJ0BYBZd2FUSIvBsbwPUlLt0m2tkWwthXYf2CV64rKMe5rWSdx30s93wnUAtzbJbt10tu195vtqdbJph1+Vg2HK0OypjKVySWEzLYV8JrVhpreLB2htygM08am1DhcFnaJ66CvHlkCpp+kOVXxXNqnEaVTL5eOrQHPrya9ZYuGxn/S/6fjmiaqOm6bTbQ5rmmeySf0plGc2nIXlLF8S+2RBU6Y7J+V+vEc1/YzutPLinWrI//6UsxWfwSbtWt8fJPjW+Pu+fme2i2HFIzzqCsBbgReksPqPoEgd8JSbBma0WAL5cyU6arvEk/GVkuZbk0WIB5YcrEbbFdNlwJT1qxBIXy07HGgUxakgrCyLxeo3Iq27mtL6nlsX0x6btmO0LCu1cJiN8IybFsnTnBe1BqVf6MwP561UU3MZtuGpsmaeS7Wmmvr9mThkcQx9skFuAzU4qrVrLnTCuNFNQvOrgu0LPRcwIws0hSIQaA09B+2rZnRXp3vO4+r1+p7Q1M+W+iSzbmPFsxRdSMLSHWKPdX5sedTltH+nF88n4JmcX6eDS9+hUDX/wA= \ No newline at end of file diff --git a/docs/UML/Implementation/ViewWeeklyPlan/ViewWeeklyPlan.png b/docs/UML/Implementation/ViewWeeklyPlan/ViewWeeklyPlan.png new file mode 100644 index 0000000000..aef52b9aa6 Binary files /dev/null and b/docs/UML/Implementation/ViewWeeklyPlan/ViewWeeklyPlan.png differ diff --git a/docs/UML/Implementation/addRecipe/AddRecipe.png b/docs/UML/Implementation/addRecipe/AddRecipe.png new file mode 100644 index 0000000000..0ac286033c Binary files /dev/null and b/docs/UML/Implementation/addRecipe/AddRecipe.png differ diff --git a/docs/UML/Implementation/editRecipe/EditRecipe.png b/docs/UML/Implementation/editRecipe/EditRecipe.png new file mode 100644 index 0000000000..b62cb0f048 Binary files /dev/null and b/docs/UML/Implementation/editRecipe/EditRecipe.png differ diff --git a/docs/UML/IngredientList/.$IngredientListClassDiagram.drawio.bkp b/docs/UML/IngredientList/.$IngredientListClassDiagram.drawio.bkp new file mode 100644 index 0000000000..b1ddbaa8a4 --- /dev/null +++ b/docs/UML/IngredientList/.$IngredientListClassDiagram.drawio.bkp @@ -0,0 +1 @@ +7Vltb+I4EP41SHsfWJFX4GOB7ou2vUNHpW0/euMhserYnGMK9NffmDgkIQFyt+UbEqri8czYM8/jycTtedN0+1WRVfIoKfCeO6Dbnjfrue4o9PCvEexyQeC4uSBWjOaiQSlYsHfIhU4hXTMKmZXlIi0l12xVF0ZSCIh0TUaUkpu62lJyWhOsSAwNwSIivCn9yahOrNQJx+XEN2BxYpceucN8IiWFso0kSwiVm4rIu+95UyWlzp/S7RS4yV09L19OzB42pkDoLgbz4Omf5/j1LZkMftBR7MDwJepbL2+Er23AdrN6V2RAybWgYJwMet5kkzANixWJzOwGIUdZolOOIwcfM63kK0wllwolQgpUm9g1QGnYnty8c0gJUglkClrtUKUwGNuNWRo5I+tiU4LijyzXkgoegTOyZLBEiA++y1zhg01Xe+pGS/a4+2sXvj/O0oSsv/kvz2lL6r6LWAFlGNIDy3QjkdmGpZzsM7KUQi/sjEkb4SwW+ByhLWDmJiZZDFl4Zye0NImOEsbpA9nJtYku0yR6LUaTRCr2jm5JCQVR2h4oN6xpLIylBVRBhjrzAgnnSPRItjXFB5JpK4gk52SVsV+HMFKiYiYmUmuZWqUTQLfQ4ST2ruvVsHcHYRN7N2xij6y5EvZuA/s+hpgZ6O5QPs+fjwmA8epTZ2TJOD8SFaTgsNQnKZHhUWQiftjrzPxS8rfNgxFJtF3yffFJGKUgDJxSE01y7AxQK8mE3ucpmOAPa8l08DnoBbjxKY6dcow/o670VAqMhbA9soDE2IAhRwvmZw/QZSIUwHfF/VpH3rtcLTnbY5djXLwxnP8FcIpQcSgRfTKAz/pOA3WvibrXgjAnv4DPZcY0k8a/ynWPkL8Ebp29TCSg2DVBD/xuoF8Lc7951DlW97LWZ5/+yE/9QitEoGc25zl57UPUBS1Vp/gq1Z/YYfwnSaFqax19x5dAbOrHsaf77Yqp3YxouOzkQSKVjGrdDaGV/VS8FCsXY+vlTWJyag5wWnfyYOoG5rYe/FGARgMOQbVE0Vwf+0vQcGYHBQyXVj+5wgrt2/A9obkHBWgng4gDUd01L7m+vV8+tNSEbrdS4wTDKxWb4ExPeesnP6qfHAxr/aQ/HjRx99twLz5CPhz3sPmSOV3gK4Wif768lnrHRbbt7dBnmS04uc4Ev7OBiFuZOV9mgs78O9PGttEtDK7EtuGtjf3dNvY/gz5uua9oA/1abeyoBfNJaxfVLDe/18fV2pqOfZuATddlDpXsaKX1iqLQFrRb23RNao+7UXvsXYnbTodbTBD0zlwH44gykkpBnxJmEo4TXxgvmhwcFS2OX7/ZrF+DwpbpZ/OMuc9HL5WZ2bY62BUDgcHmRv5oWAiMnfN5MHAKQWm8H9Ws51ilMGempTvfDWVyrSK4jDL2dDGcY4P9/AVauyZvcqECvhPafkoBJ5q91W/S29C37uaG62Wf5hV3vEWfNvTrLvIgrVX15vvIUeBfcJRnoeFoT8ZDjG38xGF5gZ+rl/8F8e7/BQ== \ No newline at end of file diff --git a/docs/UML/IngredientList/.$IngredientListClassDiagram.png.bkp b/docs/UML/IngredientList/.$IngredientListClassDiagram.png.bkp new file mode 100644 index 0000000000..73d87e8a27 Binary files /dev/null and b/docs/UML/IngredientList/.$IngredientListClassDiagram.png.bkp differ diff --git a/docs/UML/IngredientList/.$RecipeListClassDiagram.drawio.bkp b/docs/UML/IngredientList/.$RecipeListClassDiagram.drawio.bkp new file mode 100644 index 0000000000..edc1a7c2e6 --- /dev/null +++ b/docs/UML/IngredientList/.$RecipeListClassDiagram.drawio.bkp @@ -0,0 +1 @@ +ddHNEoIgEADgp+Gu0Fid7e/SyUNnxt2EGXQdpNF6+nTAjLFOLN8ufwsTeT2crWzVlQAN4wkMTBwY52kisnGY5OklSzceKqvBU7JAoV84rwz60IBdME+OyDjdxlhS02DpIpPWUh+X3clABK2scAVFKc1abxqcCppm+yVxQV2pcPSOb32ilnNxeEmnJFD/ReLIRG6JnI/qIUczNS/uy+lP9nMxi437sWAMlr3HSfRD4vgG \ No newline at end of file diff --git a/docs/UML/IngredientList/IngredientListClassDiagram.drawio b/docs/UML/IngredientList/IngredientListClassDiagram.drawio new file mode 100644 index 0000000000..36a74567b5 --- /dev/null +++ b/docs/UML/IngredientList/IngredientListClassDiagram.drawio @@ -0,0 +1 @@ +7Vltj9o4EP41SL2TqHDeFj7ysm1X2r1ypep175s3NomvTowcs5D99R0Th8QkgfS6fFsJiXg8M87M83g8mIE7T/YfJd7ED4JQPnBGZD9wFwPHQSM3gC8tyQtJgLxCEElGCtGoEqzYCy0tjXTLCM2MrBApIbhiG1sYijSlobJkWEqxs9XWghNLsMERbQhWIeZN6T+MqNhIUTCpJj5RFsVm6bFzU0wkuFQ2kWQxJmJXE7m3A3cuhVDFU7KfU66TZ+flQ8fs8cUkTVUfg285mT7+nXxZ/pv/97j4+BKMonzoFF6eMd+agM3LqrzMgBTblFDtZDRwZ7uYKbra4FDP7gBzkMUq4TBC8JgpKX7QueBCgiQVKajNzBpUKrrvfHl0TAlwiYqEKpmDSmkwMfk2NEJj86K7ChTfNamPa3j4ruEbNkSIjr6rXMGDSVd76sZr9pB/zoOXh0US4+0n7/F7MkSN1N2lkaSEQUj3LFONRGY7lnB8yMhapGplZnTaMGdRCs8h2FLI3EwniwELp2ZCCZ3oMGac3ONcbHV0mcLhj3I0i4VkL+AWV1BgqcyGcgJLY6UtDaCSZqCzLJFAJ6IHvLcU73GmjCAUnONNxp6OYSRYRiydCaVEYpQ6gG6hQyf2juNa2DujoIG9N/aa2DvIuRL2zW0zhBAzDd0U5Mvi+ZQAEK/q2iNrxvmJqCQFp2vVSYkMtiJLo/uDzsKrJF9MHrRIgO2aH4pPzAihqYZTKKxwgZ0GaiNYqg558mfwgVoyH733Bz68+BzGqBrDR6tLNRcpxILZAVkKxNhRTY4WzM9uoMtEKIHvi/v4SrC7l6slZwfsCozLEwP9L4ATgIrTCtGvGvDFEDVQd5uouy0Ic/xE+VJkTDGh/ctC9wT5S+Da7GVpTCW7Jui+1w/0a2HuNbc6h+pe1frs3R/Frl8pCQgMNEtdVNQ+QD0lleocjlL1jh3Hf+GE1m2Nozs4BCJdP0493e43TOYLrOhlJ/cCqKRVbTeY1N6n5qVcuRwbL88CkmM5gGnVy4OuG5BbO/iTALUGPQbVEkVzfWgwqaJn3qCE4dLqnStswL4N3w7NAyiU9DIIOcWyv+Yl12/ny6uWmqDZU7aWGuSPrlRs/DM95Vs/+Vr9pOtb/aQ3GTdx94IW3CfX6ieD5iHTXeBrhWJ4vrxWeqdFtu10GLLMFJxCZwa/sylO38rM+TLj9+bfmTa2jW6BfyW23by1sb/bxv4y6BPUD/RrtbHjFsxnrV1Us9z8Xh9ntTU9+7aU7vouc6xkJyttNwSEpqC9tU3XpPakH7Un7pW4jZpXcQ2QaUqm+joYRoThRKTka8x0wmHiA+NlkwOjssXx7JtN+xqU7pn6rp8h98XosTaz2NcHeTlIIdjCyBvflAJth96PRqgUVMaHkWW9hCoFOdMt3fluKBNbGdLLKENPF9FzbDDtLyXWNXmTCzXwUWDaY0k5VuzZvklvQ9+4W2quV32aO0Z2n3ZzcpVbBGms6jffJ45874KjIgsNRwcyHmPsxc+7pzu02j1Px9vJLLl5evwWfqYtN8V/dhWhS1fpZ+pRZ69/PIDb7u5tWr9Cd+2V/3cck91sd0qC1KuD++u/qWBY/X1SgFX9CeXe/gQ= \ No newline at end of file diff --git a/docs/UML/IngredientList/IngredientListClassDiagram.png b/docs/UML/IngredientList/IngredientListClassDiagram.png new file mode 100644 index 0000000000..2be6b7bc4a Binary files /dev/null and b/docs/UML/IngredientList/IngredientListClassDiagram.png differ diff --git a/docs/UML/Meal360/.$IngredientListClassDiagram.png.bkp b/docs/UML/Meal360/.$IngredientListClassDiagram.png.bkp new file mode 100644 index 0000000000..73d87e8a27 Binary files /dev/null and b/docs/UML/Meal360/.$IngredientListClassDiagram.png.bkp differ diff --git a/docs/UML/Meal360/.$Meal360ClassDiagram.drawio.bkp b/docs/UML/Meal360/.$Meal360ClassDiagram.drawio.bkp new file mode 100644 index 0000000000..b1ddbaa8a4 --- /dev/null +++ b/docs/UML/Meal360/.$Meal360ClassDiagram.drawio.bkp @@ -0,0 +1 @@ +7Vltb+I4EP41SHsfWJFX4GOB7ou2vUNHpW0/euMhserYnGMK9NffmDgkIQFyt+UbEqri8czYM8/jycTtedN0+1WRVfIoKfCeO6Dbnjfrue4o9PCvEexyQeC4uSBWjOaiQSlYsHfIhU4hXTMKmZXlIi0l12xVF0ZSCIh0TUaUkpu62lJyWhOsSAwNwSIivCn9yahOrNQJx+XEN2BxYpceucN8IiWFso0kSwiVm4rIu+95UyWlzp/S7RS4yV09L19OzB42pkDoLgbz4Omf5/j1LZkMftBR7MDwJepbL2+Er23AdrN6V2RAybWgYJwMet5kkzANixWJzOwGIUdZolOOIwcfM63kK0wllwolQgpUm9g1QGnYnty8c0gJUglkClrtUKUwGNuNWRo5I+tiU4LijyzXkgoegTOyZLBEiA++y1zhg01Xe+pGS/a4+2sXvj/O0oSsv/kvz2lL6r6LWAFlGNIDy3QjkdmGpZzsM7KUQi/sjEkb4SwW+ByhLWDmJiZZDFl4Zye0NImOEsbpA9nJtYku0yR6LUaTRCr2jm5JCQVR2h4oN6xpLIylBVRBhjrzAgnnSPRItjXFB5JpK4gk52SVsV+HMFKiYiYmUmuZWqUTQLfQ4ST2ruvVsHcHYRN7N2xij6y5EvZuA/s+hpgZ6O5QPs+fjwmA8epTZ2TJOD8SFaTgsNQnKZHhUWQiftjrzPxS8rfNgxFJtF3yffFJGKUgDJxSE01y7AxQK8mE3ucpmOAPa8l08DnoBbjxKY6dcow/o670VAqMhbA9soDE2IAhRwvmZw/QZSIUwHfF/VpH3rtcLTnbY5djXLwxnP8FcIpQcSgRfTKAz/pOA3WvibrXgjAnv4DPZcY0k8a/ynWPkL8Ebp29TCSg2DVBD/xuoF8Lc7951DlW97LWZ5/+yE/9QitEoGc25zl57UPUBS1Vp/gq1Z/YYfwnSaFqax19x5dAbOrHsaf77Yqp3YxouOzkQSKVjGrdDaGV/VS8FCsXY+vlTWJyag5wWnfyYOoG5rYe/FGARgMOQbVE0Vwf+0vQcGYHBQyXVj+5wgrt2/A9obkHBWgng4gDUd01L7m+vV8+tNSEbrdS4wTDKxWb4ExPeesnP6qfHAxr/aQ/HjRx99twLz5CPhz3sPmSOV3gK4Wif768lnrHRbbt7dBnmS04uc4Ev7OBiFuZOV9mgs78O9PGttEtDK7EtuGtjf3dNvY/gz5uua9oA/1abeyoBfNJaxfVLDe/18fV2pqOfZuATddlDpXsaKX1iqLQFrRb23RNao+7UXvsXYnbTodbTBD0zlwH44gykkpBnxJmEo4TXxgvmhwcFS2OX7/ZrF+DwpbpZ/OMuc9HL5WZ2bY62BUDgcHmRv5oWAiMnfN5MHAKQWm8H9Ws51ilMGempTvfDWVyrSK4jDL2dDGcY4P9/AVauyZvcqECvhPafkoBJ5q91W/S29C37uaG62Wf5hV3vEWfNvTrLvIgrVX15vvIUeBfcJRnoeFoT8ZDjG38xGF5gZ+rl/8F8e7/BQ== \ No newline at end of file diff --git a/docs/UML/Meal360/.$RecipeListClassDiagram.drawio.bkp b/docs/UML/Meal360/.$RecipeListClassDiagram.drawio.bkp new file mode 100644 index 0000000000..edc1a7c2e6 --- /dev/null +++ b/docs/UML/Meal360/.$RecipeListClassDiagram.drawio.bkp @@ -0,0 +1 @@ +ddHNEoIgEADgp+Gu0Fid7e/SyUNnxt2EGXQdpNF6+nTAjLFOLN8ufwsTeT2crWzVlQAN4wkMTBwY52kisnGY5OklSzceKqvBU7JAoV84rwz60IBdME+OyDjdxlhS02DpIpPWUh+X3clABK2scAVFKc1abxqcCppm+yVxQV2pcPSOb32ilnNxeEmnJFD/ReLIRG6JnI/qIUczNS/uy+lP9nMxi437sWAMlr3HSfRD4vgG \ No newline at end of file diff --git a/docs/UML/Meal360/Meal360ClassDiagram.drawio b/docs/UML/Meal360/Meal360ClassDiagram.drawio new file mode 100644 index 0000000000..0f76c6ff83 --- /dev/null +++ b/docs/UML/Meal360/Meal360ClassDiagram.drawio @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/docs/UML/Meal360/Meal360ClassDiagram.png b/docs/UML/Meal360/Meal360ClassDiagram.png new file mode 100644 index 0000000000..1c309e69d1 Binary files /dev/null and b/docs/UML/Meal360/Meal360ClassDiagram.png differ diff --git a/docs/UML/Parser/ParserClassDiagram.png b/docs/UML/Parser/ParserClassDiagram.png new file mode 100644 index 0000000000..cdd0ad69e9 Binary files /dev/null and b/docs/UML/Parser/ParserClassDiagram.png differ diff --git a/docs/UML/Parser/parseDate.drawio b/docs/UML/Parser/parseDate.drawio new file mode 100644 index 0000000000..b59bd0d0c7 --- /dev/null +++ b/docs/UML/Parser/parseDate.drawio @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/UML/Parser/parseDate.drawio.png b/docs/UML/Parser/parseDate.drawio.png new file mode 100644 index 0000000000..7111bd7df9 Binary files /dev/null and b/docs/UML/Parser/parseDate.drawio.png differ diff --git a/docs/UML/Parser/parseDate2.drawio b/docs/UML/Parser/parseDate2.drawio new file mode 100644 index 0000000000..c62f023d5f --- /dev/null +++ b/docs/UML/Parser/parseDate2.drawio @@ -0,0 +1,158 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/UML/RecipeList/.$IngredientListClassDiagram.drawio.bkp b/docs/UML/RecipeList/.$IngredientListClassDiagram.drawio.bkp new file mode 100644 index 0000000000..6872fe0f78 --- /dev/null +++ b/docs/UML/RecipeList/.$IngredientListClassDiagram.drawio.bkp @@ -0,0 +1 @@ +7Vldb9owFP01SN1DJ5JACo8F1nVS2aoVaduji01izbGRY77663cd2yROQqFreUOq1vj4+ia+5/jkNutE42z7VaJlOhWYsE7YxdtONOmEYdCNYvilkZ1B4qBngERSbKBuCTzRF+JWWnRFMcktZiAlBFN06YNzwTmZKw9DUoqNH7YQDHvAEiWkATzNEWuivyhWqUWDeFhO3BOapPbWg/DGTGTIBdud5CnCYlOBoi+daCyFUOYq244J08Xz63J3YHb/YJJwdcqCwYJOdz928ct0kqVodd/78zu7tlnWiK3shn+SOV2SB5or+9hq52qRb2jGEIfRaCG4erIzAYwRowmH6zk8DJEArIlUFMp4ayeUWAI6TynDD2gnVvqRc4Xmf91olApJXyAtYjYnTEtlFRHGXsSTXglwF1BJcoh5dHUIatAUbb3AB5QrC8wFY2iZ0+f9NjIkE8pHQimR2aBmmV3NYIdkW4Fs2b8SkREldxBiZ8MwMkvsGQi79lBsSkX1QoulFTGFDkRWxck+d0k0XFiu38B72OC9E4702UIJ3OoWru5Rnk7RshPGTLPzpCTlSSccN6eMYszUN6A/AQGEcaKK4rnfNSlB5VRBsRR/yVgwAZqZcGG0RRmrQU5ejCzUQXHlSzSHZ3woYia9EvlpK6ohAWsXrDiHKcWYcC0MoZBCRgWa8qWgXBUV74/gB47VuPu53+lP9B77o6Acw48Ol2osOOwF0UIjBCS2IblqVc+rx/C4pJyETlXQ4EwCiloEVOOY0YI7w7Ezz+C/CM6AKkZKRmea8Ml10GA9arIetTDM0DNhjyKnigqdX5rYGvPHyPXVS3lKJD0n6f3eaaSfi/Nem2ksKMej3XeUkSsO/xjzMG7xyQycP8CTRoGx1BHC2MBX0sxWIu2ytYCH8RYRTNXBVcZ/4K4SwkjB4hEbq3nVwdtCS0MU8W78fZWZaNDEKbuciRlKrsBcq+UxT/GG/UuSgX2ZoDspso/IyeBFb0LyKziX8PLOvXzmHJja5jN3s2dowQji3tZNy1DPDUuOpj+SBq0RZfrwukRH4iXiWGSWrxo7l9fQhzpSHJ7mSEHcO5Mn9Q80sJfm9cOa167XvPYGgxbOuy2cD8MzcR43OL+GYf3tUzEFPb13ERMzMg5WCypfH4WtFJHffMyzmve9cS529Lod9U/W6mtdcYs04/6ZlHlz6Yrf2xW/mfRhcBrp5+qKB21dse0+mp707ibV85+EqKLz/nTA+ExI6WD73uktJlW/4Sr7sWhJqYXgxdL81nmui3l2rnsxvo89A8PTzsAwOtMhCJofEhskE45v9RdZGGGKMsHxLKW64DBxR5nrnGDk+ibNUKoyNyPFimOCLS1kS9VvfQ21N6M/lZnJtjrYuQGHzZpFvcGNA/S64HO3GzigXFyMvNWPYGdQM90nvt5i5WIl5+Q4y9Aowqk6/mc3wd6X6qYWKuQHsSVfEoYUXfsfs9vYt+keRXGMXfMXDQK/+bup9fFmk3ZV9eNzLVG/dySRqUIjUSHG/R7b9AnD8hu6CS//JyL68g8= \ No newline at end of file diff --git a/docs/UML/RecipeList/.$RecipeListClassDiagram.drawio.bkp b/docs/UML/RecipeList/.$RecipeListClassDiagram.drawio.bkp new file mode 100644 index 0000000000..edc1a7c2e6 --- /dev/null +++ b/docs/UML/RecipeList/.$RecipeListClassDiagram.drawio.bkp @@ -0,0 +1 @@ +ddHNEoIgEADgp+Gu0Fid7e/SyUNnxt2EGXQdpNF6+nTAjLFOLN8ufwsTeT2crWzVlQAN4wkMTBwY52kisnGY5OklSzceKqvBU7JAoV84rwz60IBdME+OyDjdxlhS02DpIpPWUh+X3clABK2scAVFKc1abxqcCppm+yVxQV2pcPSOb32ilnNxeEmnJFD/ReLIRG6JnI/qIUczNS/uy+lP9nMxi437sWAMlr3HSfRD4vgG \ No newline at end of file diff --git a/docs/UML/RecipeList/RecipeListClassDiagram.drawio b/docs/UML/RecipeList/RecipeListClassDiagram.drawio new file mode 100644 index 0000000000..ab5a5e0134 --- /dev/null +++ b/docs/UML/RecipeList/RecipeListClassDiagram.drawio @@ -0,0 +1 @@ +7Vlbb+I6EP41SN2VusoFUngs0Ju2bKtS6XTPy8olJrHq2BzHFOiv33FsE5yEQk/hrVJF4/F4bM8383nitMJBtrwSaJaOeIxpK/DiZSsctoKg3QvhVwlWWhD0Ai1IBIm1yCsFY/KGtdC30jmJcW5kWiQ5p5LMXOGEM4Yn0pEhIfjCVZtyGjuCGUpwTTCeIFqX/kNimRqpH/XKjmtMktRM3Q3OdEeGrLLZSZ6imC82ROFFKxwIzqV+ypYDTJXvXL9cbuldL0xgJvcZMBq+eTf+6HmxuMjw8D/278Pdz1Nj5RXRudmwWaxcWQ8IPmcxVka8VthfpETi8QxNVO8CIAdZKjMKLR8ecyn4Cx5wygVIGGeg1q+v1E6LhcTLDZFZ+RXmGZZiBSqmN/BMHJkw8rumvShBaUcdLUs38OgERhGZQEjWtktfwYNxV7PrulMyWt2torfRMEvR/Lr9+ylrcN0DnpAZviW5rDkxX5CMosIbU87k2PQolyFKEgbPE/AOBq/1lVcIROC56ZBcOXmSEhrfohWfq53lEk1ebKufckHewCwqYUBCmmQKIkdjrEYaMAXOQefeAuNXRCO0dBRvUS6NYMIpRbOcPK+3kSGRENbnUvLMKB0C98DFPfCiOu5BVMc9sMKD4x40pExf0RJKYKpzeLpGeTqCzAgiqtAZS0FY0goG9S4dMbrrBuBPIACCKJGF8+z/SiiB5+S2TJsSSisiG14UT+XW4MohoWGNt4XOsF1KHoxHlYjD2CktKCwlcYyZCgwukUQ6ChTkM06YLDze6cMfMNLA+9FpdYZqj52+X7bhT6kLOeAM9oJIESMYQmyBc9kYPe+m4e6QsiG0bwR1jxRA4W7OpaTATmNszx3/fwGcAVQUl4g+KsCHp34N9bCOetiAMEXPmN7znEjClX2hdSvI7wLXjV7CUizIMUHvtPcD/ViYt5tIY0pY3F/9Qhk+YfCjyUOzxTfdsPwAKw19Tal9FMdafCJ074amGfbKYTHOIBwTuXWU5h+YVYAaLlDcQWMVrto6LRSDWGJn4l/zTGtDTOyzy0f+iJITINdN9+hVfGD/AmdAX1rpUvDsEDYpHPRaJT+BvITDO3fs6TzQvs0f7WTPUL1ixJyt65KhahuG7DS/wwx6RYSq5LWGdugLxGKeGbwq6HwdQwdlpCjYj5H8qH0kTupsKWC/iteDFa+eU7y2u90GzL0GzO1L8sExj2qYn0KzevpskILqXrOI1ulrBqsolcdHQSuF5o0rc6jmcyfOFx29T0edvWP1vaq4ITTtO/bBI/Psqyr+bFX8YdB7/n6gH6sq7jZVxab6qHPSp4tUh38SLIvK+9sW4tMqJYOta6ePkFR1wnl2N20wqQLB0SX5ueVcq/NsWfeL+A6bA739cqB3tHvEPe5gMYvP1WU2tGKCMs7ix5Qoh0PHJaG2coKWrZsUQhv3su4lLl4S+aSewfe69XujZ7jcbKxsg8Fm9aB298wK1Dj/h+f5VlAOLlrO6HugM/CZqhPfL7FyPhcTvBtlKBQhq3a/duPYueSvx8IG+H5kwBeYIkle3e8ATegbc/e8SGNb/IVd3y3+zip1vN6kGbV5b18x1GnvMKS9UDNUBON6j3vFJwtGdHDFpmyOf/YGf56GLwluuOf+vo2Edn0IeIePtr5ArE/qpi8PblhvSf2GENtasrf9Sske1euiqIEdQu/D7ADN8uOPBqv8ghZe/AU= \ No newline at end of file diff --git a/docs/UML/RecipeList/RecipeListClassDiagram.png b/docs/UML/RecipeList/RecipeListClassDiagram.png new file mode 100644 index 0000000000..f0dad56118 Binary files /dev/null and b/docs/UML/RecipeList/RecipeListClassDiagram.png differ diff --git a/docs/UML/Ui/Ui class diagram b/docs/UML/Ui/Ui class diagram new file mode 100644 index 0000000000..bcd64be978 --- /dev/null +++ b/docs/UML/Ui/Ui class diagram @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/UML/Ui/UiClassDiagram.drawio.png b/docs/UML/Ui/UiClassDiagram.drawio.png new file mode 100644 index 0000000000..c6fdd4adb6 Binary files /dev/null and b/docs/UML/Ui/UiClassDiagram.drawio.png differ diff --git a/docs/UML/Ui/Untitled Diagram.drawio b/docs/UML/Ui/Untitled Diagram.drawio new file mode 100644 index 0000000000..0b0d588231 --- /dev/null +++ b/docs/UML/Ui/Untitled Diagram.drawio @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/UML/Ui/commandList.drawio.png b/docs/UML/Ui/commandList.drawio.png new file mode 100644 index 0000000000..d9051723bb Binary files /dev/null and b/docs/UML/Ui/commandList.drawio.png differ diff --git a/docs/UML/WeeklyPlan/.$WeeklyPlanClassDiagram.drawio.bkp b/docs/UML/WeeklyPlan/.$WeeklyPlanClassDiagram.drawio.bkp new file mode 100644 index 0000000000..0648ff7f56 --- /dev/null +++ b/docs/UML/WeeklyPlan/.$WeeklyPlanClassDiagram.drawio.bkp @@ -0,0 +1 @@ +7Vnbbts4EP0aA+2DC4m6xH6M7aTZIsEGSRftPtISLRGhRC9F3/L1O5RI6+pba7dBEcOApeHMcDjnDDWie844WX8WeB4/8JCwHrLCdc+Z9BCyLceHHyXZFBLfdgtBJGhYiKxS8ExfibHU0gUNSaZlhUhyziSd14UBT1MSyJoMC8FXdbUZZ2FNMMcRaQmeA8za0m80lLGW2v6wHLgjNIr11AN0VQwk2CjrlWQxDvmqInJues5YcC6Lq2Q9Jkwlr56X2x2j28AESeUxBkmKvvCZJe5Hkzv6tLamkn7v62CXmC30gnWwcmMyIPgiDYlyYvWc0SqmkjzPcaBGV4A5yGKZMLiz4ZLhKWEjHLxEudmYMy5yN85t/gGVGU/lLU4oU4y4I2xJJA2wHtAEsJG+rziw8g/IMyn4CzEjKU/BYtTOhk7QkghJ1hWRzs5nwhMixQZUzOhAI6WpipBT3K9K4H1P68QVzNGVJjnWZIu2vks84EJD0g3PYEYfNn9v/NeHSRLjxZ377/ekb7fgmWCJpzgjLZiyFU0YznORJ1KPKFAwo1EK1wHkhkDORionkHN2rQckVzAGMWXhPd7whVpXJgFFczeKuaCv4BYboGFYGLSQX9N4VpaaLoJkoPNoYLEboge8rine40xqQcAZw/OMTrfLSLCIaDriUvJEK50BdWQN66i7Vy3U0cDtQN0U9tlRRy3U+3ArSEDnJDMEuKWMzPM95hoGn6WgadQDJJBjq8woixUhL2zzCKw4wWiREfFXGgkSUkjqgekaHISUy10FOqOMNUSGl4zM5E5WZrDXwFz3uc7ELSVPGgol4mA7Y/nuGtMwJKliFJcq9jw2tbg5p7AeFag3gi+AN7Y+eT0PAh/DvV3ew1epC9h9UlgLpjm5CHBzRRQ/O2i3t3oPc9Fwzz+Oer59IeY5hx8HjObYFRibR6L9QwAnABUjJaJfFeCTvt1C3Wmj7nQgnD98HnlGJeXKvyh0G8gfArfOXprGRNBLgj44cr8ZXAhztwNzWKzFOA6f6nvOh49F+Rfiewr5KjePwirDS7LDaskhjqa+muVba58yJuVI50S7DXfO9U/39mbsyqGdq9tj3TlrSGZ4wWSZs64svm+kZ62poXdcTdnI+/mq6mys250bEIOpNU8ZD17+W3BJapj7SpSnpHIV6d/cEOBKO02m2367HxTEUPQS0fQD8jwFCrKqFx/3zqKax85Z1EA/yzs+5d5G83XDkTUrXgtKWdnet+b7kvFt1YLaHBpT5ONEMTWPo7bHFPJanIBKEarfFBd5aomreTeDjbKrvMicobP0BqjeWQ7sNivtDla6lyJlu7F8J+VJpKw9j/4IUjrO8DeTsqvnfCflCaRs9Sx/BjGHHX3xLyVmuzG2WzkgaXitThnLXqySFRisnmflRyZGO6Q44Wn4NaZpr36aYrtGAO/cW1dhRMyRDvRTMY94itlNKT3UA071scmofpz304dxsEYd5t7zmIwvhGb83jMPWHZE5OG+SuXjGFZBI+qat2VBGJZ0WceyizXa26Pqc0tX7tBpPM4Hn+zGwV+xTG1YPYtt+PK9w76KVLR8AYHwpqKmu/GdYfuW25hquD+0/fpwUUTQsDbh8NksI7LXrL0tMj9ejt57Of66cnSPLEf0ZsrR8W0oIWv7aTg8ujId9xS3ZypSD7UWszfKA/q/r0j99yJ9e0XqvJkidR10iSI94PZcRerW/x90m3+4nqZ/iSKF2/IP5UK9/Fveufkf \ No newline at end of file diff --git a/docs/UML/WeeklyPlan/.$WeeklyPlanClassDiagram.png.bkp b/docs/UML/WeeklyPlan/.$WeeklyPlanClassDiagram.png.bkp new file mode 100644 index 0000000000..3fa6a0c6cb Binary files /dev/null and b/docs/UML/WeeklyPlan/.$WeeklyPlanClassDiagram.png.bkp differ diff --git a/docs/UML/WeeklyPlan/.$WeeklyPlanClassDiagram.png.dtmp b/docs/UML/WeeklyPlan/.$WeeklyPlanClassDiagram.png.dtmp new file mode 100644 index 0000000000..e646ffd72d --- /dev/null +++ b/docs/UML/WeeklyPlan/.$WeeklyPlanClassDiagram.png.dtmp @@ -0,0 +1 @@ +7ZZdb9owFIZ/TaTuolM+CNDLJnSttqJVZVq3Szc+xB5OzBxTSH/9jolDSBNatoq7SQjZr4+/3ufYsRPE2eZakSWbSgrC8V26cYKJ4/uh6+K/EcpKCGohVZxW0p4w489QiV6trjiFwmqVpKUUmi/bYiLzHBLd0ohSct0Om0tBW8KSpNARZgkRXfWBU82s6g0vmoYb4CmzU4/9UdWQkTrY7qRghMr1nhRcOUGspNRVKdvEIIx3bV8+HWjdLUxBro/pQKn3Oxwxeh2kv5g3+vyFTKJzO8oTESu7YbtYXdYOKLnKKZhBXCeI1oxrmC1JYlrXiBw1pjOBNQ+LhVZyAbEUUqGSyxzDIjsHKA2bg4v3dpZgKoHMQKsSQ+oOF+Oqi00jPwir+rqBErjDSmN7PLzQ8iA2EdLd2I1XWLB29Vs3nvNp+bUcPk8nGSOrm8HPH1mPdQ8AC1HeCZJ3TCzWPEPduDGXuZ7ZFmMZETzNsZygFYCuRcYojhl4aRu0NCYnjAt6S0q5MjsrNEkWdS1iUvFnHJY0GIjS9jD5w1bEzPS0MBUUGHNXU/BeSFOyaQXekkJbIZFCkGXBH3fbyIhKeR5JrWVmgw5A7kmFg9x996LNfTDqcPfHXg/3wD8Rd797ZGLfuXQ7zHGL+tCRmHMhXkh1HgiY64NZUODJ43l6u42ZDBrl3m7dSBL7zsX2rmGcUsgNQamJJhUuw2Ypea631oQR/nD1sfsxdEJceIx1r6njz4QrHcsc90L4FiZgLqzB5EMP5lfPy9vsa9bD41D7gxORDt6+HAXfsqsY1x8I758AZ4hKQEP0mwE+Ofc61IMu9aCHsCCPIO5kwTWXZnxVxb4g/xbcdvbynIHip4QeusdBH5+I+aCHeWQmotRc7MWZgoQvYWo+fJeoN3f+h0p4kji+Sd3Aq+5A0xsfRaDhHQMkAogyQWevRjFIFt8xzyjXpZ3olhuOpsv9rm7HeMSHFPR8rP5fXO/M4fFxOTz6+4sLq82Lcdu29+wOrv4A \ No newline at end of file diff --git a/docs/UML/WeeklyPlan/AddWeeklyPlanUML.drawio b/docs/UML/WeeklyPlan/AddWeeklyPlanUML.drawio new file mode 100644 index 0000000000..ef8f47c11e --- /dev/null +++ b/docs/UML/WeeklyPlan/AddWeeklyPlanUML.drawio @@ -0,0 +1 @@ +7VrbcqM4EP0aV80+hJIQ4vIYnHimtjK1qfHD7DxtySBjNthiACd2vn4lkLgJHDuxM5daPySohRo4fU53C3uCpuvdx4ykq88spMnEBOFugm4mpgkBsvk/YdlXFhtalSHK4rAygcYwj5+pWimt2zikubRVpoKxpIjTrjFgmw0Nio6NZBl76p62ZEnYMaQkopphHpBEt36Nw2IlrdD2molPNI5W8tKu6VQTa6JOlk+Sr0jInlomdDtB04yxojpa76Y0EeB1cZmNzNY3ltFNccyCtf3PY2DOo3+n0dyCj9NvH8PvV9LLI0m28oHlzRZ7hUDGtpuQCidggvynVVzQeUoCMfvEY85tq2Kd8BHkh9IdzQq6G71PWD89pw1la1pke36KWuBKhijGSECfGvhdx61sqxbyliWBJjLkUe26QYUfSGCGQfK/3OH1w1/z1e7P5/vnu0/+M7u7Uo4PoRRxmNKRRx0ERBKWLJQHcBgVz+6gAoGnw2JiHRZkO4YS4dmRQUfwp8WOZZwkU5awrJxBSzegQcDteZGxB9qaWbjYwqDmUxvRwxHq4zyKJwJQww+b1iB+FwIPHkGrcfBCQt3lIHh24NLF8pLgqRUt7Hhif2fuwSHu2Qm/tL/gB5E4uCdZTjNl5pepZzSk+aMX3VxGkjja8OOAw8edIF8AFPPScC0n1nEYiuV+RvP4WSpZsDZl8aYonxf7E3wjfG0LllfFDWox27AN7QVYmi4XQRsbGFg2TzsQexCb3eRiGgAA07Ftx3Mt6AGka2Ug1ZgXSzPmy6H+SulDsr9PyOb/cOvVw3QN3AuxXj4Gst/FQqrawHZI0bXSay9YvHVKxeF2ndzFS5rEJVopzWJ+MyJWN4k03ze2l1oVXoALwpdk9ThJSJrHTVwzGmyzPH6kX6gMprCybSGuNK0bTqAn59nM9LF9UQFzjaqUK0NqIQNj0Hz0AgcxMPBA62R6nmHhS4l3MNJtuf7q0TZnvje7lfa5fBCRAi5JAA8bvAlsPtavwAVon9Ty6Im1zQKWFSsWsQ1J2jx4fyU6HLBuboXIcLwW+nquhdgAtg6+h4x683pu7BXUvxX2HjRUc6LAtywOvg440NG+2IbRRBrUqahs12E4jzdRQpvs94F3KGQtktX3rXgT4D+VUxOxHZyRMOQHi20WiaIoRNw99w8tgDSMqMpATYxuG2vd5iR0KVzkPGPyW7orRzccap9uwmvx4oQPFwkLHkRq7LwC4EHM9n9LmpSDb3ImIQua+CR4iMoVve6mRa7qRJaFNHtbC1QQjs2hExXJBTDHMAoYwILdVkkmyYwmpODloeVlmDrS970QUJuqWKMq78oUVZWjnG2zgMq1DQ15RMi+dZpU5+jVMC8A41ebnbCqkVOjiOpuej7UrbHlMqfFpK+hGuo3dBL6hpkX7Tiln0l6KJWNNvIvU/1FOR2mfE9sJ5L7WM5ecdIis1eDLC0FuuAsRMZAz7kuJ4nrNZ/ermGE1AMasTTXvB1BvTcwlejPoBFw5IOM60X3wO933MOP047Kaf2S1K1Ex9Wg0epzQHMLVhRsfYzmupvlkC7JttzOH1Ya3cVFWZMMAG05LsuSIVrjanyzU7IWg31r0Gpibl7x2rHi96HMhU9XNADupSRsAtsAZoukTpfD0OKaa/Wv6JVyFp0xQA42PVj+HbiKiaFjueIv7j3GiMrfteDofdyrCs4Q+VlK+ZQfknxVUr9qptQ3YK4mi8P0HxNN06sZlQzqds2w+C5PGhphlKN9e/RGabzYoL1GGsiC5+nJXC1/m8DR6s3RfAdHufsZiK1Xg0N8PsNesH5HciJ/TnjHaZu9/PLaXThferltuGLDbwW904XetPTu81034MjVQOZ9jOhy8g91Bn/N7nng+4L330iPJfuzp2bntNQsst95NsvQ6XYk9Y8ozrxN5ul58Do/vF1H3klZ4udpOE7k4AnUAgqTt3JLvfWWMUcmPopbjaOxhRct7HzY/FaoOr35xRW6/Q8= \ No newline at end of file diff --git a/docs/UML/WeeklyPlan/AddWeeklyPlanUML.png b/docs/UML/WeeklyPlan/AddWeeklyPlanUML.png new file mode 100644 index 0000000000..fd7c954ee4 Binary files /dev/null and b/docs/UML/WeeklyPlan/AddWeeklyPlanUML.png differ diff --git a/docs/UML/WeeklyPlan/WeeklyPlanClassDiagram.drawio b/docs/UML/WeeklyPlan/WeeklyPlanClassDiagram.drawio new file mode 100644 index 0000000000..9221fd3069 --- /dev/null +++ b/docs/UML/WeeklyPlan/WeeklyPlanClassDiagram.drawio @@ -0,0 +1 @@ +7Zffb5swEMf/mkjdQyd+JLR9LKRrJyVrtGzrujcXLmDF2Mg4JfSv3xlMCCFps615mxSp9vfOh32f80EHbpCubyXJkqmIgA0cK1oP3PHAcWzL9fCPVspa8exhLcSSRrVktcKcvkCz0qgrGkFutFpSQjBFs64YCs4hVB2NSCmKrttCsKgjZCSGnjAPCeurDzRSiVFt76o13AGNE/PoS+eiNqSkcTYnyRMSiWJLcm8GbiCFUPUoXQfAdPK6efl0wLrZmASujlkQ3Ge/vpe+B9ZY8fmjd19Es3MT5ZmwlTmw2awqmwxIseIR6CDWwPWLhCqYZyTU1gKZo5aolOHMxmGupFhCIJiQqHDB0c3v77R5LEgF6y3J7PwWRApKluhirI5llpgyclyT5qKF4rrGJ9niYV+Z9BNTCPEmdpsrHJh07U/d5YJOy/vSe5mO04Ss7oaPP9M9qXsAWLJyxgjvJTEvaIq6zsZCcDU3Fp0ywmjMcRxidgCz5uusUKzAa2NQQic5TCiLJqQUK32yXJFw2cz8REj6gmFJi4FIZS6T43U85nqlgSkhR59ZA8bekaZk3XGckFwZIRSMkSynT5tjpETGlPtCKZEap3fhftXlPtzDvWkpHe4Xp+Lu9K9M4AyurR5zPKI6dCUWlLEdqakDBgt1sApyvHmUx5PKZzxsla/m6FoSuHbBql6T0CgCrgkKRRSpcWk2maBcVakZ+fjD3QfWx9FghBsPcG63c/xpd6kCwfEshFYwAWuhAF0PezC/el/eZt+w9o5D7QxPRNp9uzkyWrGrGTcvCPuvAKeIikFL9JsGPj63e9TdPnV3D2FGnoDNRE4VFTq+rH13yL8Ft1u9lCcg6Smhj6zjoF+eiPlwD3NfPyiKdGPPzySENIOpfvFdo972/A+18Cwwvi5d1657oF6NX0Wg4B8ChAyI1E5nr3olEC5/YJ1FVJXmQROqOeolXzdzE+MJP6RAv6x2wmArX46xZk2ELySFesFcSSw63SEc63D0yt6GNM3ROtFglYP8zGMJEYWqrvVmWmHruHXK/jfp972vl0e+j23nj28sTtvP48q29U+Ge/Mb \ No newline at end of file diff --git a/docs/UML/WeeklyPlan/WeeklyPlanClassDiagram.png b/docs/UML/WeeklyPlan/WeeklyPlanClassDiagram.png new file mode 100644 index 0000000000..37b1643601 Binary files /dev/null and b/docs/UML/WeeklyPlan/WeeklyPlanClassDiagram.png differ diff --git a/docs/UserGuide.md b/docs/UserGuide.md index abd9fbe891..e7b3e7617b 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -2,41 +2,434 @@ ## Introduction -{Give a product intro} +Meal360 is a desktop app for managing your recipes and weekly meal plans, optimized for use via a +Command Line Interface (CLI) while still having the benefits of a Graphical User Interface (GUI). If +you can type fast, Meal360 can get your recipe management tasks done faster than traditional GUI +apps. -## Quick Start +* [Quick Start](#quick-start) +* [Features](#features) +* [Command Summary](#command-summary) + +___ -{Give steps to get started quickly} +## Quick Start 1. Ensure that you have Java 11 or above installed. -1. Down the latest version of `Duke` from [here](http://link.to/duke). +2. Down the latest version of `Meal360` + from [here](https://github.com/AY2223S2-CS2113-F10-3/tp/releases). +3. Upon the first launch, the app will create a `database` folder in the same directory as the + `tp.jar` file. This folder will contain all the data files that the app will use. Please do not + touch these files to ensure smooth operation of the app. +4. To allow for immediate testing of features, the app comes preloaded with 10 recipes. +5. Take note that changes to the ingredient list, recipe list, and weekly meal plan would be saved + only when exiting the program properly using `bye` command. -## Features +___ -{Give detailed description of each feature} +## Features -### Adding a todo: `todo` -Adds a new item to the list of todo items. +* [Add Recipes](#add-recipes) +* [Edit Recipes](#edit-recipes) +* [Delete Recipes](#delete-recipes) +* [View recipes](#view-recipes) +* [List Relevant Recipes](#list-recipes) +* [Tag/Categorize Recipes](#tagcategorize-recipes) +* [Add Single Recipe To Weekly Plan](#add-single-recipe-to-weekly-plan) +* [Add Multiple Recipe To Weekly Plan](#add-multiple-recipes-to-weekly-plan) +* [Remove Single Recipe From Weekly Plan](#delete-single-recipe-from-weekly-plan) +* [Remove Multiple Recipe From Weekly Plan](#delete-multiple-recipes-from-weekly-plan) +* [Clear Weekly Plan](#clear-weekly-plan) +* [Mark Recipe In Weekly Plan As Done](#mark-recipe-in-weekly-plan-as-done) +* [View Weekly Plan Ingredients](#view-weekly-plan-ingredients) +* [View User Ingredients](#view-user-ingredients) +* [View Weekly Plan](#view-weekly-plan) +* [Random A Recipe](#random-a-recipe) +* [Exit Program](#exit-the-program) +* [Add User Ingredient](#add-user-ingredient) +* [Delete User Ingredient](#delete-user-ingredient) +* [View Available Ingredients](#view-available-ingredients) +* [Help Tab](#help-tab) -Format: `todo n/TODO_NAME d/DEADLINE` +### HOW TO ADD INGREDIENTS TO A RECIPE: -* The `DEADLINE` can be in a natural language format. -* The `TODO_NAME` cannot contain punctuation. +* Please follow the below-mentioned format. -Example of usage: +Format: `ingredient1_name=ingredient1_quantity ingredient2_name=ingredient2_quantity ...` -`todo n/Write the rest of the User Guide d/next week` +Examples of usage: -`todo n/Refactor the User Guide to remove passive voice d/13/04/2020` +* `chicken=100 meat and oil=200` +* `white rice=300 vegetables=400 pepper,chilli and seeds=500` -## FAQ -**Q**: How do I transfer my data to another computer? +* Type the ingredient name followed by equal sign and quantity in positive integer values. +* There is no `and` between two separate ingredients. + * If user types, `meat=100 and chicken=200`, it will get stored as `meat=100`, `and chicken=200`. + * Use `and` only in cases like: `pepper and spices=100`, `meat and vegetables=200`. +* After the ingredients are key-in in, please type `done` in the next line to finish the process. + * **exception:** for editing ingredients partially, you just have to key in the 1 new ingredient + and the recipe + manager knows that you have entered 1 ingredient. No need to type `done`. -**A**: {your answer here} +
-## Command Summary +### Add Recipes + +Add new recipes to your list. + +Format: `add /r [RECIPE_NAME]` + +* `RECIPE_NAME` is basically a string. +* Type the recipe name after typing `/r `. + +Example of usage: + +`add /r chicken rice` +`add /r noodles` + +
+ +### Edit Recipes + +Edit recipes in your list partially, fully or add new ingredients to already existing recipe. + +Format: `edit /r [RECIPE_NAME]` + +* `RECIPE_NAME` is basically a string. +* Type the recipe name after typing `/r `. +* If you want to edit fully: press 1, edit partially: press 2, or add new ingredients: press 3. +* Follow the proper format while editing/adding ingredients. +* **NOTE:** If you add the same ingredient while editing, then the quantity will be the only one +that will be changed to avoid duplicates. + +Example of usage: + +`edit /r chicken rice` +`edit /r noodles` + +
+ +### List Recipes + +List all recipes or filtered list recipes by the name or ingredients. + +Format: `list [/t] [KEYWORD && KEYWORD && ...]` + +* The `KEYWORD` and `/t` are optional. +* `KEYWORD` can be a name of recipe or ingredients or tag. +* If user want to list recipes from tags `/t` is required. When `/t` is used, + at least one `KEYWORD`, replacing by the tag name, is required. +* To list all recipes, do not add `KEYWORD` and `/t`. +* Use `&&` to list all the recipe that contain **all** specified `KEYWORD`. + +Example of usage: + +* `list` lists all the recipes. +* `list pizza` lists recipes that contain 'pizza' in the name or ingredients. +* `list milk && egg` lists recipes that contain __both__ 'milk' and 'egg' in the + name or ingredients. +* `list /t western` lists the recipes that are in 'western' tag. +* `list /t western && chinese` lists recipes that is under __both__ 'western' and + 'chinese' tag. + +
+ +### View Recipes + +Views the list of ingredients and their quantities for a recipe. + +Format: `view INDEX` + +* Views the recipe at the specified `INDEX`. +* The index refers to the index number shown in the displayed person list. +* The index **must be a positive integer** 1,2,3, ... + +Example of usage: + +`view 1` + +
+ +### Delete Recipes + +Deletes one, a range, or all recipes currently in list. + +Format: `delete INDEX/RANGE` or `delete /r NAME` + +* Deletes the recipe(s) specified by the user either through the recipe index or name. +* The index refers to the index number shown in the displayed person list. +* The name refers to the recipe name in the list. +* + +Example of usage: + +`delete 1` +`delete 1-3` +`delete /r pizza` +`delete /r all` + +
+ +### Tag/Categorize Recipes + +__Categorize recipes into a specific tag__ + +Adding one or more recipes into a tag, so that users can organize the recipes into categories + +Format: `tag LABEL << [RECIPE_NAME && RECIPE_NAME && ...]` + +* `LABEL` is required, and replace with the tag label that user want to + add the recipes into. +* At least one `RECIPE_NAME` is required. +* To add multiple recipes into the tag, use `&&` followed by next `RECIPE_NAME`. + +Example of usage: + +* `tag western << burger` adds burger into 'western' tag. +* `tag junk foods << burger && hotdog` adds burger and hotdog into 'junk foods' tag. +* `tag breakfast << milk && boiled egg && bread` adds milk, boiled egg and + bread into 'breakfast' tag. + +__Removing recipes from a tag__ + +Remove recipes from a specific tag. + +Format: `tag LABEL >> [RECIPE_NAME && RECIPE_NAME && ...]` + +* `LABEL` is required, and replace with the tag label that user want to + remove the recipes from. +* At least one `RECIPE_NAME` is required. +* To remove multiple recipes from the tag, use `&&` followed by next `RECIPE_NAME`. + +Example of usage: + +* `tag western>> burger` removes burger from 'western' tag. +* `tag junk foods >> burger && hotdog` remove burger and hotdog from 'junk foods' tag. +* `tag breakfast >> milk && boilded egg && bread` removes milk, boiled egg, and bread + from 'breakfast' tag. + +
+ +### Add single recipe to weekly plan + +Adds an existing recipe to this week's plan. + +Format: `weekly /add RECIPE_NAME QUANTITY` + +* Adds the specified `RECIPE_ NAME` to this week's plan`QUANTITY` number of times, with quantity + representing the number of days the user plans to prepare the recipe within the week. +* The recipe name refers to the name of the recipe shown in the displayed recipe list. +* The quantity **must be a positive + integer** between 1 and 1000. + +Example of usage: + +* `weekly /add pizza 2` adds pizza to this week's plan twice. +* `weekly /add burger 1` adds burger to this week's plan once. + +
+ +### Add multiple recipes to weekly plan + +Adds multiple existing recipe to this week's plan. + +Format: `weekly /multiadd [/r RECIPE_NAME /q QUANTITY]` + +* Adds the specified `RECIPE NAME` to this week's plan`QUANTITY` number of times. +* At least one pair of `RECIPE_NAME` and `QUANTITY` is required. +* Each `RECIPE_NAME` and `QUANTITY` requires `/r` and `/q` before it respectively. +* The recipe name refers to the name of the recipe shown in the displayed recipe list. +* The quantity **must be a positive + integer** between 1 and 1000. +* If the same recipe is specified multiple times, only the **last** quantity specified + will be used. + +Example of usage: -{Give a 'cheat sheet' of commands here} +* `weekly /multiadd /r pizza /q 2 /r burger /q 9` adds pizza twice and burger once to this week's + plan. + +
+ +### Delete single recipe from weekly plan + +Deletes an existing recipe from this week's plan. + +Format: `weekly /delete RECIPE_NAME QUANTITY` + +* Deletes the specified `RECIPE_NAME` from this week's plan `QUANTITY` number of times. +* The recipe name refers to the name of the recipe shown in the displayed recipe list. +* The quantity **must be a positive + integer** between 1 and 1000. +* Indicating a quantity more than the current quantity deletes the recipe completely from + this week's plan. + +Example of usage: + +* `weekly /delete pizza 1` removes pizza from this week's plan once. + +
+ +### Delete multiple recipes from weekly plan + +Deletes multiple existing recipe from this week's plan. + +Format: `weekly /multidelete [/r RECIPE_NAME /q QUANTITY]` + +* Deletes the specified `RECIPE_NAME` from this week's plan`QUANTITY` number of times. +* At least one pair of `RECIPE_NAME` and `QUANTITY` is required. +* Each `RECIPE_NAME` and `QUANTITY` requires `/r` and `/q` before it respectively. +* The recipe name refers to the name of the recipe shown in the displayed recipe list. +* The quantity **must be a positive + integer** between 1 and 1000. +* Indicating a quantity more than the current quantity deletes the recipe completely from + this week's plan. +* If the same recipe is specified multiple times, only the **last** quantity specified + will be used. + +Example of usage: + +* `weekly /multidelete /r pizza /q 2 /r burger /q 9` deletes pizza twice and burger once from this + week's + plan. + +
+ +### Clear weekly plan + +Clears this week's plan by removing all recipes listed in weekly plan. + +Format: `weekly /clear` + +
+ +### Mark recipe in weekly plan as done + +Mark a recipe in the weekly plan as completed. A single count of the recipe and its corresponding +ingredients will be removed from the weekly plan and the list of ingredients. + +Format: `weekly /done RECIPE_NAME` + +* This command executes only if the user has sufficient ingredients to prepare the recipe. +* This command executes only if the recipe is in the weekly plan. +* The recipe name refers to the name of the recipe shown in the displayed recipe list. + +Example of usage: + +* `weekly /done pizza` will mark pizza as done in the weekly plan, assuming the user has `pizza` in + the weekly plan and there are sufficient ingredients. + +
+ +### View weekly plan + +View this week's plan. + +Format: `weeklyplan` + +
+ +### View weekly plan ingredients + +View this week's ingredients. + +Format: `weeklyingredients` + +
+ +### Random a recipe + +Random a recipe from all the recipes that user have, and show the list of +ingredients and their quantities for a recipe. + +Format: `random` + +
+ +### Exit the program + +Exits the program. + +Format: `bye` + +
+ +### Add user ingredient + +Add user's ingredients into the ingredient list. +Format : `add_i /n INGREDIENT_NAME /c QUANTITY /d DATE` + +* The quantity **must be a positive + integer** between 1 and 1000. +* The date **must be in the format of DD/MM/YYYY** +* If the ingredient already exists in the list, the quantity will be added to the existing quantity, + while the expiry date will be updated to reflect the expiry date of the new addition. + +Example of usage: + +* `add_i /n chicken /c 1 /d 01/01/2020` adds chicken with quantity 1 and expiry date 01/01/2020 + +
+ +### Delete user ingredient + +Delete user's ingredients from the ingredient list. +Format : `del_i /n INGREDIENT_NAME /c QUANTITY` + +* The quantity has to be **less than or equal** to the quantity of the ingredient in the list. +* The quantity **must be a positive + integer** between 1 and 1000. +* Indicating a quantity more than the current quantity deletes the ingredient completely from + the list. + +Example of usage: + +* `del_i /n chicken /c 1` deletes chicken with quantity 1 + +
+ +### View user ingredients + +View user's ingredients from the ingredient list. +Format : `view_ingredients` + +
+ +### View available ingredients + +View the available ingredients. +Format: `available` + +
+ +### Help Tab + +View all the 21 proper commands used in the recipe manager. +Format: `help` + +## Command Summary -* Add todo `todo n/TODO_NAME d/DEADLINE` +| Action | Format, Examples | +|------------------------------------|-------------------------------------------------------------------------------------------------------------| +| Add recipe | `add /r [RECIPE_NAME]`
e.g `add /r chicken rice` | +| Edit recipe | `edit /r [RECIPE_NAME]`
e.g `edit /r chicken rice` | +| List recipe | `list [/t] [KEYWORD]`
e.g `list pizza` | +| View recipe | `view INDEX`
e.g `view 1` | +| Add tag/Categorise to recipes | `tag LABEL << RECIPE_NAME`
e.g `tag western << pizza` | +| Remove tag/Categorise from recipes | `tag LABEL >> RECIPE_NAME`
e.g `tag western >> pizza` | +| Random a recipe | `random` | +| Add to weekly plan | `weekly /add RECIPE_NAME QUANTITY`
e.g `weekly /add pizza 2` | +| Add multiple to weekly plan | `weekly /multiadd [/r RECIPE_NAME /q QUANTITY]`
e.g `weekly /multiadd /r pizza /q 1 /r burger /q 3` | +| Delete from weekly plan | `weekly /delete RECIPE_NAME`
e.g `weekly /delete pizza` | +| Delete multiple from weekly plan | `weekly /multidelete [/r RECIPE_NAME /q QUANTITY]`
e.g `weekly /multiadd /r pizza /q 1 /r burger /q 4` | +| Clear weekly plan | `weekly /clear` | +| Mark recipe in weekly plan as done | `weekly /done RECIPE_NAME`
e.g `weekly /done pizza` | +| View weekly plan | `weeklyplan` | +| View weekly ingredients | `weeklyingredients` | +| View user ingredients | `view_ingredients` | +| Add user ingredient | `add_i /n NAME /c COUNT /d DATE`
e.g `add_i /n Rice /c 100 /d 04/09/2023` | +| Delete user ingredient | `del_i /n NAME /c COUNT`
e.g `delete_i /n Rice /c 50` | +| Available ingredients | `available` | +| Exit the program | `bye` | +| Help Tab | `help` | diff --git a/docs/team/gurmankalkat.md b/docs/team/gurmankalkat.md new file mode 100644 index 0000000000..3dfe378ad0 --- /dev/null +++ b/docs/team/gurmankalkat.md @@ -0,0 +1,69 @@ +# Gurman Kalkat - Project Portfolio Page + +## Project: Meal360 +### Overview +Meal360 is a desktop app for managing your recipes and weekly meal plans, optimized for use via a +Command Line Interface (CLI) while still having the benefits of a Graphical User Interface (GUI). If +you can type fast, Meal360 can get your recipe management tasks done faster than traditional GUI +apps. + +### Summary of Contributions +* __Code contributed__: [RepoSense link](https://nus-cs2113-ay2223s2.github.io/tp-dashboard/?search=gurmankalkat&breakdown=true) + + +* __Enhancements implemented:__ + * Added `delete` feature to give users the ability to delete a recipe in the list by its index or name + * [#20](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/20) + * Added `weeklyingredients` feature to list out all ingredients needed for the week based on user's weeklyplan + * [#50](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/50) + * Added `weekly /clear` feature to be able to clear the entire weeklyplan + * [#50](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/50) + * Updated `delete` feature so use can delete all or a range of recipes + * [#34](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/34) + * Added exception handler for `delete` feature + * [#34](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/34) + * [#150](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/150) + * Wrote tests for `delete`, `weeklyingredients` and `weekly /clear` features + * [#50](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/50) + * [#20](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/20/files) + * [#150](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/150) + * Updated `add` feature so users can input all ingredients in one line with `and` between ingredients (code was + not used in final implementation as we decided to take a different approach) + * [#137](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/137) + + +* __Documentation__: + * User Guide + * Added documentation for `delete`, `weeklyingredients`, and `weekly /clear` features + * Updated documentation for `add` + * Developer Guide + * Added documentation for `delete` feature + * Added UML diagrams for `delete` feature + + +* __Contributions to the team-based tasks__: + * Maintaining & solving issues in tracker + * [#124](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/124) + * Solves issues: [#112](https://github.com/AY2223S2-CS2113-F10-3/tp/issues/112), + [#108](https://github.com/AY2223S2-CS2113-F10-3/tp/issues/108), + [#92](https://github.com/AY2223S2-CS2113-F10-3/tp/issues/92), + [#91](https://github.com/AY2223S2-CS2113-F10-3/tp/issues/91) + * [#137](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/137) + * Solves issues: [#79](https://github.com/AY2223S2-CS2113-F10-3/tp/issues/79), + [#76](https://github.com/AY2223S2-CS2113-F10-3/tp/issues/76), + [#78](https://github.com/AY2223S2-CS2113-F10-3/tp/issues/78) + + * PRs reviewed: [#4](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/4), + [#15](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/15), + [#39](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/39), + [#125](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/125), + [#127](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/127), + [#135](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/135) + + +* __Contributions beyond the project team__: + * Bugs & suggestions in PE-Dry Run: [#1](https://github.com/gurmankalkat/ped/issues/1), + [#2](https://github.com/gurmankalkat/ped/issues/2), + [#3](https://github.com/gurmankalkat/ped/issues/3), + [#4](https://github.com/gurmankalkat/ped/issues/4), + [#5](https://github.com/gurmankalkat/ped/issues/5) diff --git a/docs/team/jaredoong.md b/docs/team/jaredoong.md new file mode 100644 index 0000000000..c2d95da666 --- /dev/null +++ b/docs/team/jaredoong.md @@ -0,0 +1,89 @@ +# Jared Oong - Project Portfolio Page + +## Overview + +Meal360 is a desktop app for managing your recipes and weekly meal plans, optimized for use via a +Command Line Interface (CLI) while still having the benefits of a Graphical User Interface (GUI). If +you can type fast, Meal360 can get your recipe management tasks done faster than traditional GUI +apps. + +### Summary of Contributions + +* __Code + contributed__ : [RepoSense link](https://nus-cs2113-ay2223s2.github.io/tp-dashboard/?search=jaredoong&breakdown=true) +* __Functions/enhancements implemented__: + * Added viewing of ingredients needed for a recipe based on index in recipe + list. ([#15](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/15)) + * Added functionality to add single/multiple recipes to the weekly + plan. ([#30](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/30), + [#48](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/48)) + * Added functionality to remove single/multiple recipes from the weekly + plan. ([#30](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/30), + [#48](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/48)) + * Added functionality to view the weekly + plan. ([#30](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/30)) + * Added functionality to mark recipes in the weekly plan as + done. ([#74](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/74)) + * Added functionality to add user ingredients to the ingredient + list. ([#64](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/64)) + * Added functionality to remove user ingredients from the ingredient + list. ([#64](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/64)) + * Added functionality to view the ingredient + list. ([#64](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/64)) + * Added loading and saving to + databases. ([#39](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/39)) +* __Contributions to the UG__ ([#30](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/30), + [#74](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/74)): + * Added documentation for the following commands: + * Recipe related: `view INDEX` + * Weekly meal plan related: `weekly /add RECIPE_NAME QUANTITY` + , `weekly /multiadd [/r RECIPE_NAME /q QUANTITY]` + , `weekly /delete RECIPE_NAME`, `weekly /multidelete [/r RECIPE_NAME /q QUANTITY]` + , `weekly /done RECIPE_NAME`, `weeklyplan` + * Ingredient related: `view_ingredients`, `add_i /n NAME /c COUNT /d DATE` + , `del_i /n NAME /c COUNT` +* __Contributions to the DG__ ([#48](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/48) + , [#123](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/123) + ,[#129](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/129)): + * Updated DG with anchor links for easier navigation. + * Added documentation for the overall Architecture. + * Added documentation for the following components: + * `Meal360`, `RecipeList`, `IngredientList`, + `WeeklyPlan`, `Database` + * Added documentation for the following implementations: + * Add Ingredient Feature + * Delete Ingredient Feature + * List Ingredient Feature + * Edit Weekly Meal Plan Feature + * List Weekly Plan Feature + * Mark Recipe as Done Feature +* __Contributions to the team-based tasks__: + * Setting up of GitHub team org/repo + * Maintaining issue tracker + * Release management (`v1.0`, `v2.0`, `v2.1`) +* __Review/mentoring contributions__: + * [#14](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/14), + [#26](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/26), + [#29](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/29), + [#31](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/31), + [#32](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/32), + [#34](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/34), + [#35](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/35), + [#45](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/45), + [#47](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/47), + [#51](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/51), + [#60](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/60), + [#67](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/67), + [#68](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/68), + [#69](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/69), + [#70](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/70), + [#71](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/71), + [#121](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/121), + [#122](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/122), + [#130](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/130), + [#133](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/133), + [#139](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/139) + +* __Tools__: + * Integrated third party library (GSON) to save and load data from + databases. ([#39](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/39)) \ No newline at end of file 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/junenita.md b/docs/team/junenita.md new file mode 100644 index 0000000000..8f1c1f4053 --- /dev/null +++ b/docs/team/junenita.md @@ -0,0 +1,51 @@ +# JuneNita - Project Portfolio Page + +## Project: Meal360 +### Overview +Meal360 is a desktop app for managing your recipes and weekly meal plans, optimized for use via a Command +Line Interface (CLI) while still having the benefits of a Graphical User Interface (GUI). +If you can type fast, Meal360 can get your recipe management tasks done faster than traditional GUI apps. + + +### Summary of Contributions +* __Code contributed__: [RepoSense link](https://nus-cs2113-ay2223s2.github.io/tp-dashboard/?search=junenita&breakdown=true) +* __Enhancements implemented__ + * Added `list` feature to list all recipes users have + * Updated `list` feature to list relevant recipes by names or the ingredients + * Added `tag` feature to categorise recipe that users have into groups + * Categorise recipes into a specific tag + * Removing recipes from a tag + * Updated `list` feature to be able to list menu in specific tags + * Added `random` feature to random a recipe for users + * Added exception handler for `list`, `tag`, and `random` features + * Wrote tests for `list`, `tag` and `random` features + * Added custom exceptions class. + * `NoRecipeException` + * `RecipeNotFoundInTagException` + * `TagNotFoundException` +* __Documentation__: + * User Guide + * Added documentation for `list` feature + * Added documentation for `tag` feature + * Categorise recipes into a specific tag + * Removing recipes from a tag + * Added documentation for `random` features + * Developer Guide + * Added documentation for `list` feature + * Added documentation for `tag` feature + * Added documentation for `random` features + * Added UML diagrams for `list`, `tag`, and `random` features + * Added instruction for manual testing for list recipes and tag recipes +* __Contributions to the team-based tasks__: + * Maintaining issue tracker + * Updated User Stories in Developer Guide +* __Review/Mentoring contributions__: + * PRs reviewed: [#20](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/20), + [#30](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/30), + [#48](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/48), + [#53](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/53), + [#64](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/64), + [#120](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/120), + [#123](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/123), + [#124](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/124), + [#129](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/129) diff --git a/docs/team/notbingsu.md b/docs/team/notbingsu.md new file mode 100644 index 0000000000..b9d3ef445a --- /dev/null +++ b/docs/team/notbingsu.md @@ -0,0 +1,61 @@ +# Lu Bingyuan - Project Portfolio Page + +## Project: Meal360 + +### Overview + +Meal360 is a desktop app for managing your recipes and weekly meal plans, optimized for use via a Command +Line Interface (CLI) while still having the benefits of a Graphical User Interface (GUI). +If you can type fast, Meal360 can get your recipe management tasks done faster than traditional GUI apps. + +### Summary of Contributions + +* Code + contributed: [RepoSense link](https://nus-cs2113-ay2223s2.github.io/tp-dashboard/?search=notbingsu&breakdown=true) +* __Enhancements implemented__ + * Added edit recipe function to allow users to rewrite recipe + ingredients ([#22](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/22)) + * Improved `edit` recipe function, added 3rd option for users to add additional ingredients to existing + recipe ([#37](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/37)) + * Added `Ingredient` class ([#44](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/44)) + * Added `IngredientList` class ([#44](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/44)) + * Added `available` function to allow users to view + available `Recipe` ([#66](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/66)) + * Update `help` function to include all available functions in + list ([#142](![img.png](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/142))) + * Added checks for `add` recipe method to check for + - empty ingredient name + - empty ingredient count + - negative ingredient count\ + ([#130](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/130)) +* __Documentation__:= + * Developer Guide + * Added architecture and description for `Ui` + component ([#130](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/130)) + * Added architecture and description for `Parser` + component ([#130](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/130)) + * Added `available` feature to implementation + component ([#147](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/147)) + * Added class diagram for `Ui` + * Added sequence diagram for `Ui` + * Added sequence diagram for `Parser` + * Added sequence diagram for `List available Recipes` +* __Contributions to the team-based tasks__: + * Maintaining issue tracker +* __Review/Mentoring contributions__: + * PRs reviewed: ([#64](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/64)), + ([#74](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/74)) +* __Contributions beyond the project team:__ + * PE Dry run + * Reported bugs and documented how they were + generated ([#1](https://github.com/notbingsu/ped/issues/1)), + ([#2](https://github.com/notbingsu/ped/issues/2)), + ([#3](https://github.com/notbingsu/ped/issues/3)), + ([#4](https://github.com/notbingsu/ped/issues/4)), + ([#5](https://github.com/notbingsu/ped/issues/5)), + ([#6](https://github.com/notbingsu/ped/issues/6)), + ([#7](https://github.com/notbingsu/ped/issues/7)), + ([#8](https://github.com/notbingsu/ped/issues/8)), + ([#9](https://github.com/notbingsu/ped/issues/9)), + ([#10](https://github.com/notbingsu/ped/issues/10)) + \ No newline at end of file diff --git a/docs/team/topgun2001.md b/docs/team/topgun2001.md new file mode 100644 index 0000000000..dd4e665c0c --- /dev/null +++ b/docs/team/topgun2001.md @@ -0,0 +1,70 @@ +# Suresh Abijith Ram - Project Portfolio Page + +## Project: Meal360 +### Overview +Meal360 is a desktop app for managing your recipes, optimized for use via a Command Line Interface (CLI) while still +having the benefits of a Graphical User Interface (GUI). If you can type fast, Meal360 can get your recipe management +tasks done faster than traditional GUI apps. + +Given below are my contributions to the project: +### Summary of Contributions +* __Code contributed__: [RepoSense link](https://nus-cs2113-ay2223s2.github.io/tp-dashboard/?search=topgun2001&breakdown=true) + + +* __Functions / Enhancements implemented__ + * Implemented `add` feature for adding of new recipes to the user's recipe list. + ([#26](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/26)) + * Implemented `edit` feature for editing already existing recipes in user's recipe list. + ([#32](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/32)) + * Implemented `help` feature for displaying all types of user commands for execution. + ([#28](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/28)) + * Implemented `combineWords` feature for trimming words with excess white space characters and joining them to + form a single sentence for recipe names and ingredient names. + ([#51](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/51)) + * Implemented enhancement feature of accepting ingredients in a single line. + ([#51](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/51)) + + +* __Contributions to User Guide__ ([#69](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/69), + [#139](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/139)): + * Added anchor links for quick jump to respective tabs. + * Added documentation for the following: + * HOW TO ADD INGREDIENTS TO A RECIPE? + * `add` + * `edit` + * `help` + + +* __Contributions to Developer Guide__ ([#54](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/54),[#139](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/139)): + * Added anchor links for quick jump to respective tabs. + * Added documentation for the following classes: + * `Parser` + * Added documentation for the following: + * `Add Recipes` feature + * `Edit Recipes` feature + * `edit` + * `help` + + +* __Pull Requests reviewed for Team Project:__ + * Gave comments on code quality and code suggestions. + * ([#15](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/15)), + ([#20](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/20)), + ([#23](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/23)), + ([#29](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/29)), + ([#30](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/30)), + ([#31](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/31)), + ([#48](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/48)), + ([#47](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/47)), + ([#56](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/56)), + ([#127](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/127)), + ([#129](https://github.com/AY2223S2-CS2113-F10-3/tp/pull/129)) + + +* __Community__: + * Reported bugs and suggestions for other teams during Practical Dry-Run.([I](https://github.com/TopGun2001/ped/issues)) + * Bugs: ([#1](https://github.com/TopGun2001/ped/issues/1)),([#2](https://github.com/TopGun2001/ped/issues/2)), + ([#3](https://github.com/TopGun2001/ped/issues/3)),([#5](https://github.com/TopGun2001/ped/issues/5)), + ([#7](https://github.com/TopGun2001/ped/issues/7)) + * Suggestions: ([#4](https://github.com/TopGun2001/ped/issues/4)),([#6](https://github.com/TopGun2001/ped/issues/6)), + ([#10](https://github.com/TopGun2001/ped/issues/10)) 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/meal360/Ingredient.java b/src/main/java/seedu/meal360/Ingredient.java new file mode 100644 index 0000000000..fb1a41d201 --- /dev/null +++ b/src/main/java/seedu/meal360/Ingredient.java @@ -0,0 +1,35 @@ +package seedu.meal360; + +import java.time.LocalDate; + +public class Ingredient { + + private static final Parser parser = new Parser(); + public String ingredientName; + public Integer ingredientCount; + public LocalDate expiryDate; + public Boolean isExpired; + + public Ingredient(String ingredientName, Integer ingredientCount, String expiryDate) { + this.ingredientName = ingredientName; + this.ingredientCount = ingredientCount; + // parse expiry date using parseDate method + this.expiryDate = parser.parseDate(expiryDate); + this.updateExpired(); + } + + public Ingredient(String ingredient, int newCount, LocalDate expiryDate) { + this.ingredientName = ingredient; + this.ingredientCount = newCount; + this.expiryDate = expiryDate; + this.updateExpired(); + } + + public void updateExpired() { + if (expiryDate.isBefore(LocalDate.now())) { + isExpired = true; + } else { + isExpired = false; + } + } +} diff --git a/src/main/java/seedu/meal360/IngredientList.java b/src/main/java/seedu/meal360/IngredientList.java new file mode 100644 index 0000000000..a55d925bf4 --- /dev/null +++ b/src/main/java/seedu/meal360/IngredientList.java @@ -0,0 +1,152 @@ +package seedu.meal360; + +import java.util.HashMap; +import java.time.LocalDate; +import seedu.meal360.exceptions.IngredientNotFoundException; + +public class IngredientList extends HashMap { + + private static final Parser parser = new Parser(); + + /** + * This method is designed to find the count of a particular ingredient. + * + * @param ingredientName name of ingredient + * + **/ + public Integer findIngredientCount(String ingredientName) throws IngredientNotFoundException { + if (this.containsKey(ingredientName)) { + return this.get(ingredientName).ingredientCount; + } + throw new IngredientNotFoundException("Ingredient not found"); + } + + /** + * This method is designed to find the expiry date of a particular ingredient, + * + * @param ingredientName name of ingredient + * + **/ + public LocalDate findExpiryDate(String ingredientName) throws IngredientNotFoundException { + if (this.containsKey(ingredientName)) { + return this.get(ingredientName).expiryDate; + } + throw new IngredientNotFoundException("Ingredient not found"); + } + + /** + * Adds an ingredient to the ingredient list. If the ingredient already exists, the count of the + * ingredient is updated, and the expiry date is also updated. + * + * @author jaredoong + * @param ingredient Ingredient to be added. + */ + public void addIngredient(Ingredient ingredient) { + if (this.containsKey(ingredient.ingredientName)) { + int currentCount = this.get(ingredient.ingredientName).ingredientCount; + int newCount = currentCount + ingredient.ingredientCount; + this.put(ingredient.ingredientName, + new Ingredient(ingredient.ingredientName, newCount, ingredient.expiryDate)); + } else { + this.put(ingredient.ingredientName, ingredient); + } + } + + /** + * This method is designed to edit the ingredient count and expiry date. + * It also checks if the ingredient is expired or not. + * + * @param ingredient the ingredient we want to check + * @param ingredientCount count of ingredient + * @param expiryDate expiry date of ingredient + * + **/ + public void editIngredient(Ingredient ingredient, Integer ingredientCount, String expiryDate) + throws IngredientNotFoundException { + if (this.containsKey(ingredient.ingredientName)) { + ingredient.ingredientCount = ingredientCount; + ingredient.expiryDate = parser.parseDate(expiryDate); + ingredient.isExpired = ingredient.expiryDate.isBefore(LocalDate.now()); + super.put(ingredient.ingredientName, ingredient); + } + throw new IngredientNotFoundException("Ingredient not found"); + } + + /** + * Deletes an ingredient from the ingredient list. If the ingredient count is greater than the count to be + * deleted, the count of the ingredient is updated. If the ingredient count is equal or less than to the + * count to be deleted, the ingredient is removed from the list. + * + * @author jaredoong + * @param ingredient Ingredient to be deleted. + * @throws IngredientNotFoundException If the ingredient is not found in the list. + */ + public void deleteIngredient(Ingredient ingredient) throws IngredientNotFoundException { + String ingredientName = ingredient.ingredientName; + int ingredientCount = ingredient.ingredientCount; + + if (this.containsKey(ingredientName)) { + int currentCount = this.get(ingredientName).ingredientCount; + int newCount = currentCount - ingredientCount; + if (newCount > 0) { + this.put(ingredientName, + new Ingredient(ingredientName, newCount, this.get(ingredientName).expiryDate)); + } else { + this.remove(ingredientName); + } + } else { + throw new IngredientNotFoundException("Ingredient not found"); + } + } + + /** + * This method is designed to print the contents of an ingredient. + * + **/ + private String listIngredients() { + String ingredientList = ""; + int index = 1; + for (Ingredient ingredient : this.values()) { + ingredientList += String.format("%d. %s (count: %d, expires on %s)", index, + ingredient.ingredientName, ingredient.ingredientCount, ingredient.expiryDate); + } + return ingredientList; + } + + // public method to print ingredients in list with indexing + public void printIngredients() { + System.out.println(listIngredients()); + } + + // public method to print expired ingredients and expiry date + // if no expired ingredients, print "No expired ingredients" + public void printExpiredIngredients() { + String expiredIngredients = ""; + int index = 1; + for (Ingredient ingredient : this.values()) { + if (ingredient.isExpired) { + expiredIngredients += String.format("%d. %s (expires on %s)", index, + ingredient.ingredientName, ingredient.expiryDate); + } + } + if (expiredIngredients.equals("")) { + System.out.println("No expired ingredients."); + } else { + System.out.println(expiredIngredients); + } + } + + // public method to clear all ingredients + public void clearIngredients() { + this.clear(); + } + + // public method to clear expired ingredients + public void clearExpiredIngredients() { + for (Ingredient ingredient : this.values()) { + if (ingredient.isExpired) { + this.remove(ingredient.ingredientName); + } + } + } +} diff --git a/src/main/java/seedu/meal360/Meal360.java b/src/main/java/seedu/meal360/Meal360.java new file mode 100644 index 0000000000..bcd654b073 --- /dev/null +++ b/src/main/java/seedu/meal360/Meal360.java @@ -0,0 +1,319 @@ +package seedu.meal360; + +import java.io.IOException; +import java.util.Scanner; + +import seedu.meal360.exceptions.IngredientNotFoundException; +import seedu.meal360.exceptions.InvalidRecipeNameException; +import seedu.meal360.exceptions.InvalidValueException; +import seedu.meal360.exceptions.NoRecipeException; +import seedu.meal360.exceptions.RecipeNotFoundInTagException; +import seedu.meal360.exceptions.TagNotFoundException; +import seedu.meal360.storage.Database; + +public class Meal360 { + + /** + * Main entry-point for the java.duke.Meal360 application. + */ + + private static Boolean canExit = false; + private static final Ui ui = new Ui(); + private static final Parser parser = new Parser(); + private static final Database database = new Database(); + private static RecipeList recipeList = new RecipeList(); + private static WeeklyPlan weeklyPlan = new WeeklyPlan(); + + private static IngredientList userIngredients = new IngredientList(); + + + + /** + * This method is designed to execute when the application is started. + * It loads in the recipes, weekly plan and ingredients if they exist. + * + **/ + public static void startApp() { + ui.printSeparator(); + ui.printWelcomeMessage(); + ui.printSeparator(); + + // Load databases + try { + ui.printMessage("Loading recipes..."); + recipeList = database.loadRecipesDatabase(); + ui.printMessage("Recipes loaded successfully."); + } catch (Exception e) { + ui.printMessage("Error loading recipes, loading default recipes instead."); + recipeList = database.defaultRecipeList(); + } + + try { + ui.printMessage("Loading weekly plan..."); + weeklyPlan = database.loadWeeklyPlanDatabase(); + boolean isClean = weeklyPlan.checkValidity(recipeList); + if (!isClean) { + ui.printMessage("Weekly plan has invalid recipes, removing them..."); + } + ui.printMessage("Weekly plan loaded successfully."); + } catch (Exception e) { + ui.printMessage("Error loading weekly plan, loading default empty weekly plan instead."); + weeklyPlan = new WeeklyPlan(); + } + + try { + ui.printMessage("Loading ingredients..."); + userIngredients = database.loadUserIngredientsDatabase(); + ui.printMessage("Ingredients loaded successfully."); + } catch (Exception e) { + ui.printMessage("Error loading ingredients, loading default empty list of ingredients instead."); + userIngredients = new IngredientList(); + } + + ui.printSeparator(); + } + + /** + * This method is designed to receive the user input and perform the + * required tasks. + * + **/ + public static void receiveInput(String input) { + String[] command = parser.cleanUserInput(input); + + if (input.trim().equalsIgnoreCase("bye")) { + canExit = true; + } else if (command[0].equals("delete")) { + ui.printSeparator(); + try { + String deletedRecipe = parser.parseDeleteRecipe(command, recipeList); + ui.printMessage("Noted. I've removed this recipe:"); + ui.printMessage(deletedRecipe); + ui.printMessage("Now you have " + recipeList.size() + " recipes in the list."); + } catch (ArrayIndexOutOfBoundsException e) { + ui.printMessage(e.getMessage()); + } catch (IndexOutOfBoundsException e) { + ui.printMessage(e.getMessage()); + } + ui.printSeparator(); + } else if (command[0].equals("view")) { + ui.printSeparator(); + try { + Recipe recipe = parser.parseViewRecipe(command, recipeList); + ui.printRecipe(recipe); + } catch (NumberFormatException | IndexOutOfBoundsException e) { + ui.printMessage(e.getMessage()); + } + ui.printSeparator(); + } else if (command[0].equals("list")) { + try { + ui.printSeparator(); + RecipeList recipeListToPrint = parser.parseListRecipe(command, recipeList); + ui.listRecipe(recipeListToPrint); + } catch (TagNotFoundException | NoRecipeException | IllegalArgumentException | IndexOutOfBoundsException + | NullPointerException e) { + ui.printMessage(e.getMessage()); + } + ui.printSeparator(); + } else if (command[0].equals("add")) { + ui.printSeparator(); + try { + Recipe newRecipe = parser.parseAddRecipe(command, recipeList); + ui.printMessage("I've added this new recipe:" + newRecipe.getName()); + ui.printMessage("Now you have " + recipeList.size() + " recipes in the list."); + } catch (ArrayIndexOutOfBoundsException e) { + String errorMessage = "Please enter a valid recipe name."; + ui.printMessage(errorMessage); + } catch (NullPointerException e) { + String errorMessage = "Recipe already exists. Add a new recipe."; + ui.printMessage(errorMessage); + } + ui.printSeparator(); + } else if (command[0].equals("edit")) { + ui.printSeparator(); + try { + Recipe recipeToEdit = parser.parseEditRecipe(command, recipeList); + ui.printSeparator(); + ui.printMessage("I've edited this recipe:" + recipeToEdit.getName()); + } catch (NumberFormatException e) { + String errorMessage = String.format( + "Please enter a valid recipe number. You entered %s, " + "which is not a number.", + command[1]); + ui.printMessage(errorMessage); + } catch (ArrayIndexOutOfBoundsException e) { + String errorMessage = "Please enter a valid recipe name."; + ui.printMessage(errorMessage); + } catch (IndexOutOfBoundsException e) { + String errorMessage = String.format( + "Please enter a valid recipe number. You entered %s, " + "which is out of bounds.", + command[1]); + ui.printMessage(errorMessage); + } catch (NullPointerException e) { + String errorMessage = "Recipe doesn't exist for editing."; + ui.printMessage(errorMessage); + } + ui.printSeparator(); + } else if (command[0].equals("tag")) { + try { + ui.printSeparator(); + String returnMessage = parser.parseTagRecipe(command, recipeList); + ui.printTagMessage(returnMessage); + } catch (IllegalArgumentException | IndexOutOfBoundsException | NullPointerException + | RecipeNotFoundInTagException | TagNotFoundException | NoRecipeException e) { + ui.printMessage(e.getMessage()); + } + ui.printSeparator(); + } else if (command[0].equals("random")) { + ui.printSeparator(); + try { + Recipe randomRecipe = parser.parseRandomRecipe(recipeList); + ui.printRandomMessage(); + ui.printRecipe(randomRecipe); + } catch (NoRecipeException e) { + ui.printMessage(e.getMessage()); + } + ui.printSeparator(); + } else if (command[0].equals("weekly")) { + try { + ui.printSeparator(); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipeList); + + switch (command[1]) { + case "/add": + case "/multiadd": + weeklyPlan.addPlans(recipeMap); + ui.printMessage("I've added the recipes to your weekly plan!"); + break; + case "/delete": + case "/multidelete": + weeklyPlan.deletePlans(recipeMap); + ui.printMessage("I've deleted the recipes from your weekly plan!"); + break; + case "/clear": + weeklyPlan.clearPlan(); + ui.printMessage("I've cleared your entire weekly plan!"); + break; + case "/done": + String recipeName = parser.parseMarkDone(command, userIngredients, weeklyPlan, + recipeList); + weeklyPlan.markDone(recipeName, recipeList, userIngredients); + ui.printMessage("I've recorded that you've done this recipe!"); + ui.printMessage("Ingredients list has been updated accordingly!"); + break; + default: + ui.printMessage("Please enter a valid command."); + break; + } + } catch (IllegalArgumentException | InvalidValueException | InvalidRecipeNameException + | ArrayIndexOutOfBoundsException | IngredientNotFoundException e) { + ui.printMessage(e.getMessage()); + } + ui.printSeparator(); + } else if (command[0].equals("available")) { + // list recipes with ingredients all in ingredient list + ui.printSeparator(); + RecipeList availableRecipes = recipeList.availableRecipes(); + ui.listAvailableRecipes(availableRecipes); + ui.printSeparator(); + } else if (command[0].equals("weeklyingredients")) { + ui.printSeparator(); + ui.printWeeklyIngredients(weeklyPlan, recipeList); + ui.printSeparator(); + } else if (command[0].equals("weeklyplan")) { + ui.printSeparator(); + ui.printWeeklyPlan(weeklyPlan); + ui.printSeparator(); + } else if (command[0].equals("add_i")) { + ui.printSeparator(); + try { + Ingredient ingredientToAdd = parser.parseAddUserIngredients(command); + userIngredients.addIngredient(ingredientToAdd); + ui.printMessage("Ingredient successfully added!"); + } catch (IllegalArgumentException | IndexOutOfBoundsException | InvalidValueException e) { + ui.printMessage(e.getMessage()); + } + ui.printSeparator(); + } else if (command[0].equals("del_i")) { + ui.printSeparator(); + try { + Ingredient ingredientToDelete = parser.parseDeleteUserIngredients(command); + userIngredients.deleteIngredient(ingredientToDelete); + ui.printMessage("Ingredients successfully deleted!"); + } catch (IllegalArgumentException | IndexOutOfBoundsException | IngredientNotFoundException + | InvalidValueException e) { + ui.printMessage(e.getMessage()); + } + ui.printSeparator(); + } else if (command[0].equals("view_ingredients")) { + ui.printSeparator(); + ui.printUserIngredients(userIngredients); + ui.printSeparator(); + } else if (command[0].equals("help")) { + ui.printSeparator(); + ui.printHelp(); + ui.printSeparator(); + } else { + ui.printSeparator(); + ui.printMessage("I'm sorry, but I don't know what that means :-("); + ui.printSeparator(); + } + } + + /** + * This method is designed to exit the application. + * + * It saves the recipes, weekly plan and ingredients if any entered by the user. + * + **/ + public static void exitApp() { + ui.printSeparator(); + + // Save database + ui.printMessage("Saving recipes..."); + try { + database.saveRecipesDatabase(recipeList); + ui.printMessage("Recipes saved successfully."); + } catch (IOException error) { + ui.printMessage("Error saving database."); + } + + // Save weekly plan + ui.printMessage("Saving weekly plan..."); + try { + database.saveWeeklyPlanDatabase(weeklyPlan); + ui.printMessage("Weekly plan saved successfully."); + } catch (IOException error) { + ui.printMessage("Error saving weekly plan."); + } + + // Save ingredients + ui.printMessage("Saving ingredients..."); + try { + database.saveIngredientsDatabase(userIngredients); + ui.printMessage("Ingredients saved successfully."); + } catch (IOException error) { + ui.printMessage("Error saving ingredients."); + } + + ui.printGoodbyeMessage(); + ui.printSeparator(); + } + + public static void main(String[] args) { + startApp(); + + String line; + Scanner userInput = new Scanner(System.in); + + try { + do { + line = userInput.nextLine(); + receiveInput(line); + } while (!canExit); + } catch (Exception error) { + ui.printMessage("Force exit detected, attempting to save data..."); + } finally { + exitApp(); + } + } +} diff --git a/src/main/java/seedu/meal360/Parser.java b/src/main/java/seedu/meal360/Parser.java new file mode 100644 index 0000000000..d481e0eafd --- /dev/null +++ b/src/main/java/seedu/meal360/Parser.java @@ -0,0 +1,1142 @@ +package seedu.meal360; + +import java.security.InvalidParameterException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Objects; +import java.util.Scanner; + +import seedu.meal360.exceptions.IngredientNotFoundException; +import seedu.meal360.exceptions.InvalidRecipeNameException; +import seedu.meal360.exceptions.InvalidValueException; +import seedu.meal360.exceptions.NoRecipeException; +import seedu.meal360.exceptions.RecipeNotFoundInTagException; +import seedu.meal360.exceptions.TagNotFoundException; + +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.time.LocalDate; + +public class Parser { + + Ui ui = new Ui(); + + String recipeErrorMessage1 = + "Wrong Format or Invalid Quantity. Please enter ingredients properly " + "[eg:chicken=100]"; + + String recipeErrorMessage2 = "Enter \"done\" when finished entering ingredients!"; + + /** + * This method is designed to combine multiple words into a single sentence + * after trimming the spaces. + * + * @author AbijithRam + * @param input the user input, + * @param startIndex index to start from + * @param length length of substring + * + * @return an array of strings representing the user input + */ + public String combineWords(String[] input, int startIndex, int length) { + StringBuilder word = new StringBuilder(input[startIndex].trim()); + for (int i = startIndex + 1; i < length; i++) { + word.append(" ").append(input[i].trim()); + } + + return word.toString(); + } + + /** + * Parses the user input into an array of strings. Trims away leading and trailing whitespaces, and + * converts all words to lowercase. + * + * @author jaredoong + * @param input the user input + * @return an array of strings representing the user input + */ + public String[] cleanUserInput(String input) { + input = input.replaceAll("\\s+", " "); + input = input.toLowerCase(); + return input.trim().split(" "); + } + + /** + * Parses an array of strings representing ingredients and their quantities. + * + * @param commands an array of strings in the format "name=quantity" + * @return a map of ingredient names to their quantities + * @throws IllegalArgumentException if an ingredient quantity is negative or an ingredient name is empty + */ + public HashMap parseIngredientName(String[] commands) { + HashMap ingredients = new HashMap<>(); + int flag = 0; + String currentIngredient = null; + + for (String command : commands) { + int indexOfEqual = command.indexOf("="); + if (indexOfEqual == -1) { + if (command.isEmpty()) { + continue; + } + if (currentIngredient != null) { + currentIngredient += " " + command; + } else { + currentIngredient = command; + } + flag++; + } else { + if (flag > 0) { + currentIngredient += " " + command.substring(0, indexOfEqual); + flag = 0; + } else { + currentIngredient = command.substring(0, indexOfEqual); + } + String quantityString = command.substring(indexOfEqual + 1); + if (quantityString.isEmpty()) { + throw new IllegalArgumentException("Ingredient quantity cannot be empty"); + } + int quantity = Integer.parseInt(quantityString); + if (quantity < 0) { + throw new IllegalArgumentException("Ingredient quantity cannot be negative"); + } + if (currentIngredient.isEmpty()) { + throw new IllegalArgumentException("Ingredient name cannot be empty"); + } + ingredients.put(currentIngredient, quantity); + currentIngredient = null; + } + } + return ingredients; + } + + + /** + * This method is designed to add in a recipe to the user's + * recipe list. + * The method will also check if a recipe already exists or not + * to prevent duplicate recipes. + * + * @author AbijithRam + * @param input the user input, + * @param recipeList the recipe list of the user containing all recipes + * + * @return Recipe the recipe that has been added + */ + public Recipe parseAddRecipe(String[] input, RecipeList recipeList) { + int addedIngredient = 0; + String recipeName = combineWords(input, 2, input.length); + if (recipeList.findByName(recipeName) != null) { + return null; + } else if (!Objects.equals(input[1], "/r")) { + throw new ArrayIndexOutOfBoundsException(); + } + HashMap ingredients = new HashMap<>(); + Scanner userInput = new Scanner(System.in); + ui.printMessage("Please Enter The Ingredients & Quantity (enter \"done\" when complete): "); + ui.printSeparator(); + while (true) { + try { + String line = userInput.nextLine(); + if (line.equals("done")) { + ui.printSeparator(); + if (addedIngredient == 0 || ingredients.size() == 0) { + ui.printMessage( + "Add at least 1 ingredient before entering 'done'! [eg: chicken=100]"); + ui.printSeparator(); + } else { + break; + } + } else { + addedIngredient = 1; + String[] command = line.trim().split(" "); + ingredients = parseIngredientName(command); + if (ingredients.size() == 0) { + ui.printSeparator(); + ui.printMessage(recipeErrorMessage1); + ui.printMessage(recipeErrorMessage2); + ui.printSeparator(); + } + } + } catch (IllegalArgumentException e) { + ui.printSeparator(); + ui.printMessage(recipeErrorMessage1); + ui.printMessage(recipeErrorMessage2); + ui.printSeparator(); + } + } + Recipe newRecipe = new Recipe(recipeName, ingredients); + recipeList.addRecipe(newRecipe); + return newRecipe; + } + + /** + * This method is designed to edit an already existing recipe + * to the user's recipe list. + * + * There are 3 types of editing operations: + * 1. Edit the ingredient list fully. + * 2. edit the ingredient list partially (1 recipe at a time). + * 3. Add new ingredients to the ingredient list. + * + * @author AbijithRam + * @param input the user input, + * @param recipeList the recipe list of the user containing all recipes + * + * @return Recipe the recipe that has been edited + */ + public Recipe parseEditRecipe(String[] input, RecipeList recipeList) { + int addedIngredient = 0; + String recipeName = combineWords(input, 2, input.length); + Recipe recipeToEdit; + HashMap ingredients = new HashMap<>(); + Scanner userInput = new Scanner(System.in); + if (recipeList.findByName(recipeName) == null) { + return null; + } + recipeToEdit = recipeList.findByName(recipeName); + ui.printSeparator(); + ui.printMessage("Do you want to edit recipe fully or partially or add new ingredients?"); + ui.printMessage("Press 1 for full edit | Press 2 for partial edit | Press 3 to add ingredients"); + ui.printSeparator(); + int index; + Scanner getNum = new Scanner(System.in); + while (true) { + try { + index = Integer.parseInt(getNum.nextLine()); + if (index < 1 || index > 3) { + ui.printSeparator(); + ui.printMessage("Index cannot be negative or out of bounds!"); + ui.printSeparator(); + } else { + break; + } + } catch (NumberFormatException e) { + ui.printSeparator(); + ui.printMessage("Enter valid index for respective editing options!"); + ui.printSeparator(); + } + } + if (index == 1) { + ui.printSeparator(); + ui.printMessage("Please Enter New Ingredients & Quantity: "); + ui.printSeparator(); + while (true) { + try { + String line = userInput.nextLine(); + if (line.equals("done")) { + ui.printSeparator(); + if (addedIngredient == 0 || ingredients.size() == 0) { + ui.printSeparator(); + ui.printMessage( + "Add at least 1 ingredient before entering 'done'! [eg: chicken=100]"); + ui.printSeparator(); + } else { + break; + } + } else { + addedIngredient = 1; + String[] command = line.trim().split(" "); + ingredients = parseIngredientName(command); + if (ingredients.size() != 0) { + recipeList.editRecipe(recipeToEdit, ingredients); + } else { + ui.printSeparator(); + ui.printMessage(recipeErrorMessage1); + ui.printMessage(recipeErrorMessage2); + ui.printSeparator(); + } + } + } catch (IllegalArgumentException | StringIndexOutOfBoundsException e) { + ui.printSeparator(); + ui.printMessage(recipeErrorMessage1); + ui.printMessage(recipeErrorMessage2); + ui.printSeparator(); + } + } + } else if (index == 2) { + ui.printSeparator(); + ui.printMessage("These are the ingredients for the recipe:"); + ui.printSeparator(); + Recipe recipe = parseViewRecipe(recipeName, recipeList); + ui.printRecipe(recipe); + ui.printSeparator(); + ui.printMessage("Which ingredient do you want to change?"); + ui.printSeparator(); + int ingredientIndex = 0; + Scanner getIndex = new Scanner(System.in); + while (true) { + try { + ingredientIndex = Integer.parseInt(getIndex.nextLine()); + if (ingredientIndex <= 0 || ingredientIndex > recipe.getNumOfIngredients()) { + ui.printSeparator(); + ui.printMessage("Index cannot be negative or out of bounds!"); + ui.printSeparator(); + } else { + break; + } + } catch (NumberFormatException e) { + ui.printSeparator(); + ui.printMessage("Enter valid ingredient index!"); + ui.printSeparator(); + } + } + ingredientIndex -= 1; + int count = 0; + String ingredientToRemove = null; + for (String ingredient : recipeToEdit.getIngredients().keySet()) { + if (ingredientIndex == count) { + ingredientToRemove = ingredient; + ui.printSeparator(); + ui.printMessage("Ingredient to be changed:"); + ui.printSeparator(); + String toPrint = String.format("%s(%d)", ingredient, + recipeToEdit.getIngredients().get(ingredient)); + System.out.println(ui.formatMessage(toPrint)); + ui.printSeparator(); + break; + } + count++; + } + ui.printSeparator(); + ui.printMessage("Please enter the new ingredient:"); + ui.printSeparator(); + + while (true) { + try { + String line = userInput.nextLine(); + String command = line.replaceAll("\\s+", " "); + int indexOfEqual = command.indexOf("="); + if (command.charAt(indexOfEqual + 1) == '-') { + ui.printSeparator(); + ui.printMessage("Invalid ingredient quantity!"); + ui.printSeparator(); + } else { + String newIngredientName = command.substring(0, indexOfEqual); + int newIngredientQuantity = Integer.parseInt(command.substring(indexOfEqual + 1)); + recipeToEdit.getIngredients().remove(ingredientToRemove); + recipeToEdit.getIngredients().put(newIngredientName, newIngredientQuantity); + recipeList.editRecipe(recipeToEdit, recipeToEdit.getIngredients()); + break; + } + } catch (IllegalArgumentException | StringIndexOutOfBoundsException e) { + ui.printSeparator(); + ui.printMessage(recipeErrorMessage1); + ui.printMessage("Enter only 1 ingredient!"); + ui.printSeparator(); + } + } + } else { + HashMap newIngredientList = recipeToEdit.getIngredients(); + ui.printSeparator(); + ui.printMessage("These are the current ingredients:"); + ui.printSeparator(); + Recipe recipe = parseViewRecipe(recipeName, recipeList); + ui.printRecipe(recipe); + ui.printSeparator(); + ui.printMessage("Please Enter Additional Ingredients & Quantity: "); + ui.printSeparator(); + while (true) { + try { + String line = userInput.nextLine(); + if (line.equals("done")) { + ui.printSeparator(); + if (addedIngredient == 0 || ingredients.size() == 0) { + ui.printSeparator(); + ui.printMessage( + "Add at least 1 ingredient before entering 'done'! [eg: chicken=100]"); + ui.printSeparator(); + } else { + break; + } + } else { + addedIngredient = 1; + String[] command = line.trim().split(" "); + ingredients = parseIngredientName(command); + newIngredientList.putAll(ingredients); + if (ingredients.size() != 0) { + recipeList.editRecipe(recipeToEdit, newIngredientList); + } else { + ui.printSeparator(); + ui.printMessage(recipeErrorMessage1); + ui.printMessage(recipeErrorMessage2); + ui.printSeparator(); + } + } + } catch (IllegalArgumentException | StringIndexOutOfBoundsException e) { + ui.printSeparator(); + ui.printMessage(recipeErrorMessage1); + ui.printMessage(recipeErrorMessage2); + ui.printSeparator(); + } + } + } + return recipeToEdit; + } + + //@@author gurmankalkat + + /** + * This method deletes a single, range, or all recipes. + * + * @author gurmankalkat + * @param input array containing string of inputs + * @param recipeList list containing all recipes data + * @return a String of the recipe name deleted + * @throws ArrayIndexOutOfBoundsException If users enters an invalid recipe name. + * @throws RecipeNotFoundInTagException If users entered a recipe index or range that is out of bounds. + */ + public String parseDeleteRecipe(String[] input, RecipeList recipeList) { + try { + // make sure user inputted recipe name or number + if (!input[1].equals("/r") && !input[1].contains("-")) { + Integer.parseInt(input[1]); + } + } catch (NumberFormatException e) { + throw new ArrayIndexOutOfBoundsException( + "Please enter a valid recipe number, name, or range in the " + "correct format."); + } catch (ArrayIndexOutOfBoundsException e) { + throw new ArrayIndexOutOfBoundsException( + "Please enter a valid recipe number, name, or range in " + "the correct format."); + } + if (input[1].contains("/r")) { + // skip over /r in recipe name + String recipeToDelete = ""; + for (int i = 2; i < input.length; i++) { + recipeToDelete += input[i] + " "; + } + recipeToDelete = recipeToDelete.trim(); + if (recipeToDelete.equals("all")) { + String allRecipes = ""; + int index = 0; + while (recipeList.size() != 0) { + allRecipes += recipeList.deleteRecipe(index).getName() + ", "; + } + allRecipes = allRecipes.substring(0, allRecipes.length() - 2); + return allRecipes; + } else { + int recipeIndex = 0; + boolean recipeFound = false; + for (Recipe recipe : recipeList) { + // find index of recipe we want to delete + if (recipe.getName().equals(recipeToDelete)) { + recipeFound = true; + break; + } + recipeIndex++; + } + if (!recipeFound) { + throw new ArrayIndexOutOfBoundsException( + "Please enter a valid recipe number, name, or range in " + "the correct format."); + } + return recipeList.deleteRecipe(recipeIndex).getName(); + } + // user inputted index of recipe in list + } else { + // deleting a range of recipes + if (input[1].length() >= 3 && input[1].contains("-")) { + String[] range = input[1].trim().split("-"); + if (range.length != 2) { + throw new ArrayIndexOutOfBoundsException( + "Please enter a valid recipe number, name, or range in " + "the correct format."); + } + int startIndex = Integer.parseInt(range[0]); + int endIndex = Integer.parseInt(range[1]); + startIndex -= 1; + endIndex -= 1; + if (startIndex < 0 || endIndex >= recipeList.size() || endIndex < startIndex) { + throw new IndexOutOfBoundsException( + "Please enter a valid recipe number, name, or range."); + } + int newSize = recipeList.size() - ((endIndex - startIndex) + 1); + String rangeRecipes = ""; + while (recipeList.size() != newSize) { + rangeRecipes += recipeList.deleteRecipe(startIndex).getName() + ", "; + } + rangeRecipes = String.valueOf( + new StringBuilder(rangeRecipes.substring(0, rangeRecipes.length() - 2))); + return rangeRecipes; + // deleting a single recipe + } else { + int recipeIndex = Integer.parseInt(input[1]); + if (recipeIndex <= 0 || recipeIndex > recipeList.size()) { + throw new IndexOutOfBoundsException( + "Please enter a valid recipe number, name, or range."); + } + recipeIndex = Integer.parseInt(input[1]); + // need to subtract 1 since list is 1-based + return recipeList.deleteRecipe(recipeIndex - 1).getName(); + } + } + } + + //@@author junenita + + /** + * Extract inputs from users whether it is adding recipes to a tag or removing recipe from a tag. + * Then, proceed to add or remove the recipes from the tag, and returns a string whether + * the recipes are successfully add to or remove from the tag. + * + * @author junenita + * @param inputs array containing string of inputs + * @param recipeList list containing all recipes data + * @return a string whether successfully added or remove recipes from the tag + * @throws NoRecipeException If users entered invalid recipe. + * @throws RecipeNotFoundInTagException If users try to remove a recipe that is not in the tag. + * @throws TagNotFoundException If users try to remove recipes from a tag that has not been created. + */ + public String parseTagRecipe(String[] inputs, RecipeList recipeList) + throws RecipeNotFoundInTagException, TagNotFoundException, NoRecipeException { + String returnMessage; + String tag; + boolean isOnlyTagWordInCommand = inputs.length == 1; + boolean isAddTag; + boolean isRemoveTag; + + if (isOnlyTagWordInCommand) { + throw new IllegalArgumentException("Please indicate at least a tag and a recipe."); + } + + StringBuilder commandString = new StringBuilder(inputs[1]); + for (int i = 2; i < inputs.length; i++) { + commandString.append(" ").append(inputs[i]); + } + + isAddTag = commandString.indexOf(">>") == -1 && commandString.indexOf("<<") != -1; + isRemoveTag = commandString.indexOf("<<") == -1 && commandString.indexOf(">>") != -1; + if (!(isAddTag || isRemoveTag)) { + throw new IllegalArgumentException("Please enter the command in the correct format."); + } else if (isAddTag) { + tag = parseAddRecipeTag(commandString.toString(), recipeList); + returnMessage = "add " + tag; + } else if (isRemoveTag) { + tag = parseRemoveRecipeTag(commandString.toString(), recipeList); + returnMessage = "remove " + tag; + } else { + throw new IllegalArgumentException("Invalid command."); + } + return returnMessage; + } + + //@@author junenita + + /** + * Extract the tag label and recipes. Then, proceed to add the recipes to the tag, + * and returns the tag label that users modified + * + * @author junenita + * @param command string contain tag label and recipes that users want to add + * @param recipeList list containing all recipes data + * @return tag label that is modified + * @throws NoRecipeException If users entered invalid recipe. + */ + public String parseAddRecipeTag(String command, RecipeList recipeList) throws NoRecipeException { + String tag; + Recipe recipe; + String[] recipesToTag; + boolean isUnableToFindTheRecipe; + String[] args = command.trim().split("<<"); + boolean isNotEnoughArgs = args.length < 2 || args[0].equals("") || args[1].equals(""); + + if (isNotEnoughArgs) { + throw new IllegalArgumentException("Please enter the command in the correct format."); + } + + tag = args[0].trim(); + recipesToTag = args[1].split("&&"); + for (String recipeName : recipesToTag) { + recipeName = recipeName.trim(); + recipe = recipeList.findByName(recipeName); + isUnableToFindTheRecipe = recipe == null; + if (isUnableToFindTheRecipe) { + String errorMessage1 = "Unable to find the recipe: \"" + recipeName + "\" in the" + " tag."; + String errorMessage2 = "All the recipe before \"" + recipeName + "\" (if any) are " + + "successfully added from the tag."; + throw new NoRecipeException(String.format("%-97s|\n| %-97s", errorMessage1, errorMessage2)); + } + recipeList.addRecipeToTag(tag, recipe); + } + return tag; + } + + //@@author junenita + + /** + * Extract the tag label and recipes. Then, proceed to remove the recipes from the tag, + * and returns the tag label that users modified + * + * @author junenita + * @param command string contain tag label and recipes that users want to remove + * @param recipeList list containing all recipes data + * @return tag label that is modified + * @throws NoRecipeException If users entered invalid recipe. + * @throws RecipeNotFoundInTagException If users try to remove a recipe that is not in the tag. + * @throws TagNotFoundException If users try to remove recipes from a tag that has not been created. + */ + public String parseRemoveRecipeTag(String command, RecipeList recipeList) + throws RecipeNotFoundInTagException, TagNotFoundException, NoRecipeException { + String tag; + Recipe recipe; + String[] recipesToRemove; + boolean isUnableToFindTag; + boolean isNoRecipeInTheList; + String[] args = command.trim().split(">>"); + boolean isNotEnoughArgs = args.length < 2 || args[0].equals("") || args[1].equals(""); + + if (isNotEnoughArgs) { + throw new IllegalArgumentException("Please enter the command in the correct format."); + } + + tag = args[0].trim(); + isUnableToFindTag = !recipeList.tags.containsKey(tag); + if (isUnableToFindTag) { + throw new TagNotFoundException("There is no \"" + tag + "\" tag found. Please make sure you have " + + "entered the correct tag."); + } + + recipesToRemove = args[1].split("&&"); + for (String recipeName : recipesToRemove) { + recipeName = recipeName.trim(); + recipe = recipeList.findByName(recipeName); + isNoRecipeInTheList = recipe == null; + if (isNoRecipeInTheList) { + String errorMessage1 = "Unable to find the recipe: \"" + recipeName + "\" in the" + " tag."; + String errorMessage2 = "All the recipe before \"" + recipeName + "\" (if any) are " + + "successfully removed from the tag."; + throw new NoRecipeException(String.format("%-97s|\n| %-97s", errorMessage1, errorMessage2)); + } + + try { + recipeList.removeRecipeFromTag(tag, recipe); + } catch (IndexOutOfBoundsException | RecipeNotFoundInTagException e) { + String errorMessage1 = "Unable to find the recipe: \"" + recipeName + "\" in the" + " tag."; + String errorMessage2 = "All the recipe before \"" + recipeName + "\" (if any) are " + + "successfully removed from the tag."; + throw new RecipeNotFoundInTagException( + String.format("%-97s|\n| %-97s", errorMessage1, errorMessage2)); + } + } + return tag; + } + + //@@author junenita + + /** + * Extract the filters from users' input. Then, proceed to extract the recipes by the filters. + * + * @author junenita + * @param inputs array containing string of inputs, including the filter + * @param recipeList list containing all recipes data + * @return list of recipes that are filtered by the input + * @throws NoRecipeException If users entered invalid recipe. + * @throws TagNotFoundException If users try to remove recipes from a tag that has not been created. + */ + public RecipeList parseListRecipe(String[] inputs, RecipeList recipeList) + throws TagNotFoundException, NoRecipeException { + String[] filters; + RecipeList recipeListToPrint; + boolean hasTagArgs = inputs.length > 2; + boolean isOnlyListWordInCommand = inputs.length == 1; + boolean isTag = false; + int firstArgsIndex = 1; + + if (isOnlyListWordInCommand) { + filters = null; + } else { + isTag = inputs[1].equals("/t"); + if (isTag && !hasTagArgs) { + throw new TagNotFoundException("Please include at least a tag."); + } else if (isTag) { + firstArgsIndex = 2; + } + + StringBuilder filterString = new StringBuilder(inputs[firstArgsIndex]); + for (int i = firstArgsIndex + 1; i < inputs.length; i++) { + filterString.append(" ").append(inputs[i]); + } + + filters = filterString.toString().split("&&"); + } + recipeListToPrint = recipeList.listRecipes(filters, isTag); + return recipeListToPrint; + } + + //@@author jaredoong + + /** + * Extract the recipe index that the users wishes to view. Then, proceed to extract the recipe and returns + * the recipe. + * + * @author jaredoong + * @param command string contain `view` and recipe index + * @param recipes list containing all recipes data + * @return recipe that user wishes to view + * @throws NumberFormatException If users entered invalid recipe index. + * @throws ArrayIndexOutOfBoundsException If users did not enter a recipe index. + * @throws IndexOutOfBoundsException If users entered a recipe index that is out of bounds. + */ + public Recipe parseViewRecipe(String[] command, RecipeList recipes) { + assert command[0].equals("view"); + Recipe requestedRecipe; + int recipeIndex = 0; + try { + recipeIndex = Integer.parseInt(command[1]); + requestedRecipe = recipes.get(recipeIndex - 1); + } catch (NumberFormatException error) { + String errorMessage = String.format( + "Please enter a valid recipe number. You entered %s, " + "which is not a valid number.", + command[1]); + throw new NumberFormatException(errorMessage); + } catch (ArrayIndexOutOfBoundsException error) { + String errorMessage = "Please enter a valid recipe number. You did not enter a recipe number."; + throw new ArrayIndexOutOfBoundsException(errorMessage); + } catch (IndexOutOfBoundsException error) { + String errorMessage = String.format( + "Please enter a valid recipe number. You entered %d, " + "which is out of bounds.", + recipeIndex); + throw new IndexOutOfBoundsException(errorMessage); + } + return requestedRecipe; + } + + //@@author AbijithRam + + /** + * This method is designed to find the index of a recipe from + * the recipe list of the user and return the recipe. + * + * @author AbijithRam + * @param recipeName name of recipe + * @param recipes recipe list of user + * @return Recipe + */ + public Recipe parseViewRecipe(String recipeName, RecipeList recipes) { + int recipeIndex = 1; + for (Recipe recipe : recipes) { + if (recipe.getName().equals(recipeName)) { + break; + } + recipeIndex++; + } + return recipes.get(recipeIndex - 1); + } + + //@@author junenita + + /** + * Returns a Recipe object that contain a recipe's name and ingredients. + * + * @author junenita + * @param recipes an object containing all recipes data + * @return a random recipe from the input recipes. + * @throws NoRecipeException If recipes.size() == 0. + */ + public Recipe parseRandomRecipe(RecipeList recipes) throws NoRecipeException { + if (recipes.size() == 0) { + throw new NoRecipeException("There is no recipe in the list for random."); + } + return recipes.randomRecipe(); + } + + //@@author jaredoong + + /** + * Checks whether the user wants to edit single, multiple, or clear all the recipes in the weekly plan. + * Then, return a WeeklyPlan object that contains the recipes that the user wants to add or delete. + * + * @author jaredoong + * @param command an array containing the user input + * @param recipes an object containing all recipes data + * @return a WeeklyPlan object that contains the recipes that the user wants to add or delete + * @throws NumberFormatException If users entered invalid character for the quantity. + * @throws InvalidRecipeNameException If users entered invalid recipe name. + * @throws InvalidValueException If users entered invalid quantity. + * @throws IllegalArgumentException If users did not enter a valid command. + **/ + public WeeklyPlan parseWeeklyPlan(String[] command, RecipeList recipes) + throws IllegalArgumentException, NumberFormatException, InvalidRecipeNameException, + InvalidValueException { + WeeklyPlan updatedWeeklyPlan; + try { + switch (command[1]) { + case "/add": + case "/delete": + updatedWeeklyPlan = parseEditSingleWeeklyPlan(command, recipes); + break; + case "/multiadd": + case "/multidelete": + updatedWeeklyPlan = parseEditMultiWeeklyPlan(command, recipes); + break; + case "/clear": + case "/done": + updatedWeeklyPlan = new WeeklyPlan(); + break; + default: + throw new InvalidParameterException(); + } + + return updatedWeeklyPlan; + } catch (ArrayIndexOutOfBoundsException | InvalidParameterException error) { + throw new IllegalArgumentException( + "Please indicate if you would want to add to, delete from, clear weekly plan, or mark " + + "as done."); + } + + } + + //@@author jaredoong + + /** + * Parses the user input to extract the single recipe that the user wants to add or delete from the weekly + * plan. + * + * @author jaredoong + * @param command an array containing the user input + * @param recipes an object containing all recipes data + * @return a WeeklyPlan object that contains the single recipe and the quantity that the user wants to add + * or delete + * @throws NumberFormatException If users entered invalid character for the quantity. + * @throws InvalidRecipeNameException If users entered invalid recipe name. + * @throws InvalidValueException If users entered invalid quantity. + * @throws IllegalArgumentException If users did not enter a valid command. + */ + private WeeklyPlan parseEditSingleWeeklyPlan(String[] command, RecipeList recipes) + throws IllegalArgumentException, NumberFormatException, InvalidValueException, + InvalidRecipeNameException { + int numDays; + + if (command.length < 4) { + throw new IllegalArgumentException( + "Please enter the command in the correct format with all parameters provided."); + } + + try { + numDays = Integer.parseInt(command[command.length - 1]); + } catch (NumberFormatException e) { + throw new NumberFormatException( + "Please ensure that you entered a valid quantity as the last argument."); + } + + if (numDays > 1000 || numDays < 1) { + throw new InvalidValueException("Please enter a number between 1 to 1000 for the quantity."); + } + + int nameLastIndex = command.length - 1; + WeeklyPlan edits = new WeeklyPlan(); + StringBuilder recipeName = new StringBuilder(command[2]); + for (int i = 3; i < nameLastIndex; i++) { + recipeName.append(" ").append(command[i]); + } + + if (recipes.findByName(recipeName.toString().trim()) != null) { + edits.put(recipeName.toString(), numDays); + return edits; + } else { + throw new InvalidRecipeNameException("Please indicate a valid recipe name."); + } + } + + //@@author jaredoong + + /** + * Parses the user input to extract the multiple recipes that the user wants to add or delete from the + * weekly plan. + * + * @author jaredoong + * @param command an array containing the user input + * @param recipes an object containing all recipes data + * @return a WeeklyPlan object that contains the multiple recipes and the quantities that the user wants + * to add or delete + * @throws NumberFormatException If users entered invalid character for the quantity. + * @throws InvalidRecipeNameException If users entered invalid recipe name. + * @throws InvalidValueException If users entered invalid quantity. + * @throws IllegalArgumentException If users did not enter a valid command. + */ + private WeeklyPlan parseEditMultiWeeklyPlan(String[] command, RecipeList recipes) + throws IllegalArgumentException, NumberFormatException, InvalidValueException, + InvalidRecipeNameException { + int quantity; + if (command.length < 6) { + throw new IllegalArgumentException( + "Please enter the command in the correct format with all parameters provided."); + } + + WeeklyPlan recipesToEdit = new WeeklyPlan(); + ArrayList quantities = new ArrayList<>(); + ArrayList recipeNames = new ArrayList<>(); + ArrayList startIndices = new ArrayList<>(); + ArrayList endIndices = new ArrayList<>(); + StringBuilder recipeName = new StringBuilder(); + + for (int i = 0; i < command.length; i++) { + if (command[i].equals("/r")) { + startIndices.add(i); + } else if (command[i].equals("/q")) { + endIndices.add(i); + try { + quantity = Integer.parseInt(command[i + 1]); + } catch (NumberFormatException e) { + throw new NumberFormatException( + "Please enter a positive number between 1 to 1000 for the quantity."); + } catch (ArrayIndexOutOfBoundsException e) { + throw new IllegalArgumentException("Please ensure a number is provided after /q."); + } + if (quantity < 1 || quantity > 1000) { + throw new InvalidValueException( + "Please enter a positive number between 1 to 1000 for the quantity."); + } + quantities.add(quantity); + } + } + + // Checks that command is entered in the correct format + if (startIndices.size() != endIndices.size() || startIndices.size() == 0) { + throw new IllegalArgumentException("Please ensure the number of /r and /q are the same."); + } + + // Checks that command flags entered in correct order + for (int i = 0; i < startIndices.size(); i++) { + if (endIndices.get(i) < startIndices.get(i)) { + throw new IllegalArgumentException( + "Please ensure that the /r and /q flags are entered in the correct order."); + } + } + + // Building the recipe names + for (int i = 0; i < startIndices.size(); i++) { + int nameStartIndex = startIndices.get(i) + 1; + int nameEndIndex = endIndices.get(i) - 1; + recipeName = getRecipeNames(command, recipeNames, recipeName, nameStartIndex, nameEndIndex); + } + + // Add each recipe to the weekly plan + try { + for (int i = 0; i < recipeNames.size(); i++) { + if (recipes.findByName(recipeNames.get(i)) != null) { + recipesToEdit.put(recipeNames.get(i), quantities.get(i)); + } else { + throw new InvalidRecipeNameException("Please indicate a valid recipe name."); + } + } + } catch (NumberFormatException error) { + throw new NumberFormatException( + "Please enter a positive number between 1 to 1000 for the quantity."); + } + + return recipesToEdit; + } + + //@@author jaredoong + private StringBuilder getRecipeNames(String[] command, ArrayList recipeNames, + StringBuilder recipeName, int nameStartIndex, int nameEndIndex) { + recipeName.append(command[nameStartIndex].toLowerCase().trim()); + for (int j = nameStartIndex + 1; j <= nameEndIndex; j++) { + recipeName.append(" ").append(command[j].toLowerCase().trim()); + } + recipeNames.add(recipeName.toString()); + recipeName = new StringBuilder(); + return recipeName; + } + + //@@author notbingsu + // parser to read dd/mm/yyyy format as local date catching invalid date format + public LocalDate parseDate(String input) { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy"); + LocalDate date = null; + try { + date = LocalDate.parse(input, formatter); + return date; + } catch (DateTimeParseException e) { + throw new IllegalArgumentException("Please enter a valid date in the format dd/mm/yyyy."); + } + } + + //@@author jaredoong + + /** + * Parses the user input to extract the ingredient name, quantity, and expiry date that the user wants to + * add to the ingredient list. Returns the Ingredient object that contains parsed data. + * + * @author jaredoong + * @param command an array containing the user input + * @return an Ingredient object that contains the ingredient name, quantity, and expiry date + * @throws NumberFormatException If users entered invalid character for the quantity. + * @throws InvalidValueException If users entered invalid quantity. + * @throws IllegalArgumentException If users did not enter a valid command. + */ + public Ingredient parseAddUserIngredients(String[] command) + throws NumberFormatException, IllegalArgumentException, InvalidValueException { + String ingredientName = null; + Integer ingredientCount = null; + String expiryDate = null; + + try { + for (int i = 1; i < command.length; i++) { + switch (command[i]) { + case "/n": + StringBuilder nameBuilder = new StringBuilder(); + while (++i < command.length && !command[i].startsWith("/")) { + if (nameBuilder.length() > 0) { + nameBuilder.append(" "); + } + nameBuilder.append(command[i]); + } + ingredientName = nameBuilder.toString(); + i--; // Move back to the previous flag + + // Check if ingredient name is empty + if (ingredientName.isEmpty()) { + throw new IllegalArgumentException("Ingredient name cannot be empty."); + } + break; + case "/c": + try { + ingredientCount = Integer.parseInt(command[++i]); + } catch (NumberFormatException e) { + throw new NumberFormatException( + "Please enter a positive number between 1 to 1000 for the quantity."); + } + + if (ingredientCount < 1 || ingredientCount > 1000) { + throw new InvalidValueException( + "Please enter a positive number between 1 to 1000 for the quantity."); + } + break; + case "/d": + expiryDate = command[++i]; + break; + default: + throw new IllegalArgumentException("Missing required information. Please provide " + + "ingredient name, count, and expiry date."); + } + } + } catch (ArrayIndexOutOfBoundsException error) { + throw new ArrayIndexOutOfBoundsException( + "Missing required information. Please provide ingredient name, count, and expiry date."); + } + + if (ingredientName == null || ingredientCount == null || expiryDate == null) { + throw new IllegalArgumentException("Missing required information. Please provide " + + "ingredient name, count, and expiry date."); + } + + return new Ingredient(ingredientName, ingredientCount, expiryDate); + } + + //@@author jaredoong + + /** + * Parses the user input to extract the ingredient name and quantity that the user wants to delete from + * the ingredient list. Returns the Ingredient object that contains parsed data. + * + * @author jaredoong + * @param command an array containing the user input + * @return an Ingredient object that contains the ingredient name, quantity, and dummy expiry date + * @throws NumberFormatException If users entered invalid character for the quantity. + * @throws IngredientNotFoundException If users entered invalid ingredient name. + * @throws InvalidValueException If users entered invalid quantity. + * @throws IllegalArgumentException If users did not enter a valid command. + */ + public Ingredient parseDeleteUserIngredients(String[] command) + throws NumberFormatException, IllegalArgumentException, IngredientNotFoundException, + InvalidValueException { + String ingredientName = null; + Integer ingredientCount = null; + + try { + for (int i = 1; i < command.length; i++) { + switch (command[i]) { + case "/n": + StringBuilder nameBuilder = new StringBuilder(); + while (++i < command.length && !command[i].startsWith("/")) { + if (nameBuilder.length() > 0) { + nameBuilder.append(" "); + } + nameBuilder.append(command[i]); + } + ingredientName = nameBuilder.toString(); + i--; // Move back to the previous flag + + // Check if ingredient name is empty + if (ingredientName.isEmpty()) { + throw new IllegalArgumentException("Ingredient name cannot be empty."); + } + break; + case "/c": + try { + ingredientCount = Integer.parseInt(command[++i]); + } catch (NumberFormatException e) { + throw new NumberFormatException( + "Please enter a positive number between 1 to 1000 for the quantity."); + } + + if (ingredientCount < 1 || ingredientCount > 1000) { + throw new InvalidValueException( + "Please enter a positive number between 1 to 1000 for the quantity."); + } + break; + default: + throw new IllegalArgumentException( + "Missing required information. Please provide ingredient name and count."); + } + } + } catch (ArrayIndexOutOfBoundsException error) { + throw new ArrayIndexOutOfBoundsException( + "Missing required information. Please provide ingredient name and count."); + } + + if (ingredientName == null || ingredientCount == null) { + throw new IllegalArgumentException( + "Missing required information. Please provide ingredient name and count."); + } + + return new Ingredient(ingredientName, ingredientCount, "01/01/2020"); + } + + //@@author jaredoong + + /** + * Parses the user input to extract the recipe which the user wants to mark as done. Returns the recipe + * name to be deleted from the weekly plan. + * + * @author jaredoong + * @param command an array containing the user input + * @param userIngredients an object containing all ingredients data + * @param weeklyPlan an object containing all weekly plan data + * @param recipes an object containing all recipes data + * @return a string containing the recipe name to be deleted from the weekly plan. + * @throws NumberFormatException If users entered invalid character for the quantity. + * @throws IngredientNotFoundException If users entered invalid ingredient name. + * @throws IllegalArgumentException If users enter a recipe name that does not exist in the weekly + * plan, or if the user does not have enough ingredients to make the + * recipe. + */ + public String parseMarkDone(String[] command, IngredientList userIngredients, WeeklyPlan weeklyPlan, + RecipeList recipes) + throws IllegalArgumentException, IngredientNotFoundException, InvalidRecipeNameException { + if (command.length == 2) { + throw new IllegalArgumentException("Please enter a recipe name."); + } + String recipeName = combineWords(command, 2, command.length); + if (recipes.findByName(recipeName) == null) { + throw new InvalidRecipeNameException("Please enter a valid recipe name."); + } + + if (weeklyPlan.get(recipeName) == null) { + throw new IllegalArgumentException("Recipe does not exist in weekly plan."); + } + + try { + HashMap ingredients = recipes.findByName(recipeName).getIngredients(); + for (String ingredient : ingredients.keySet()) { + int currentCount = userIngredients.findIngredientCount(ingredient); + if (currentCount < ingredients.get(ingredient)) { + throw new IngredientNotFoundException(); + } + } + return recipeName; + } catch (IngredientNotFoundException e) { + throw new IngredientNotFoundException( + "You do not have enough ingredients to mark this recipe as done."); + } + } +} diff --git a/src/main/java/seedu/meal360/Recipe.java b/src/main/java/seedu/meal360/Recipe.java new file mode 100644 index 0000000000..8fd5a46c0d --- /dev/null +++ b/src/main/java/seedu/meal360/Recipe.java @@ -0,0 +1,44 @@ +package seedu.meal360; + +import java.util.HashMap; + +public class Recipe { + + // changed ingredients to public to edit via editRecipe + private static IngredientList ingredientList = new IngredientList(); + public HashMap ingredients; + private String name; + private Boolean available; + + + public Recipe(String name, HashMap ingredients) { + this.name = name; + this.ingredients = ingredients; + } + + public String getName() { + return name; + } + + public HashMap getIngredients() { + return ingredients; + } + + public int getNumOfIngredients() { + return ingredients.size(); + } + + public boolean isAvailable() { + for (String ingredientName : ingredients.keySet()) { + if (ingredientList.containsKey(ingredientName)) { + Ingredient ingredient = ingredientList.get(ingredientName); + if (ingredient.ingredientCount < ingredients.get(ingredientName)) { + return false; + } + } else { + return false; + } + } + return true; + } +} diff --git a/src/main/java/seedu/meal360/RecipeList.java b/src/main/java/seedu/meal360/RecipeList.java new file mode 100644 index 0000000000..0ba12abf6e --- /dev/null +++ b/src/main/java/seedu/meal360/RecipeList.java @@ -0,0 +1,187 @@ +package seedu.meal360; + +import seedu.meal360.exceptions.NoRecipeException; +import seedu.meal360.exceptions.RecipeNotFoundInTagException; +import seedu.meal360.exceptions.TagNotFoundException; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Random; + +public class RecipeList extends ArrayList { + + private static IngredientList ingredientList = new IngredientList(); + public HashMap> tags = new HashMap<>(); + + public Recipe findByName(String name) { + for (Recipe recipe : this) { + if (recipe.getName().equalsIgnoreCase(name)) { + return recipe; + } + } + return null; + } + + public void addRecipe(Recipe recipe) { + super.add(recipe); + } + + public void editRecipe(Recipe recipe, HashMap ingredients) { + recipe.ingredients = ingredients; + } + + public Recipe deleteRecipe(int recipeNum) { + Recipe recipeToDelete = super.get(recipeNum); + super.remove(recipeToDelete); + return recipeToDelete; + } + + /** + * Add a recipe to a tag. If the tag has not been created, it will be created. + * + * @author junenita + * @param tag tag label that users want to modify + * @param recipe a recipe to be added to the tag + */ + public void addRecipeToTag(String tag, Recipe recipe) { + boolean hasTag = tags.containsKey(tag); + int tagDummy = 0; + if (hasTag) { + tags.get(tag).put(recipe, tagDummy); + } else { + assert !tags.containsKey(tag); + HashMap tagRecipes = new HashMap<>(); + tagRecipes.put(recipe, tagDummy); + tags.put(tag, tagRecipes); + assert tags.size() > 0 : "tag's size is still 0."; + } + } + + /** + * Remove a recipe from a tag. + * + * @author junenita + * @param tag tag label that users want to modify + * @param recipe a recipe to be removed from the tag + * @throws RecipeNotFoundInTagException If the recipe to be removed is not already in + * the tag + */ + public void removeRecipeFromTag(String tag, Recipe recipe) throws RecipeNotFoundInTagException { + HashMap tagRecipeList = tags.get(tag); + boolean isAbleToFindTheRecipe = tagRecipeList.containsKey(recipe); + if (!isAbleToFindTheRecipe) { + String errorMessage1 = "Unable to find the recipe: \"" + recipe.getName() + "\" in the" + " tag."; + String errorMessage2 = "All the recipe before \"" + recipe.getName() + "\" (if any) are " + + "successfully removed from the tag."; + throw new RecipeNotFoundInTagException(String.format("%-97s|\n| %-97s", errorMessage1, errorMessage2)); + } + tagRecipeList.remove(recipe); + } + + /** + * Accumulate all recipes that pass the filters into a list of recipes. + * + * @author junenita + * @param filters array containing strings of filters + * @param isTag flag whether the filters are tags label + * @return RecipeList containing all recipes passing the filters + * @throws TagNotFoundException If the filter is a tag and the tag is invalid. + * @throws NoRecipeException If the filter is a tag and the tag contains no recipe + */ + public RecipeList listRecipes(String[] filters, boolean isTag) throws TagNotFoundException, NoRecipeException { + RecipeList filteredRecipeList = new RecipeList(); + boolean isNoFilter = filters == null; + boolean isNotPassTheFilter; + + if (isTag) { + filteredRecipeList = this.listTagRecipes(filters); + } else if (!isTag && isNoFilter) { + return this; + } else if (!isTag) { + for (Recipe recipe : this) { + filteredRecipeList.add(recipe); + for (String filter : filters) { + filter = filter.trim(); + isNotPassTheFilter = !recipe.getName().contains(filter) && + !recipe.getIngredients().containsKey(filter); + if (isNotPassTheFilter) { + filteredRecipeList.remove(recipe); + } + } + } + } + return filteredRecipeList; + } + + /** + * Accumulate all recipes contains all tag from users' input into a list of recipes. + * + * @author junenita + * @param filters array containing strings of tag label + * @return RecipeList containing all recipes containing all tags + * @throws TagNotFoundException If the filter is a tag and the tag is invalid + * @throws NoRecipeException If the filter is a tag and the tag contains no recipe + */ + public RecipeList listTagRecipes(String[] filters) throws TagNotFoundException, NoRecipeException { + RecipeList filteredRecipeList = new RecipeList(); + HashMap tagRecipes; + boolean hasNoRecipeInTheTag; + boolean hasNoRecipeToReturn; + boolean isNotFoundTag; + + // add all recipe from first tag to the list + isNotFoundTag = !this.tags.containsKey(filters[0].trim()); + if (isNotFoundTag) { + throw new TagNotFoundException("There is no \"" + filters[0] + "\" tag found. Please make sure you have " + + "entered the correct tag."); + } + this.tags.get(filters[0].trim()).forEach((recipe, dummy) -> filteredRecipeList.add(recipe)); + + for (String filter : filters) { + filter = filter.trim(); + tagRecipes = this.tags.get(filter); + hasNoRecipeInTheTag = tagRecipes == null; + hasNoRecipeToReturn = filteredRecipeList.size() == 0; + + if (hasNoRecipeInTheTag) { + throw new NoRecipeException("There is nothing to list."); + } + if (hasNoRecipeToReturn) { + return filteredRecipeList; + } + for (int index = filteredRecipeList.size() - 1; index >= 0; index--) { + Recipe currentRecipe = filteredRecipeList.get(index); + if (!tagRecipes.containsKey(currentRecipe)) { + filteredRecipeList.remove(currentRecipe); + } + } + } + return filteredRecipeList; + } + + /** + * This method is designed to return any available recipes if they exist. + * + **/ + public RecipeList availableRecipes() { + RecipeList availableRecipeList = new RecipeList(); + for (Recipe recipe : this) { + if (recipe.isAvailable()) { + availableRecipeList.add(recipe); + } + } + return availableRecipeList; + } + + /** + * Returns a Recipe object that contain a recipe's name and ingredients. + * + * @author junenita + * @return a random recipe from this RecipeList. + */ + public Recipe randomRecipe() { + Random random = new Random(); + Recipe randomRecipe = this.get(random.nextInt(this.size())); + return randomRecipe; + } +} diff --git a/src/main/java/seedu/meal360/Ui.java b/src/main/java/seedu/meal360/Ui.java new file mode 100644 index 0000000000..814995f6b6 --- /dev/null +++ b/src/main/java/seedu/meal360/Ui.java @@ -0,0 +1,251 @@ +package seedu.meal360; + +import java.time.format.DateTimeFormatter; + +public class Ui { + + private static final int BOXWIDTH = 100; + private static final String SEPARATORCHAR = "-"; + + public void printSeparator() { + String separatorLine = SEPARATORCHAR.repeat(BOXWIDTH); + System.out.println(separatorLine); + } + + public void printWelcomeMessage() { + String logo = " __ __ _ ____ __ __\n" + "| \\/ |___ __ _| |__ / / / / \\\n" + + "| |\\/| / -_) _` | ||_ \\/ _ \\ () |\n" + "|_| |_\\___\\__,_|_|___/\\___/\\__/\n"; + System.out.println("Welcome to Meal360, your ultimate Recipe Manager!\n" + logo); + } + + public void printGoodbyeMessage() { + System.out.println(formatMessage("Bye. Hope to see you again soon!")); + } + + public String formatMessage(String message) { + return String.format("| %-97s|", message); + } + + public void printMessage(String message) { + String outputMessage = formatMessage(message); + System.out.println(outputMessage); + } + + /** + * This method is designed to print the contents of a recipe. + * + * @param recipe recipe whose contents are to be printed. + * + **/ + public void printRecipe(Recipe recipe) { + System.out.println(formatMessage("Name of recipe: " + recipe.getName())); + int index = 1; + for (String ingredient : recipe.getIngredients().keySet()) { + String outputMessage = String.format("%s(%d)", ingredient, + recipe.getIngredients().get(ingredient)); + System.out.println(formatMessage(index + ". " + outputMessage)); + index++; + } + } + + /** + * This method is designed to print the weeklyplan. + * + * @param weeklyPlan weeklyPlan whose contents are to be printed. + * + **/ + public void printWeeklyPlan(WeeklyPlan weeklyPlan) { + if (weeklyPlan.isEmpty()) { + printMessage("Your weekly plan is empty!"); + } else { + printMessage("Here is your weekly plan:"); + weeklyPlan.forEach((recipe, count) -> { + String outputMessage = String.format("%s x%d", recipe, count); + printMessage(outputMessage); + }); + } + + } + + /** + * This method is designed to print the weekly ingredients. + * + * @param weeklyPlan weeklyPlan whose contents are to be printed. + * @param recipeList recipeList of user + * + **/ + public void printWeeklyIngredients(WeeklyPlan weeklyPlan, RecipeList recipeList) { + if (weeklyPlan.isEmpty()) { + printMessage("Your weekly plan is empty!"); + } else { + printMessage("Here are your weekly ingredients:"); + weeklyPlan.forEach((recipe, count) -> { + Recipe currRecipe = recipeList.findByName(recipe.toString().trim()); + for (String ingredient : currRecipe.getIngredients().keySet()) { + String outputMessage = String.format("%s (%d)", ingredient, + currRecipe.getIngredients().get(ingredient) * count); + System.out.println(formatMessage(outputMessage)); + } + }); + } + + } + + + /** + * This method is designed to print all the recipes in the user's recipe list. + * + * @param recipeListToPrint recipeList of user + * + **/ + public void listRecipe(RecipeList recipeListToPrint) { + listRecipes(recipeListToPrint, "There is nothing to list.", + "These are the recipes you have"); + } + + + /** + * This method is designed to print all the available recipes in the user's recipe list. + * + * @param recipeListToPrint recipeList of user + * + **/ + public void listAvailableRecipes(RecipeList recipeListToPrint) { + listRecipes(recipeListToPrint, "There are no available recipes.", + "These are the recipes you can cook"); + } + + /** + * Print ordered list of recipes including recipes' name and ingredients + * + * @author notbingsu + * @author junenita + * @param recipeListToPrint list containing recipes to be printed + * @param emptyListMsg error message for empty list + * @param listHeaderMsg print list header message + */ + private void listRecipes(RecipeList recipeListToPrint, String emptyListMsg, String listHeaderMsg) { + int numberOfRecipes = recipeListToPrint.size(); + int order = 0; + if (numberOfRecipes == 0) { + printMessage(emptyListMsg); + return; + } + printMessage(listHeaderMsg + " (" + numberOfRecipes + " recipes):"); + for (Recipe recipe : recipeListToPrint) { + order++; + printMessage(order + ". " + recipe.getName() + " (" + recipe.getNumOfIngredients() + + " ingredients)"); + } + } + + /** + * This method is designed to display all the commands used by the + * recipe manager to assist the user in executing the required + * operations. + * + * @author AbijithRam + */ + public void printHelp() { + printMessage("These are the 21 operations you can do. Please follow the proper input" + + " formats while typing."); + printSeparator(); + printMessage("1. Add Recipe: add /r {RECIPE_NAME}"); + printMessage("2. Edit Recipe: edit /r {RECIPE_NAME}"); + printMessage("3. View Recipe: view {RECIPE_INDEX}"); + printMessage("4. Delete Recipe: delete {RECIPE_INDEX} or delete {START_INDEX-END_INDEX} or"); + printMessage(" delete /r {RECIPE_NAME} or delete /r all"); + printMessage("5. List All Recipes: list"); + printMessage("6. List Available Recipes: available"); + printMessage("7. View Weekly Plan: weeklyplan"); + printMessage("8. View Weekly Ingredients: weeklyingredients"); + printMessage("9. Add Single Recipe to Weekly Plan: weekly /add {RECIPE_NAME} {QUANTITY}"); + printMessage("10. Add Multiple Recipes to Weekly Plan: weekly /multiadd /r {RECIPE1_NAME} /q {QUANTITY1}"); + printMessage(" /r {RECIPE2_NAME} /q {QUANTITY2}"); + printMessage("11. Delete Single Recipe from Weekly Plan: weekly /delete {RECIPE_NAME} {QUANTITY}"); + printMessage("12. Delete Multiple Recipes from Weekly Plan: weekly /multidelete /r {RECIPE1_NAME}"); + printMessage(" /q {QUANTITY1} /r {RECIPE2_NAME} /q {QUANTITY2}"); + printMessage("13. Marking recipe in weekly plan as done: weekly /done {RECIPE_NAME}"); + printMessage("14. Clear weekly plan: weekly /clear"); + printMessage("15. Tagging/Categorizing Recipes: tag {LABEL_name} << {RECIPE_NAME && RECIPE_NAME && ...}"); + printMessage("16. Removing recipes from a Tag: tag {LABEL_name} >> {RECIPE_NAME && RECIPE_NAME && ...}"); + printMessage("17. Add User Ingredient: add_i /n {INGREDIENT_NAME} /c {QUANTITY} /d {DATE}"); + printMessage("18. Delete User Ingredient: del_i /n {INGREDIENT_NAME} /c {QUANTITY}"); + printMessage("19. View User Ingredients: view_ingredients"); + printMessage("20. Exit Recipe Manager: bye"); + printMessage("21. Help Tab: help"); + printSeparator(); + printMessage("NOTE:"); + printMessage("* Curly brackets {} are just to indicate what goes inside the command!"); + printMessage("* No need to type them while entering commands!"); + } + + + /** + * This method is designed to print the user ingredients. + * + * @param userIngredients ingredient list of user + * + **/ + public void printUserIngredients(IngredientList userIngredients) { + if (userIngredients.isEmpty()) { + printMessage("Your ingredient list is empty!"); + } else { + printMessage("Here is your ingredient list:"); + userIngredients.forEach((name, ingredient) -> { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/dd/yyyy"); + String formattedDate = ingredient.expiryDate.format(formatter); + String outputMessage = String.format("%s (%d) [by:%s]", name, ingredient.ingredientCount, + formattedDate); + System.out.println(formatMessage(outputMessage)); + }); + } + } + + /** + * Extract message received whether users added or removed recipes from a tag. + * Then, proceed to print a successful message. + * + * @author junenita + * @param receivedMessage string containing 'add' or 'remove' tag and tag label + */ + public void printTagMessage(String receivedMessage) { + String[] args = receivedMessage.split(" ", 2); + String command = args[0].trim(); + String tag = args[1].trim(); + if (command.equals("add")) { + printSuccessfullyAddTag(tag); + } else if (command.equals("remove")) { + printSuccessfullyRemoveTag(tag); + } + } + + /** + * Print successfully add recipes to the tag + * + * @author junenita + * @param tag tag label + */ + public void printSuccessfullyAddTag(String tag) { + printMessage("You have successfully added the recipe(s) to \"" + tag + "\" tag."); + } + + /** + * Print successfully remove recipes from the tag + * + * @author junenita + * @param tag tag label + */ + public void printSuccessfullyRemoveTag(String tag) { + printMessage("You have successfully removed the recipe(s) from \"" + tag + "\" tag."); + } + + /** + * Print random result header + * + * @author junenita + */ + public void printRandomMessage() { + printMessage("Random Result....."); + } +} diff --git a/src/main/java/seedu/meal360/WeeklyPlan.java b/src/main/java/seedu/meal360/WeeklyPlan.java new file mode 100644 index 0000000000..39917a99bb --- /dev/null +++ b/src/main/java/seedu/meal360/WeeklyPlan.java @@ -0,0 +1,114 @@ +package seedu.meal360; + +import java.util.ArrayList; +import java.util.HashMap; +import seedu.meal360.exceptions.IngredientNotFoundException; + +//@@author jaredoong +public class WeeklyPlan extends HashMap { + + /** + * Adds a recipe to the weekly plan. + * + * @author jaredoong + * @param recipeMap the recipe to be added. + * @throws IllegalArgumentException if the total quantity of the recipe exceeds 1000. + */ + public void addPlans(WeeklyPlan recipeMap) throws IllegalArgumentException { + final boolean[] validNumber = {true}; + recipeMap.forEach((recipe, count) -> { + if (validNumber[0] && this.containsKey(recipe)) { + int newCount = this.get(recipe) + count; + validNumber[0] = (newCount > 1000) ? false : true; + } + }); + + if (validNumber[0]) { + recipeMap.forEach((recipe, count) -> { + if (this.containsKey(recipe)) { + this.put(recipe, this.get(recipe) + count); + } else { + this.put(recipe, count); + } + }); + } else { + throw new IllegalArgumentException("Total quantity cannot exceed 1000!"); + } + } + + /** + * Deletes a recipe from the weekly plan. + * + * @author jaredoong + * @param recipeMap the recipe to be deleted. + * @throws IllegalArgumentException if the recipe does not exist in the weekly plan. + */ + public void deletePlans(WeeklyPlan recipeMap) throws IllegalArgumentException { + recipeMap.forEach((recipe, count) -> { + if (this.containsKey(recipe)) { + int currentCount = this.get(recipe); + int newCount = currentCount - count; + this.remove(recipe); + if (newCount > 0) { + this.put(recipe, newCount); + } + } else { + throw new IllegalArgumentException("Recipe does not exist in weekly plan!"); + } + }); + } + + //@@author gurmankalkat + public void clearPlan() { + this.clear(); + } + + //@@author jaredoong + /** + * Checks if the weekly plan contains any invalid recipes. + * + * @author jaredoong + * @param recipeList the list of recipes. + * @return true if the weekly plan contains no invalid recipes, false otherwise. + */ + public boolean checkValidity(RecipeList recipeList) { + ArrayList toRemove = new ArrayList<>(); + this.forEach((recipe, count) -> { + if (recipeList.findByName(recipe) == null) { + toRemove.add(recipe); + } + }); + + if (toRemove.size() == 0) { + return true; + } + toRemove.forEach(recipe -> this.remove(recipe)); + return false; + } + + /** + * Marks a recipe in the weekly plan as done. + * + * @author jaredoong + * @param recipeName the name of the recipe to be marked as done. + * @param recipeList the list of recipes. + * @param userIngredients the list of ingredients. + * @throws IngredientNotFoundException if the ingredient does not exist in the list of ingredients. + */ + public void markDone(String recipeName, RecipeList recipeList, IngredientList userIngredients) + throws IngredientNotFoundException { + HashMap ingredients = recipeList.findByName(recipeName).getIngredients(); + + for (String ingredient : ingredients.keySet()) { + Ingredient ingredientToDelete = new Ingredient(ingredient, ingredients.get(ingredient), + "01/01/2023"); + userIngredients.deleteIngredient(ingredientToDelete); + } + + if (this.get(recipeName) == 1) { + this.remove(recipeName); + } else { + this.put(recipeName, this.get(recipeName) - 1); + } + } +} diff --git a/src/main/java/seedu/meal360/exceptions/IngredientNotFoundException.java b/src/main/java/seedu/meal360/exceptions/IngredientNotFoundException.java new file mode 100644 index 0000000000..3cdc934160 --- /dev/null +++ b/src/main/java/seedu/meal360/exceptions/IngredientNotFoundException.java @@ -0,0 +1,11 @@ +package seedu.meal360.exceptions; + +public class IngredientNotFoundException extends Exception { + public IngredientNotFoundException(String message) { + super(message); + } + + public IngredientNotFoundException() { + super("Ingredient not found"); + } +} diff --git a/src/main/java/seedu/meal360/exceptions/InvalidRecipeNameException.java b/src/main/java/seedu/meal360/exceptions/InvalidRecipeNameException.java new file mode 100644 index 0000000000..ad4a8cf8db --- /dev/null +++ b/src/main/java/seedu/meal360/exceptions/InvalidRecipeNameException.java @@ -0,0 +1,8 @@ +package seedu.meal360.exceptions; + +public class InvalidRecipeNameException extends Exception { + + public InvalidRecipeNameException(String message) { + super(message); + } +} diff --git a/src/main/java/seedu/meal360/exceptions/InvalidValueException.java b/src/main/java/seedu/meal360/exceptions/InvalidValueException.java new file mode 100644 index 0000000000..963f766747 --- /dev/null +++ b/src/main/java/seedu/meal360/exceptions/InvalidValueException.java @@ -0,0 +1,8 @@ +package seedu.meal360.exceptions; + +public class InvalidValueException extends Exception { + + public InvalidValueException(String message) { + super(message); + } +} diff --git a/src/main/java/seedu/meal360/exceptions/NoRecipeException.java b/src/main/java/seedu/meal360/exceptions/NoRecipeException.java new file mode 100644 index 0000000000..af2d783f06 --- /dev/null +++ b/src/main/java/seedu/meal360/exceptions/NoRecipeException.java @@ -0,0 +1,7 @@ +package seedu.meal360.exceptions; + +public class NoRecipeException extends Exception { + public NoRecipeException(String message) { + super(message); + } +} diff --git a/src/main/java/seedu/meal360/exceptions/RecipeNotFoundInTagException.java b/src/main/java/seedu/meal360/exceptions/RecipeNotFoundInTagException.java new file mode 100644 index 0000000000..21be98b159 --- /dev/null +++ b/src/main/java/seedu/meal360/exceptions/RecipeNotFoundInTagException.java @@ -0,0 +1,7 @@ +package seedu.meal360.exceptions; + +public class RecipeNotFoundInTagException extends Exception{ + public RecipeNotFoundInTagException(String message) { + super(message); + } +} diff --git a/src/main/java/seedu/meal360/exceptions/TagNotFoundException.java b/src/main/java/seedu/meal360/exceptions/TagNotFoundException.java new file mode 100644 index 0000000000..b76e2a9a18 --- /dev/null +++ b/src/main/java/seedu/meal360/exceptions/TagNotFoundException.java @@ -0,0 +1,7 @@ +package seedu.meal360.exceptions; + +public class TagNotFoundException extends Exception { + public TagNotFoundException(String message) { + super(message); + } +} diff --git a/src/main/java/seedu/meal360/storage/Database.java b/src/main/java/seedu/meal360/storage/Database.java new file mode 100644 index 0000000000..5a21c77c42 --- /dev/null +++ b/src/main/java/seedu/meal360/storage/Database.java @@ -0,0 +1,189 @@ +package seedu.meal360.storage; + +import java.io.IOException; +import java.util.HashMap; +import seedu.meal360.IngredientList; +import seedu.meal360.Recipe; +import seedu.meal360.RecipeList; +import seedu.meal360.WeeklyPlan; + +public class Database { + + private static final String recipesDatabaseFilepath = "." + System.getProperty("file.separator") + "database" + + System.getProperty("file.separator") + + "recipesDatabase.json"; + + private static final String weeklyPlanDatabaseFilepath = "." + System.getProperty("file.separator") + "database" + + System.getProperty("file.separator") + + "weeklyPlanDatabase.json"; + + private static final String userIngredientsDatabaseFilepath = "." + System.getProperty("file.separator") + + "database" + System.getProperty( + "file.separator") + + "userIngredientsDatabase.json"; + + JsonDatabaseHelper recipeListJsonDatabaseHelper = new JsonDatabaseHelper<>( + recipesDatabaseFilepath, defaultRecipeList(), RecipeList.class); + + JsonDatabaseHelper weeklyPlanJsonDatabaseHelper = new JsonDatabaseHelper<>( + weeklyPlanDatabaseFilepath, new WeeklyPlan(), WeeklyPlan.class); + + JsonDatabaseHelper userIngredientsJsonDatabaseHelper = new JsonDatabaseHelper<>( + userIngredientsDatabaseFilepath, new IngredientList(), IngredientList.class); + + public RecipeList loadRecipesDatabase() throws IOException { + return recipeListJsonDatabaseHelper.loadDatabase(); + } + + public void saveRecipesDatabase(RecipeList recipeList) throws IOException { + recipeListJsonDatabaseHelper.saveDatabase(recipeList); + } + + public WeeklyPlan loadWeeklyPlanDatabase() throws IOException { + return weeklyPlanJsonDatabaseHelper.loadDatabase(); + } + + public void saveWeeklyPlanDatabase(WeeklyPlan weeklyPlan) throws IOException { + weeklyPlanJsonDatabaseHelper.saveDatabase(weeklyPlan); + } + + public IngredientList loadUserIngredientsDatabase() throws IOException { + return userIngredientsJsonDatabaseHelper.loadDatabase(); + } + + public void saveIngredientsDatabase(IngredientList userIngredients) throws IOException { + userIngredientsJsonDatabaseHelper.saveDatabase(userIngredients); + } + + public RecipeList defaultRecipeList() { + RecipeList defaultRecipeList = new RecipeList(); + defaultRecipeList.add(new Recipe("chicken rice", new HashMap<>() { + { + put("chicken", 1); + put("rice", 1); + put("salt", 1); + put("pepper", 1); + } + })); + + defaultRecipeList.add(new Recipe("tomato fritters", new HashMap<>() { + { + put("ripe mixed-colour cherry tomatoes ", 250); + put("flat-leaf parsley", 15); + put("garlic cloves", 2); + put("plain flour", 2); + put("eggs", 2); + put("breadcrumbs", 100); + put("vegetable oil", 2); + put("feta cheese", 30); + } + })); + + defaultRecipeList.add(new Recipe("chicken and mushroom pie", new HashMap<>() { + { + put("chicken breast fillets", 2); + put("mushrooms", 250); + put("onion", 1); + put("plain flour", 2); + put("butter", 2); + put("milk", 2); + put("puff pastry", 1); + } + })); + + defaultRecipeList.add(new Recipe("seafood paella", new HashMap<>() { + { + put("prawns", 500); + put("mussels", 500); + put("chorizo", 100); + put("onion", 1); + put("garlic cloves", 2); + put("rice", 1); + put("cherry tomatoes", 250); + put("vegetable oil", 2); + put("chicken stock", 1); + put("saffron", 1); + } + })); + + defaultRecipeList.add(new Recipe("avocado toast", new HashMap<>() { + { + put("avocado", 1); + put("bread", 2); + put("lemon", 1); + put("salt", 1); + put("pepper", 1); + } + })); + + defaultRecipeList.add(new Recipe("italian sausage orzo soup", new HashMap<>() { + { + put("italian sausage", 1); + put("onion", 1); + put("garlic cloves", 2); + put("carrot", 1); + put("celery", 1); + put("dried oregano", 1); + put("dried basil", 1); + put("dried thyme", 1); + put("dried parsley", 1); + put("dried rosemary", 1); + put("chicken stock", 1); + put("orzo", 1); + put("parmesan cheese", 1); + } + })); + + defaultRecipeList.add(new Recipe("chicken and mushroom pasta", new HashMap<>() { + { + put("chicken breast fillets", 2); + put("mushrooms", 250); + put("onion", 1); + put("garlic cloves", 2); + put("plain flour", 2); + put("butter", 2); + put("milk", 2); + put("pasta", 1); + } + })); + + defaultRecipeList.add(new Recipe("creamy hummus pasta", new HashMap<>() { + { + put("pasta", 1); + put("hummus", 1); + put("garlic cloves", 2); + put("lemon", 1); + put("salt", 1); + put("pepper", 1); + } + })); + + defaultRecipeList.add(new Recipe("ground beef and potato casserole", new HashMap<>() { + { + put("ground beef", 1); + put("potato", 1); + put("onion", 1); + put("garlic cloves", 2); + put("tomato paste", 1); + put("tomato sauce", 1); + put("beef stock", 1); + put("cheddar cheese", 1); + } + })); + + defaultRecipeList.add(new Recipe("buffalo chicken mac 'n' cheese", new HashMap<>() { + { + put("chicken breast fillets", 2); + put("macaroni", 1); + put("butter", 2); + put("flour", 2); + put("milk", 2); + put("cheddar cheese", 1); + put("clue cheese", 1); + put("hot sauce", 1); + } + })); + + return defaultRecipeList; + } +} diff --git a/src/main/java/seedu/meal360/storage/JsonDatabaseHelper.java b/src/main/java/seedu/meal360/storage/JsonDatabaseHelper.java new file mode 100644 index 0000000000..aa8934796d --- /dev/null +++ b/src/main/java/seedu/meal360/storage/JsonDatabaseHelper.java @@ -0,0 +1,82 @@ +package seedu.meal360.storage; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.time.LocalDate; +import java.time.LocalDateTime; + +public class JsonDatabaseHelper { + + private final String databaseFilepath; + private final T defaultObject; + private final Class objectType; + private final Gson gson; + + public JsonDatabaseHelper(String databaseFilepath, T defaultObject, Class objectType) { + this.databaseFilepath = databaseFilepath; + this.defaultObject = defaultObject; + this.objectType = objectType; + + // Register the LocalDateTimeJsonAdapter and LocalDateJsonAdapter with Gson + this.gson = new GsonBuilder().registerTypeAdapter(LocalDateTime.class, + LocalDateTimeJsonAdapter.INSTANCE) + .registerTypeAdapter(LocalDate.class, LocalDateJsonAdapter.INSTANCE).create(); + } + + public T loadDatabase() throws IOException { + T database; + Gson gson = new Gson(); + + // Check if the JSON file exists and create one if it does not + File file = new File(databaseFilepath); + if (!file.exists()) { + try { + // Create the parent directory if it does not exist + File parentDir = file.getParentFile(); + if (!parentDir.exists()) { + parentDir.mkdirs(); + } + file.createNewFile(); + } catch (IOException e) { + throw new IOException("Error creating database file."); + } + + // Return a default object if database file does not exist + return defaultObject; + } + + // Read the JSON data from the file if it already exists + try { + // Read the JSON data from the file + BufferedReader reader = new BufferedReader(new FileReader(databaseFilepath)); + database = gson.fromJson(reader, objectType); + reader.close(); + } catch (FileNotFoundException e) { + throw new FileNotFoundException("Unable to find database file."); + } catch (IOException e) { + throw new IOException("Error reading from database file."); + } + + // Ensure non-empty object is returned + if (database == null) { + return defaultObject; + } else { + return database; + } + } + + public void saveDatabase(T dataObject) throws IOException { + Gson gson = new Gson(); + // Write the data object to the JSON file + FileWriter writer; + writer = new FileWriter(databaseFilepath); + gson.toJson(dataObject, writer); + writer.close(); + } +} diff --git a/src/main/java/seedu/meal360/storage/LocalDateJsonAdapter.java b/src/main/java/seedu/meal360/storage/LocalDateJsonAdapter.java new file mode 100644 index 0000000000..232bdda8b7 --- /dev/null +++ b/src/main/java/seedu/meal360/storage/LocalDateJsonAdapter.java @@ -0,0 +1,38 @@ +package seedu.meal360.storage; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonNull; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import java.lang.reflect.Type; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +public final class LocalDateJsonAdapter implements JsonSerializer, JsonDeserializer { + + public static final LocalDateJsonAdapter INSTANCE = new LocalDateJsonAdapter(); + + private LocalDateJsonAdapter() { + } + + @Override + public LocalDate deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + if (!json.isJsonNull()) { + return LocalDate.parse(json.getAsString(), DateTimeFormatter.ISO_LOCAL_DATE); + } + return null; + } + + @Override + public JsonElement serialize(LocalDate src, Type typeOfSrc, JsonSerializationContext context) { + if (src != null) { + return new JsonPrimitive(src.format(DateTimeFormatter.ISO_LOCAL_DATE)); + } + return JsonNull.INSTANCE; + } +} diff --git a/src/main/java/seedu/meal360/storage/LocalDateTimeJsonAdapter.java b/src/main/java/seedu/meal360/storage/LocalDateTimeJsonAdapter.java new file mode 100644 index 0000000000..d1f8e67ed4 --- /dev/null +++ b/src/main/java/seedu/meal360/storage/LocalDateTimeJsonAdapter.java @@ -0,0 +1,39 @@ +package seedu.meal360.storage; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonNull; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import java.lang.reflect.Type; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +public final class LocalDateTimeJsonAdapter implements JsonSerializer, + JsonDeserializer { + + public static final LocalDateTimeJsonAdapter INSTANCE = new LocalDateTimeJsonAdapter(); + + private LocalDateTimeJsonAdapter() { + } + + @Override + public LocalDateTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + if (!json.isJsonNull()) { + return LocalDateTime.parse(json.getAsString(), DateTimeFormatter.ISO_LOCAL_DATE_TIME); + } + return null; + } + + @Override + public JsonElement serialize(LocalDateTime src, Type typeOfSrc, JsonSerializationContext context) { + if (src != null) { + return new JsonPrimitive(src.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)); + } + return JsonNull.INSTANCE; + } +} diff --git a/src/main/resources/META-INF/MANIFEST.MF b/src/main/resources/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..d54b3b8812 --- /dev/null +++ b/src/main/resources/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: seedu.meal360.Meal360 + diff --git a/src/test/java/seedu/duke/DukeTest.java b/src/test/java/seedu/duke/DukeTest.java deleted file mode 100644 index 2dda5fd651..0000000000 --- a/src/test/java/seedu/duke/DukeTest.java +++ /dev/null @@ -1,12 +0,0 @@ -package seedu.duke; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -import org.junit.jupiter.api.Test; - -class DukeTest { - @Test - public void sampleTest() { - assertTrue(true); - } -} diff --git a/src/test/java/seedu/meal360/Meal360Test.java b/src/test/java/seedu/meal360/Meal360Test.java new file mode 100644 index 0000000000..7ef7810e0e --- /dev/null +++ b/src/test/java/seedu/meal360/Meal360Test.java @@ -0,0 +1,1624 @@ +package seedu.meal360; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.util.HashMap; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import seedu.meal360.exceptions.IngredientNotFoundException; +import seedu.meal360.exceptions.InvalidRecipeNameException; +import seedu.meal360.exceptions.InvalidValueException; +import seedu.meal360.exceptions.NoRecipeException; +import seedu.meal360.exceptions.RecipeNotFoundInTagException; +import seedu.meal360.exceptions.TagNotFoundException; +import seedu.meal360.storage.Database; + +class Meal360Test { + + private static RecipeList recipes = new RecipeList(); + private static final Parser parser = new Parser(); + private static final Ui ui = new Ui(); + + private static final Database database = new Database(); + + private static IngredientList userIngredients = new IngredientList(); + + private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); + private final PrintStream originalOut = System.out; + + //@@author jaredoong + @BeforeEach + public void setUpStreams() { + System.setOut(new PrintStream(outContent)); + } + + @AfterEach + public void restoreStreams() { + System.setOut(originalOut); + } + + @BeforeEach + public void setUpRecipes() { + // Adding of recipes + HashMap burgerIngredients = new HashMap<>(); + burgerIngredients.put("buns", 2); + burgerIngredients.put("meat patty", 1); + burgerIngredients.put("lettuce", 3); + Recipe burger = new Recipe("burger", burgerIngredients); + + HashMap pizzaIngredients = new HashMap<>(); + pizzaIngredients.put("dough", 1); + pizzaIngredients.put("tomato sauce", 1); + pizzaIngredients.put("cheese", 1); + pizzaIngredients.put("pepperoni", 1); + Recipe pizza = new Recipe("pizza", pizzaIngredients); + + HashMap saladIngredients = new HashMap<>(); + saladIngredients.put("lettuce", 1); + saladIngredients.put("tomato", 1); + saladIngredients.put("cucumber", 1); + Recipe salad = new Recipe("salad", saladIngredients); + + HashMap chickenRiceIngredients = new HashMap<>(); + chickenRiceIngredients.put("rice", 1); + chickenRiceIngredients.put("chicken", 1); + Recipe chickenRice = new Recipe("chicken rice", chickenRiceIngredients); + + recipes.addRecipe(burger); + recipes.addRecipe(pizza); + recipes.addRecipe(salad); + recipes.addRecipe(chickenRice); + + // Adding of ingredients + Ingredient buns = new Ingredient("buns", 10, "10/10/2024"); + Ingredient meatPatty = new Ingredient("meat patty", 10, "10/10/2024"); + Ingredient lettuce = new Ingredient("lettuce", 10, "10/10/2024"); + Ingredient dough = new Ingredient("dough", 10, "10/10/2024"); + Ingredient tomatoSauce = new Ingredient("tomato sauce", 10, "10/10/2024"); + Ingredient cheese = new Ingredient("cheese", 10, "10/10/2024"); + Ingredient pepperoni = new Ingredient("pepperoni", 10, "10/10/2024"); + Ingredient tomato = new Ingredient("tomato", 10, "10/10/2024"); + Ingredient cucumber = new Ingredient("cucumber", 10, "10/10/2024"); + Ingredient rice = new Ingredient("rice", 10, "10/10/2024"); + Ingredient chicken = new Ingredient("chicken", 10, "10/10/2024"); + Ingredient peasandcorn = new Ingredient("peas and corn", 10, "10/10/2024"); + + userIngredients.addIngredient(buns); + userIngredients.addIngredient(meatPatty); + userIngredients.addIngredient(lettuce); + userIngredients.addIngredient(dough); + userIngredients.addIngredient(tomatoSauce); + userIngredients.addIngredient(cheese); + userIngredients.addIngredient(pepperoni); + userIngredients.addIngredient(tomato); + userIngredients.addIngredient(cucumber); + userIngredients.addIngredient(rice); + userIngredients.addIngredient(chicken); + userIngredients.addIngredient(peasandcorn); + } + + @AfterEach + public void tearDownRecipes() { + recipes = new RecipeList(); + userIngredients = new IngredientList(); + } + + @Test + public void testViewRecipe() { + + // Testing positive case + try { + String userInput = "view 1"; + String[] command = parser.cleanUserInput(userInput); + Recipe recipe = parser.parseViewRecipe(command, recipes); + assertEquals("burger", recipe.getName()); + assertEquals(2, (int) recipe.getIngredients().get("buns")); + assertEquals(1, (int) recipe.getIngredients().get("meat patty")); + assertEquals(3, (int) recipe.getIngredients().get("lettuce")); + } catch (Exception e) { + assert false; + } + + // Testing negative case (invalid index) + try { + String userInput = "view 5"; + String[] command = parser.cleanUserInput(userInput); + parser.parseViewRecipe(command, recipes); + assert false; + } catch (Exception e) { + assertEquals("Please enter a valid recipe number. You entered 5, which is out of bounds.", + e.getMessage()); + } + + // Testing negative case (invalid index) + try { + String userInput = "view 0"; + String[] command = parser.cleanUserInput(userInput); + parser.parseViewRecipe(command, recipes); + assert false; + } catch (Exception e) { + assertEquals("Please enter a valid recipe number. You entered 0, which is out of bounds.", + e.getMessage()); + } + + // Testing negative case (missing index) + try { + String userInput = "view"; + String[] command = parser.cleanUserInput(userInput); + parser.parseViewRecipe(command, recipes); + assert false; + } catch (Exception e) { + assertEquals("Please enter a valid recipe number. You did not enter a recipe number.", + e.getMessage()); + } + + // Testing negative case (index is not a number) + try { + String userInput = "view a"; + String[] command = parser.cleanUserInput(userInput); + parser.parseViewRecipe(command, recipes); + assert false; + } catch (Exception e) { + assertEquals("Please enter a valid recipe number. You entered a, which is not a valid number.", + e.getMessage()); + } + + // Testing negative case (index is negative number) + try { + String userInput = "view -1"; + String[] command = parser.cleanUserInput(userInput); + parser.parseViewRecipe(command, recipes); + assert false; + } catch (Exception e) { + assertEquals("Please enter a valid recipe number. You entered -1, which is out of bounds.", + e.getMessage()); + } + } + + //@@author gurmankalkat + @Test + public void testDeleteRecipe() { + HashMap testIngredients = new HashMap<>(); + testIngredients.put("test ingredient", 100); + // create a fake recipe + Recipe testR = new Recipe("test recipe name", testIngredients); + recipes.addRecipe(testR); + + int oldRecipeListSize = recipes.size(); + // delete recipe + recipes.deleteRecipe(recipes.indexOf(testR)); + int newRecipeListSize = recipes.size(); + // check if recipe list size is 1 less than before + assertEquals((oldRecipeListSize - 1), newRecipeListSize); + + // No recipe inputted + try { + String userInput = "delete"; + String[] command = parser.cleanUserInput(userInput); + parser.parseDeleteRecipe(command, recipes); + assert false; // Not supposed to reach this line + } catch (Exception e) { + assertEquals(ArrayIndexOutOfBoundsException.class, e.getClass()); + assertEquals("Please enter a valid recipe number, name, or range in the correct format.", + e.getMessage()); + } + + // Recipe in incorrect format + try { + String userInput = "delete pizza"; + String[] command = parser.cleanUserInput(userInput); + parser.parseDeleteRecipe(command, recipes); + assert false; // Not supposed to reach this line + } catch (Exception e) { + assertEquals(ArrayIndexOutOfBoundsException.class, e.getClass()); + assertEquals("Please enter a valid recipe number, name, or range in the correct format.", + e.getMessage()); + } + + // Index out of bounds + try { + String userInput = "delete 100000"; + String[] command = parser.cleanUserInput(userInput); + parser.parseDeleteRecipe(command, recipes); + assert false; // Not supposed to reach this line + } catch (Exception e) { + assertEquals(IndexOutOfBoundsException.class, e.getClass()); + assertEquals("Please enter a valid recipe number, name, or range.", e.getMessage()); + } + + // Index out of bounds + try { + String userInput = "delete -5"; + String[] command = parser.cleanUserInput(userInput); + parser.parseDeleteRecipe(command, recipes); + assert false; // Not supposed to reach this line + } catch (Exception e) { + assertEquals(IndexOutOfBoundsException.class, e.getClass()); + assertEquals("Please enter a valid recipe number, name, or range.", e.getMessage()); + } + + // Range invalid + try { + String userInput = "delete 0-2000000"; + String[] command = parser.cleanUserInput(userInput); + parser.parseDeleteRecipe(command, recipes); + assert false; // Not supposed to reach this line + } catch (Exception e) { + assertEquals(IndexOutOfBoundsException.class, e.getClass()); + assertEquals("Please enter a valid recipe number, name, or range.", e.getMessage()); + } + + // Range invalid + try { + String userInput = "delete 1---------"; + String[] command = parser.cleanUserInput(userInput); + parser.parseDeleteRecipe(command, recipes); + assert false; // Not supposed to reach this line + } catch (Exception e) { + assertEquals(ArrayIndexOutOfBoundsException.class, e.getClass()); + assertEquals("Please enter a valid recipe number, name, or range in the correct format.", + e.getMessage()); + } + + // Range invalid + HashMap testIngredients1 = new HashMap<>(); + testIngredients1.put("test ingredient", 100); + // create a fake recipe + Recipe testR1 = new Recipe("test recipe name", testIngredients); + recipes.addRecipe(testR1); + + HashMap testIngredients2 = new HashMap<>(); + testIngredients2.put("test ingredient", 100); + // create a fake recipe + Recipe testR2 = new Recipe("test recipe name", testIngredients); + recipes.addRecipe(testR2); + try { + String userInput = "delete 2-1"; + String[] command = parser.cleanUserInput(userInput); + parser.parseDeleteRecipe(command, recipes); + assert false; // Not supposed to reach this line + } catch (Exception e) { + assertEquals(IndexOutOfBoundsException.class, e.getClass()); + assertEquals("Please enter a valid recipe number, name, or range.", e.getMessage()); + } + + // Delete all + try { + String userInput = "delete /r all"; + String[] command = parser.cleanUserInput(userInput); + parser.parseDeleteRecipe(command, recipes); + assertEquals(0, recipes.size()); + } catch (Exception e) { + assert false; + } + + // Recipe does not exist in recipe list) + try { + String userInput = "delete /r pizza"; + String[] command = parser.cleanUserInput(userInput); + parser.parseDeleteRecipe(command, recipes); + assert false; // Not supposed to reach this line + } catch (Exception e) { + assertEquals(ArrayIndexOutOfBoundsException.class, e.getClass()); + assertEquals("Please enter a valid recipe number, name, or range in the correct format.", + e.getMessage()); + } + } + + //@@author jaredoong + @Test + public void testAddWeeklyPlan() { + WeeklyPlan weeklyPlan = new WeeklyPlan(); + + // Testing positive case + try { + String userInput = "weekly /add burger 1"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.addPlans(recipeMap); + assertTrue(weeklyPlan.containsKey("burger")); + assertEquals(1, (int) weeklyPlan.get("burger")); + } catch (Exception e) { + assert false; + } + + // Testing positive case (case-insensitive) + try { + String userInput = "weekly /add BURGER 1"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.addPlans(recipeMap); + assertTrue(weeklyPlan.containsKey("burger")); + assertEquals(2, (int) weeklyPlan.get("burger")); + } catch (Exception e) { + assert false; + } + + // Testing positive case (name with space) + try { + String userInput = "weekly /add chicken rice 1"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.addPlans(recipeMap); + assertTrue(weeklyPlan.containsKey("chicken rice")); + assertEquals(1, (int) weeklyPlan.get("chicken rice")); + } catch (Exception e) { + assert false; + } + + // Testing negative case (recipe does not exist in recipe list) + try { + String userInput = "weekly /add chicken 1"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.addPlans(recipeMap); + assert false; // Not supposed to reach this line + } catch (Exception e) { + assertEquals(InvalidRecipeNameException.class, e.getClass()); + assertEquals("Please indicate a valid recipe name.", e.getMessage()); + } + + // Testing negative case (quantity is 0) + try { + String userInput = "weekly /add burger 0"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.addPlans(recipeMap); + assert false; // Not supposed to reach this line + } catch (Exception e) { + assertEquals(InvalidValueException.class, e.getClass()); + assertEquals("Please enter a number between 1 to 1000 for the quantity.", e.getMessage()); + } + + // Testing negative case (quantity is negative) + try { + String userInput = "weekly /add burger -1"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.addPlans(recipeMap); + assert false; // Not supposed to reach this line + } catch (Exception e) { + assertEquals(InvalidValueException.class, e.getClass()); + assertEquals("Please enter a number between 1 to 1000 for the quantity.", e.getMessage()); + } + + // Testing negative case (quantity is greater than 1000) + try { + String userInput = "weekly /add burger 1001"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.addPlans(recipeMap); + assert false; // Not supposed to reach this line + } catch (Exception e) { + assertEquals(InvalidValueException.class, e.getClass()); + assertEquals("Please enter a number between 1 to 1000 for the quantity.", e.getMessage()); + } + + // Testing negative case (quantity is not a number) + try { + String userInput = "weekly /add burger a"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.addPlans(recipeMap); + assert false; // Not supposed to reach this line + } catch (Exception e) { + assertEquals(NumberFormatException.class, e.getClass()); + assertEquals("Please ensure that you entered a valid quantity as the last argument.", + e.getMessage()); + } + + // Testing negative case (recipe name is not specified) + try { + String userInput = "weekly /add"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.addPlans(recipeMap); + assert false; // Not supposed to reach this line + } catch (Exception e) { + assertEquals(IllegalArgumentException.class, e.getClass()); + assertEquals("Please enter the command in the correct format with all parameters provided.", + e.getMessage()); + } + + // Testing negative case (quantity is not specified) + try { + String userInput = "weekly /add burger"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.addPlans(recipeMap); + assert false; // Not supposed to reach this line + } catch (Exception e) { + assertEquals(IllegalArgumentException.class, e.getClass()); + assertEquals("Please enter the command in the correct format with all parameters provided.", + e.getMessage()); + } + + // Testing negative case (quantity specified is not an integer) + try { + String userInput = "weekly /add burger 1.5"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.addPlans(recipeMap); + assert false; // Not supposed to reach this line + } catch (Exception e) { + assertEquals(NumberFormatException.class, e.getClass()); + assertEquals("Please ensure that you entered a valid quantity as the last argument.", + e.getMessage()); + } + + // Testing negative case (quantity specified causes integer overflow) + try { + String userInput = "weekly /add burger 21474836480000000000"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.addPlans(recipeMap); + assert false; // Not supposed to reach this line + } catch (Exception e) { + assertEquals(NumberFormatException.class, e.getClass()); + assertEquals("Please ensure that you entered a valid quantity as the last argument.", + e.getMessage()); + } + + // Testing negative case (missing all arguments) + try { + String userInput = "weekly"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.addPlans(recipeMap); + assert false; // Not supposed to reach this line + } catch (Exception e) { + assertEquals(IllegalArgumentException.class, e.getClass()); + assertEquals("Please indicate if you would want to add to, delete from, clear weekly plan, or " + + "mark as done.", e.getMessage()); + } + + // Testing negative case (arguments are not in the correct order) + try { + String userInput = "weekly /add 1 burger"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.addPlans(recipeMap); + assert false; // Not supposed to reach this line + } catch (Exception e) { + assertEquals(NumberFormatException.class, e.getClass()); + assertEquals("Please ensure that you entered a valid quantity as the last argument.", + e.getMessage()); + } + } + + @Test + public void testAddMultiWeeklyPlan() { + WeeklyPlan weeklyPlan = new WeeklyPlan(); + + // Testing positive case + try { + String userInput = "weekly /multiadd /r burger /q 1"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.addPlans(recipeMap); + assertTrue(weeklyPlan.containsKey("burger")); + assertEquals(1, (int) weeklyPlan.get("burger")); + } catch (Exception e) { + assert false; + } + + // Testing positive case (adding multiple recipes) + try { + String userInput = "weekly /multiadd /r burger /q 1 /r pizza /q 5"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.addPlans(recipeMap); + assertTrue(weeklyPlan.containsKey("burger")); + assertEquals(2, (int) weeklyPlan.get("burger")); + assertTrue(weeklyPlan.containsKey("pizza")); + assertEquals(5, (int) weeklyPlan.get("pizza")); + } catch (Exception e) { + assert false; + } + + // Testing case where same recipe name is specified twice + try { + String userInput = "weekly /multiadd /r burger /q 1 /r burger /q 20"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.addPlans(recipeMap); + assertTrue(weeklyPlan.containsKey("burger")); + assertEquals(22, (int) weeklyPlan.get("burger")); + } catch (Exception e) { + assert false; + } + + // Testing case where recipe name has space + try { + String userInput = "weekly /multiadd /r burger /q 1 /r chicken rice /q 20"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.addPlans(recipeMap); + assertTrue(weeklyPlan.containsKey("burger")); + assertEquals(23, (int) weeklyPlan.get("burger")); + assertTrue(weeklyPlan.containsKey("chicken rice")); + assertEquals(20, (int) weeklyPlan.get("chicken rice")); + } catch (Exception e) { + assert false; + } + + // Testing negative case (quantity is negative) + try { + String userInput = "weekly /multiadd /r burger /q -1 /r pizza /q 5"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.addPlans(recipeMap); + assert false; // Not supposed to reach this line + } catch (Exception e) { + assertEquals(InvalidValueException.class, e.getClass()); + assertEquals("Please enter a positive number between 1 to 1000 for the quantity.", + e.getMessage()); + } + + // Testing negative case (quantity is greater than 1000) + try { + String userInput = "weekly /multiadd /r burger /q 1001 /r pizza /q 5"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.addPlans(recipeMap); + assert false; // Not supposed to reach this line + } catch (Exception e) { + assertEquals(InvalidValueException.class, e.getClass()); + assertEquals("Please enter a positive number between 1 to 1000 for the quantity.", + e.getMessage()); + } + + // Testing negative case (quantity is not a number) + try { + String userInput = "weekly /multiadd /r burger /q a /r pizza /q 5"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.addPlans(recipeMap); + assert false; // Not supposed to reach this line + } catch (Exception e) { + assertEquals(NumberFormatException.class, e.getClass()); + assertEquals("Please enter a positive number between 1 to 1000 for the quantity.", + e.getMessage()); + } + + // Testing negative case (recipe name is not in the recipe list) + try { + String userInput = "weekly /multiadd /r randomname /q 1 /r burger /q 5"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.addPlans(recipeMap); + assert false; // Not supposed to reach this line + } catch (Exception e) { + assertEquals(InvalidRecipeNameException.class, e.getClass()); + assertEquals("Please indicate a valid recipe name.", e.getMessage()); + } + + // Testing negative case (recipe name is not specified) + try { + String userInput = "weekly /multiadd /q 1 /r burger /q 5"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.addPlans(recipeMap); + assert false; // Not supposed to reach this line + } catch (Exception e) { + assertEquals(IllegalArgumentException.class, e.getClass()); + assertEquals("Please ensure the number of /r and /q are the same.", e.getMessage()); + } + + // Testing negative case (quantity is not specified) + try { + String userInput = "weekly /multiadd /r burger /r pizza /q 5"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.addPlans(recipeMap); + assert false; // Not supposed to reach this line + } catch (Exception e) { + assertEquals(IllegalArgumentException.class, e.getClass()); + assertEquals("Please ensure the number of /r and /q are the same.", e.getMessage()); + } + + // Testing negative case (quantity given results in integer overflow) + try { + String userInput = "weekly /multiadd /r burger /q 10000000000000000000000 /r pizza /q 5"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.addPlans(recipeMap); + assert false; // Not supposed to reach this line + } catch (Exception e) { + assertEquals(NumberFormatException.class, e.getClass()); + assertEquals("Please enter a positive number between 1 to 1000 for the quantity.", + e.getMessage()); + } + + // Testing negative case (arguments are not in the correct order) + try { + String userInput = "weekly /multiadd /q 1 /r burger"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.addPlans(recipeMap); + assert false; // Not supposed to reach this line + } catch (Exception e) { + assertEquals(IllegalArgumentException.class, e.getClass()); + assertEquals("Please ensure that the /r and /q flags are entered in the correct order.", + e.getMessage()); + } + } + + @Test + public void testDeleteWeeklyPlan() { + WeeklyPlan weeklyPlan = new WeeklyPlan(); + weeklyPlan.put("salad", 10); + weeklyPlan.put("pizza", 30); + weeklyPlan.put("chicken rice", 10); + + // Testing positive cases + try { + String userInput = "weekly /delete salad 1"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.deletePlans(recipeMap); + assertEquals(9, weeklyPlan.get("salad")); + } catch (Exception e) { + assert false; // Not supposed to throw any exception here + } + + // Testing case-insensitive + try { + String userInput = "weekly /delete Pizza 1"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.deletePlans(recipeMap); + assertEquals(29, weeklyPlan.get("pizza")); + } catch (InvalidRecipeNameException | InvalidValueException e) { + assert false; // Not supposed to throw any exception here + } + + // Testing case where recipe name has space + try { + String userInput = "weekly /delete chicken rice 1"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.deletePlans(recipeMap); + assertEquals(9, weeklyPlan.get("chicken rice")); + } catch (InvalidRecipeNameException | InvalidValueException e) { + assert false; // Not supposed to throw any exception here + } + + // Testing negative case (recipe exist in recipe list but not in weekly plan) + try { + String userInput = "weekly /delete burger 20"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.deletePlans(recipeMap); + assert false; // Not supposed to reach here + } catch (Exception e) { + assertEquals(IllegalArgumentException.class, e.getClass()); + assertEquals("Recipe does not exist in weekly plan!", e.getMessage()); + } + + // Testing negative case (recipe does not exist in recipe list) + try { + String userInput = "weekly /delete unknown 1"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.deletePlans(recipeMap); + assert false; // Not supposed to reach here + } catch (Exception e) { + assertEquals(InvalidRecipeNameException.class, e.getClass()); + assertEquals("Please indicate a valid recipe name.", e.getMessage()); + } + + // Testing negative case (quantity is 0) + try { + String userInput = "weekly /delete salad 0"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.deletePlans(recipeMap); + assert false; // Not supposed to reach here + } catch (Exception e) { + assertEquals(InvalidValueException.class, e.getClass()); + assertEquals("Please enter a number between 1 to 1000 for the quantity.", e.getMessage()); + } + + // Testing negative case (quantity is greater than 1000) + try { + String userInput = "weekly /delete salad 1001"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.deletePlans(recipeMap); + assert false; // Not supposed to reach here + } catch (Exception e) { + assertEquals(InvalidValueException.class, e.getClass()); + assertEquals("Please enter a number between 1 to 1000 for the quantity.", e.getMessage()); + } + + // Testing negative case (quantity is negative) + try { + String userInput = "weekly /delete salad -1"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.deletePlans(recipeMap); + assert false; // Not supposed to reach here + } catch (Exception e) { + assertEquals(InvalidValueException.class, e.getClass()); + assertEquals("Please enter a number between 1 to 1000 for the quantity.", e.getMessage()); + } + + // Testing negative case (quantity is not a number) + try { + String userInput = "weekly /delete salad one"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.deletePlans(recipeMap); + assert false; // Not supposed to reach here + } catch (Exception e) { + assertEquals(NumberFormatException.class, e.getClass()); + assertEquals("Please ensure that you entered a valid quantity as the last argument.", + e.getMessage()); + } + + // Testing negative case (quantity is not specified) + try { + String userInput = "weekly /delete salad"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.deletePlans(recipeMap); + assert false; // Not supposed to reach here + } catch (Exception e) { + assertEquals(IllegalArgumentException.class, e.getClass()); + assertEquals("Please enter the command in the correct format with all parameters provided.", + e.getMessage()); + } + + // Testing negative case (recipe name is not specified) + try { + String userInput = "weekly /delete"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.deletePlans(recipeMap); + assert false; // Not supposed to reach here + } catch (Exception e) { + assertEquals(IllegalArgumentException.class, e.getClass()); + assertEquals("Please enter the command in the correct format with all parameters provided.", + e.getMessage()); + } + + // Testing negative case (quantity results in integer overflow) + try { + String userInput = "weekly /delete salad 1000000000000000000000000000000000"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.deletePlans(recipeMap); + assert false; // Not supposed to reach here + } catch (Exception e) { + assertEquals(NumberFormatException.class, e.getClass()); + assertEquals("Please ensure that you entered a valid quantity as the last argument.", + e.getMessage()); + } + + // Testing negative case (arguments are not in the correct order) + try { + String userInput = "weekly /delete 1 burger"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.deletePlans(recipeMap); + assert false; // Not supposed to reach this line + } catch (Exception e) { + assertEquals(NumberFormatException.class, e.getClass()); + assertEquals("Please ensure that you entered a valid quantity as the last argument.", + e.getMessage()); + } + } + + @Test + public void testDeleteMultiWeeklyPlan() { + WeeklyPlan weeklyPlan = new WeeklyPlan(); + weeklyPlan.put("salad", 10); + weeklyPlan.put("pizza", 30); + weeklyPlan.put("burger", 100); + weeklyPlan.put("chicken rice", 50); + + // Testing positive case + try { + String userInput = "weekly /multidelete /r salad /q 1 /r pizza /q 2"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.deletePlans(recipeMap); + assertEquals(9, weeklyPlan.get("salad")); + assertEquals(28, weeklyPlan.get("pizza")); + } catch (InvalidRecipeNameException | InvalidValueException e) { + assert false; // Not supposed to throw any exception here + } + + // Testing positive case (case-insensitive) + try { + String userInput = "weekly /multidelete /r SALAD /q 1 /r pIzZa /q 2"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.deletePlans(recipeMap); + assertEquals(8, weeklyPlan.get("salad")); + assertEquals(26, weeklyPlan.get("pizza")); + } catch (InvalidRecipeNameException | InvalidValueException e) { + assert false; // Not supposed to throw any exception here + } + + // Testing positive case (quantity given exceeds the current quantity) + try { + String userInput = "weekly /multidelete /r salad /q 100 /r pizza /q 100"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.deletePlans(recipeMap); + assertNull(weeklyPlan.get("salad")); + assertNull(weeklyPlan.get("pizza")); + } catch (InvalidRecipeNameException | InvalidValueException e) { + assert false; // Not supposed to throw any exception here + } + + // Testing positive case (recipe name has space) + try { + String userInput = "weekly /multidelete /r chicken rice /q 1"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.deletePlans(recipeMap); + assertEquals(49, weeklyPlan.get("chicken rice")); + } catch (InvalidRecipeNameException | InvalidValueException e) { + assert false; // Not supposed to throw any exception here + } + + // Testing negative case (recipe name is not specified) + try { + String userInput = "weekly /multidelete /q 1 /r pizza /q 2"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.deletePlans(recipeMap); + assert false; // Not supposed to reach here + } catch (Exception e) { + assertEquals(IllegalArgumentException.class, e.getClass()); + assertEquals("Please ensure the number of /r and /q are the same.", e.getMessage()); + } + + // Testing negative case (quantity is not specified) + try { + String userInput = "weekly /multidelete /r salad /r pizza /q 2"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.deletePlans(recipeMap); + assert false; // Not supposed to reach here + } catch (Exception e) { + assertEquals(IllegalArgumentException.class, e.getClass()); + assertEquals("Please ensure the number of /r and /q are the same.", e.getMessage()); + } + + // Testing negative case (quantity is not a number) + try { + String userInput = "weekly /multidelete /r salad /q one /r pizza /q 2"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.deletePlans(recipeMap); + assert false; // Not supposed to reach here + } catch (Exception e) { + assertEquals(NumberFormatException.class, e.getClass()); + assertEquals("Please enter a positive number between 1 to 1000 for the quantity.", + e.getMessage()); + } + + // Testing negative case (quantity results in integer overflow) + try { + String userInput = "weekly /multidelete /r salad /q 100000000000000000000000000 /r pizza /q 2"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.deletePlans(recipeMap); + assert false; // Not supposed to reach here + } catch (Exception e) { + assertEquals(NumberFormatException.class, e.getClass()); + assertEquals("Please enter a positive number between 1 to 1000 for the quantity.", + e.getMessage()); + } + + // Testing negative case (quantity is negative) + try { + String userInput = "weekly /multidelete /r salad /q -1 /r pizza /q 2"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.deletePlans(recipeMap); + assert false; // Not supposed to reach here + } catch (Exception e) { + assertEquals(InvalidValueException.class, e.getClass()); + assertEquals("Please enter a positive number between 1 to 1000 for the quantity.", + e.getMessage()); + } + + // Testing negative case (quantity is 0) + try { + String userInput = "weekly /multidelete /r salad /q 0 /r pizza /q 2"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.deletePlans(recipeMap); + assert false; // Not supposed to reach here + } catch (Exception e) { + assertEquals(InvalidValueException.class, e.getClass()); + assertEquals("Please enter a positive number between 1 to 1000 for the quantity.", + e.getMessage()); + } + + // Testing negative case (quantity is greater than 1000) + try { + String userInput = "weekly /multidelete /r salad /q 1001 /r pizza /q 2"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.deletePlans(recipeMap); + assert false; // Not supposed to reach here + } catch (Exception e) { + assertEquals(InvalidValueException.class, e.getClass()); + assertEquals("Please enter a positive number between 1 to 1000 for the quantity.", + e.getMessage()); + } + + // Testing negative case (recipe name is not in the weekly plan) + try { + String userInput = "weekly /multidelete /r randomname /q 1 /r pizza /q 2"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.deletePlans(recipeMap); + assert false; // Not supposed to reach here + } catch (Exception e) { + assertEquals(InvalidRecipeNameException.class, e.getClass()); + assertEquals("Please indicate a valid recipe name.", e.getMessage()); + } + + // Testing negative case (arguments are not in the correct order) + try { + String userInput = "weekly /multidelete /q 1 /r burger"; + String[] command = parser.cleanUserInput(userInput); + WeeklyPlan recipeMap = parser.parseWeeklyPlan(command, recipes); + weeklyPlan.deletePlans(recipeMap); + assert false; // Not supposed to reach this line + } catch (Exception e) { + assertEquals(IllegalArgumentException.class, e.getClass()); + assertEquals("Please ensure that the /r and /q flags are entered in the correct order.", + e.getMessage()); + } + } + + //@@author gurmankalkat + @Test + public void testClearWeeklyPlan() { + WeeklyPlan weeklyPlan = new WeeklyPlan(); + weeklyPlan.put("salad", 1); + weeklyPlan.put("pizza", 3); + // Testing clearing weekly plan + weeklyPlan.clearPlan(); + assertEquals(weeklyPlan.size(), 0); + + // weekly plan of size 0 + weeklyPlan = new WeeklyPlan(); + weeklyPlan.clearPlan(); + assertEquals(weeklyPlan.size(), 0); + } + + @Test + public void testViewWeeklyIngredients() { + HashMap pastaIngredients = new HashMap<>(); + pastaIngredients.put("penne", 1); + Recipe pasta = new Recipe("pasta", pastaIngredients); + recipes.add(pasta); + + WeeklyPlan weeklyPlan = new WeeklyPlan(); + weeklyPlan.put("pasta", 1); + + ui.printWeeklyIngredients(weeklyPlan, recipes); + assertEquals(ui.formatMessage("Here are your weekly ingredients:") + System.lineSeparator() + + ui.formatMessage("penne (1)"), outContent.toString().trim()); + } + + //@@author jaredoong + @Test + public void testViewEmptyWeeklyPlan() { + WeeklyPlan weeklyPlan = new WeeklyPlan(); + ui.printWeeklyPlan(weeklyPlan); + assertEquals(ui.formatMessage("Your weekly plan is empty!"), outContent.toString().trim()); + } + + @Test + public void testViewNonEmptyWeeklyPlan() { + WeeklyPlan weeklyPlan = new WeeklyPlan(); + weeklyPlan.put("salad", 1); + ui.printWeeklyPlan(weeklyPlan); + assertEquals( + ui.formatMessage("Here is your weekly plan:") + System.lineSeparator() + ui.formatMessage( + "salad x1"), outContent.toString().trim()); + } + + //@@author junenita + @Test + public void testListRecipe() { + RecipeList recipeListToPrint; + String[] inputs; + + try { + inputs = new String[]{"list"}; + recipeListToPrint = parser.parseListRecipe(inputs, recipes); + assertEquals(4, recipeListToPrint.size()); + assertEquals(3, recipeListToPrint.get(0).getNumOfIngredients()); + assertEquals(4, recipeListToPrint.get(1).getNumOfIngredients()); + assertEquals(3, recipeListToPrint.get(2).getNumOfIngredients()); + + inputs = new String[]{"list", "tomato", "sauce"}; + recipeListToPrint = parser.parseListRecipe(inputs, recipes); + assertEquals(1, recipeListToPrint.size()); + assertEquals(4, recipeListToPrint.get(0).getNumOfIngredients()); + assertEquals("pizza", recipeListToPrint.get(0).getName()); + + inputs = new String[]{"list", "salad", "&&", "tomato"}; + recipeListToPrint = parser.parseListRecipe(inputs, recipes); + assertEquals(1, recipeListToPrint.size()); + assertEquals(3, recipeListToPrint.get(0).getNumOfIngredients()); + assertEquals("salad", recipeListToPrint.get(0).getName()); + + inputs = new String[]{"list", "salad", "&&", "pizza"}; + recipeListToPrint = parser.parseListRecipe(inputs, recipes); + assertEquals(0, recipeListToPrint.size()); + } catch (Exception e) { + assert false; + } + } + + @Test + public void testListTagAndAddTag() { + try { + RecipeList recipeListToPrint; + String[] inputs; + // check add tag (1) + String[] addTagInputs = new String[]{"tag", "breakfast", "<<", "salad"}; + parser.parseTagRecipe(addTagInputs, recipes); + + // check list tag (1) + inputs = new String[]{"list", "/t", "breakfast"}; + recipeListToPrint = parser.parseListRecipe(inputs, recipes); + assertEquals(recipeListToPrint.size(), 1); + assertEquals(recipeListToPrint.get(0).getName(), "salad"); + + // add tag (2) + addTagInputs = new String[]{"tag", "breakfast", "<<", "pizza"}; + parser.parseTagRecipe(addTagInputs, recipes); + + addTagInputs = new String[]{"tag", "western", "<<", "burger"}; + parser.parseTagRecipe(addTagInputs, recipes); + + // list tag (2) + inputs = new String[]{"list", "/t", "breakfast"}; + recipeListToPrint = parser.parseListRecipe(inputs, recipes); + assertEquals(recipeListToPrint.size(), 2); + + inputs = new String[]{"list", "/t", "breakfast", "&&", "western"}; + recipeListToPrint = parser.parseListRecipe(inputs, recipes); + assertEquals(recipeListToPrint.size(), 0); + + // add tag (3) + addTagInputs = new String[]{"tag", "western", "<<", "pizza"}; + parser.parseTagRecipe(addTagInputs, recipes); + + // list tag (3) + inputs = new String[]{"list", "/t", "breakfast", "&&", "western"}; + recipeListToPrint = parser.parseListRecipe(inputs, recipes); + assertEquals(recipeListToPrint.size(), 1); + assertEquals(recipeListToPrint.get(0).getName(), "pizza"); + } catch (Exception e) { + assert false; + } + } + + @Test + public void testPrintAddTagMessage() { + String[] addTagInputs = new String[]{"tag", "breakfast", "<<", "salad"}; + try { + String addTagReturnMessage = parser.parseTagRecipe(addTagInputs, recipes); + ui.printTagMessage(addTagReturnMessage); + assertEquals(ui.formatMessage("You have successfully added the recipe(s) to \"breakfast\" tag."), + outContent.toString().trim()); + } catch (Exception e) { + assert false; + } + } + + @Test + public void testPrintRemoveTagMessage() { + String[] addTagInputs = new String[]{"tag", "breakfast", "<<", "salad"}; + try { + parser.parseTagRecipe(addTagInputs, recipes); + String[] removeTagInputs = new String[]{"tag", "breakfast", ">>", "salad"}; + String removeTagReturnMessage = parser.parseTagRecipe(removeTagInputs, recipes); + ui.printTagMessage(removeTagReturnMessage); + assertEquals( + ui.formatMessage("You have successfully removed the recipe(s) from \"breakfast\" tag."), + outContent.toString().trim()); + } catch (Exception e) { + assert false; + } + + } + + @Test + public void testListTagException() { + String[] inputs; + + //check exception + String[] noArgInputs = new String[]{"list", "/t"}; + assertThrows(TagNotFoundException.class, () -> parser.parseListRecipe(noArgInputs, recipes)); + + String[] notFoundTagInputs1 = new String[]{"list", "/t", "/t"}; + assertThrows(TagNotFoundException.class, () -> parser.parseListRecipe(notFoundTagInputs1, recipes)); + + String[] notFoundTagInputs2 = new String[]{"list", "/t", "phone", "&&", "computer"}; + assertThrows(TagNotFoundException.class, () -> parser.parseListRecipe(notFoundTagInputs2, recipes)); + + String[] beforeAddTagInputs = new String[]{"list", "/t", "breakfast"}; + assertThrows(TagNotFoundException.class, () -> parser.parseListRecipe(beforeAddTagInputs, recipes)); + + // add tag for testing + try { + inputs = new String[]{"tag", "breakfast", "<<", "salad"}; + parser.parseTagRecipe(inputs, recipes); + inputs = new String[]{"tag", "breakfast", ">>", "salad"}; + parser.parseTagRecipe(inputs, recipes); + + String[] nothingInTagInputs = new String[]{"list", "/t", "breakfast"}; + + ui.listRecipe(parser.parseListRecipe(nothingInTagInputs, recipes)); + assertEquals(ui.formatMessage("There is nothing to list."), outContent.toString().trim()); + } catch (Exception e) { + assert false; + } + } + + @Test + public void testRemoveTagAndAddTag() { + String[] inputs; + Recipe invalidRecipe = new Recipe("invalid Recipe", new HashMap<>(1, 1)); + + try { + inputs = new String[]{"tag", "western", "<<", "burger"}; + parser.parseTagRecipe(inputs, recipes); + inputs = new String[]{"tag", "western", "<<", "pizza"}; + parser.parseTagRecipe(inputs, recipes); + } catch (Exception e) { + assert false; + } + + //exception + assertThrows(RecipeNotFoundInTagException.class, + () -> recipes.removeRecipeFromTag("western", invalidRecipe)); + String[] invalidTagInputs = new String[]{"tag", "random", "tag", ">>", "burger"}; + assertThrows(TagNotFoundException.class, () -> parser.parseTagRecipe(invalidTagInputs, recipes)); + + String[] noRecipeInTagInputs = new String[]{"tag", "western", ">>", "random"}; + assertThrows(NoRecipeException.class, () -> parser.parseTagRecipe(noRecipeInTagInputs, recipes)); + + try { + inputs = new String[]{"tag", "western", ">>", "pizza"}; + parser.parseTagRecipe(inputs, recipes); + assertEquals(recipes.tags.get("western").size(), 1); + } catch (Exception e) { + assert false; + } + } + + @Test + public void testRandomRecipe() { + // no recipe in the lists + RecipeList blankRecipeList = new RecipeList(); + assertThrows(NoRecipeException.class, () -> parser.parseRandomRecipe(blankRecipeList)); + + // general case + try { + Recipe randomRecipe1 = parser.parseRandomRecipe(recipes); + assertNotNull(randomRecipe1); + assertNotNull(recipes.randomRecipe()); + } catch (Exception e) { + assert false; + } + } + + //@@author jaredoong + @Test + public void testWeeklyDone() { + WeeklyPlan weeklyPlan = new WeeklyPlan(); + weeklyPlan.put("pizza", 100); + weeklyPlan.put("burger", 100); + weeklyPlan.put("chicken rice", 100); + + // Testing positive case + try { + String userInput = "weekly /done burger"; + String[] command = parser.cleanUserInput(userInput); + String recipeToDelete = parser.parseMarkDone(command, userIngredients, weeklyPlan, recipes); + weeklyPlan.markDone(recipeToDelete, recipes, userIngredients); + assertEquals(8, userIngredients.findIngredientCount("buns")); + assertEquals(9, userIngredients.findIngredientCount("meat patty")); + assertEquals(7, userIngredients.findIngredientCount("lettuce")); + assertEquals(99, weeklyPlan.get("burger")); + } catch (Exception e) { + assert false; + } + + // Testing positive case (case-insensitive) + try { + String userInput = "weekly /done Pizza"; + String[] command = parser.cleanUserInput(userInput); + String recipeToDelete = parser.parseMarkDone(command, userIngredients, weeklyPlan, recipes); + weeklyPlan.markDone(recipeToDelete, recipes, userIngredients); + assertEquals(9, userIngredients.findIngredientCount("dough")); + assertEquals(9, userIngredients.findIngredientCount("tomato sauce")); + assertEquals(9, userIngredients.findIngredientCount("cheese")); + assertEquals(9, userIngredients.findIngredientCount("pepperoni")); + assertEquals(99, weeklyPlan.get("pizza")); + } catch (Exception e) { + assert false; + } + + // Testing positive case (entire command case-insensitive) + try { + String userInput = "WEEKLY /DONE pizza"; + String[] command = parser.cleanUserInput(userInput); + String recipeToDelete = parser.parseMarkDone(command, userIngredients, weeklyPlan, recipes); + weeklyPlan.markDone(recipeToDelete, recipes, userIngredients); + assertEquals(8, userIngredients.findIngredientCount("dough")); + assertEquals(8, userIngredients.findIngredientCount("tomato sauce")); + assertEquals(8, userIngredients.findIngredientCount("cheese")); + assertEquals(8, userIngredients.findIngredientCount("pepperoni")); + assertEquals(98, weeklyPlan.get("pizza")); + } catch (Exception e) { + assert false; + } + + // Testing positive case (recipe name with space) + try { + String userInput = "weekly /done chicken rice"; + String[] command = parser.cleanUserInput(userInput); + String recipeToDelete = parser.parseMarkDone(command, userIngredients, weeklyPlan, recipes); + weeklyPlan.markDone(recipeToDelete, recipes, userIngredients); + assertEquals(9, userIngredients.findIngredientCount("chicken")); + assertEquals(9, userIngredients.findIngredientCount("rice")); + assertEquals(99, weeklyPlan.get("chicken rice")); + } catch (Exception e) { + assert false; + } + + // Testing negative case (recipe not found) + try { + String userInput = "weekly /done pizzaandburgers"; + String[] command = parser.cleanUserInput(userInput); + String recipeToDelete = parser.parseMarkDone(command, userIngredients, weeklyPlan, recipes); + weeklyPlan.markDone(recipeToDelete, recipes, userIngredients); + assert false; // should not reach here + } catch (Exception e) { + assertEquals(InvalidRecipeNameException.class, e.getClass()); + assertEquals("Please enter a valid recipe name.", e.getMessage()); + } + + // Testing negative case (recipe not in weekly plan) + try { + String userInput = "weekly /done salad"; + String[] command = parser.cleanUserInput(userInput); + String recipeToDelete = parser.parseMarkDone(command, userIngredients, weeklyPlan, recipes); + weeklyPlan.markDone(recipeToDelete, recipes, userIngredients); + assert false; // should not reach here + } catch (Exception e) { + assertEquals(IllegalArgumentException.class, e.getClass()); + assertEquals("Recipe does not exist in weekly plan.", e.getMessage()); + } + + // Testing negative case (not enough ingredients) + try { + String userInput = "weekly /done burger"; + String[] command = parser.cleanUserInput(userInput); + for (int i = 0; i < 3; i++) { + String recipeToDelete = parser.parseMarkDone(command, userIngredients, weeklyPlan, recipes); + weeklyPlan.markDone(recipeToDelete, recipes, userIngredients); + } + assert false; // should not reach here + } catch (Exception e) { + assertEquals(IngredientNotFoundException.class, e.getClass()); + assertEquals("You do not have enough ingredients to mark this recipe as done.", e.getMessage()); + } + } + + @Test + public void testAddUserIngredients() { + + // Test positive case (new ingredient) + try { + String userInput = "add_i /n fish /c 10 /d 10/10/2024"; + String[] command = parser.cleanUserInput(userInput); + Ingredient ingredientToAdd = parser.parseAddUserIngredients(command); + userIngredients.addIngredient(ingredientToAdd); + assertEquals(10, userIngredients.findIngredientCount("fish")); + } catch (Exception e) { + assert false; + } + + // Test positive case (ingredient name already exists) + try { + String userInput = "add_i /n chicken /c 10 /d 10/10/2024"; + String[] command = parser.cleanUserInput(userInput); + Ingredient ingredientToAdd = parser.parseAddUserIngredients(command); + userIngredients.addIngredient(ingredientToAdd); + assertEquals(20, userIngredients.findIngredientCount("chicken")); + } catch (Exception e) { + assert false; + } + + // Test positive case (case-insensitive) + try { + String userInput = "add_i /n CHICKEN /c 10 /d 10/10/2024"; + String[] command = parser.cleanUserInput(userInput); + Ingredient ingredientToAdd = parser.parseAddUserIngredients(command); + userIngredients.addIngredient(ingredientToAdd); + assertEquals(30, userIngredients.findIngredientCount("chicken")); + } catch (Exception e) { + assert false; + } + + // Test positive case (expiry date gets updated) + try { + String userInput = "add_i /n chicken /c 10 /d 21/10/2025"; + String[] command = parser.cleanUserInput(userInput); + Ingredient ingredientToAdd = parser.parseAddUserIngredients(command); + userIngredients.addIngredient(ingredientToAdd); + assertEquals(40, userIngredients.findIngredientCount("chicken")); + assertEquals(parser.parseDate("21/10/2025"), userIngredients.findExpiryDate("chicken")); + } catch (Exception e) { + assert false; + } + + // Test positive case (ingredient name with spaces) + try { + String userInput = "add_i /n peas and corn and carrot /c 10 /d 10/10/2024"; + String[] command = parser.cleanUserInput(userInput); + Ingredient ingredientToAdd = parser.parseAddUserIngredients(command); + userIngredients.addIngredient(ingredientToAdd); + assertEquals(10, userIngredients.findIngredientCount("peas and corn and carrot")); + } catch (Exception e) { + assert false; + } + + // Test negative case (quantity is 0) + try { + String userInput = "add_i /n chicken /c 0 /d 10/10/2024"; + String[] command = parser.cleanUserInput(userInput); + Ingredient ingredientToAdd = parser.parseAddUserIngredients(command); + userIngredients.addIngredient(ingredientToAdd); + assert false; // should not reach here + } catch (Exception e) { + assertEquals(InvalidValueException.class, e.getClass()); + assertEquals("Please enter a positive number between 1 to 1000 for the quantity.", + e.getMessage()); + } + + // Test negative case (quantity is negative) + try { + String userInput = "add_i /n chicken /c -10 /d 10/10/2024"; + String[] command = parser.cleanUserInput(userInput); + Ingredient ingredientToAdd = parser.parseAddUserIngredients(command); + userIngredients.addIngredient(ingredientToAdd); + assert false; // should not reach here + } catch (Exception e) { + assertEquals(InvalidValueException.class, e.getClass()); + assertEquals("Please enter a positive number between 1 to 1000 for the quantity.", + e.getMessage()); + } + + // Test negative case (quantity is more than 1000) + try { + String userInput = "add_i /n chicken /c 1001 /d 10/10/2024"; + String[] command = parser.cleanUserInput(userInput); + Ingredient ingredientToAdd = parser.parseAddUserIngredients(command); + userIngredients.addIngredient(ingredientToAdd); + assert false; // should not reach here + } catch (Exception e) { + assertEquals(InvalidValueException.class, e.getClass()); + assertEquals("Please enter a positive number between 1 to 1000 for the quantity.", + e.getMessage()); + } + + // Test negative case (quantity is not a number) + try { + String userInput = "add_i /n chicken /c abc /d 10/10/2024"; + String[] command = parser.cleanUserInput(userInput); + Ingredient ingredientToAdd = parser.parseAddUserIngredients(command); + userIngredients.addIngredient(ingredientToAdd); + assert false; // should not reach here + } catch (Exception e) { + assertEquals(NumberFormatException.class, e.getClass()); + assertEquals("Please enter a positive number between 1 to 1000 for the quantity.", + e.getMessage()); + } + + // Test negative case (expiry date is not in the correct format) + try { + String userInput = "add_i /n chicken /c 10 /d 10/10/202"; + String[] command = parser.cleanUserInput(userInput); + Ingredient ingredientToAdd = parser.parseAddUserIngredients(command); + userIngredients.addIngredient(ingredientToAdd); + assert false; // should not reach here + } catch (Exception e) { + assertEquals(IllegalArgumentException.class, e.getClass()); + assertEquals("Please enter a valid date in the format dd/mm/yyyy.", e.getMessage()); + } + + // Test negative case (expiry date is not a valid date) + try { + String userInput = "add_i /n chicken /c 10 /d 10/13/2024"; + String[] command = parser.cleanUserInput(userInput); + Ingredient ingredientToAdd = parser.parseAddUserIngredients(command); + userIngredients.addIngredient(ingredientToAdd); + assert false; // should not reach here + } catch (Exception e) { + assertEquals(IllegalArgumentException.class, e.getClass()); + assertEquals("Please enter a valid date in the format dd/mm/yyyy.", e.getMessage()); + } + } + + @Test + public void testDeleteUserIngredients() { + + // Test positive case + try { + String userInput = "del_i /n chicken /c 5"; + String[] command = parser.cleanUserInput(userInput); + Ingredient ingredientToDelete = parser.parseDeleteUserIngredients(command); + userIngredients.deleteIngredient(ingredientToDelete); + assertEquals(5, userIngredients.findIngredientCount("chicken")); + } catch (Exception e) { + assert false; + } + + // Test positive case (case-insensitive) + try { + String userInput = "del_i /n CHEESE /c 5"; + String[] command = parser.cleanUserInput(userInput); + Ingredient ingredientToDelete = parser.parseDeleteUserIngredients(command); + userIngredients.deleteIngredient(ingredientToDelete); + assertEquals(5, userIngredients.findIngredientCount("cheese")); + } catch (Exception e) { + assert false; + } + + // Test positive case (ingredient name with spaces) + try { + String userInput = "del_i /n peas and corn /c 5"; + String[] command = parser.cleanUserInput(userInput); + Ingredient ingredientToDelete = parser.parseDeleteUserIngredients(command); + userIngredients.deleteIngredient(ingredientToDelete); + assertEquals(5, userIngredients.findIngredientCount("peas and corn")); + } catch (Exception e) { + assert false; + } + + // Test positive case (quantity is more than the current quantity) + try { + String userInput = "del_i /n chicken /c 100"; + String[] command = parser.cleanUserInput(userInput); + Ingredient ingredientToDelete = parser.parseDeleteUserIngredients(command); + userIngredients.deleteIngredient(ingredientToDelete); + assertNull(userIngredients.get("chicken")); + } catch (Exception e) { + assert false; + } + + // Test negative case (quantity is 0) + try { + String userInput = "del_i /n chicken /c 0"; + String[] command = parser.cleanUserInput(userInput); + Ingredient ingredientToDelete = parser.parseDeleteUserIngredients(command); + userIngredients.deleteIngredient(ingredientToDelete); + assert false; // should not reach here + } catch (Exception e) { + assertEquals(InvalidValueException.class, e.getClass()); + assertEquals("Please enter a positive number between 1 to 1000 for the quantity.", + e.getMessage()); + } + + // Test negative case (quantity is negative) + try { + String userInput = "del_i /n chicken /c -5"; + String[] command = parser.cleanUserInput(userInput); + Ingredient ingredientToDelete = parser.parseDeleteUserIngredients(command); + userIngredients.deleteIngredient(ingredientToDelete); + assert false; // should not reach here + } catch (Exception e) { + assertEquals(InvalidValueException.class, e.getClass()); + assertEquals("Please enter a positive number between 1 to 1000 for the quantity.", + e.getMessage()); + } + + // Test negative case (quantity is more than 1000) + try { + String userInput = "del_i /n chicken /c 1001"; + String[] command = parser.cleanUserInput(userInput); + Ingredient ingredientToDelete = parser.parseDeleteUserIngredients(command); + userIngredients.deleteIngredient(ingredientToDelete); + assert false; // should not reach here + } catch (Exception e) { + assertEquals(InvalidValueException.class, e.getClass()); + assertEquals("Please enter a positive number between 1 to 1000 for the quantity.", + e.getMessage()); + } + + // Test negative case (quantity is not a number) + try { + String userInput = "del_i /n chicken /c abc"; + String[] command = parser.cleanUserInput(userInput); + Ingredient ingredientToDelete = parser.parseDeleteUserIngredients(command); + userIngredients.deleteIngredient(ingredientToDelete); + assert false; // should not reach here + } catch (Exception e) { + assertEquals(NumberFormatException.class, e.getClass()); + assertEquals("Please enter a positive number between 1 to 1000 for the quantity.", + e.getMessage()); + } + + // Test negative case (ingredient name is not found) + try { + String userInput = "del_i /n beef /c 5"; + String[] command = parser.cleanUserInput(userInput); + Ingredient ingredientToDelete = parser.parseDeleteUserIngredients(command); + userIngredients.deleteIngredient(ingredientToDelete); + assert false; // should not reach here + } catch (Exception e) { + assertEquals(IngredientNotFoundException.class, e.getClass()); + assertEquals("Ingredient not found", e.getMessage()); + } + } + + @Test + public void testNonEmptyListUserIngredients() { + IngredientList testIngredientList = new IngredientList(); + Ingredient buns = new Ingredient("buns", 10, "10/10/2024"); + testIngredientList.addIngredient(buns); + + // Test positive case + ui.printUserIngredients(testIngredientList); + assertEquals( + ui.formatMessage("Here is your ingredient list:") + System.lineSeparator() + ui.formatMessage( + "buns " + "(10) " + "[by:10/10/2024]") + System.lineSeparator(), + outContent.toString()); + } + + @Test + public void testEmptyListUserIngredients() { + IngredientList testIngredientList = new IngredientList(); + + // Testing positive case (no ingredients) + ui.printUserIngredients(testIngredientList); + assertEquals(ui.formatMessage("Your ingredient list is empty!") + System.lineSeparator(), + outContent.toString()); + } + + @Test + public void testLoadDatabase() { + assertDoesNotThrow(database::loadRecipesDatabase); + } + + //@@author AbijithRam + @Test + public void testCombineWords() { + String[] input1 = {"One", "Two", "Three"}; + assertEquals("One Two Three", parser.combineWords(input1, 0, 3)); + + String[] input2 = {"Pizza ", "Burger ", " Ice Cream"}; + assertEquals("Pizza Burger Ice Cream", parser.combineWords(input2, 0, 3)); + + String[] input3 = {"1 1 1", "2 2 2", "3 3 3"}; + assertEquals("1 1 1 2 2 2 3 3 3", parser.combineWords(input3, 0, 3)); + + } +} diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 892cb6cae7..da922933cc 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,9 +1,171 @@ -Hello from - ____ _ -| _ \ _ _| | _____ -| | | | | | | |/ / _ \ -| |_| | |_| | < __/ -|____/ \__,_|_|\_\___| +---------------------------------------------------------------------------------------------------- +Welcome to Meal360, your ultimate Recipe Manager! + __ __ _ ____ __ __ +| \/ |___ __ _| |__ / / / / \ +| |\/| / -_) _` | ||_ \/ _ \ () | +|_| |_\___\__,_|_|___/\___/\__/ -What is your name? -Hello James Gosling +---------------------------------------------------------------------------------------------------- +| Loading recipes... | +| Recipes loaded successfully. | +| Loading weekly plan... | +| Weekly plan loaded successfully. | +| Loading ingredients... | +| Ingredients loaded successfully. | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| Please include at least a tag. | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| There is no "random tag" tag found. Please make sure you have entered the correct tag. | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| There is no "/t" tag found. Please make sure you have entered the correct tag. | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| There is nothing to list. | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| There is nothing to list. | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| There is nothing to list. | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| There is nothing to list. | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| There is nothing to list. | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| There is no "tag#1" tag found. Please make sure you have entered the correct tag. | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| Please include at least a tag. | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| Please enter the command in the correct format. | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| You have successfully added the recipe(s) to "tag#1" tag. | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| There is nothing to list. | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| Please enter the command in the correct format. | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| Unable to find the recipe: "strawberry" in the tag. | +| All the recipe before "strawberry" (if any) are successfully removed from the tag. | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| Unable to find the recipe: "avocado toast" in the tag. | +| All the recipe before "avocado toast" (if any) are successfully removed from the tag. | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| There is nothing to list. | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| There is nothing to list. | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| Please enter a valid recipe number. You did not enter a recipe number. | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| Please enter a valid recipe number. You entered -2, which is out of bounds. | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| Please enter a valid recipe number. You entered abc, which is not a valid number. | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| Please indicate if you would want to add to, delete from, clear weekly plan, or mark as done. | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| Please indicate if you would want to add to, delete from, clear weekly plan, or mark as done. | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| Please indicate a valid recipe name. | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| Please indicate a valid recipe name. | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| Please ensure that you entered a valid quantity as the last argument. | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| I've added the recipes to your weekly plan! | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| Please ensure that you entered a valid quantity as the last argument. | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| Please ensure that you entered a valid quantity as the last argument. | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| I've deleted the recipes from your weekly plan! | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| Here is your weekly plan: | +| avocado toast x5 | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| Please ensure that you entered a valid quantity as the last argument. | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| Here is your weekly plan: | +| avocado toast x5 | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| Missing required information. Please provide ingredient name, count, and expiry date. | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| Ingredient name cannot be empty. | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| Ingredient successfully added! | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| Here is your ingredient list: | +| carrot (99) [by:01/01/2020] | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| Ingredient successfully added! | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| Here is your ingredient list: | +| carrot (198) [by:01/01/2020] | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| Ingredient successfully added! | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| Here is your ingredient list: | +| carrot (297) [by:01/01/2020] | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| Missing required information. Please provide ingredient name and count. | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| Ingredients successfully deleted! | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| Here is your ingredient list: | +| carrot (287) [by:01/01/2020] | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| Ingredients successfully deleted! | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| Here is your ingredient list: | +| carrot (277) [by:01/01/2020] | +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- +| Saving recipes... | +| Recipes saved successfully. | +| Saving weekly plan... | +| Weekly plan saved successfully. | +| Saving ingredients... | +| Ingredients saved successfully. | +| Bye. Hope to see you again soon! | +---------------------------------------------------------------------------------------------------- diff --git a/text-ui-test/database/recipesDatabase.json b/text-ui-test/database/recipesDatabase.json new file mode 100644 index 0000000000..0beacc58a3 --- /dev/null +++ b/text-ui-test/database/recipesDatabase.json @@ -0,0 +1 @@ +[{"ingredients":{"Salt":1,"Pepper":1,"Chicken":1,"Rice":1},"name":"Chicken Rice"},{"ingredients":{"Plain flour":2,"Breadcrumbs":100,"Vegetable oil":2,"Ripe mixed-colour cherry tomatoes ":250,"Flat-leaf parsley":15,"Garlic cloves":2,"Feta cheese":30,"Eggs":2},"name":"Tomato fritters"},{"ingredients":{"Puff pastry":1,"Mushrooms":250,"Onion":1,"Plain flour":2,"Butter":2,"Chicken breast fillets":2,"Milk":2},"name":"Chicken and mushroom pie"},{"ingredients":{"Prawns":500,"Mussels":500,"Onion":1,"Chicken stock":1,"Cherry tomatoes":250,"Vegetable oil":2,"Saffron":1,"Chorizo":100,"Garlic cloves":2,"Rice":1},"name":"Seafood paella"},{"ingredients":{"Salt":1,"Pepper":1,"Avocado":1,"Lemon":1,"Bread":2},"name":"Avocado toast"},{"ingredients":{"Dried rosemary":1,"Carrot":1,"Onion":1,"Dried parsley":1,"Chicken stock":1,"Dried basil":1,"Garlic cloves":2,"Dried thyme":1,"Parmesan cheese":1,"Celery":1,"Dried oregano":1,"Italian sausage":1,"Orzo":1},"name":"Italian sausage orzo soup"},{"ingredients":{"Mushrooms":250,"Onion":1,"Plain flour":2,"Butter":2,"Chicken breast fillets":2,"Pasta":1,"Garlic cloves":2,"Milk":2},"name":"Chicken and mushroom pasta"},{"ingredients":{"Salt":1,"Pepper":1,"Pasta":1,"Hummus":1,"Garlic cloves":2,"Lemon":1},"name":"Creamy Hummus Pasta"},{"ingredients":{"Cheddar cheese":1,"Potato":1,"Tomato sauce":1,"Onion":1,"Ground beef":1,"Tomato paste":1,"Garlic cloves":2,"Beef stock":1},"name":"Ground beef and potato casserole"},{"ingredients":{"Cheddar cheese":1,"Blue cheese":1,"Hot sauce":1,"Butter":2,"Chicken breast fillets":2,"Macaroni":1,"Flour":2,"Milk":2},"name":"Buffalo Chicken Mac \u0027n\u0027 Cheese"}] \ No newline at end of file diff --git a/text-ui-test/database/userIngredientsDatabase.json b/text-ui-test/database/userIngredientsDatabase.json new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/text-ui-test/database/userIngredientsDatabase.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/text-ui-test/database/weeklyPlanDatabase.json b/text-ui-test/database/weeklyPlanDatabase.json new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/text-ui-test/database/weeklyPlanDatabase.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index f6ec2e9f95..b4b9113b55 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -1 +1,47 @@ -James Gosling \ No newline at end of file +list /t +list /t random tag +list /t /t +list apple +list soup && apple +list soup apple +list soup /t +list soup && /t +list /t tag#1 +list /t +tag tag#1 << avocado toast >> +tag tag#1 << avocado toast << +list /t tag#1 && tag#2 +tag tag#1 >> avocado toast << +tag tag#1 >> avocado toast && strawberry>> +tag tag#1 >> avocado toast && strawberry +list /t tag#1 +list /t tag#1 && tag#2 /t +view +view -2 +view abc +weekly +weekly random name +weekly /add random name 1 +weekly /add test recipe name 1 +weekly /add 10 avocado toast +weekly /add avocado toast 10 +weekly /delete avocado toast +weekly /delete 10 avocado toast +weekly /delete avocado toast 5 +weeklyplan +weekly /delete test recipe name +weeklyplan +add_i +add_i /n /c /d +add_i /n carrot /c 99 /d 01/01/2020 +view_ingredients +add_i /d 01/01/2020 /n carrot /c 99 +view_ingredients +add_i /c 99 /n carrot /d 01/01/2020 +view_ingredients +del_i +del_i /n carrot /c 10 +view_ingredients +del_i /c 10 /n carrot +view_ingredients +bye \ No newline at end of file diff --git a/text-ui-test/src/main/resources/recipesDatabase.json b/text-ui-test/src/main/resources/recipesDatabase.json new file mode 100644 index 0000000000..0beacc58a3 --- /dev/null +++ b/text-ui-test/src/main/resources/recipesDatabase.json @@ -0,0 +1 @@ +[{"ingredients":{"Salt":1,"Pepper":1,"Chicken":1,"Rice":1},"name":"Chicken Rice"},{"ingredients":{"Plain flour":2,"Breadcrumbs":100,"Vegetable oil":2,"Ripe mixed-colour cherry tomatoes ":250,"Flat-leaf parsley":15,"Garlic cloves":2,"Feta cheese":30,"Eggs":2},"name":"Tomato fritters"},{"ingredients":{"Puff pastry":1,"Mushrooms":250,"Onion":1,"Plain flour":2,"Butter":2,"Chicken breast fillets":2,"Milk":2},"name":"Chicken and mushroom pie"},{"ingredients":{"Prawns":500,"Mussels":500,"Onion":1,"Chicken stock":1,"Cherry tomatoes":250,"Vegetable oil":2,"Saffron":1,"Chorizo":100,"Garlic cloves":2,"Rice":1},"name":"Seafood paella"},{"ingredients":{"Salt":1,"Pepper":1,"Avocado":1,"Lemon":1,"Bread":2},"name":"Avocado toast"},{"ingredients":{"Dried rosemary":1,"Carrot":1,"Onion":1,"Dried parsley":1,"Chicken stock":1,"Dried basil":1,"Garlic cloves":2,"Dried thyme":1,"Parmesan cheese":1,"Celery":1,"Dried oregano":1,"Italian sausage":1,"Orzo":1},"name":"Italian sausage orzo soup"},{"ingredients":{"Mushrooms":250,"Onion":1,"Plain flour":2,"Butter":2,"Chicken breast fillets":2,"Pasta":1,"Garlic cloves":2,"Milk":2},"name":"Chicken and mushroom pasta"},{"ingredients":{"Salt":1,"Pepper":1,"Pasta":1,"Hummus":1,"Garlic cloves":2,"Lemon":1},"name":"Creamy Hummus Pasta"},{"ingredients":{"Cheddar cheese":1,"Potato":1,"Tomato sauce":1,"Onion":1,"Ground beef":1,"Tomato paste":1,"Garlic cloves":2,"Beef stock":1},"name":"Ground beef and potato casserole"},{"ingredients":{"Cheddar cheese":1,"Blue cheese":1,"Hot sauce":1,"Butter":2,"Chicken breast fillets":2,"Macaroni":1,"Flour":2,"Milk":2},"name":"Buffalo Chicken Mac \u0027n\u0027 Cheese"}] \ No newline at end of file