diff --git a/.idea/artifacts/homework_5_jar.xml b/.idea/artifacts/homework_5_jar.xml
new file mode 100644
index 000000000..147705cce
--- /dev/null
+++ b/.idea/artifacts/homework_5_jar.xml
@@ -0,0 +1,20 @@
+
+
+ $PROJECT_DIR$/out/artifacts/homework_5_jar
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 000000000..69d41be32
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 000000000..aa00ffab7
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
index 712ab9d98..947ef884c 100644
--- a/.idea/jarRepositories.xml
+++ b/.idea/jarRepositories.xml
@@ -6,6 +6,11 @@
+
+
+
+
+
diff --git a/.idea/material_theme_project_new.xml b/.idea/material_theme_project_new.xml
new file mode 100644
index 000000000..a9127f925
--- /dev/null
+++ b/.idea/material_theme_project_new.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 000000000..9e0563eb9
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 000000000..48c5ce071
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml
new file mode 100644
index 000000000..2b63946d5
--- /dev/null
+++ b/.idea/uiDesigner.xml
@@ -0,0 +1,124 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 000000000..35eb1ddfb
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index 8c4770018..907bd31ac 100644
--- a/README.md
+++ b/README.md
@@ -1,181 +1,145 @@
-# Lab 5: Logout
+# CSC207 Project - Recipe Generator
-## Preamble
-
-In the current homework, you added code to the login use case to save the currently-logged-in
-user by saving the user in the Data Access Layer. You also added a unit test for this.
-
-In this lab, you will complete a logout use case as a team. You will also begin to discuss your project
-and the use cases that need to be implemented.
-
-We have created all the Clean Architecture classes necessary for the logout use case.
-
-By Friday, your team will submit:
-- your completed lab code [for credit]
-- a draft of your project blueprint proposal. [required, but not for credit]
-
-# Phase 2 [for credit]
-_(recall, Phase 1 was your solo task of adding the storage of the currently-logged-in user)_
-
-## Task 0: Fork this repo on GitHub
-**To get started, one team member should fork this repo on GitHub and share it with the team.
-All of you should then clone it.**
-
-* * *
-
-Suggested logistics: One of you should invite the others to collaborate on their fork of the
-original repo on GitHub. You can do this in your repo on GitHub under `Settings -> Collaborators`.
-This will allow you to push branches to a common repo and then use pull requests to contribute
-your code and review. To prevent others from pushing directly to the main branch,
-we recommend you set branch protection rules on GitHub. Below are how the settings might look if you
-add branch protection rules:
-
-
-
-* * *
-
-Open the project in IntelliJ and make sure you can successfully run `app/Main.java`.
-Note: you may need to set the Project SDK in the `Project Structure...` menu, and possibly
-also manually link the Maven project, as you did in Phase 1.
-
-## Task 1: Understanding the Program
-
-You may notice that we have refactored the CA engine code _slightly_ since Phase 1, but the
-way we build the engine is drastically different: we have switched from using Factories to
-using the Builder design pattern, which we'll be discussing in lecture soon.
-
-Open up `app.Main` and read it as a team.
-- What are the Views and what are the current Use Cases?
-- Which Uses Cases are triggered from each View?
-- Which version of the DAO is `app.Main` using?
-
-The major change since Phase 1 is that we have added the `app.AppBuilder` class which makes
-it easier to understand how our CA engine is being constructed — it also makes `app.Main` nice and concise!
-- Why do all those `addX` methods end in `return this;`?
-
-Run the program and make sure the signup and login Use Cases work.
-
-Currently, you'll notice that the "Log Out" button still doesn't actually log you out. It's time to fix
-that button, which is part of the `LoggedInView`.
-We have created all the classes for you, but some of the code is missing.
-As a team, your task is to fill in the missing code so that the Logout Use Case is implemented.
-**The next part of the readme describes how your team will do this.**
-
-* * *
-
-**Your team will know when you are done when:**
-
-- Clicking the "Log Out" button takes the user back to the Login View when you use the program.
-- The provided `LogoutInteractorTest` test passes.
-
-The "Log Out" button is an instance variable in class `LoggedInVew`. Go find it.
-Also look at the `interface_adapter.change_password.LoggedInViewModel`, which contains any
-data showing on the `LoggedInVew`.
-
-* * *
-
-## Task 2: Dividing up the work
-
-There are `TODO` comments left in the files
-Recall that you can use the TODO tool window to conveniently pull up a complete list.
-
-Once the TODOs are all complete, the "Log Out" button _should_ work!
-
-As a team, split up the TODOs (see below) between the members of your team.
-
-There are TODOs in seven of the files.
-Make sure each member has at least one TODO which they will be responsible for completing.
-If your team prefers to work in pairs, that is fine too. Your individual branches
-will not be graded for this — only the final, working version.
-
-The TODOs are summarized below (by file) to help your team decide how to split them up:
-
-* * *
-
-- `Main.java`
-
- - [ ] TODO: add the Logout Use Case to the app using the appBuilder
-
-* * *
-
-- `LoggedInView.java` (tip: refer to the other views for similar code)
-
- - [ ] TODO: save the logout controller in the instance variable.
- - [ ] TODO: execute the logout use case through the Controller
-
-* * *
-
-- `LogoutController.java` (tip: refer to the other controllers for similar code)
-
- - [ ] TODO: Save the interactor in the instance variable.
- - [ ] TODO: run the use case interactor for the logout use case
-
-* * *
-
-- `LogoutInputData.java` (should be done with the LogoutInteractor TODOs below)
-
- - [ ] TODO: save the current username in an instance variable and add a getter.
-
-- `LogoutInteractor.java` (tip: refer to `ChangePasswordInteractor.java` for similar code)
-
- - [ ] TODO: save the DAO and Presenter in the instance variables.
- - [ ] TODO: implement the logic of the Logout Use Case
-
-* * *
-
-- `LogoutOutputData.java`
-
- - [ ] TODO: save the parameters in the instance variables.
-
-* * *
-
-- `LogoutPresenter.java` (tip: refer to `SignupPresenter.java` for similar code)
-
- - [ ] TODO: assign to the three instance variables.
- - [ ] TODO: have prepareSuccessView update the LoggedInState
- - [ ] TODO: have prepareSuccessView update the LoginState
-
-* * *
-
-1. Make a branch named the first part of your UofT email address, everything before the `@`.
-For example, if your email address is `paul.gries@mail.utoronto.ca`, then the branch name would
-be `paul.gries`.
-
-Make sure you switch to the new branch.
-
-In the terminal, this would look like below, but replaced with your own information:
-```
-git branch paul.gries
-git switch paul.gries
-```
-
-2. Complete your assigned TODOs and make a pull request on GitHub. In your pull request,
- briefly describe what your TODOs were and how you implemented them. If you aren't sure
- about part of it, include this in your pull request so everyone knows what to look
- for when reviewing — or you can of course discuss with your team before making your
- pull request since you are physically working in the same space.
- - **Important: don't push any changes to the `.idea` folder, as that
- may cause issues for your other teammates, as some files contain
- configurations specific to your individual IntelliJ projects.**
-
-3. Review each other's pull requests to ensure each TODO is correctly implemented and that
- there are no Checkstyle issues in the files that were modified.
-
-4. Once all TODOs are completed, your team should debug as needed to ensure the
- correctness of the code. Setting a breakpoint where the log-out use case
- interactor starts its work will likely be a great place to start when debugging.
-
-And that's it; you now have a working Logout Use Case! Instructions for
-how to submit your work on MarkUs will be posted later.
-
-Your team should spend the rest of the lab working on your project blueprint.
-
-* * *
-
-# Project Blueprint
-
-See Quercus for details about the project blueprint! By the end of the week,
-the goal is for your team to have a fully drafted blueprint so that your team
-will be ready to get started on your project after Reading Week.
+## Project Contributors
+
+Farshad Haddadi
+* GitHub username: farshad-haddadi
+* Email: farshad.haddadi@mail.utoronto.ca
+
+Hongcheng (Morgan)Huo
+* Github username: hhcgoodluck
+* Email: hhcgoodluck@outlook.com
+
+Sung-chi (William) Wu
+* GitHub username: wusungch
+* Email: sungchi2024@outlook.com
+
+Xinyu (Cindy) Zhang
+* GitHub username: Cindyzzz616
+* Email: cxy.zhang16@gmail.com
+
+Shuxin (Kate) Zhou
+* GitHub username: kayzoo8
+* Email: katezhou2005@gmail.com
+
+## Purpose
+This project generates a list of recipes from user-inputted ingredients (up to 20 char long). Users can focus on
+ingredients they have and generate recipes that meets their needs, rather than coming up with a specific dish
+that might use those ingredients. If the user had specific dietary needs and a unique ingredient, it may be difficult
+to know what to search for. Each recipe will have the option to display its full nutritional information for those
+who have specific health requirements. This project allows users to bookmark their favourite recipes as well as
+organize them into folders for easy access. Oftentimes, one may find a recipe but only be missing a couple of
+ingredients, our program can generate a shopping list for the user available for export. (*double check)
+
+## Table of Contents
+1) [Features](#features-of-the-project)
+2) [Installation Instructions](#installation-instructions)
+3) [Usage](#usage)
+4) [License](#license)
+5) [Feedback and Contributions](#feedback-and-contributions)
+
+
+## Features of the Project
+### User Stories (that each contributor was mainly responsible for)
+* **William**: The core feature of the program allows users to input ingredients they have on hand and receive
+recipe suggestions. Users can enter ingredients separated by commas, and the system will generate relevant
+recipes. If some ingredients are missing, the system can suggest close alternatives or highlight what else is
+needed. This feature helps users efficiently use the ingredients they already own, potentially reducing food
+waste and unnecessary shopping trips.
+
+
+* **Kate**: A key component of the system is the ability for users to filter recipes by diet and cuisine.
+This feature allows users to customize results based on dietary needs—such as vegan, gluten-free, or keto—or
+by preferred cuisines like Italian or Mexican. Users can apply multiple filters simultaneously to find recipes
+that meet both dietary restrictions and taste preferences. Managing these filters effectively ensures that the
+results remain useful even when dietary and cultural preferences overlap.
+
+
+* **Hongcheng**: Another feature provides detailed nutritional information for each recipe. Users can click a button
+to access data such as calorie counts, macronutrients (carbs, proteins, fats), and other key nutrients. This
+feature supports those who are tracking their nutrition or following specific dietary goals. It is important
+for the system to present this information clearly, accounting for potential variations in portion sizes and
+ingredient substitutions.
+
+
+* **Cindy**: The system will also allow users to bookmark recipes for future reference. This feature enables
+users to save recipes they find interesting or useful and organize them into categories like "Dinner" or
+"Favorites." Additionally, a "Recently Viewed" section can help users retrieve recipes they forgot to bookmark.
+
+
+* **Farshad**: Finally, the program offers a shopping list feature for adjacent recipes. When users are missing
+one or two ingredients for a recipe, the system will generate a shopping list with only the required items. This
+function can help users decide what to buy if they want to try new meals without doing extensive shopping.
+Export options, such as saving the list as a PDF or sharing it with other apps, enhance usability. The system
+will also need to distinguish between essential ingredients and common pantry staples to keep the lists concise.
+
+### Software
+This project closely follows Clean Architecture, so the program is divided from high-level to low-level layers,
+from entities to the database. This program makes use of the Spoonacular API to retrieve recipes and their
+details, like ingredients, nutrition, diet, etc. The Recipe Generator is displayed using the Java Swing GUI in
+a user-friendly manner with instructions at each step.
+
+
+
+## Installation Instructions
+Ensure that you have Java installed on your machine. This program was developed with the version Java 22, so
+ensure you are up-to-date. This program is guaranteed to run on macOS or Windows,
+other OS or hardware has not been tested. Fork the Recipe Generator repository by clicking the 'Fork' button
+on GitHub and select 'copy the `main` branch only'. Then, create a local copy by coping the web URL, open your
+preferred IDE, and create a new project from version control by pasting the link.
+
+The `pom.xml` file contains the project configuration. The IDE should have detected this file and run the
+maven commands. If src/main/java isn't 'blue' (or marked as the Sources Root) and src/test/java isn't 'green',
+you may need to right click `pom.xml` and select 'Maven -> Reload project'.
+
+## Usage
+To run the application, go to the `src` folder, then open up `main`, click `java`, then `app`, and run `Main.java`.
+The app will automatically load, then follow the prompts. Closing the application will close the
+entire program.
+
+## License
+This project has the Creative Commons License. See [LICENSE](LICENSE) for more information.
+
+## Feedback and Contributions
+Contributions are welcome! If you find any issues or have suggestions for improvement,
+please open an issue.
+
+If you wish to contribute, fork the repository by clicking the `Fork` button on GitHub
+and copy the `main` branch only. Then, create a local copy by coping the web URL, open your preferred IDE,
+and create a new project from version control by pasting the link.
+
+To provide feedback or suggestions, go to the `Issues` tab on GitHub to create an issue. Add a title highlighting
+your main points and a description of what you think could be changed. Feel free to mention specific files if
+you'd like.
+
+## Notes
+- ~~should the bookmarks and recently viewed lists of a user be stored in the user.json file?~~ - implemented
+- ~~individual recipe view only displays one ingredient right now~~ - fixed
+- add a find recipe by name search function in the main search page
+- ~~make ingredients clickable in individual recipe view? Or at least display the amounts~~
+- for the ingredient list in individual recipe view, split it into "used ingredients", "unused ingredients" and "missed ingredients" (right now there's only "missed ingredients")
+- ^ might be too complicated to do
+- when you double click on the recipes in the search view, you somehow get two copies of the individual recipe view??
+- ^ doesn't happen all the time tho
+- allow the user to choose how many recipes they want to see? Page flipping feature?
+- show random recipes feature?
+- ~~**allow users to create custom folders???**~~
+- allow users to press enter instead of using the search button?
+- ~~*add clickable url to individual recipe view*~~
+- allow users to select ingredients from individual recipe view and add them to their shopping list?
+- ^ would be difficult to implement
+- have an ingredient list where users can record the ingredients they have in their fridge
+- change background colour?
+- *make the recipes in shopping list view clickable*
+- display no recipes found in search view
+~~- add clear recently viewed button~~
+- modify how the close button works - so that you don't terminate the program when you close one window
+- ~~auto adjust window size~~
+- **add the fire property changed thingy so that the lists update in real time**
+- allow users to delete a bookmark
+
+### folder view ###
+- [X] add create folder user interface - either create a new button on home page or in BookmarkView
+- [X] add dropdown menu of folders in IndividualRecipeView - name it "add recipe to xxx folder" - or make it a checklist instead?
+- [X] add a save folder to json file thingy in UserDAOImpl
+- ~~make the whole thing a tag and filter system instead??? This would then entail changing the Recipe class~~
diff --git a/accessibility-report.md b/accessibility-report.md
new file mode 100644
index 000000000..a47a17ddc
--- /dev/null
+++ b/accessibility-report.md
@@ -0,0 +1,52 @@
+# Accessibility Report
+
+## Question 1
+*For each Principle of Universal Design, write 2-3 sentences — or point form notes — explaining which features of your program adhere to that principle. If you do not have any such features, you can either:*
+
+*(a) Describe features that you could implement in the future that would adhere to the principle or*
+*(b) Explain why the principle does not apply to a program like yours.*
+
+### Principle 1: Equitable Use
+- this software can be accessed from any smart device - it does not require any special hardware or plugins
+- we can potentially find an API that allows users to download the entire database of recipes, so that those without connection to internet can still use the software
+### Principle 2: Flexibility in Use
+- there are not a lot of parameters that a user can change currently...
+### Principle 3: Simple and Intuitive Use
+- the design of the software is quite minimal, and all the buttons, text fields and dropdown menus are labelled
+- since it does not have explicit instructions, this software might require the user to have some experience with softwares in general (and features such as signing in, searching and bookmarking). We can improve this by adding a user manual on the welcome page
+### Principle 4: Perceptible Information
+- this software has the potential of being incorporated with screen readers, so that it is accessible to those with vision impairments
+- in the future, we can add settings that allow users to adjust font size and background colour
+### Principle 5: Tolerance for Error
+- right now, empty entries on the login/signup and search pages would not crash the program
+- in the future, we can incorporate an autocorrect feature into the search function, so that users can still get results if they do not know how to spell an ingredient correctly
+### Principle 6: Low Physical Effort
+- this software is inclusive because it does not require a lot of physical effort to use
+- the shopping list feature can potentially be connected to grocery delivery services, so users who cannot purchase groceries themselves can still benefit from the recipe generator
+### Principle 7: Size and Space for Approach and Use
+- not applicable here since the software is not connect to any hardware
+
+## Question 2
+*Write a paragraph (3-6 sentences) about who you would market your program towards, if you were to sell or license your program to customers. This could be a specific category of users, such as "students", or more vague, such as "people who like games". Try to give a bit more detail along with the category.*
+
+This software could be market towards a variety of users who have a similar need - to find recipes based on the ingredients
+they had on hand. Users might seek out this program for a variety of reasons: for example, university students learning to cook
+would find it useful, since they might not know what dishes can be made from the ingredients they have. They are also likely
+to need instructions when cooking, which the software can provide by opening details of recipes in a browsers. This software
+could also be helpful for busy professionals or parents who are too busy for meal planning. They can use the software to find
+last-minute recipes they can make. People exploring new cuisines and recipes can also benefit from this software, since it
+provides features like filtering by cuisines. Finally, this software is useful to those on special diets or are conscious about
+nutrition, since it allows users to filter recipes by diet and view the nutrition breakdown of each recipe.
+
+## Question 3
+*Write a paragraph about whether or not your program is less likely to be used by certain demographics. Your discussion here should be informed by the content of our embedded ethics modules this term.*
+
+This software could be challenging to use by older people who are not familiar with technology or who might prefer traditional
+recipes. They might suffer more tangible harm from the lack of access to practical recipes, since it is more likely for them to
+have mobility impairments and thus a more limited stock of ingredients. Those with visual impairments might also have difficulty
+using the software, since it does not currently have features such as speech-to-text and alternate captions, which allow for
+hands-free usage. This is an intervention that stems from a social model of disability, since it would also allow users to use
+the software when their hands are occupied with cooking. Furthermore, the software might not be favoured by people form cultural
+groups whose cuisines are not well-represented in the recipe database from Spoonacular. This potentially causes relational harm,
+since users from those cultural groups might feel like their cuisines are marginalized. A similar reasoning applies for users
+with religious dietary restrictions, since there are no filters for categories such as halal and kosher.
diff --git a/homework-5.iml b/homework-5.iml
new file mode 100644
index 000000000..ffc2b2afe
--- /dev/null
+++ b/homework-5.iml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/images/Welcome_Homepage.png b/images/Welcome_Homepage.png
new file mode 100644
index 000000000..66d56da1b
Binary files /dev/null and b/images/Welcome_Homepage.png differ
diff --git a/src/main/java/API Key b/src/main/java/API Key
new file mode 100644
index 000000000..1f0b8b93e
--- /dev/null
+++ b/src/main/java/API Key
@@ -0,0 +1 @@
+38143f425a9f4698bd2e63550825a4ce
\ No newline at end of file
diff --git a/src/main/java/SpoonacularExample.java b/src/main/java/SpoonacularExample.java
new file mode 100644
index 000000000..85c109dea
--- /dev/null
+++ b/src/main/java/SpoonacularExample.java
@@ -0,0 +1,45 @@
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import org.json.JSONArray;
+import org.json.JSONObject;
+import java.io.IOException;
+
+public class SpoonacularExample {
+ private static final String API_KEY = "5d06ae7be18746aaa4d1f1e5877f4450";
+ private static final String BASE_URL = "https://api.spoonacular.com";
+
+ public static void main(String[] args) {
+ final OkHttpClient client = new OkHttpClient();
+
+ // Define the endpoint and query parameters
+ final String endpoint = "/recipes/complexSearch";
+ // This is where we specify the query (ingredients we have, number of recipes we want, cuisine, etc.)
+ final String url = BASE_URL + endpoint + "?apiKey=" + API_KEY + "&query=pork&number=5&cuisine=chinese";
+
+ // Build the request
+ final Request request = new Request.Builder()
+ .url(url)
+ .build();
+
+ try (Response response = client.newCall(request).execute()) {
+ if (response.isSuccessful() && response.body() != null) {
+ // Parse the JSON response
+ final String jsonResponse = response.body().string();
+ final JSONObject jsonObject = new JSONObject(jsonResponse);
+ final JSONArray results = jsonObject.getJSONArray("results");
+
+ // Loop through the results and print recipe titles and IDs
+ for (int i = 0; i < results.length(); i++) {
+ final JSONObject recipe = results.getJSONObject(i);
+ }
+ }
+ else {
+ System.out.println("Request failed with code: " + response.code());
+ }
+ }
+ catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/main/java/app/AppBuilder.java b/src/main/java/app/AppBuilder.java
index e9eef5c81..7c54509ce 100644
--- a/src/main/java/app/AppBuilder.java
+++ b/src/main/java/app/AppBuilder.java
@@ -1,185 +1,170 @@
-package app;
-
-import java.awt.CardLayout;
-
-import javax.swing.JFrame;
-import javax.swing.JPanel;
-import javax.swing.WindowConstants;
-
-import data_access.InMemoryUserDataAccessObject;
-import entity.CommonUserFactory;
-import entity.UserFactory;
-import interface_adapter.ViewManagerModel;
-import interface_adapter.change_password.ChangePasswordController;
-import interface_adapter.change_password.ChangePasswordPresenter;
-import interface_adapter.change_password.LoggedInViewModel;
-import interface_adapter.login.LoginController;
-import interface_adapter.login.LoginPresenter;
-import interface_adapter.login.LoginViewModel;
-import interface_adapter.logout.LogoutController;
-import interface_adapter.logout.LogoutPresenter;
-import interface_adapter.signup.SignupController;
-import interface_adapter.signup.SignupPresenter;
-import interface_adapter.signup.SignupViewModel;
-import use_case.change_password.ChangePasswordInputBoundary;
-import use_case.change_password.ChangePasswordInteractor;
-import use_case.change_password.ChangePasswordOutputBoundary;
-import use_case.login.LoginInputBoundary;
-import use_case.login.LoginInteractor;
-import use_case.login.LoginOutputBoundary;
-import use_case.logout.LogoutInputBoundary;
-import use_case.logout.LogoutInteractor;
-import use_case.logout.LogoutOutputBoundary;
-import use_case.signup.SignupInputBoundary;
-import use_case.signup.SignupInteractor;
-import use_case.signup.SignupOutputBoundary;
-import view.LoggedInView;
-import view.LoginView;
-import view.SignupView;
-import view.ViewManager;
-
-/**
- * The AppBuilder class is responsible for putting together the pieces of
- * our CA architecture; piece by piece.
- *
- * This is done by adding each View and then adding related Use Cases.
- */
-// Checkstyle note: you can ignore the "Class Data Abstraction Coupling"
-// and the "Class Fan-Out Complexity" issues for this lab; we encourage
-// your team to think about ways to refactor the code to resolve these
-// if your team decides to work with this as your starter code
-// for your final project this term.
-public class AppBuilder {
- private final JPanel cardPanel = new JPanel();
- private final CardLayout cardLayout = new CardLayout();
- // thought question: is the hard dependency below a problem?
- private final UserFactory userFactory = new CommonUserFactory();
- private final ViewManagerModel viewManagerModel = new ViewManagerModel();
- private final ViewManager viewManager = new ViewManager(cardPanel, cardLayout, viewManagerModel);
-
- // thought question: is the hard dependency below a problem?
- private final InMemoryUserDataAccessObject userDataAccessObject = new InMemoryUserDataAccessObject();
-
- private SignupView signupView;
- private SignupViewModel signupViewModel;
- private LoginViewModel loginViewModel;
- private LoggedInViewModel loggedInViewModel;
- private LoggedInView loggedInView;
- private LoginView loginView;
-
- public AppBuilder() {
- cardPanel.setLayout(cardLayout);
- }
-
- /**
- * Adds the Signup View to the application.
- * @return this builder
- */
- public AppBuilder addSignupView() {
- signupViewModel = new SignupViewModel();
- signupView = new SignupView(signupViewModel);
- cardPanel.add(signupView, signupView.getViewName());
- return this;
- }
-
- /**
- * Adds the Login View to the application.
- * @return this builder
- */
- public AppBuilder addLoginView() {
- loginViewModel = new LoginViewModel();
- loginView = new LoginView(loginViewModel);
- cardPanel.add(loginView, loginView.getViewName());
- return this;
- }
-
- /**
- * Adds the LoggedIn View to the application.
- * @return this builder
- */
- public AppBuilder addLoggedInView() {
- loggedInViewModel = new LoggedInViewModel();
- loggedInView = new LoggedInView(loggedInViewModel);
- cardPanel.add(loggedInView, loggedInView.getViewName());
- return this;
- }
-
- /**
- * Adds the Signup Use Case to the application.
- * @return this builder
- */
- public AppBuilder addSignupUseCase() {
- final SignupOutputBoundary signupOutputBoundary = new SignupPresenter(viewManagerModel,
- signupViewModel, loginViewModel);
- final SignupInputBoundary userSignupInteractor = new SignupInteractor(
- userDataAccessObject, signupOutputBoundary, userFactory);
-
- final SignupController controller = new SignupController(userSignupInteractor);
- signupView.setSignupController(controller);
- return this;
- }
-
- /**
- * Adds the Login Use Case to the application.
- * @return this builder
- */
- public AppBuilder addLoginUseCase() {
- final LoginOutputBoundary loginOutputBoundary = new LoginPresenter(viewManagerModel,
- loggedInViewModel, loginViewModel);
- final LoginInputBoundary loginInteractor = new LoginInteractor(
- userDataAccessObject, loginOutputBoundary);
-
- final LoginController loginController = new LoginController(loginInteractor);
- loginView.setLoginController(loginController);
- return this;
- }
-
- /**
- * Adds the Change Password Use Case to the application.
- * @return this builder
- */
- public AppBuilder addChangePasswordUseCase() {
- final ChangePasswordOutputBoundary changePasswordOutputBoundary =
- new ChangePasswordPresenter(loggedInViewModel);
-
- final ChangePasswordInputBoundary changePasswordInteractor =
- new ChangePasswordInteractor(userDataAccessObject, changePasswordOutputBoundary, userFactory);
-
- final ChangePasswordController changePasswordController =
- new ChangePasswordController(changePasswordInteractor);
- loggedInView.setChangePasswordController(changePasswordController);
- return this;
- }
-
- /**
- * Adds the Logout Use Case to the application.
- * @return this builder
- */
- public AppBuilder addLogoutUseCase() {
- final LogoutOutputBoundary logoutOutputBoundary = new LogoutPresenter(viewManagerModel,
- loggedInViewModel, loginViewModel);
-
- final LogoutInputBoundary logoutInteractor =
- new LogoutInteractor(userDataAccessObject, logoutOutputBoundary);
-
- final LogoutController logoutController = new LogoutController(logoutInteractor);
- loggedInView.setLogoutController(logoutController);
- return this;
- }
-
- /**
- * Creates the JFrame for the application and initially sets the SignupView to be displayed.
- * @return the application
- */
- public JFrame build() {
- final JFrame application = new JFrame("Login Example");
- application.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
-
- application.add(cardPanel);
-
- viewManagerModel.setState(signupView.getViewName());
- viewManagerModel.firePropertyChanged();
-
- return application;
- }
-}
+//package app;
+//
+//import data_access.UserDAOImpl;
+//import interface_adapter.RecipeListViewModel;
+//import interface_adapter.ViewManagerModel;
+//import interface_adapter.search_recipe_list_by_ingredient.SearchRecipeListByIngredientController;
+//import interface_adapter.search_recipe_list_by_ingredient.SearchRecipeListByIngredientPresenter;
+//import interface_adapter.search_recipe_list_by_name.SearchRecipeListByNameController;
+//import interface_adapter.search_recipe_list_by_name.SearchRecipeListByNamePresenter;
+//import use_case.search_recipe_list_by_ingredient.SearchRecipeListByIngredientInputBoundary;
+//import use_case.search_recipe_list_by_ingredient.SearchRecipeListByIngredientInteractor;
+//import use_case.search_recipe_list_by_ingredient.SearchRecipeListByIngredientOutputBoundary;
+//import use_case.search_recipe_list_by_name.SearchRecipeListByNameInputBoundary;
+//import use_case.search_recipe_list_by_name.SearchRecipeListByNameInteractor;
+//import use_case.search_recipe_list_by_name.SearchRecipeListByNameOutputBoundary;
+//import view.*;
+//
+//import java.awt.CardLayout;
+//
+//import javax.swing.JFrame;
+//import javax.swing.JPanel;
+//import javax.swing.WindowConstants;
+//
+///**
+// * The AppBuilder class is responsible for putting together the pieces of
+// * our CA architecture; piece by piece.
+// *
+// * This is done by adding each View and then adding related Use Cases.
+// */
+//public class AppBuilder {
+// private final JPanel cardPanel = new JPanel();
+// private final CardLayout cardLayout = new CardLayout();
+// private final ViewManagerModel viewManagerModel = new ViewManagerModel();
+// private final ViewManager viewManager = new ViewManager(cardPanel, cardLayout, viewManagerModel);
+//
+// private final UserDAOImpl userDataAccessObject = new UserDAOImpl();
+//
+// private BookmarkView bookmarkView;
+// private FolderView folderView;
+// private HomePage homepage;
+// private IndividualRecipeView individualRecipeView;
+// private LoginSignupPage loginSignupPage;
+// private NutritionInformationView nutritionInformationView;
+// private RecentlyViewedView recentlyViewedView;
+// private RecipeListView recipeListView;
+// private RecipeView recipeView;
+// private ShoppingListGUI shoppingListGUI;
+// private RecipeListViewModel recipeListViewModel;
+//
+// public AppBuilder() {
+// cardPanel.setLayout(cardLayout);
+// }
+//
+// /**
+// * Adds the Bookmark View to the application.
+// * @return this builder
+// */
+// public AppBuilder addBookmarkView() {
+// recipeListViewModel = new RecipeListViewModel();
+// // TODO figure out how to remove user
+// bookmarkView = new BookmarkView(user, "bookmarks", recipeListViewModel);
+// cardPanel.add(bookmarkView);
+// return this;
+// }
+//
+//// /**
+//// * Adds the Login View to the application.
+//// * @return this builder
+//// */
+//// public AppBuilder addLoginView() {
+//// loginViewModel = new LoginViewModel();
+//// loginView = new LoginView(loginViewModel);
+//// cardPanel.add(loginView, loginView.getViewName());
+//// return this;
+//// }
+////
+//// /**
+//// * Adds the LoggedIn View to the application.
+//// * @return this builder
+//// */
+//// public AppBuilder addLoggedInView() {
+//// loggedInViewModel = new LoggedInViewModel();
+//// loggedInView = new LoggedInView(loggedInViewModel);
+//// cardPanel.add(loggedInView, loggedInView.getViewName());
+//// return this;
+//// }
+//
+// /**
+// * Adds the Signup Use Case to the application.
+// * @return this builder
+// */
+// public AppBuilder addSearchRecipeListByIngredientUseCase() {
+// final SearchRecipeListByIngredientOutputBoundary searchRecipeListByIngredientOutputBoundary =
+// new SearchRecipeListByIngredientPresenter(recipeListViewModel);
+// final SearchRecipeListByIngredientInputBoundary searchRecipeListByIngredientInteractor =
+// new SearchRecipeListByIngredientInteractor(
+// userDataAccessObject, searchRecipeListByIngredientOutputBoundary);
+//
+// final SearchRecipeListByIngredientController controller =
+// new SearchRecipeListByIngredientController(searchRecipeListByIngredientInteractor);
+// recipeListView.setSearchRecipeListByIngredientController(controller);
+// return this;
+// }
+//
+// /**
+// * Adds the Login Use Case to the application.
+// * @return this builder
+// */
+// public AppBuilder addSearchRecipeListByNameUseCase() {
+// final SearchRecipeListByNameOutputBoundary searchRecipeListByNameOutputBoundary =
+// new SearchRecipeListByNamePresenter(recipeListViewModel);
+// final SearchRecipeListByNameInputBoundary searchRecipeListByNameInteractor =
+// new SearchRecipeListByNameInteractor(
+// userDataAccessObject, searchRecipeListByNameOutputBoundary);
+//
+// final SearchRecipeListByNameController controller =
+// new SearchRecipeListByNameController(searchRecipeListByNameInteractor);
+// recipeListView.setSearchRecipeListByNameController(controller);
+// return this;
+// }
+//
+//// /**
+//// * Adds the Change Password Use Case to the application.
+//// * @return this builder
+//// */
+//// public AppBuilder addChangePasswordUseCase() {
+//// final ChangePasswordOutputBoundary changePasswordOutputBoundary =
+//// new ChangePasswordPresenter(loggedInViewModel);
+////
+//// final ChangePasswordInputBoundary changePasswordInteractor =
+//// new ChangePasswordInteractor(userDataAccessObject, changePasswordOutputBoundary, userFactory);
+////
+//// final ChangePasswordController changePasswordController =
+//// new ChangePasswordController(changePasswordInteractor);
+//// loggedInView.setChangePasswordController(changePasswordController);
+//// return this;
+//// }
+////
+//// /**
+//// * Adds the Logout Use Case to the application.
+//// * @return this builder
+//// */
+//// public AppBuilder addLogoutUseCase() {
+//// final LogoutOutputBoundary logoutOutputBoundary = new LogoutPresenter(viewManagerModel,
+//// loggedInViewModel, loginViewModel);
+////
+//// final LogoutInputBoundary logoutInteractor =
+//// new LogoutInteractor(userDataAccessObject, logoutOutputBoundary);
+////
+//// final LogoutController logoutController = new LogoutController(logoutInteractor);
+//// loggedInView.setLogoutController(logoutController);
+//// return this;
+//// }
+//
+// /**
+// * Creates the JFrame for the application and initially sets the SignupView to be displayed.
+// * @return the application
+// */
+// public JFrame build() {
+// final JFrame application = new JFrame("Recipe Generator");
+// application.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
+//
+// application.add(cardPanel);
+//
+// viewManagerModel.setState(homepage.getName());
+// viewManagerModel.firePropertyChanged();
+//
+// return application;
+// }
+//}
diff --git a/src/main/java/app/Main.java b/src/main/java/app/Main.java
index bef63ad7a..9e09ffc77 100644
--- a/src/main/java/app/Main.java
+++ b/src/main/java/app/Main.java
@@ -1,28 +1,24 @@
package app;
-import javax.swing.JFrame;
+// import view.HomePage;
+import view.LoginSignupPage;
/**
- * The Main class of our application.
+ * This is where the application starts.
*/
public class Main {
+
/**
- * Builds and runs the CA architecture of the application.
- * @param args unused arguments
+ * The entry point of the application.
+ *
+ * @param args command-line arguments (not used in this application).
*/
public static void main(String[] args) {
- final AppBuilder appBuilder = new AppBuilder();
- // TODO: add the Logout Use Case to the app using the appBuilder
- final JFrame application = appBuilder
- .addLoginView()
- .addSignupView()
- .addLoggedInView()
- .addSignupUseCase()
- .addLoginUseCase()
- .addChangePasswordUseCase()
- .build();
+ // Launch the login/signup page as the first view
+ new LoginSignupPage();
- application.pack();
- application.setVisible(true);
+ // Simulate successful login/signup
+ // Uncomment and use this logic after user authentication is implemented
+ // new HomePage();
}
}
diff --git a/src/main/java/data_access/AppConstants.java b/src/main/java/data_access/AppConstants.java
new file mode 100644
index 000000000..b658f7c75
--- /dev/null
+++ b/src/main/java/data_access/AppConstants.java
@@ -0,0 +1,16 @@
+package data_access;
+
+public final class AppConstants {
+
+ public static final String API_KEY = "9c13038e18cc407ca440bd756225cd13";
+ public static final String NUMBER_OF_RESULTS = "5";
+ public static final String FONT = "Arial";
+ public static final int WELCOME_FONT_SIZE = 18;
+ public static final int BUTTON_FONT_SIZE = 16;
+ public static final int RESPONSE_CODE_200 = 200;
+
+ // Prevent instantiation
+ private AppConstants() {
+ throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/data_access/DBUserDataAccessObject.java b/src/main/java/data_access/DBUserDataAccessObject.java
deleted file mode 100644
index 377ee6e7e..000000000
--- a/src/main/java/data_access/DBUserDataAccessObject.java
+++ /dev/null
@@ -1,165 +0,0 @@
-package data_access;
-
-import java.io.IOException;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import entity.User;
-import entity.UserFactory;
-import okhttp3.MediaType;
-import okhttp3.OkHttpClient;
-import okhttp3.Request;
-import okhttp3.RequestBody;
-import okhttp3.Response;
-import use_case.change_password.ChangePasswordUserDataAccessInterface;
-import use_case.login.LoginUserDataAccessInterface;
-import use_case.logout.LogoutUserDataAccessInterface;
-import use_case.signup.SignupUserDataAccessInterface;
-
-/**
- * The DAO for user data.
- */
-public class DBUserDataAccessObject implements SignupUserDataAccessInterface,
- LoginUserDataAccessInterface,
- ChangePasswordUserDataAccessInterface,
- LogoutUserDataAccessInterface {
- private static final int SUCCESS_CODE = 200;
- private static final String CONTENT_TYPE_LABEL = "Content-Type";
- private static final String CONTENT_TYPE_JSON = "application/json";
- private static final String STATUS_CODE_LABEL = "status_code";
- private static final String USERNAME = "username";
- private static final String PASSWORD = "password";
- private static final String MESSAGE = "message";
- private final UserFactory userFactory;
-
- public DBUserDataAccessObject(UserFactory userFactory) {
- this.userFactory = userFactory;
- // No need to do anything to reinitialize a user list! The data is the cloud that may be miles away.
- }
-
- @Override
- public User get(String username) {
- // Make an API call to get the user object.
- final OkHttpClient client = new OkHttpClient().newBuilder().build();
- final Request request = new Request.Builder()
- .url(String.format("http://vm003.teach.cs.toronto.edu:20112/user?username=%s", username))
- .addHeader("Content-Type", CONTENT_TYPE_JSON)
- .build();
- try {
- final Response response = client.newCall(request).execute();
-
- final JSONObject responseBody = new JSONObject(response.body().string());
-
- if (responseBody.getInt(STATUS_CODE_LABEL) == SUCCESS_CODE) {
- final JSONObject userJSONObject = responseBody.getJSONObject("user");
- final String name = userJSONObject.getString(USERNAME);
- final String password = userJSONObject.getString(PASSWORD);
-
- return userFactory.create(name, password);
- }
- else {
- throw new RuntimeException(responseBody.getString(MESSAGE));
- }
- }
- catch (IOException | JSONException ex) {
- throw new RuntimeException(ex);
- }
- }
-
- @Override
- public void setCurrentUsername(String name) {
- // this isn't implemented for the lab
- }
-
- @Override
- public boolean existsByName(String username) {
- final OkHttpClient client = new OkHttpClient().newBuilder()
- .build();
- final Request request = new Request.Builder()
- .url(String.format("http://vm003.teach.cs.toronto.edu:20112/checkIfUserExists?username=%s", username))
- .addHeader(CONTENT_TYPE_LABEL, CONTENT_TYPE_JSON)
- .build();
- try {
- final Response response = client.newCall(request).execute();
-
- final JSONObject responseBody = new JSONObject(response.body().string());
-
- return responseBody.getInt(STATUS_CODE_LABEL) == SUCCESS_CODE;
- }
- catch (IOException | JSONException ex) {
- throw new RuntimeException(ex);
- }
- }
-
- @Override
- public void save(User user) {
- final OkHttpClient client = new OkHttpClient().newBuilder()
- .build();
-
- // POST METHOD
- final MediaType mediaType = MediaType.parse(CONTENT_TYPE_JSON);
- final JSONObject requestBody = new JSONObject();
- requestBody.put(USERNAME, user.getName());
- requestBody.put(PASSWORD, user.getPassword());
- final RequestBody body = RequestBody.create(requestBody.toString(), mediaType);
- final Request request = new Request.Builder()
- .url("http://vm003.teach.cs.toronto.edu:20112/user")
- .method("POST", body)
- .addHeader(CONTENT_TYPE_LABEL, CONTENT_TYPE_JSON)
- .build();
- try {
- final Response response = client.newCall(request).execute();
-
- final JSONObject responseBody = new JSONObject(response.body().string());
-
- if (responseBody.getInt(STATUS_CODE_LABEL) == SUCCESS_CODE) {
- // success!
- }
- else {
- throw new RuntimeException(responseBody.getString(MESSAGE));
- }
- }
- catch (IOException | JSONException ex) {
- throw new RuntimeException(ex);
- }
- }
-
- @Override
- public void changePassword(User user) {
- final OkHttpClient client = new OkHttpClient().newBuilder()
- .build();
-
- // POST METHOD
- final MediaType mediaType = MediaType.parse(CONTENT_TYPE_JSON);
- final JSONObject requestBody = new JSONObject();
- requestBody.put(USERNAME, user.getName());
- requestBody.put(PASSWORD, user.getPassword());
- final RequestBody body = RequestBody.create(requestBody.toString(), mediaType);
- final Request request = new Request.Builder()
- .url("http://vm003.teach.cs.toronto.edu:20112/user")
- .method("PUT", body)
- .addHeader(CONTENT_TYPE_LABEL, CONTENT_TYPE_JSON)
- .build();
- try {
- final Response response = client.newCall(request).execute();
-
- final JSONObject responseBody = new JSONObject(response.body().string());
-
- if (responseBody.getInt(STATUS_CODE_LABEL) == SUCCESS_CODE) {
- // success!
- }
- else {
- throw new RuntimeException(responseBody.getString(MESSAGE));
- }
- }
- catch (IOException | JSONException ex) {
- throw new RuntimeException(ex);
- }
- }
-
- @Override
- public String getCurrentUsername() {
- return null;
- }
-}
diff --git a/src/main/java/data_access/FileUserDataAccessObject.java b/src/main/java/data_access/FileUserDataAccessObject.java
deleted file mode 100644
index d301a3241..000000000
--- a/src/main/java/data_access/FileUserDataAccessObject.java
+++ /dev/null
@@ -1,117 +0,0 @@
-package data_access;
-
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileReader;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-import entity.User;
-import entity.UserFactory;
-import use_case.change_password.ChangePasswordUserDataAccessInterface;
-import use_case.login.LoginUserDataAccessInterface;
-import use_case.signup.SignupUserDataAccessInterface;
-
-/**
- * DAO for user data implemented using a File to persist the data.
- */
-public class FileUserDataAccessObject implements SignupUserDataAccessInterface,
- LoginUserDataAccessInterface,
- ChangePasswordUserDataAccessInterface {
-
- private static final String HEADER = "username,password";
-
- private final File csvFile;
- private final Map headers = new LinkedHashMap<>();
- private final Map accounts = new HashMap<>();
- private String currentUsername;
-
- public FileUserDataAccessObject(String csvPath, UserFactory userFactory) throws IOException {
-
- csvFile = new File(csvPath);
- headers.put("username", 0);
- headers.put("password", 1);
-
- if (csvFile.length() == 0) {
- save();
- }
- else {
-
- try (BufferedReader reader = new BufferedReader(new FileReader(csvFile))) {
- final String header = reader.readLine();
-
- if (!header.equals(HEADER)) {
- throw new RuntimeException(String.format("header should be%n: %s%but was:%n%s", HEADER, header));
- }
-
- String row;
- while ((row = reader.readLine()) != null) {
- final String[] col = row.split(",");
- final String username = String.valueOf(col[headers.get("username")]);
- final String password = String.valueOf(col[headers.get("password")]);
- final User user = userFactory.create(username, password);
- accounts.put(username, user);
- }
- }
- }
- }
-
- private void save() {
- final BufferedWriter writer;
- try {
- writer = new BufferedWriter(new FileWriter(csvFile));
- writer.write(String.join(",", headers.keySet()));
- writer.newLine();
-
- for (User user : accounts.values()) {
- final String line = String.format("%s,%s",
- user.getName(), user.getPassword());
- writer.write(line);
- writer.newLine();
- }
-
- writer.close();
-
- }
- catch (IOException ex) {
- throw new RuntimeException(ex);
- }
- }
-
- @Override
- public void save(User user) {
- accounts.put(user.getName(), user);
- this.save();
- }
-
- @Override
- public User get(String username) {
- return accounts.get(username);
- }
-
- @Override
- public void setCurrentUsername(String name) {
- this.currentUsername = name;
- }
-
- @Override
- public String getCurrentUsername() {
- return this.currentUsername;
- }
-
- @Override
- public boolean existsByName(String identifier) {
- return accounts.containsKey(identifier);
- }
-
- @Override
- public void changePassword(User user) {
- // Replace the User object in the map
- accounts.put(user.getName(), user);
- save();
- }
-}
diff --git a/src/main/java/data_access/InMemoryFilterRecipesDataAccessObject.java b/src/main/java/data_access/InMemoryFilterRecipesDataAccessObject.java
new file mode 100644
index 000000000..7b5c36c7f
--- /dev/null
+++ b/src/main/java/data_access/InMemoryFilterRecipesDataAccessObject.java
@@ -0,0 +1,57 @@
+package data_access;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import entity.Ingredient;
+import entity.Recipe;
+import use_case.filter_recipes.FilterRecipesDataAccessInterface;
+
+/**
+ * In-memory implementation of the DAO for getting filtered recipes.
+ */
+public class InMemoryFilterRecipesDataAccessObject implements FilterRecipesDataAccessInterface {
+
+// private List ingredients;
+// private String diet;
+// private String cuisine;
+
+ @Override
+ public List filterSearchRecipes(List ingredients, String diet, String cuisine) {
+ final List recipes = new ArrayList<>();
+ final List ingredientList = new ArrayList<>();
+ for (String item : ingredients) {
+ final Ingredient ingredient = new Ingredient(item, 1.0, "cup");
+ ingredientList.add(ingredient);
+ }
+ if (("vegetarian".equals(diet)) && ("chinese".equals(cuisine))) {
+ recipes.add(new Recipe("veggie fried rice", "url1", ingredientList, "image1"));
+ }
+ else if (("vegan".equals(diet)) && ("italian".equals(cuisine))) {
+ recipes.add(new Recipe("vegan pasta", "url2", ingredientList, "image2"));
+ }
+ else {
+ recipes.add(new Recipe("sandwich", "url3", ingredientList, "image3"));
+ }
+ return recipes;
+ }
+
+ @Override
+ public List getAvailableDiets() {
+ final List diets = new ArrayList<>();
+ diets.add("vegetarian");
+ diets.add("vegan");
+ diets.add("ketogenic");
+ return diets;
+ }
+
+ @Override
+ public List getAvailableCuisines() {
+ final List cuisines = new ArrayList<>();
+ cuisines.add("italian");
+ cuisines.add("chinese");
+ cuisines.add("indian");
+ return cuisines;
+ }
+
+}
diff --git a/src/main/java/data_access/InMemorySearchRecipeListDataAccessObject.java b/src/main/java/data_access/InMemorySearchRecipeListDataAccessObject.java
new file mode 100644
index 000000000..aaa69d932
--- /dev/null
+++ b/src/main/java/data_access/InMemorySearchRecipeListDataAccessObject.java
@@ -0,0 +1,86 @@
+package data_access;
+
+import entity.Ingredient;
+import entity.Recipe;
+import entity.User;
+import use_case.search_recipe_list_by_ingredient.SearchRecipeListByIngredientDataAccessInterface;
+import use_case.search_recipe_list_by_name.SearchRecipeListByNameDataAccessInterface;
+
+import java.util.*;
+
+public class InMemorySearchRecipeListDataAccessObject implements SearchRecipeListByIngredientDataAccessInterface,
+ SearchRecipeListByNameDataAccessInterface {
+ private final Map users = new HashMap<>();
+
+ private String currentUsername;
+
+ @Override
+ public List searchRecipeListByIngredient(List ingredients, User user, String folder) {
+ final User userFromFile = users.get(user.getUsername());
+ List recipeList = new ArrayList<>();
+ final List results = new ArrayList<>();
+ if ("bookmarks".equals(folder)) {
+ recipeList.addAll(userFromFile.getBookmarks());
+ }
+ else if ("recentlyViewed".equals(folder)) {
+ recipeList.addAll(userFromFile.getRecentlyViewed());
+ }
+ else {
+ recipeList.addAll(userFromFile.getFolder(folder));
+ }
+ for (Recipe recipe : recipeList) {
+ boolean match = false;
+ final List recipeIngredients = recipe.getIngredients();
+ final List recipeIngredientsString = new ArrayList<>();
+ for (Ingredient ingredient : recipeIngredients) {
+ final String[] words = ingredient.getName().split(" ");
+ recipeIngredientsString.addAll(Arrays.asList(words));
+ }
+ for (String recipeIngredient : recipeIngredientsString) {
+ for (String enteredIngredient : ingredients) {
+ if (recipeIngredient.equalsIgnoreCase(enteredIngredient)) {
+ match = true;
+ }
+ }
+
+ }
+ if (match) {
+ results.add(recipe);
+ }
+ }
+ return results;
+ }
+
+ @Override
+ public List searchRecipeListByName(String recipeName, User user, String folder) {
+ final User userFromFile = users.get(user.getUsername());
+ final List results = new ArrayList<>();
+
+ List recipeList = new ArrayList<>();
+ if ("bookmarks".equals(folder)) {
+ recipeList.addAll(userFromFile.getBookmarks());
+ }
+ else if ("recentlyViewed".equals(folder)) {
+ recipeList.addAll(userFromFile.getRecentlyViewed());
+ }
+ else {
+ recipeList.addAll(userFromFile.getFolder(folder));
+ }
+
+ for (Recipe recipe : recipeList) {
+ final String[] words = recipe.getName().split(" ");
+ for (String word : words) {
+ if (word.equalsIgnoreCase(recipeName)) {
+ results.add(recipe);
+ }
+ }
+ }
+ return results;
+ }
+
+ @Override
+ public boolean addUser(User user) {
+ users.put(user.getUsername(), user);
+ return true;
+ }
+}
diff --git a/src/main/java/data_access/InMemoryUserDataAccessObject.java b/src/main/java/data_access/InMemoryUserDataAccessObject.java
deleted file mode 100644
index 71f00862c..000000000
--- a/src/main/java/data_access/InMemoryUserDataAccessObject.java
+++ /dev/null
@@ -1,55 +0,0 @@
-package data_access;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import entity.User;
-import use_case.change_password.ChangePasswordUserDataAccessInterface;
-import use_case.login.LoginUserDataAccessInterface;
-import use_case.logout.LogoutUserDataAccessInterface;
-import use_case.signup.SignupUserDataAccessInterface;
-
-/**
- * In-memory implementation of the DAO for storing user data. This implementation does
- * NOT persist data between runs of the program.
- */
-public class InMemoryUserDataAccessObject implements SignupUserDataAccessInterface,
- LoginUserDataAccessInterface,
- ChangePasswordUserDataAccessInterface,
- LogoutUserDataAccessInterface {
-
- private final Map users = new HashMap<>();
-
- private String currentUsername;
-
- @Override
- public boolean existsByName(String identifier) {
- return users.containsKey(identifier);
- }
-
- @Override
- public void save(User user) {
- users.put(user.getName(), user);
- }
-
- @Override
- public User get(String username) {
- return users.get(username);
- }
-
- @Override
- public void changePassword(User user) {
- // Replace the old entry with the new password
- users.put(user.getName(), user);
- }
-
- @Override
- public void setCurrentUsername(String name) {
- this.currentUsername = name;
- }
-
- @Override
- public String getCurrentUsername() {
- return this.currentUsername;
- }
-}
diff --git a/src/main/java/data_access/NutritionInformationDAO.java b/src/main/java/data_access/NutritionInformationDAO.java
new file mode 100644
index 000000000..7ae7d1405
--- /dev/null
+++ b/src/main/java/data_access/NutritionInformationDAO.java
@@ -0,0 +1,66 @@
+package data_access;
+
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import use_case.nutrition_information.NutritionInformationAccessInterface;
+
+import java.io.IOException;
+
+/**
+ * Data Access Object (DAO) implementation for fetching nutrition information.
+ * This class communicates with the Spoonacular API to retrieve nutrition data
+ * for a specific recipe based on its ID.
+ */
+public class NutritionInformationDAO implements NutritionInformationAccessInterface {
+
+ // Base URL for the Spoonacular API
+ private static final String BASE_URL = "https://api.spoonacular.com";
+
+ // API key for authenticating requests to the Spoonacular API
+ private static final String API_KEY = "5fcf2eef76af4e6893959ceefae0a087";
+
+ /**
+ * Fetches nutrition information for a specific recipe from the API.
+ * @param recipeId The ID of the recipe for which nutrition information is required.
+ * @return A JSON string containing the nutrition information.
+ * @throws RuntimeException If there is an error during the API request.
+ */
+ @Override
+ public String fetchNutritionInformation(int recipeId) {
+ // Step 1: Initialize OkHttpClient for making HTTP requests
+ OkHttpClient client = new OkHttpClient();
+
+ // Step 2: Build the URL for the API request
+ String url = buildUrl(recipeId);
+
+ // Step 3: Create a new HTTP request object
+ Request request = new Request.Builder().url(url).build();
+
+ // Step 4: Execute the HTTP request and handle the response
+ try (Response response = client.newCall(request).execute()) {
+ if (response.isSuccessful() && response.body() != null) {
+ return response.body().string();
+ } else {
+ throw new IOException("Failed to fetch data: HTTP code " + response.code());
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Builds the complete URL for the Spoonacular API request.
+ * @param recipeId The ID of the recipe for which the URL is generated.
+ * @return A fully formed URL string for the API request.
+ */
+ private String buildUrl(int recipeId) {
+
+ // Construct the API endpoint specific to the recipe
+ String endpoint = "/recipes/" + recipeId + "/nutritionWidget.json";
+
+ // Combine the base URL, endpoint, and API key to form the full URL
+ return BASE_URL + endpoint + "?apiKey=" + API_KEY;
+ }
+}
+
diff --git a/src/main/java/data_access/RecipeDAO.java b/src/main/java/data_access/RecipeDAO.java
new file mode 100644
index 000000000..1f869664e
--- /dev/null
+++ b/src/main/java/data_access/RecipeDAO.java
@@ -0,0 +1,34 @@
+package data_access;
+
+import java.util.List;
+
+import entity.Recipe;
+
+public interface RecipeDAO {
+ List getRecipesByIngredients(List ingredients);
+
+ List getAllRecipes();
+
+ /**
+ * Get diets from API.
+ * @return list of diets
+ */
+ List getAvailableDiets();
+
+ /**
+ * Get cuisines from API.
+ * @return list of cuisines
+ */
+ List getAvailableCuisines();
+
+ /**
+ * Filter searches recipes.
+ * @param ingredients list of ingredients
+ * @param diet diet choice
+ * @param cuisine cuisine choice
+ * @return list of filtered recipes
+ */
+ List filterSearchRecipes(List ingredients, String diet, String cuisine);
+
+}
+
diff --git a/src/main/java/data_access/RecipeIdDAO.java b/src/main/java/data_access/RecipeIdDAO.java
new file mode 100644
index 000000000..772653850
--- /dev/null
+++ b/src/main/java/data_access/RecipeIdDAO.java
@@ -0,0 +1,74 @@
+package data_access;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+/**
+ * Data Access Object (DAO) for retrieving recipe IDs by recipe name.
+ * This class communicates with the Spoonacular API's "complexSearch" endpoint
+ * to find recipe IDs based on a given recipe name.
+ */
+public class RecipeIdDAO {
+
+ // API key for authenticating requests to the Spoonacular API
+ private static final String API_KEY = "5fcf2eef76af4e6893959ceefae0a087"; //
+
+ // Base URL for the Spoonacular "complexSearch" endpoint
+ private static final String BASE_URL = "https://api.spoonacular.com/recipes/complexSearch";
+
+ /**
+ * Fetches the recipe ID for a given recipe name by querying the Spoonacular API.
+ * @param recipeName The name of the recipe to search for.
+ * @return The ID of the first recipe returned by the API, or -1 if no recipe is found or an error occurs.
+ */
+ public static int getRecipeIdByName(String recipeName) {
+ try {
+ // Construct the full API URL with the recipe name and API key
+ String apiUrl = BASE_URL + "?query=" + recipeName.replace(" ", "+") + "&apiKey=" + API_KEY;
+
+ // Create a URL object and open an HTTP connection
+ URL url = new URL(apiUrl);
+ HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+ connection.setRequestMethod("GET");
+
+ // Check the API response code
+ int responseCode = connection.getResponseCode();
+ if (responseCode != 200) {
+ System.out.println("Error: API returned response code " + responseCode);
+ return -1;
+ }
+
+ // Read the response from the API
+ BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
+ StringBuilder response = new StringBuilder();
+ String line;
+ while ((line = reader.readLine()) != null) {
+ response.append(line);
+ }
+ reader.close();
+
+ // Parse the JSON response
+ JSONObject jsonResponse = new JSONObject(response.toString());
+ JSONArray results = jsonResponse.getJSONArray("results");
+
+ if (results.length() == 0) {
+ System.out.println("No recipes found for: " + recipeName);
+ return -1;
+ }
+
+ // Get the ID of the first recipe in the results
+ JSONObject firstResult = results.getJSONObject(0);
+ int recipeId = firstResult.getInt("id");
+ System.out.println("Recipe ID for " + recipeName + ": " + recipeId);
+
+ return recipeId;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return -1;
+ }
+ }
+}
diff --git a/src/main/java/data_access/SpoonacularAPI.java b/src/main/java/data_access/SpoonacularAPI.java
new file mode 100644
index 000000000..d3705c942
--- /dev/null
+++ b/src/main/java/data_access/SpoonacularAPI.java
@@ -0,0 +1,142 @@
+package data_access;
+
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Scanner;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+public class SpoonacularAPI {
+ private static final String API_KEY = AppConstants.API_KEY;
+ private static final String RESPONSE_METHOD = "GET";
+
+ // Define pantry staples
+ private final List pantryStaples = Arrays.asList(
+ "salt", "pepper", "olive oil", "vegetable oil", "sugar", "flour", "baking powder", "butter", "water"
+ );
+
+ // Fetch recipes based on the user's available ingredients
+ public List getRecipesContaining(List availableIngredients) {
+ final List recipes = new ArrayList<>();
+ try {
+ final String ingredients = String.join(",", availableIngredients);
+
+ // API call to fetch recipes based on available ingredients
+ final String urlString = "https://api.spoonacular.com/recipes/findByIngredients?ingredients=" + ingredients
+ + "&number=10&ranking=2&apiKey=" + API_KEY;
+ final URL url = new URL(urlString);
+ final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setRequestMethod(RESPONSE_METHOD);
+
+ final Scanner scanner = new Scanner(conn.getInputStream());
+ final StringBuilder jsonResponse = new StringBuilder();
+ while (scanner.hasNext()) {
+ jsonResponse.append(scanner.nextLine());
+ }
+ scanner.close();
+
+ // Parse the JSON response
+ final JSONArray jsonArray = new JSONArray(jsonResponse.toString());
+ for (int i = 0; i < jsonArray.length(); i++) {
+ final JSONObject recipe = jsonArray.getJSONObject(i);
+
+ // Get the missed ingredients for the recipe
+ final JSONArray missedIngredients = recipe.getJSONArray("missedIngredients");
+
+ // Count how many ingredients are truly missing
+ int missingCount = 0;
+ for (int j = 0; j < missedIngredients.length(); j++) {
+ final String ingredientName = missedIngredients.getJSONObject(j).getString("name").toLowerCase();
+ if (!availableIngredients.contains(ingredientName)) {
+ missingCount++;
+ }
+ }
+
+ // Include only recipes with 2 or fewer additional (missing) ingredients
+ if (missingCount <= 2) {
+ recipes.add(recipe.getString("title"));
+ }
+ }
+ }
+ catch (Exception exception) {
+ System.err.println("Error fetching recipes: " + exception.getMessage());
+ }
+ return recipes;
+ }
+
+ // Fetch missing ingredients for selected recipes
+ public List getMissingIngredients(List selectedRecipes, List availableIngredients) {
+ final List shoppingList = new ArrayList<>();
+ try {
+ for (String recipeTitle : selectedRecipes) {
+ final String searchUrl = "https://api.spoonacular.com/recipes/complexSearch?query=" + recipeTitle
+ + "&apiKey=" + API_KEY;
+ final URL searchRequest = new URL(searchUrl);
+ final HttpURLConnection searchConn = (HttpURLConnection) searchRequest.openConnection();
+ searchConn.setRequestMethod(RESPONSE_METHOD);
+
+ final Scanner searchScanner = new Scanner(searchConn.getInputStream());
+ final StringBuilder searchResponse = new StringBuilder();
+ while (searchScanner.hasNext()) {
+ searchResponse.append(searchScanner.nextLine());
+ }
+ searchScanner.close();
+
+ final JSONObject searchResult = new JSONObject(searchResponse.toString());
+ final JSONArray results = searchResult.getJSONArray("results");
+
+ if (!results.isEmpty()) {
+ final JSONObject recipe = results.getJSONObject(0);
+ final int id = recipe.getInt("id");
+
+ // Fetch ingredients for the recipe using its ID
+ final String ingredientUrl = "https://api.spoonacular.com/recipes/" + id
+ + "/information?includeNutrition=false&apiKey=" + API_KEY;
+ final URL ingredientRequest = new URL(ingredientUrl);
+ final HttpURLConnection ingredientConn = (HttpURLConnection) ingredientRequest.openConnection();
+ ingredientConn.setRequestMethod(RESPONSE_METHOD);
+
+ final Scanner ingredientScanner = new Scanner(ingredientConn.getInputStream());
+ final StringBuilder ingredientResponse = new StringBuilder();
+ while (ingredientScanner.hasNext()) {
+ ingredientResponse.append(ingredientScanner.nextLine());
+ }
+ ingredientScanner.close();
+
+ final JSONObject ingredientJson = new JSONObject(ingredientResponse.toString());
+ final JSONArray extendedIngredients = ingredientJson.getJSONArray("extendedIngredients");
+
+ // Include missing ingredients that are not in the user's list and not pantry staples
+ for (int i = 0; i < extendedIngredients.length(); i++) {
+ final JSONObject ingredient = extendedIngredients.getJSONObject(i);
+ final String ingredientName = ingredient.getString("original").toLowerCase();
+
+ // Check if the ingredient is not already available and not a pantry staple (substring match)
+ if (!availableIngredients.contains(ingredientName) && !containsPantryStaple(ingredientName)) {
+ shoppingList.add(ingredient.getString("original"));
+ }
+ }
+ }
+ }
+ }
+ catch (Exception exception) {
+ System.err.println("Error fetching missing ingredients: " + exception.getMessage());
+ }
+ return shoppingList;
+ }
+
+ // Helper method to check if an ingredient contains a pantry staple
+ private boolean containsPantryStaple(String ingredient) {
+ for (String staple : pantryStaples) {
+ if (ingredient.contains(staple)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/src/main/java/data_access/SpoonacularRecipeDAO.java b/src/main/java/data_access/SpoonacularRecipeDAO.java
new file mode 100644
index 000000000..863af50b3
--- /dev/null
+++ b/src/main/java/data_access/SpoonacularRecipeDAO.java
@@ -0,0 +1,272 @@
+package data_access;
+
+import entity.Ingredient;
+import entity.Recipe;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import org.json.JSONArray;
+import org.json.JSONObject;
+import use_case.filter_recipes.FilterRecipesDataAccessInterface;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class SpoonacularRecipeDAO implements RecipeDAO, FilterRecipesDataAccessInterface {
+ private static final String API_KEY = AppConstants.API_KEY;
+ private static final String BASE_URL = "https://api.spoonacular.com";
+ private final OkHttpClient client;
+
+ public SpoonacularRecipeDAO() {
+ this.client = new OkHttpClient();
+ }
+
+ @Override
+ public List getAllRecipes() {
+ // If you need to fetch all recipes, you could use a default search (e.g., return popular recipes)
+ return getRecipesByIngredients(List.of("pasta")); // Example default search
+ }
+
+ private String ingredientsToString(List ingredients) {
+ // Convert ingredients list to a comma-separated string
+ final String ingredientsQuery = ingredients.stream()
+ .map(String::trim)
+ .collect(Collectors.joining(","));
+ return ingredientsQuery;
+ }
+
+ @Override
+ public List getRecipesByIngredients(List ingredients) {
+ // Convert ingredients list to a comma-separated string
+ // String ingredientsQuery = ingredients.stream()
+ // .map(String::trim)
+ // .collect(Collectors.joining(","));
+ final String ingredientsQuery = ingredientsToString(ingredients);
+
+ // Construct the URL with the ingredients query
+ String endpoint = "/recipes/findByIngredients";
+ String url = BASE_URL + endpoint + "?apiKey=" + API_KEY + "&ingredients=" + ingredientsQuery + "&number="
+ + AppConstants.NUMBER_OF_RESULTS;
+
+ // Build the request
+ Request request = new Request.Builder()
+ .url(url)
+ .build();
+
+ List recipes = new ArrayList<>();
+ try (Response response = client.newCall(request).execute()) {
+ if (response.isSuccessful() && response.body() != null) {
+ // Parse the JSON response
+ String jsonResponse = response.body().string();
+ JSONArray results = new JSONArray(jsonResponse);
+
+ // Loop through the results and create Recipe objects
+ for (int i = 0; i < results.length(); i++) {
+ JSONObject recipeJson = results.getJSONObject(i);
+
+ // call the API again to retrieve the full recipe object
+ int id = recipeJson.getInt("id");
+ // String recipeApiUrl = BASE_URL + "/recipes/" + "?apiKey=" + API_KEY + "/" + Integer.toString(id) + "/information";
+ // String recipeApiUrl = "https://api.spoonacular.com/recipes/716429/information?includeNutrition=false";
+ String recipeApiUrl = BASE_URL + "/recipes/" + id + "/information?apiKey=" + API_KEY + "&includeNutrition=false";
+// String recipeApiUrl = "https://api.spoonacular.com/recipes/findByIngredients?apiKey="
+// + API_KEY
+// + "&ingredients=chicken,cheese&number=1";
+ Request recipeRequest = new Request.Builder()
+ .url(recipeApiUrl)
+ .build();
+ Response recipeResponse = client.newCall(recipeRequest).execute();
+ JSONObject completeRecipe = null;
+ if (recipeResponse.isSuccessful() && recipeResponse.body() != null) {
+ String jsonRecipeResponse = recipeResponse.body().string();
+ completeRecipe = new JSONObject(jsonRecipeResponse);
+ }
+ else {
+ System.out.println("Request failed with code: " + response.code());
+ }
+
+ String title = recipeJson.getString("title");
+ final String recipeUrl = completeRecipe.getString("spoonacularSourceUrl");
+ final JSONArray missedIngredientsJson = recipeJson.getJSONArray("missedIngredients");
+ final JSONArray usedIngredientsJson = recipeJson.getJSONArray("usedIngredients");
+ final JSONArray unusedIngredientsJson = recipeJson.getJSONArray("unusedIngredients");
+ String image = recipeJson.getString("image");
+
+ // Collect ingredients from the JSON response
+ final List recipeIngredients = new ArrayList<>();
+ for (int j = 0; j < usedIngredientsJson.length(); j++) {
+ recipeIngredients.add(new Ingredient(usedIngredientsJson.getJSONObject(j).getString("name"),
+ usedIngredientsJson.getJSONObject(j).getDouble("amount"),
+ usedIngredientsJson.getJSONObject(j).getString("unit")));
+ }
+ for (int j = 0; j < missedIngredientsJson.length(); j++) {
+ recipeIngredients.add(new Ingredient(missedIngredientsJson.getJSONObject(j).getString("name"),
+ missedIngredientsJson.getJSONObject(j).getDouble("amount"),
+ missedIngredientsJson.getJSONObject(j).getString("unit")));
+ }
+
+ // Create and add Recipe object to the list
+ Recipe recipe = new Recipe(title, recipeUrl, recipeIngredients, image);
+ recipes.add(recipe);
+ }
+ } else {
+ System.out.println("Request failed with code: " + response.code());
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return recipes;
+ }
+
+/* private JSONArray searchByIngredients(List ingredients) {
+ final String ingredientsQuery = ingredientsToString(ingredients);
+ String endpoint = "/recipes/findByIngredients";
+ String url = BASE_URL + endpoint + "?apiKey=" + API_KEY + "&ingredients=" + ingredientsQuery + "&number=5";
+
+ }*/
+
+ private JSONObject getCompleteRecipe(int id) {
+ String recipeApiUrl = BASE_URL + "/recipes/" + id + "/information?apiKey=" + API_KEY + "&includeNutrition=false";
+ Request recipeRequest = new Request.Builder()
+ .url(recipeApiUrl)
+ .build();
+ final Response recipeResponse;
+ try {
+ recipeResponse = client.newCall(recipeRequest).execute();
+ }
+ catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ JSONObject completeRecipe = null;
+ if (recipeResponse.isSuccessful() && recipeResponse.body() != null) {
+ final String jsonRecipeResponse;
+ try {
+ jsonRecipeResponse = recipeResponse.body().string();
+ }
+ catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ completeRecipe = new JSONObject(jsonRecipeResponse);
+ }
+ else {
+ System.out.println("Request failed with code: " + recipeResponse.code());
+ }
+ return completeRecipe;
+
+ }
+
+ /**
+ * Also searches for recipes but with some extra filters.
+ * @param ingredients list of string ingredients
+ * @param diet diet choice obtained from user; if none selected, diet will be "Any"
+ * @param cuisine cuisine choice obtained from user; if none selected, cuisine will be "Any"
+ * @return a list of recipes that match filters
+ */
+ @Override
+ public List filterSearchRecipes(List ingredients, String diet, String cuisine) {
+ final String ingredientsQuery = ingredientsToString(ingredients);
+ final String endpoint = BASE_URL + "/recipes/complexSearch";
+ final StringBuilder urlBuilder = new StringBuilder(endpoint);
+ urlBuilder.append("?apiKey=" + API_KEY);
+ urlBuilder.append("&includeIngredients=").append(ingredientsQuery);
+ urlBuilder.append("&fillIngredients=true");
+
+ if (!"Any".equals(diet)) {
+ urlBuilder.append("&diet=").append(diet);
+ }
+ if (!"Any".equals(cuisine)) {
+ urlBuilder.append("&cuisine=").append(cuisine);
+ }
+
+ // Build the request
+ final Request request = new Request.Builder()
+ .url(String.valueOf(urlBuilder))
+ .build();
+
+ final List recipes = new ArrayList<>();
+ try (Response response = client.newCall(request).execute()) {
+ if (response.isSuccessful() && response.body() != null) {
+ // Parse the JSON response
+ final String jsonResponse = response.body().string();
+ final JSONObject jsonObject = new JSONObject(jsonResponse);
+ final JSONArray results = jsonObject.getJSONArray("results");
+
+ // Loop through the results and create Recipe objects
+ for (int i = 0; i < results.length(); i++) {
+ JSONObject recipeJson = results.getJSONObject(i);
+
+ // call the API again to retrieve the full recipe object
+ final int id = recipeJson.getInt("id");
+ final JSONObject completeRecipe = getCompleteRecipe(id);
+
+ // for testing purposes
+ String title = recipeJson.getString("title");
+ final String recipeUrl = completeRecipe.getString("spoonacularSourceUrl");
+ final JSONArray missedIngredientsJson = recipeJson.getJSONArray("missedIngredients");
+ final JSONArray usedIngredientsJson = recipeJson.getJSONArray("usedIngredients");
+ final JSONArray unusedIngredientsJson = recipeJson.getJSONArray("unusedIngredients");
+ String image = recipeJson.getString("image");
+
+ // Collect ingredients from the JSON response
+ final List recipeIngredients = new ArrayList<>();
+ for (int j = 0; j < usedIngredientsJson.length(); j++) {
+ recipeIngredients.add(new Ingredient(usedIngredientsJson.getJSONObject(j).getString("name"),
+ usedIngredientsJson.getJSONObject(j).getDouble("amount"),
+ usedIngredientsJson.getJSONObject(j).getString("unit")));
+ }
+ for (int j = 0; j < missedIngredientsJson.length(); j++) {
+ recipeIngredients.add(new Ingredient(missedIngredientsJson.getJSONObject(j).getString("name"),
+ missedIngredientsJson.getJSONObject(j).getDouble("amount"),
+ missedIngredientsJson.getJSONObject(j).getString("unit")));
+ }
+
+ // Create and add Recipe object to the list
+ final Recipe recipe = new Recipe(title, recipeUrl, recipeIngredients, image);
+ recipes.add(recipe);
+ }
+ }
+ else {
+ System.out.println("Request failed with code: " + response.code());
+ }
+ }
+ catch (IOException exception) {
+ exception.printStackTrace();
+ }
+ return recipes;
+ }
+
+ // hard-coded
+ @Override
+ public List getAvailableDiets() {
+ final List diets = new ArrayList<>();
+ diets.add("glutenfree");
+ diets.add("vegetarian");
+ diets.add("vegan");
+ diets.add("ketogenic");
+ diets.add("pescetarian");
+ diets.add("paleo");
+ return diets;
+ }
+
+ @Override
+ public List getAvailableCuisines() {
+ final List cuisines = new ArrayList<>();
+ cuisines.add("african");
+ cuisines.add("american");
+ cuisines.add("caribbean");
+ cuisines.add("chinese");
+ cuisines.add("greek");
+ cuisines.add("indian");
+ cuisines.add("italian");
+ cuisines.add("japanese");
+ cuisines.add("korean");
+ cuisines.add("mediterranean");
+ cuisines.add("mexican");
+ cuisines.add("middleeastern");
+ cuisines.add("thai");
+ return cuisines;
+ }
+
+}
diff --git a/src/main/java/data_access/UserDAO.java b/src/main/java/data_access/UserDAO.java
new file mode 100644
index 000000000..fde08dcfe
--- /dev/null
+++ b/src/main/java/data_access/UserDAO.java
@@ -0,0 +1,14 @@
+package data_access;
+
+import entity.User;
+
+public interface UserDAO {
+ // Returns true if the user is added successfully
+ boolean addUser(User user);
+
+ // Finds a user by their username
+ User findUserByUsername(String username);
+
+ // Validates username and password
+ boolean validateUser(String username, String password);
+}
diff --git a/src/main/java/data_access/UserDAOImpl.java b/src/main/java/data_access/UserDAOImpl.java
new file mode 100644
index 000000000..039c98705
--- /dev/null
+++ b/src/main/java/data_access/UserDAOImpl.java
@@ -0,0 +1,344 @@
+package data_access;
+
+import entity.Ingredient;
+import entity.Recipe;
+import entity.User;
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.json.JSONTokener;
+import use_case.search_recipe_list_by_ingredient.SearchRecipeListByIngredientDataAccessInterface;
+import use_case.search_recipe_list_by_name.SearchRecipeListByNameDataAccessInterface;
+
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.*;
+
+public class UserDAOImpl implements UserDAO, SearchRecipeListByIngredientDataAccessInterface,
+ SearchRecipeListByNameDataAccessInterface {
+ private static final String FILE_PATH = "users.json";
+ private Map usersDatabase;
+
+ public UserDAOImpl() {
+ if (usersDatabase == null) {
+ usersDatabase = new HashMap<>();
+ }
+ else {
+ usersDatabase = loadUsersFromFile();
+ }
+ }
+
+ @Override
+ public boolean addUser(User user) {
+ if (usersDatabase.containsKey(user.getUsername())) {
+ System.out.println("User already exists: " + user.getUsername());
+ return false; // User already exists
+ }
+ usersDatabase.put(user.getUsername(), user);
+ saveUsersToFile();
+ System.out.println("User added successfully: " + user.getUsername());
+ return true;
+ }
+
+ @Override
+ public User findUserByUsername(String username) {
+ usersDatabase = loadUsersFromFile();
+ return usersDatabase.get(username); // Returns null if the user doesn't exist
+ }
+
+
+ @Override
+ public boolean validateUser(String username, String password) {
+ // Check for empty username or password
+ if (username == null || username.trim().isEmpty() || password == null || password.trim().isEmpty()) {
+ System.out.println("Empty username or password provided."); // Debugging output
+ return false;
+ }
+
+ User user = findUserByUsername(username);
+ if (user == null) {
+ System.out.println("User not found: " + username); // Debugging output
+ return false; // User doesn't exist
+ } else if (!user.getPassword().equals(password)) {
+ System.out.println("Incorrect password for user: " + username); // Debugging output
+ return false; // Password doesn't match
+ }
+ System.out.println("User validated successfully: " + username); // Debugging output
+ return true; // User exists and password matches
+ }
+
+ // Load users from JSON file using org.json
+ private Map loadUsersFromFile() {
+ Map users = new HashMap<>();
+ try (FileReader reader = new FileReader(FILE_PATH)) {
+ JSONArray usersArray = new JSONArray(new JSONTokener(reader));
+ for (int i = 0; i < usersArray.length(); i++) {
+ JSONObject userObject = usersArray.getJSONObject(i);
+ String username = userObject.getString("username");
+ String password = userObject.getString("password");
+
+ // reconstructing bookmarks as a list of recipes
+ JSONArray bookmarksArray = userObject.getJSONArray("bookmarks");
+ List recipeBookmarks = new ArrayList<>();
+ for (int k = 0; k < bookmarksArray.length(); k++) {
+ JSONObject recipeJson = bookmarksArray.getJSONObject(k);
+ JSONArray ingredientsJson = recipeJson.getJSONArray("ingredients");
+ List ingredients = new ArrayList<>();
+ for (int j = 0; j < ingredientsJson.length(); j++) {
+ JSONObject ingredientJson = ingredientsJson.getJSONObject(j);
+ Ingredient ingredient = new Ingredient(
+ ingredientJson.getString("name"),
+ ingredientJson.getDouble("amount"),
+ ingredientJson.getString("unit"));
+ ingredients.add(ingredient);
+ }
+ Recipe recipe = new Recipe(
+ recipeJson.getString("name"),
+ recipeJson.getString("url"),
+ ingredients,
+ recipeJson.getString("image"));
+ // ^ didn't add cuisineType and dietaryType
+ recipeBookmarks.add(recipe);
+ }
+
+ // reconstructing recentlyViewed as a list of recipes
+ JSONArray recentlyViewedArray = userObject.getJSONArray("recentlyViewed");
+ List recipeRecentlyViewed = new ArrayList<>();
+ for (int m = 0; m < recentlyViewedArray.length(); m++) {
+ JSONObject recipeJson = recentlyViewedArray.getJSONObject(m);
+ JSONArray ingredientsJson = recipeJson.getJSONArray("ingredients");
+ List ingredients = new ArrayList<>();
+ for (int j = 0; j < ingredientsJson.length(); j++) {
+ JSONObject ingredientJson = ingredientsJson.getJSONObject(j);
+ System.out.println(ingredientJson.keys());
+ Ingredient ingredient = new Ingredient(
+ ingredientJson.getString("name"),
+ ingredientJson.getDouble("amount"),
+ ingredientJson.getString("unit"));
+ ingredients.add(ingredient);
+ }
+ Recipe recipe = new Recipe(
+ recipeJson.getString("name"),
+ recipeJson.getString("url"),
+ ingredients,
+ recipeJson.getString("image"));
+ // ^ didn't add cuisineType and dietaryType
+ recipeRecentlyViewed.add(recipe);
+ }
+
+ // reconstructing folders as a map of folderNames and lists of recipes
+ JSONObject foldersJson = new JSONObject();
+ if (userObject.has("folders")) {
+ foldersJson = userObject.getJSONObject("folders");
+ }
+ else {
+ foldersJson.put("folders", new JSONArray());
+ }
+ Map> foldersMap = new HashMap<>();
+ for (String folderName : foldersJson.keySet()) {
+ JSONArray recipesArray = foldersJson.getJSONArray(folderName);
+ List recipes = new ArrayList<>();
+
+ for (int t = 0; t < recipesArray.length(); t++) {
+ JSONObject recipeJson = recipesArray.getJSONObject(t);
+ JSONArray ingredientsJson = recipeJson.getJSONArray("ingredients");
+ List ingredients = new ArrayList<>();
+
+ for (int s = 0; s < ingredientsJson.length(); s++) {
+ JSONObject ingredientJson = ingredientsJson.getJSONObject(s);
+ Ingredient ingredient = new Ingredient(
+ ingredientJson.getString("name"),
+ ingredientJson.getDouble("amount"),
+ ingredientJson.getString("unit"));
+ ingredients.add(ingredient);
+ }
+
+ Recipe recipe = new Recipe(
+ recipeJson.getString("name"),
+ recipeJson.getString("url"),
+ ingredients,
+ recipeJson.getString("image"));
+ recipes.add(recipe);
+ }
+ System.out.println(folderName);
+ System.out.println(recipes);
+ foldersMap.put(folderName, recipes);
+ }
+
+ // putting the new user object in the list of users to be returned
+ users.put(username, new User(username, password, recipeBookmarks, recipeRecentlyViewed, foldersMap));
+ System.out.println("Loaded user: " + username); // Debugging output
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ saveUsersToFile();
+ }
+ return users;
+ }
+
+ // Save users to JSON file using org.json
+ private void saveUsersToFile() {
+ JSONArray usersArray = new JSONArray();
+ for (User user : usersDatabase.values()) {
+ JSONObject userObject = new JSONObject();
+ userObject.put("username", user.getUsername());
+ userObject.put("password", user.getPassword());
+ userObject.put("bookmarks", user.getBookmarks());
+ userObject.put("recentlyViewed", user.getRecentlyViewed());
+ userObject.put("folders", user.getFolders());
+ usersArray.put(userObject);
+ }
+ try (FileWriter writer = new FileWriter(FILE_PATH)) {
+ writer.write(usersArray.toString(4)); // Indent for readability
+ } catch (IOException e) {
+ e.printStackTrace();
+ saveUsersToFile();
+ }
+ }
+
+ // TODO lots of duplicate code in the following methods...
+ // Add a recipe to a user's list of bookmarks
+ public void addBookmarkToFile(String username, Recipe recipe) {
+ final Map users = loadUsersFromFile();
+ final User user = users.get(username);
+ user.addBookmark(recipe);
+ usersDatabase.put(user.getUsername(), user);
+ saveUsersToFile();
+ System.out.println("Bookmark added successfully for: " + user.getUsername());
+ }
+
+ // Returns a user's list of bookmarks
+ // TODO double check
+ public List getBookmarksFromFile(String username) {
+ final Map users = loadUsersFromFile();
+ final User user = users.get(username);
+ System.out.println("Loaded user: " + user.getUsername());
+ System.out.println("Password: " + user.getPassword());
+ System.out.println("Bookmarks: " + user.getBookmarks());
+ System.out.println("RecentlyViewed: " + user.getRecentlyViewed());
+ System.out.println(user);
+ return user.getBookmarks();
+ }
+
+ // Add a recipe to a user's list of recentlyViewed
+ public void addRecentlyViewedToFile(String username, Recipe recipe) {
+ final Map users = loadUsersFromFile();
+ final User user = users.get(username);
+ user.addRecentlyViewed(recipe);
+ usersDatabase.put(user.getUsername(), user);
+ saveUsersToFile();
+ System.out.println("RecentlyViewed added successfully for: " + user.getUsername());
+ }
+
+ public List getRecentlyViewedFromFile(String username) {
+ final Map users = loadUsersFromFile();
+ final User user = users.get(username);
+ System.out.println("Loaded user: " + user.getUsername());
+ System.out.println("Password: " + user.getPassword());
+ System.out.println("Bookmarks: " + user.getBookmarks());
+ System.out.println("RecentlyViewed: " + user.getRecentlyViewed());
+ System.out.println(user);
+ return user.getRecentlyViewed();
+ }
+
+ public void clearRecentlyViewed(String username) {
+ final Map users = loadUsersFromFile();
+ final User user = users.get(username);
+ user.clearRecentlyViewed();
+ usersDatabase.put(user.getUsername(), user);
+ saveUsersToFile();
+ System.out.println("RecentlyViewed cleared successfully");
+ }
+
+ public void addFolderToFile(String username, String folder) {
+ final Map users = loadUsersFromFile();
+ final User user = users.get(username);
+ user.addFolder(folder);
+ usersDatabase.put(user.getUsername(), user);
+ saveUsersToFile();
+ System.out.println("Folder added successfully for: " + user.getUsername());
+ // TODO double check this method
+ }
+
+ public void addRecipeToFolderInFile(String username, String folder, Recipe recipe) {
+ final Map users = loadUsersFromFile();
+ final User user = users.get(username);
+ user.addRecipeToFolder(folder, recipe);
+ usersDatabase.put(user.getUsername(), user);
+ saveUsersToFile();
+ System.out.println("Recipe added successfully for: " + user.getFolder(folder));
+ }
+
+ public List getFolderFromFile(String username, String folder) {
+ final Map users = loadUsersFromFile();
+ final User user = users.get(username);
+ System.out.println("Loaded folder: " + user.getFolder(folder));
+ return user.getFolder(folder);
+ }
+
+ @Override
+ public List searchRecipeListByIngredient(List ingredients, User user, String folder) {
+ final Map users = loadUsersFromFile();
+ final User userFromFile = users.get(user.getUsername());
+ List recipeList = new ArrayList<>();
+ final List results = new ArrayList<>();
+ if ("bookmarks".equals(folder)) {
+ recipeList.addAll(userFromFile.getBookmarks());
+ }
+ else if ("recentlyViewed".equals(folder)) {
+ recipeList.addAll(userFromFile.getRecentlyViewed());
+ }
+ else {
+ recipeList.addAll(userFromFile.getFolder(folder));
+ }
+ for (Recipe recipe : recipeList) {
+ boolean match = false;
+ final List recipeIngredients = recipe.getIngredients();
+ final List recipeIngredientsString = new ArrayList<>();
+ for (Ingredient ingredient : recipeIngredients) {
+ final String[] words = ingredient.getName().split(" ");
+ recipeIngredientsString.addAll(Arrays.asList(words));
+ }
+ for (String recipeIngredient : recipeIngredientsString) {
+ for (String enteredIngredient : ingredients) {
+ if (recipeIngredient.equalsIgnoreCase(enteredIngredient)) {
+ match = true;
+ }
+ }
+
+ }
+ if (match) {
+ results.add(recipe);
+ }
+ }
+ return results;
+ }
+
+ @Override
+ public List searchRecipeListByName(String recipeName, User user, String folder) {
+ final Map users = loadUsersFromFile();
+ final User userFromFile = users.get(user.getUsername());
+ final List results = new ArrayList<>();
+
+ List recipeList = new ArrayList<>();
+ if ("bookmarks".equals(folder)) {
+ recipeList.addAll(userFromFile.getBookmarks());
+ }
+ else if ("recentlyViewed".equals(folder)) {
+ recipeList.addAll(userFromFile.getRecentlyViewed());
+ }
+ else {
+ recipeList.addAll(userFromFile.getFolder(folder));
+ }
+
+ for (Recipe recipe : recipeList) {
+ final String[] words = recipe.getName().split(" ");
+ for (String word : words) {
+ if (word.toLowerCase().contains(recipeName.trim().toLowerCase())) {
+ results.add(recipe);
+ }
+ }
+ }
+ return results;
+ }
+}
diff --git a/src/main/java/entity/CommonUser.java b/src/main/java/entity/CommonUser.java
deleted file mode 100644
index ba25fd20a..000000000
--- a/src/main/java/entity/CommonUser.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package entity;
-
-/**
- * A simple implementation of the User interface.
- */
-public class CommonUser implements User {
-
- private final String name;
- private final String password;
-
- public CommonUser(String name, String password) {
- this.name = name;
- this.password = password;
- }
-
- @Override
- public String getName() {
- return name;
- }
-
- @Override
- public String getPassword() {
- return password;
- }
-
-}
diff --git a/src/main/java/entity/CommonUserFactory.java b/src/main/java/entity/CommonUserFactory.java
deleted file mode 100644
index ebede69e3..000000000
--- a/src/main/java/entity/CommonUserFactory.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package entity;
-
-/**
- * Factory for creating CommonUser objects.
- */
-public class CommonUserFactory implements UserFactory {
-
- @Override
- public User create(String name, String password) {
- return new CommonUser(name, password);
- }
-}
diff --git a/src/main/java/entity/Ingredient.java b/src/main/java/entity/Ingredient.java
new file mode 100644
index 000000000..9b7b548c7
--- /dev/null
+++ b/src/main/java/entity/Ingredient.java
@@ -0,0 +1,30 @@
+package entity;
+
+/**
+ * Represents an ingredient from our recipe.
+ */
+public class Ingredient {
+ private final double amount;
+ private final String unit;
+ private final String name;
+
+ public Ingredient(String name, double amount, String unit) {
+ this.name = name;
+ this.amount = amount;
+ this.unit = unit;
+ }
+
+ // getters (no need for setters)
+ public String getName() {
+ return name;
+ }
+
+ public double getAmount() {
+ return amount;
+ }
+
+ public String getUnit() {
+ return unit;
+ }
+
+}
diff --git a/src/main/java/entity/Nutrition.java b/src/main/java/entity/Nutrition.java
new file mode 100644
index 000000000..136d65f7e
--- /dev/null
+++ b/src/main/java/entity/Nutrition.java
@@ -0,0 +1,39 @@
+package entity;
+
+/**
+ * Represents the nutritional information for a recipe.
+ */
+public class Nutrition {
+ private final String name;
+ private final double amount;
+ private final String unit;
+ private final double percentOfDailyNeeds;
+
+ public Nutrition(String name, double amount, String unit, double percentDailyNeeds) {
+ this.name = name;
+ this.amount = amount;
+ this.unit = unit;
+ this.percentOfDailyNeeds = percentDailyNeeds;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public double getAmount() {
+ return amount;
+ }
+
+ public String getUnit() {
+ return unit;
+ }
+
+ public double getPercentDailyNeeds() {
+ return percentOfDailyNeeds;
+ }
+
+ @Override
+ public String toString() {
+ return name + ": " + amount + " " + unit + " (" + percentOfDailyNeeds + "% of daily needs)";
+ }
+}
diff --git a/src/main/java/entity/Recipe.java b/src/main/java/entity/Recipe.java
new file mode 100644
index 000000000..8ae520af5
--- /dev/null
+++ b/src/main/java/entity/Recipe.java
@@ -0,0 +1,67 @@
+package entity;
+
+import java.util.List;
+
+/**
+ * This class returns a recipe from the API.
+ */
+public class Recipe {
+ private final String name;
+ private final String url;
+ private final List ingredients;
+ private final String image;
+ private final String cuisineType;
+ private final String dietaryType;
+
+ public Recipe(String name, String url, List ingredients, String image) {
+ this.name = name;
+ this.url = url;
+ this.ingredients = ingredients;
+ this.image = image;
+ this.cuisineType = "";
+ this.dietaryType = "";
+ }
+
+ // overloading the constructor
+ public Recipe(String name, String url, List ingredients, String image, String cuisineType,
+ String dietaryType) {
+ this.name = name;
+ this.url = url;
+ this.ingredients = ingredients;
+ this.image = image;
+ this.cuisineType = cuisineType;
+ this.dietaryType = dietaryType;
+ }
+
+ // getters
+ public String getName() {
+ return name;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public List getIngredients() {
+ return ingredients;
+ }
+
+ public String getCuisineType() {
+ return cuisineType;
+ }
+
+ public String getDietaryType() {
+ return dietaryType;
+ }
+
+ public String getImage() {
+ return image;
+ }
+
+ // Display the recipe as a string.
+ @Override
+ public String toString() {
+ return this.name;
+ }
+
+}
diff --git a/src/main/java/entity/User.java b/src/main/java/entity/User.java
index 0ad073902..1fae23bf2 100644
--- a/src/main/java/entity/User.java
+++ b/src/main/java/entity/User.java
@@ -1,20 +1,108 @@
package entity;
-/**
- * The representation of a user in our program.
- */
-public interface User {
-
- /**
- * Returns the username of the user.
- * @return the username of the user.
- */
- String getName();
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class User {
+ private String username;
+ private String password;
+ private final List bookmarks;
+ private final List recentlyViewed;
+ private final Map> folders;
+
+ public User(String username, String password) {
+ this.username = username;
+ this.password = password;
+ this.bookmarks = List.of();
+ this.recentlyViewed = List.of();
+ this.folders = new HashMap<>();
+ }
+
+ // Overloaded constructor for user with bookmarks and recentlyViewed
+ public User(String username, String password, List bookmarks, List recentlyViewed) {
+ this.username = username;
+ this.password = password;
+ this.bookmarks = bookmarks;
+ this.recentlyViewed = recentlyViewed;
+ this.folders = new HashMap<>();
+ }
+
+ // Overloaded constructor for user with bookmarks, recentlyViewed and folders
+ public User(String username, String password, List bookmarks, List recentlyViewed,
+ Map> folders) {
+ this.username = username;
+ this.password = password;
+ this.bookmarks = bookmarks;
+ this.recentlyViewed = recentlyViewed;
+ this.folders = folders;
+ }
+
+ // Getters and Setters
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
- /**
- * Returns the password of the user.
- * @return the password of the user.
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public List getBookmarks() {
+ return bookmarks;
+ }
+
+ public List getRecentlyViewed() {
+ return recentlyViewed;
+ }
+
+ public Map> getFolders() {
+ return folders;
+ }
+
+ public List getFolder(String folder) {
+ if (!folders.containsKey(folder)) {
+ return List.of();
+ }
+ else {
+ return folders.get(folder);
+ }
+ }
+
+ /*
+ Add a recipe to the list of bookmarks.
+ */
+ public void addBookmark(Recipe recipe) {
+ this.bookmarks.add(recipe);
+ }
+
+ /*
+ Update recentlyViewed when the user views a recipe.
*/
- String getPassword();
+ public void addRecentlyViewed(Recipe recipe) {
+ this.recentlyViewed.add(recipe);
+ }
+
+ public void clearRecentlyViewed() {
+ this.recentlyViewed.clear();
+ }
+
+ public void addFolder(String folder) {
+ this.folders.put(folder, new ArrayList<>());
+ }
+ public void addRecipeToFolder(String folder, Recipe recipe) {
+ if (!this.folders.containsKey(folder)) {
+ addFolder(folder);
+ }
+ this.folders.get(folder).add(recipe);
+ }
}
diff --git a/src/main/java/entity/UserFactory.java b/src/main/java/entity/UserFactory.java
deleted file mode 100644
index c7a508708..000000000
--- a/src/main/java/entity/UserFactory.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package entity;
-
-/**
- * Factory for creating users.
- */
-public interface UserFactory {
- /**
- * Creates a new User.
- * @param name the name of the new user
- * @param password the password of the new user
- * @return the new user
- */
- User create(String name, String password);
-
-}
diff --git a/src/main/java/interface_adapter/RecipeController.java b/src/main/java/interface_adapter/RecipeController.java
new file mode 100644
index 000000000..63d1d62b8
--- /dev/null
+++ b/src/main/java/interface_adapter/RecipeController.java
@@ -0,0 +1,20 @@
+package interface_adapter;
+
+import use_case.search_recipe.SearchRecipeInputBoundary;
+import use_case.search_recipe.SearchRecipeInputData;
+import use_case.search_recipe.SearchRecipeUseCase;
+
+import java.util.List;
+
+public class RecipeController {
+ private final SearchRecipeInputBoundary searchInteractor;
+
+ public RecipeController(SearchRecipeUseCase searchInteractor) {
+ this.searchInteractor = searchInteractor;
+ }
+
+ public void searchRecipes(List ingredients) {
+ SearchRecipeInputData inputData = new SearchRecipeInputData(ingredients);
+ searchInteractor.searchRecipes(inputData);
+ }
+}
diff --git a/src/main/java/interface_adapter/RecipeListState.java b/src/main/java/interface_adapter/RecipeListState.java
new file mode 100644
index 000000000..df3cbbf00
--- /dev/null
+++ b/src/main/java/interface_adapter/RecipeListState.java
@@ -0,0 +1,46 @@
+package interface_adapter;
+
+import entity.Recipe;
+import entity.User;
+
+import java.util.List;
+
+/**
+ * The State information representing the recipe list.
+ */
+public class RecipeListState {
+ private User user;
+ private String folder;
+ private List recipeList;
+
+ public RecipeListState() {
+ // TODO change this
+ this.user = null;
+ this.folder = null;
+ this.recipeList = null;
+ }
+
+ public User getUser() {
+ return this.user;
+ }
+
+ public String getFolder() {
+ return this.folder;
+ }
+
+ public List getRecipeList() {
+ return this.recipeList;
+ }
+
+ public void setUser(User user) {
+ this.user = user;
+ }
+
+ public void setFolder(String folder) {
+ this.folder = folder;
+ }
+
+ public void setRecipeList(List recipeList) {
+ this.recipeList = recipeList;
+ }
+}
diff --git a/src/main/java/interface_adapter/RecipeListViewModel.java b/src/main/java/interface_adapter/RecipeListViewModel.java
new file mode 100644
index 000000000..6e44df30f
--- /dev/null
+++ b/src/main/java/interface_adapter/RecipeListViewModel.java
@@ -0,0 +1,14 @@
+package interface_adapter;
+
+/**
+ * The View Model for the RecipeListView.
+ */
+public class RecipeListViewModel extends ViewModel {
+
+ public RecipeListViewModel() {
+ // TODO
+ super("bookmarks");
+ setState(new RecipeListState());
+ }
+
+}
diff --git a/src/main/java/interface_adapter/SearchRecipePresenter.java b/src/main/java/interface_adapter/SearchRecipePresenter.java
new file mode 100644
index 000000000..42f9c110d
--- /dev/null
+++ b/src/main/java/interface_adapter/SearchRecipePresenter.java
@@ -0,0 +1,21 @@
+package interface_adapter;
+
+import entity.Recipe;
+import use_case.search_recipe.SearchRecipeOutputBoundary;
+import use_case.search_recipe.SearchRecipeOutputData;
+
+import java.util.List;
+
+public class SearchRecipePresenter implements SearchRecipeOutputBoundary {
+ private List recipes;
+
+ @Override
+ public void presentRecipes(SearchRecipeOutputData outputData) {
+ this.recipes = outputData.getRecipes();
+ }
+
+ public List getRecipes() {
+ return recipes;
+ }
+
+}
diff --git a/src/main/java/interface_adapter/ShoppingListController.java b/src/main/java/interface_adapter/ShoppingListController.java
new file mode 100644
index 000000000..d9f6f25d8
--- /dev/null
+++ b/src/main/java/interface_adapter/ShoppingListController.java
@@ -0,0 +1,22 @@
+package interface_adapter;
+
+import use_case.ShoppingListUseCase;
+import java.util.List;
+
+public class ShoppingListController {
+ private final ShoppingListUseCase shoppingListUseCase;
+
+ public ShoppingListController(ShoppingListUseCase shoppingListUseCase) {
+ this.shoppingListUseCase = shoppingListUseCase;
+ }
+
+ // Fetch recipes containing available ingredients
+ public List getRecipesContaining(List availableIngredients) {
+ return shoppingListUseCase.getRecipesContaining(availableIngredients);
+ }
+
+ // Generate a shopping list using selected recipes and available ingredients
+ public String generateShoppingList(List selectedRecipes, List availableIngredients) {
+ return shoppingListUseCase.generateShoppingList(selectedRecipes, availableIngredients);
+ }
+}
diff --git a/src/main/java/interface_adapter/change_password/ChangePasswordController.java b/src/main/java/interface_adapter/change_password/ChangePasswordController.java
deleted file mode 100644
index a7abd075f..000000000
--- a/src/main/java/interface_adapter/change_password/ChangePasswordController.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package interface_adapter.change_password;
-
-import use_case.change_password.ChangePasswordInputBoundary;
-import use_case.change_password.ChangePasswordInputData;
-
-/**
- * Controller for the Change Password Use Case.
- */
-public class ChangePasswordController {
- private final ChangePasswordInputBoundary userChangePasswordUseCaseInteractor;
-
- public ChangePasswordController(ChangePasswordInputBoundary userChangePasswordUseCaseInteractor) {
- this.userChangePasswordUseCaseInteractor = userChangePasswordUseCaseInteractor;
- }
-
- /**
- * Executes the Change Password Use Case.
- * @param password the new password
- * @param username the user whose password to change
- */
- public void execute(String password, String username) {
- final ChangePasswordInputData changePasswordInputData = new ChangePasswordInputData(username, password);
-
- userChangePasswordUseCaseInteractor.execute(changePasswordInputData);
- }
-}
diff --git a/src/main/java/interface_adapter/change_password/ChangePasswordPresenter.java b/src/main/java/interface_adapter/change_password/ChangePasswordPresenter.java
deleted file mode 100644
index 3efca4e46..000000000
--- a/src/main/java/interface_adapter/change_password/ChangePasswordPresenter.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package interface_adapter.change_password;
-
-import use_case.change_password.ChangePasswordOutputBoundary;
-import use_case.change_password.ChangePasswordOutputData;
-
-/**
- * The Presenter for the Change Password Use Case.
- */
-public class ChangePasswordPresenter implements ChangePasswordOutputBoundary {
-
- private final LoggedInViewModel loggedInViewModel;
-
- public ChangePasswordPresenter(LoggedInViewModel loggedInViewModel) {
- this.loggedInViewModel = loggedInViewModel;
- }
-
- @Override
- public void prepareSuccessView(ChangePasswordOutputData outputData) {
- // currently there isn't anything to change based on the output data,
- // since the output data only contains the username, which remains the same.
- // We still fire the property changed event, but just to let the view know that
- // it can alert the user that their password was changed successfully..
- loggedInViewModel.firePropertyChanged("password");
-
- }
-
- @Override
- public void prepareFailView(String error) {
- // note: this use case currently can't fail
- }
-}
diff --git a/src/main/java/interface_adapter/change_password/LoggedInState.java b/src/main/java/interface_adapter/change_password/LoggedInState.java
deleted file mode 100644
index 54ed80537..000000000
--- a/src/main/java/interface_adapter/change_password/LoggedInState.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package interface_adapter.change_password;
-
-/**
- * The State information representing the logged-in user.
- */
-public class LoggedInState {
- private String username = "";
-
- private String password = "";
- private String passwordError;
-
- public LoggedInState(LoggedInState copy) {
- username = copy.username;
- password = copy.password;
- passwordError = copy.passwordError;
- }
-
- // Because of the previous copy constructor, the default constructor must be explicit.
- public LoggedInState() {
-
- }
-
- public String getUsername() {
- return username;
- }
-
- public void setUsername(String username) {
- this.username = username;
- }
-
- public void setPassword(String password) {
- this.password = password;
- }
-
- public void setPasswordError(String passwordError) {
- this.passwordError = passwordError;
- }
-
- public String getPassword() {
- return password;
- }
-}
diff --git a/src/main/java/interface_adapter/change_password/LoggedInViewModel.java b/src/main/java/interface_adapter/change_password/LoggedInViewModel.java
deleted file mode 100644
index 85c81e4e6..000000000
--- a/src/main/java/interface_adapter/change_password/LoggedInViewModel.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package interface_adapter.change_password;
-
-import interface_adapter.ViewModel;
-
-/**
- * The View Model for the Logged In View.
- */
-public class LoggedInViewModel extends ViewModel {
-
- public LoggedInViewModel() {
- super("logged in");
- setState(new LoggedInState());
- }
-
-}
diff --git a/src/main/java/interface_adapter/filter_recipes/FilterRecipesController.java b/src/main/java/interface_adapter/filter_recipes/FilterRecipesController.java
new file mode 100644
index 000000000..8d3b46715
--- /dev/null
+++ b/src/main/java/interface_adapter/filter_recipes/FilterRecipesController.java
@@ -0,0 +1,42 @@
+package interface_adapter.filter_recipes;
+
+import java.util.List;
+
+import use_case.filter_recipes.FilterRecipesInputBoundary;
+import use_case.filter_recipes.FilterRecipesInputData;
+
+/**
+ * Controller for the filter recipes use case.
+ */
+public class FilterRecipesController {
+ private final FilterRecipesInputBoundary filterRecipesInteractor;
+
+ public FilterRecipesController(FilterRecipesInputBoundary filterRecipesInteractor) {
+ this.filterRecipesInteractor = filterRecipesInteractor;
+ }
+
+
+ /**
+ * Executes the filter recipes use case.
+ * @param ingredients list of ingredients
+ * @param selectedDiet chosen diet filter
+ * @param selectedCuisine chosen cuisine filter
+ */
+ public void execute(List ingredients, String selectedDiet, String selectedCuisine) {
+ final FilterRecipesInputData filterRecipesInputData =
+ new FilterRecipesInputData(ingredients, selectedDiet, selectedCuisine);
+ filterRecipesInteractor.filterSearchRecipes(filterRecipesInputData);
+ }
+
+// /**
+// * Filter searches recipes.
+// * @param ingredients list of ingredients
+// * @param selectedDiet diet choice
+// * @param selectedCuisine cuisine choice
+// */
+// public void filterSearchRecipes(List ingredients, String selectedDiet, String selectedCuisine) {
+// final FilterRecipesInputData filterRecipesInputData =
+// new FilterRecipesInputData(ingredients, selectedDiet, selectedCuisine);
+// filterRecipesInputBoundary.filterSearchRecipes(filterRecipesInputData);
+// }
+}
diff --git a/src/main/java/interface_adapter/filter_recipes/FilterRecipesPresenter.java b/src/main/java/interface_adapter/filter_recipes/FilterRecipesPresenter.java
new file mode 100644
index 000000000..61feda578
--- /dev/null
+++ b/src/main/java/interface_adapter/filter_recipes/FilterRecipesPresenter.java
@@ -0,0 +1,26 @@
+package interface_adapter.filter_recipes;
+
+import use_case.filter_recipes.FilterRecipesOutputBoundary;
+import use_case.filter_recipes.FilterRecipesOutputData;
+
+/**
+ * Presenter for the filter recipes use case.
+ */
+public class FilterRecipesPresenter implements FilterRecipesOutputBoundary {
+ private final FilterRecipesOutputBoundary filterRecipesOutputBoundary;
+
+ public FilterRecipesPresenter(FilterRecipesOutputBoundary filterRecipesOutputBoundary) {
+ this.filterRecipesOutputBoundary = filterRecipesOutputBoundary;
+ }
+
+ @Override
+ public void prepareSuccessView(FilterRecipesOutputData filterRecipesOutputData) {
+ filterRecipesOutputBoundary.prepareSuccessView(filterRecipesOutputData);
+ }
+
+ @Override
+ public void prepareFailView(String error) {
+ filterRecipesOutputBoundary.prepareFailView(error);
+ }
+
+}
diff --git a/src/main/java/interface_adapter/login/LoginController.java b/src/main/java/interface_adapter/login/LoginController.java
deleted file mode 100644
index 57e950666..000000000
--- a/src/main/java/interface_adapter/login/LoginController.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package interface_adapter.login;
-
-import use_case.login.LoginInputBoundary;
-import use_case.login.LoginInputData;
-
-/**
- * The controller for the Login Use Case.
- */
-public class LoginController {
-
- private final LoginInputBoundary loginUseCaseInteractor;
-
- public LoginController(LoginInputBoundary loginUseCaseInteractor) {
- this.loginUseCaseInteractor = loginUseCaseInteractor;
- }
-
- /**
- * Executes the Login Use Case.
- * @param username the username of the user logging in
- * @param password the password of the user logging in
- */
- public void execute(String username, String password) {
- final LoginInputData loginInputData = new LoginInputData(
- username, password);
-
- loginUseCaseInteractor.execute(loginInputData);
- }
-}
diff --git a/src/main/java/interface_adapter/login/LoginPresenter.java b/src/main/java/interface_adapter/login/LoginPresenter.java
deleted file mode 100644
index 66560d51a..000000000
--- a/src/main/java/interface_adapter/login/LoginPresenter.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package interface_adapter.login;
-
-import interface_adapter.ViewManagerModel;
-import interface_adapter.change_password.LoggedInState;
-import interface_adapter.change_password.LoggedInViewModel;
-import use_case.login.LoginOutputBoundary;
-import use_case.login.LoginOutputData;
-
-/**
- * The Presenter for the Login Use Case.
- */
-public class LoginPresenter implements LoginOutputBoundary {
-
- private final LoginViewModel loginViewModel;
- private final LoggedInViewModel loggedInViewModel;
- private final ViewManagerModel viewManagerModel;
-
- public LoginPresenter(ViewManagerModel viewManagerModel,
- LoggedInViewModel loggedInViewModel,
- LoginViewModel loginViewModel) {
- this.viewManagerModel = viewManagerModel;
- this.loggedInViewModel = loggedInViewModel;
- this.loginViewModel = loginViewModel;
- }
-
- @Override
- public void prepareSuccessView(LoginOutputData response) {
- // On success, switch to the logged in view.
-
- final LoggedInState loggedInState = loggedInViewModel.getState();
- loggedInState.setUsername(response.getUsername());
- this.loggedInViewModel.setState(loggedInState);
- this.loggedInViewModel.firePropertyChanged();
-
- this.viewManagerModel.setState(loggedInViewModel.getViewName());
- this.viewManagerModel.firePropertyChanged();
- }
-
- @Override
- public void prepareFailView(String error) {
- final LoginState loginState = loginViewModel.getState();
- loginState.setLoginError(error);
- loginViewModel.firePropertyChanged();
- }
-}
diff --git a/src/main/java/interface_adapter/login/LoginState.java b/src/main/java/interface_adapter/login/LoginState.java
deleted file mode 100644
index e42632034..000000000
--- a/src/main/java/interface_adapter/login/LoginState.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package interface_adapter.login;
-
-/**
- * The state for the Login View Model.
- */
-public class LoginState {
- private String username = "";
- private String loginError;
- private String password = "";
-
- public String getUsername() {
- return username;
- }
-
- public String getLoginError() {
- return loginError;
- }
-
- public String getPassword() {
- return password;
- }
-
- public void setUsername(String username) {
- this.username = username;
- }
-
- public void setLoginError(String usernameError) {
- this.loginError = usernameError;
- }
-
- public void setPassword(String password) {
- this.password = password;
- }
-
-}
diff --git a/src/main/java/interface_adapter/login/LoginViewModel.java b/src/main/java/interface_adapter/login/LoginViewModel.java
deleted file mode 100644
index 41bea7a2e..000000000
--- a/src/main/java/interface_adapter/login/LoginViewModel.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package interface_adapter.login;
-
-import interface_adapter.ViewModel;
-
-/**
- * The View Model for the Login View.
- */
-public class LoginViewModel extends ViewModel {
-
- public LoginViewModel() {
- super("log in");
- setState(new LoginState());
- }
-
-}
diff --git a/src/main/java/interface_adapter/logout/LogoutController.java b/src/main/java/interface_adapter/logout/LogoutController.java
deleted file mode 100644
index e184a3bba..000000000
--- a/src/main/java/interface_adapter/logout/LogoutController.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package interface_adapter.logout;
-
-import use_case.logout.LogoutInputBoundary;
-
-/**
- * The controller for the Logout Use Case.
- */
-public class LogoutController {
-
- private LogoutInputBoundary logoutUseCaseInteractor;
-
- public LogoutController(LogoutInputBoundary logoutUseCaseInteractor) {
- // TODO: Save the interactor in the instance variable.
- }
-
- /**
- * Executes the Logout Use Case.
- * @param username the username of the user logging in
- */
- public void execute(String username) {
- // TODO: run the use case interactor for the logout use case
- // 1. instantiate the `LogoutInputData`, which should contain the username.
- // 2. tell the Interactor to execute.
- }
-}
diff --git a/src/main/java/interface_adapter/logout/LogoutPresenter.java b/src/main/java/interface_adapter/logout/LogoutPresenter.java
deleted file mode 100644
index 78ef306a1..000000000
--- a/src/main/java/interface_adapter/logout/LogoutPresenter.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package interface_adapter.logout;
-
-import interface_adapter.ViewManagerModel;
-import interface_adapter.change_password.LoggedInViewModel;
-import interface_adapter.login.LoginViewModel;
-import use_case.logout.LogoutOutputBoundary;
-import use_case.logout.LogoutOutputData;
-
-/**
- * The Presenter for the Logout Use Case.
- */
-public class LogoutPresenter implements LogoutOutputBoundary {
-
- private LoggedInViewModel loggedInViewModel;
- private ViewManagerModel viewManagerModel;
- private LoginViewModel loginViewModel;
-
- public LogoutPresenter(ViewManagerModel viewManagerModel,
- LoggedInViewModel loggedInViewModel,
- LoginViewModel loginViewModel) {
- // TODO: assign to the three instance variables.
- }
-
- @Override
- public void prepareSuccessView(LogoutOutputData response) {
- // We need to switch to the login view, which should have
- // an empty username and password.
-
- // We also need to set the username in the LoggedInState to
- // the empty string.
-
- // TODO: have prepareSuccessView update the LoggedInState
- // 1. get the LoggedInState out of the appropriate View Model,
- // 2. set the username in the state to the empty string
- // 3. set the state in the LoggedInViewModel to the updated state
- // 4. firePropertyChanged so that the View that is listening is updated.
-
- // TODO: have prepareSuccessView update the LoginState
- // 5. get the LoginState out of the appropriate View Model,
- // 6. set the username and password in the state to the empty string
- // 7. set the state in the LoginViewModel to the updated state
- // 8. firePropertyChanged so that the View that is listening is updated.
-
- // This code tells the View Manager to switch to the LoginView.
- this.viewManagerModel.setState(loginViewModel.getViewName());
- this.viewManagerModel.firePropertyChanged();
- }
-
- @Override
- public void prepareFailView(String error) {
- // No need to add code here. We'll assume that logout can't fail.
- // Thought question: is this a reasonable assumption?
- }
-}
diff --git a/src/main/java/interface_adapter/nutrition_information/NutritionInformationPresenter.java b/src/main/java/interface_adapter/nutrition_information/NutritionInformationPresenter.java
new file mode 100644
index 000000000..97510612a
--- /dev/null
+++ b/src/main/java/interface_adapter/nutrition_information/NutritionInformationPresenter.java
@@ -0,0 +1,14 @@
+package interface_adapter.nutrition_information;
+
+import entity.Nutrition;
+import use_case.nutrition_information.NutritionInformationOutputBoundary;
+import use_case.nutrition_information.NutritionInformationOutputData;
+
+import java.util.List;
+
+public class NutritionInformationPresenter implements NutritionInformationOutputBoundary {
+ @Override
+ public List presentNutritionInformation(NutritionInformationOutputData outputData) {
+ return outputData.getNutritionList();
+ }
+}
diff --git a/src/main/java/interface_adapter/search_recipe_list_by_ingredient/SearchRecipeListByIngredientController.java b/src/main/java/interface_adapter/search_recipe_list_by_ingredient/SearchRecipeListByIngredientController.java
new file mode 100644
index 000000000..42f3a4000
--- /dev/null
+++ b/src/main/java/interface_adapter/search_recipe_list_by_ingredient/SearchRecipeListByIngredientController.java
@@ -0,0 +1,32 @@
+package interface_adapter.search_recipe_list_by_ingredient;
+
+import entity.User;
+import use_case.search_recipe_list_by_ingredient.SearchRecipeListByIngredientInputBoundary;
+import use_case.search_recipe_list_by_ingredient.SearchRecipeListByIngredientInputData;
+
+import java.util.List;
+
+/**
+ * Controller for the Search Recipe List By Ingredient Use Case.
+ */
+public class SearchRecipeListByIngredientController {
+ private final SearchRecipeListByIngredientInputBoundary searchRecipeListByIngredientUseCaseInteractor;
+
+ public SearchRecipeListByIngredientController(
+ SearchRecipeListByIngredientInputBoundary searchRecipeListByIngredientUseCaseInteractor) {
+ this.searchRecipeListByIngredientUseCaseInteractor = searchRecipeListByIngredientUseCaseInteractor;
+ }
+
+ /**
+ * Executes the Search Recipe List By Ingredient Use Case.
+ * @param ingredients the ingredients entered by the user.
+ * @param user the user.
+ * @param folder the folder to search in.
+ */
+ public void execute(List ingredients, User user, String folder) {
+ final SearchRecipeListByIngredientInputData searchRecipeListByIngredientInputData =
+ new SearchRecipeListByIngredientInputData(ingredients, user, folder);
+
+ searchRecipeListByIngredientUseCaseInteractor.execute(searchRecipeListByIngredientInputData);
+ }
+}
diff --git a/src/main/java/interface_adapter/search_recipe_list_by_ingredient/SearchRecipeListByIngredientPresenter.java b/src/main/java/interface_adapter/search_recipe_list_by_ingredient/SearchRecipeListByIngredientPresenter.java
new file mode 100644
index 000000000..d01709cef
--- /dev/null
+++ b/src/main/java/interface_adapter/search_recipe_list_by_ingredient/SearchRecipeListByIngredientPresenter.java
@@ -0,0 +1,31 @@
+package interface_adapter.search_recipe_list_by_ingredient;
+
+import interface_adapter.RecipeListState;
+import interface_adapter.RecipeListViewModel;
+import use_case.search_recipe_list_by_ingredient.SearchRecipeListByIngredientOutputBoundary;
+import use_case.search_recipe_list_by_ingredient.SearchRecipeListByIngredientOutputData;
+
+/**
+ * The Presenter for the Search Recipe List By Ingredients Use Case.
+ */
+public class SearchRecipeListByIngredientPresenter implements SearchRecipeListByIngredientOutputBoundary {
+
+ private final RecipeListViewModel recipeListViewModel;
+
+ public SearchRecipeListByIngredientPresenter(RecipeListViewModel recipeListViewModel) {
+ this.recipeListViewModel = recipeListViewModel;
+ }
+
+ @Override
+ public void prepareSuccessView(SearchRecipeListByIngredientOutputData outputData) {
+ // TODO
+ final RecipeListState recipeListState = recipeListViewModel.getState();
+ recipeListState.setRecipeList(outputData.getRecipes());
+ recipeListViewModel.firePropertyChanged("recipes");
+ }
+
+ @Override
+ public void prepareFailView(String error) {
+ // note: this use case currently can't fail
+ }
+}
diff --git a/src/main/java/interface_adapter/search_recipe_list_by_name/SearchRecipeListByNameController.java b/src/main/java/interface_adapter/search_recipe_list_by_name/SearchRecipeListByNameController.java
new file mode 100644
index 000000000..44502a065
--- /dev/null
+++ b/src/main/java/interface_adapter/search_recipe_list_by_name/SearchRecipeListByNameController.java
@@ -0,0 +1,32 @@
+package interface_adapter.search_recipe_list_by_name;
+
+import entity.User;
+import use_case.search_recipe_list_by_name.SearchRecipeListByNameInputBoundary;
+import use_case.search_recipe_list_by_name.SearchRecipeListByNameInputData;
+
+import java.util.List;
+
+/**
+ * Controller for the Search Recipe List By Name Use Case.
+ */
+public class SearchRecipeListByNameController {
+ private final SearchRecipeListByNameInputBoundary searchRecipeListByNameUseCaseInteractor;
+
+ public SearchRecipeListByNameController(
+ SearchRecipeListByNameInputBoundary searchRecipeListByNameUseCaseInteractor) {
+ this.searchRecipeListByNameUseCaseInteractor = searchRecipeListByNameUseCaseInteractor;
+ }
+
+ /**
+ * Executes the Search Recipe List By Name Use Case.
+ * @param recipeName the recipe name entered by the user.
+ * @param user the user.
+ * @param folder the folder to search in.
+ */
+ public void execute(String recipeName, User user, String folder) {
+ final SearchRecipeListByNameInputData searchRecipeListByNameInputData =
+ new SearchRecipeListByNameInputData(recipeName, user, folder);
+
+ searchRecipeListByNameUseCaseInteractor.execute(searchRecipeListByNameInputData);
+ }
+}
diff --git a/src/main/java/interface_adapter/search_recipe_list_by_name/SearchRecipeListByNamePresenter.java b/src/main/java/interface_adapter/search_recipe_list_by_name/SearchRecipeListByNamePresenter.java
new file mode 100644
index 000000000..b252735e4
--- /dev/null
+++ b/src/main/java/interface_adapter/search_recipe_list_by_name/SearchRecipeListByNamePresenter.java
@@ -0,0 +1,31 @@
+package interface_adapter.search_recipe_list_by_name;
+
+import interface_adapter.RecipeListState;
+import interface_adapter.RecipeListViewModel;
+import use_case.search_recipe_list_by_name.SearchRecipeListByNameOutputBoundary;
+import use_case.search_recipe_list_by_name.SearchRecipeListByNameOutputData;
+
+/**
+ * The Presenter for the Search Recipe List By Name Use Case.
+ */
+public class SearchRecipeListByNamePresenter implements SearchRecipeListByNameOutputBoundary {
+
+ private final RecipeListViewModel recipeListViewModel;
+
+ public SearchRecipeListByNamePresenter(RecipeListViewModel recipeListViewModel) {
+ this.recipeListViewModel = recipeListViewModel;
+ }
+
+ @Override
+ public void prepareSuccessView(SearchRecipeListByNameOutputData outputData) {
+ // TODO
+ final RecipeListState recipeListState = recipeListViewModel.getState();
+ recipeListState.setRecipeList(outputData.getRecipes());
+ recipeListViewModel.firePropertyChanged("recipes");
+ }
+
+ @Override
+ public void prepareFailView(String error) {
+ // note: this use case currently can't fail
+ }
+}
diff --git a/src/main/java/interface_adapter/signup/SignupController.java b/src/main/java/interface_adapter/signup/SignupController.java
deleted file mode 100644
index c01d25aa5..000000000
--- a/src/main/java/interface_adapter/signup/SignupController.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package interface_adapter.signup;
-
-import use_case.signup.SignupInputBoundary;
-import use_case.signup.SignupInputData;
-
-/**
- * Controller for the Signup Use Case.
- */
-public class SignupController {
-
- private final SignupInputBoundary userSignupUseCaseInteractor;
-
- public SignupController(SignupInputBoundary userSignupUseCaseInteractor) {
- this.userSignupUseCaseInteractor = userSignupUseCaseInteractor;
- }
-
- /**
- * Executes the Signup Use Case.
- * @param username the username to sign up
- * @param password1 the password
- * @param password2 the password repeated
- */
- public void execute(String username, String password1, String password2) {
- final SignupInputData signupInputData = new SignupInputData(
- username, password1, password2);
-
- userSignupUseCaseInteractor.execute(signupInputData);
- }
-
- /**
- * Executes the "switch to LoginView" Use Case.
- */
- public void switchToLoginView() {
- userSignupUseCaseInteractor.switchToLoginView();
- }
-}
diff --git a/src/main/java/interface_adapter/signup/SignupPresenter.java b/src/main/java/interface_adapter/signup/SignupPresenter.java
deleted file mode 100644
index 9b654077e..000000000
--- a/src/main/java/interface_adapter/signup/SignupPresenter.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package interface_adapter.signup;
-
-import interface_adapter.ViewManagerModel;
-import interface_adapter.login.LoginState;
-import interface_adapter.login.LoginViewModel;
-import use_case.signup.SignupOutputBoundary;
-import use_case.signup.SignupOutputData;
-
-/**
- * The Presenter for the Signup Use Case.
- */
-public class SignupPresenter implements SignupOutputBoundary {
-
- private final SignupViewModel signupViewModel;
- private final LoginViewModel loginViewModel;
- private final ViewManagerModel viewManagerModel;
-
- public SignupPresenter(ViewManagerModel viewManagerModel,
- SignupViewModel signupViewModel,
- LoginViewModel loginViewModel) {
- this.viewManagerModel = viewManagerModel;
- this.signupViewModel = signupViewModel;
- this.loginViewModel = loginViewModel;
- }
-
- @Override
- public void prepareSuccessView(SignupOutputData response) {
- // On success, switch to the login view.
- final LoginState loginState = loginViewModel.getState();
- loginState.setUsername(response.getUsername());
- this.loginViewModel.setState(loginState);
- loginViewModel.firePropertyChanged();
-
- viewManagerModel.setState(loginViewModel.getViewName());
- viewManagerModel.firePropertyChanged();
- }
-
- @Override
- public void prepareFailView(String error) {
- final SignupState signupState = signupViewModel.getState();
- signupState.setUsernameError(error);
- signupViewModel.firePropertyChanged();
- }
-
- @Override
- public void switchToLoginView() {
- viewManagerModel.setState(loginViewModel.getViewName());
- viewManagerModel.firePropertyChanged();
- }
-}
diff --git a/src/main/java/interface_adapter/signup/SignupState.java b/src/main/java/interface_adapter/signup/SignupState.java
deleted file mode 100644
index 4a7f69327..000000000
--- a/src/main/java/interface_adapter/signup/SignupState.java
+++ /dev/null
@@ -1,70 +0,0 @@
-package interface_adapter.signup;
-
-/**
- * The state for the Signup View Model.
- */
-public class SignupState {
- private String username = "";
- private String usernameError;
- private String password = "";
- private String passwordError;
- private String repeatPassword = "";
- private String repeatPasswordError;
-
- public String getUsername() {
- return username;
- }
-
- public String getUsernameError() {
- return usernameError;
- }
-
- public String getPassword() {
- return password;
- }
-
- public String getPasswordError() {
- return passwordError;
- }
-
- public String getRepeatPassword() {
- return repeatPassword;
- }
-
- public String getRepeatPasswordError() {
- return repeatPasswordError;
- }
-
- public void setUsername(String username) {
- this.username = username;
- }
-
- public void setUsernameError(String usernameError) {
- this.usernameError = usernameError;
- }
-
- public void setPassword(String password) {
- this.password = password;
- }
-
- public void setPasswordError(String passwordError) {
- this.passwordError = passwordError;
- }
-
- public void setRepeatPassword(String repeatPassword) {
- this.repeatPassword = repeatPassword;
- }
-
- public void setRepeatPasswordError(String repeatPasswordError) {
- this.repeatPasswordError = repeatPasswordError;
- }
-
- @Override
- public String toString() {
- return "SignupState{"
- + "username='" + username + '\''
- + ", password='" + password + '\''
- + ", repeatPassword='" + repeatPassword + '\''
- + '}';
- }
-}
diff --git a/src/main/java/interface_adapter/signup/SignupViewModel.java b/src/main/java/interface_adapter/signup/SignupViewModel.java
deleted file mode 100644
index 01f0086bc..000000000
--- a/src/main/java/interface_adapter/signup/SignupViewModel.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package interface_adapter.signup;
-
-import interface_adapter.ViewModel;
-
-/**
- * The ViewModel for the Signup View.
- */
-public class SignupViewModel extends ViewModel {
-
- public static final String TITLE_LABEL = "Sign Up View";
- public static final String USERNAME_LABEL = "Choose username";
- public static final String PASSWORD_LABEL = "Choose password";
- public static final String REPEAT_PASSWORD_LABEL = "Enter password again";
-
- public static final String SIGNUP_BUTTON_LABEL = "Sign up";
- public static final String CANCEL_BUTTON_LABEL = "Cancel";
-
- public static final String TO_LOGIN_BUTTON_LABEL = "Go to Login";
-
- public SignupViewModel() {
- super("sign up");
- setState(new SignupState());
- }
-
-}
diff --git a/src/main/java/use_case/ShoppingListUseCase.java b/src/main/java/use_case/ShoppingListUseCase.java
new file mode 100644
index 000000000..331649a58
--- /dev/null
+++ b/src/main/java/use_case/ShoppingListUseCase.java
@@ -0,0 +1,24 @@
+package use_case;
+
+import data_access.SpoonacularAPI;
+import java.util.List;
+
+public class ShoppingListUseCase {
+ private final SpoonacularAPI api;
+
+ public ShoppingListUseCase(SpoonacularAPI api) {
+ this.api = api;
+ }
+
+ public List getRecipesContaining(List availableIngredients) {
+ return api.getRecipesContaining(availableIngredients);
+ }
+
+ public String generateShoppingList(List selectedRecipes, List availableIngredients) {
+ List shoppingList = api.getMissingIngredients(selectedRecipes, availableIngredients);
+ if (shoppingList.isEmpty()) {
+ return "You already have all the ingredients!";
+ }
+ return String.join("\n", shoppingList);
+ }
+}
diff --git a/src/main/java/use_case/ViewNutritionDataAccessInterface.java b/src/main/java/use_case/ViewNutritionDataAccessInterface.java
new file mode 100644
index 000000000..da088d83f
--- /dev/null
+++ b/src/main/java/use_case/ViewNutritionDataAccessInterface.java
@@ -0,0 +1,10 @@
+package use_case;
+
+import java.util.List;
+
+import entity.Nutrition;
+
+public interface ViewNutritionDataAccessInterface {
+
+ List getNutritionDataForRecipe(int recipeId);
+}
diff --git a/src/main/java/use_case/change_password/ChangePasswordInputBoundary.java b/src/main/java/use_case/change_password/ChangePasswordInputBoundary.java
deleted file mode 100644
index 06ae9448e..000000000
--- a/src/main/java/use_case/change_password/ChangePasswordInputBoundary.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package use_case.change_password;
-
-/**
- * The Change Password Use Case.
- */
-public interface ChangePasswordInputBoundary {
-
- /**
- * Execute the Change Password Use Case.
- * @param changePasswordInputData the input data for this use case
- */
- void execute(ChangePasswordInputData changePasswordInputData);
-
-}
diff --git a/src/main/java/use_case/change_password/ChangePasswordInputData.java b/src/main/java/use_case/change_password/ChangePasswordInputData.java
deleted file mode 100644
index 8e09d8d12..000000000
--- a/src/main/java/use_case/change_password/ChangePasswordInputData.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package use_case.change_password;
-
-/**
- * The input data for the Change Password Use Case.
- */
-public class ChangePasswordInputData {
-
- private final String password;
- private final String username;
-
- public ChangePasswordInputData(String password, String username) {
- this.password = password;
- this.username = username;
- }
-
- String getPassword() {
- return password;
- }
-
- String getUsername() {
- return username;
- }
-
-}
diff --git a/src/main/java/use_case/change_password/ChangePasswordInteractor.java b/src/main/java/use_case/change_password/ChangePasswordInteractor.java
deleted file mode 100644
index df91196c1..000000000
--- a/src/main/java/use_case/change_password/ChangePasswordInteractor.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package use_case.change_password;
-
-import entity.User;
-import entity.UserFactory;
-
-/**
- * The Change Password Interactor.
- */
-public class ChangePasswordInteractor implements ChangePasswordInputBoundary {
- private final ChangePasswordUserDataAccessInterface userDataAccessObject;
- private final ChangePasswordOutputBoundary userPresenter;
- private final UserFactory userFactory;
-
- public ChangePasswordInteractor(ChangePasswordUserDataAccessInterface changePasswordDataAccessInterface,
- ChangePasswordOutputBoundary changePasswordOutputBoundary,
- UserFactory userFactory) {
- this.userDataAccessObject = changePasswordDataAccessInterface;
- this.userPresenter = changePasswordOutputBoundary;
- this.userFactory = userFactory;
- }
-
- @Override
- public void execute(ChangePasswordInputData changePasswordInputData) {
- final User user = userFactory.create(changePasswordInputData.getUsername(),
- changePasswordInputData.getPassword());
- userDataAccessObject.changePassword(user);
-
- final ChangePasswordOutputData changePasswordOutputData = new ChangePasswordOutputData(user.getName(),
- false);
- userPresenter.prepareSuccessView(changePasswordOutputData);
- }
-}
diff --git a/src/main/java/use_case/change_password/ChangePasswordOutputBoundary.java b/src/main/java/use_case/change_password/ChangePasswordOutputBoundary.java
deleted file mode 100644
index fce28367b..000000000
--- a/src/main/java/use_case/change_password/ChangePasswordOutputBoundary.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package use_case.change_password;
-
-/**
- * The output boundary for the Change Password Use Case.
- */
-public interface ChangePasswordOutputBoundary {
- /**
- * Prepares the success view for the Change Password Use Case.
- * @param outputData the output data
- */
- void prepareSuccessView(ChangePasswordOutputData outputData);
-
- /**
- * Prepares the failure view for the Change Password Use Case.
- * @param errorMessage the explanation of the failure
- */
- void prepareFailView(String errorMessage);
-}
diff --git a/src/main/java/use_case/change_password/ChangePasswordOutputData.java b/src/main/java/use_case/change_password/ChangePasswordOutputData.java
deleted file mode 100644
index b47b83fbc..000000000
--- a/src/main/java/use_case/change_password/ChangePasswordOutputData.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package use_case.change_password;
-
-/**
- * Output Data for the Change Password Use Case.
- */
-public class ChangePasswordOutputData {
-
- private final String username;
-
- private final boolean useCaseFailed;
-
- public ChangePasswordOutputData(String username, boolean useCaseFailed) {
- this.username = username;
- this.useCaseFailed = useCaseFailed;
- }
-
- public String getUsername() {
- return username;
- }
-
- public boolean isUseCaseFailed() {
- return useCaseFailed;
- }
-}
diff --git a/src/main/java/use_case/change_password/ChangePasswordUserDataAccessInterface.java b/src/main/java/use_case/change_password/ChangePasswordUserDataAccessInterface.java
deleted file mode 100644
index 6b73ab6b0..000000000
--- a/src/main/java/use_case/change_password/ChangePasswordUserDataAccessInterface.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package use_case.change_password;
-
-import entity.User;
-
-/**
- * The interface of the DAO for the Change Password Use Case.
- */
-public interface ChangePasswordUserDataAccessInterface {
-
- /**
- * Updates the system to record this user's password.
- * @param user the user whose password is to be updated
- */
- void changePassword(User user);
-}
diff --git a/src/main/java/use_case/filter_recipes/FilterRecipesDataAccessInterface.java b/src/main/java/use_case/filter_recipes/FilterRecipesDataAccessInterface.java
new file mode 100644
index 000000000..6b6e9a7bd
--- /dev/null
+++ b/src/main/java/use_case/filter_recipes/FilterRecipesDataAccessInterface.java
@@ -0,0 +1,33 @@
+package use_case.filter_recipes;
+
+import java.util.List;
+
+import entity.Recipe;
+
+/**
+ * DAO for the filter recipes use case.
+ */
+public interface FilterRecipesDataAccessInterface {
+
+ /**
+ * Filters recipes by ingredients, optional diet and cuisine.
+ * @param ingredients list of ingredients
+ * @param diet diet choice, optional
+ * @param cuisine cuisine choice, optional
+ * @return a list of filtered recipes
+ */
+ List filterSearchRecipes(List ingredients, String diet, String cuisine);
+
+ /**
+ * Get available diets.
+ * @return list of diets
+ */
+ List getAvailableDiets();
+
+ /**
+ * Get available cuisines.
+ * @return list of cuisines
+ */
+ List getAvailableCuisines();
+
+}
diff --git a/src/main/java/use_case/filter_recipes/FilterRecipesInputBoundary.java b/src/main/java/use_case/filter_recipes/FilterRecipesInputBoundary.java
new file mode 100644
index 000000000..be8fbbca1
--- /dev/null
+++ b/src/main/java/use_case/filter_recipes/FilterRecipesInputBoundary.java
@@ -0,0 +1,28 @@
+package use_case.filter_recipes;
+
+import java.util.List;
+
+/**
+ * The filter recipes input boundary interface for calling the use case.
+ */
+public interface FilterRecipesInputBoundary {
+
+ /**
+ * Get available diets.
+ * @return list of available diets
+ */
+ List getAvailableDiets();
+
+ /**
+ * Get available cuisines.
+ * @return list of available cuisines
+ */
+ List getAvailableCuisines();
+
+ /**
+ * Filter searches recipes.
+ *
+ * @param filterRecipesInputData input data
+ */
+ void filterSearchRecipes(FilterRecipesInputData filterRecipesInputData);
+}
diff --git a/src/main/java/use_case/filter_recipes/FilterRecipesInputData.java b/src/main/java/use_case/filter_recipes/FilterRecipesInputData.java
new file mode 100644
index 000000000..e1967df38
--- /dev/null
+++ b/src/main/java/use_case/filter_recipes/FilterRecipesInputData.java
@@ -0,0 +1,33 @@
+package use_case.filter_recipes;
+
+import java.util.List;
+
+/**
+ * The input data for the filter recipes use case.
+ */
+public class FilterRecipesInputData {
+
+ private final List ingredients;
+ private final String diet;
+ private final String cuisine;
+
+ public FilterRecipesInputData(List ingredients, String diet, String cuisine) {
+ this.ingredients = ingredients;
+ this.diet = diet;
+ this.cuisine = cuisine;
+ }
+
+ public List getIngredients() {
+ return ingredients;
+ }
+
+ public String getDiet() {
+ return diet;
+ }
+
+ public String getCuisine() {
+ return cuisine;
+ }
+
+}
+
diff --git a/src/main/java/use_case/filter_recipes/FilterRecipesInteractor.java b/src/main/java/use_case/filter_recipes/FilterRecipesInteractor.java
new file mode 100644
index 000000000..cc6707051
--- /dev/null
+++ b/src/main/java/use_case/filter_recipes/FilterRecipesInteractor.java
@@ -0,0 +1,45 @@
+package use_case.filter_recipes;
+
+import java.util.List;
+
+import entity.Recipe;
+
+/**
+ * The Filter Recipes Interactor.
+ */
+public class FilterRecipesInteractor implements FilterRecipesInputBoundary {
+
+ private final FilterRecipesDataAccessInterface userDataAccessObject;
+ private FilterRecipesOutputBoundary outputBoundary;
+
+ public FilterRecipesInteractor(FilterRecipesDataAccessInterface userDataAccessObject,
+ FilterRecipesOutputBoundary outputBoundary) {
+ this.userDataAccessObject = userDataAccessObject;
+ this.outputBoundary = outputBoundary;
+ }
+
+ public FilterRecipesInteractor(FilterRecipesDataAccessInterface userDataAccessObject) {
+ this.userDataAccessObject = userDataAccessObject;
+ }
+
+ @Override
+ public List getAvailableDiets() {
+ return userDataAccessObject.getAvailableDiets();
+ }
+
+ @Override
+ public List getAvailableCuisines() {
+ return userDataAccessObject.getAvailableCuisines();
+ }
+
+ @Override
+ public void filterSearchRecipes(FilterRecipesInputData filterRecipesInputData) {
+ final List ingredients = filterRecipesInputData.getIngredients();
+ final String diet = filterRecipesInputData.getDiet();
+ final String cuisine = filterRecipesInputData.getCuisine();
+ final List filteredRecipes = userDataAccessObject.filterSearchRecipes(ingredients, diet, cuisine);
+ final FilterRecipesOutputData filterRecipesOutputData = new FilterRecipesOutputData(filteredRecipes);
+ outputBoundary.prepareSuccessView(filterRecipesOutputData);
+ }
+
+}
diff --git a/src/main/java/use_case/filter_recipes/FilterRecipesOutputBoundary.java b/src/main/java/use_case/filter_recipes/FilterRecipesOutputBoundary.java
new file mode 100644
index 000000000..782cdfa47
--- /dev/null
+++ b/src/main/java/use_case/filter_recipes/FilterRecipesOutputBoundary.java
@@ -0,0 +1,20 @@
+package use_case.filter_recipes;
+
+/**
+ * The output boundary for filter recipes use case.
+ */
+public interface FilterRecipesOutputBoundary {
+
+ /**
+ * Prepare the success view for filter recipes use case.
+ * @param outputData the output data
+ */
+ void prepareSuccessView(FilterRecipesOutputData outputData);
+
+ /**
+ * Prepare the failure view for filter recipes use case.
+ * @param errorMessage the explanation of the failure
+ */
+ void prepareFailView(String errorMessage);
+
+}
diff --git a/src/main/java/use_case/filter_recipes/FilterRecipesOutputData.java b/src/main/java/use_case/filter_recipes/FilterRecipesOutputData.java
new file mode 100644
index 000000000..d23912b03
--- /dev/null
+++ b/src/main/java/use_case/filter_recipes/FilterRecipesOutputData.java
@@ -0,0 +1,21 @@
+package use_case.filter_recipes;
+
+import java.util.List;
+
+import entity.Recipe;
+
+/**
+ * Output data for the filter recipes use case.
+ */
+public class FilterRecipesOutputData {
+
+ private final List recipes;
+
+ public FilterRecipesOutputData(List recipes) {
+ this.recipes = recipes;
+ }
+
+ public List getRecipes() {
+ return recipes;
+ }
+}
diff --git a/src/main/java/use_case/login/LoginInputBoundary.java b/src/main/java/use_case/login/LoginInputBoundary.java
deleted file mode 100644
index faf72dc96..000000000
--- a/src/main/java/use_case/login/LoginInputBoundary.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package use_case.login;
-
-/**
- * Input Boundary for actions which are related to logging in.
- */
-public interface LoginInputBoundary {
-
- /**
- * Executes the login use case.
- * @param loginInputData the input data
- */
- void execute(LoginInputData loginInputData);
-}
diff --git a/src/main/java/use_case/login/LoginInputData.java b/src/main/java/use_case/login/LoginInputData.java
deleted file mode 100644
index 363316832..000000000
--- a/src/main/java/use_case/login/LoginInputData.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package use_case.login;
-
-/**
- * The Input Data for the Login Use Case.
- */
-public class LoginInputData {
-
- private final String username;
- private final String password;
-
- public LoginInputData(String username, String password) {
- this.username = username;
- this.password = password;
- }
-
- String getUsername() {
- return username;
- }
-
- String getPassword() {
- return password;
- }
-
-}
diff --git a/src/main/java/use_case/login/LoginInteractor.java b/src/main/java/use_case/login/LoginInteractor.java
deleted file mode 100644
index 5b36ddcd8..000000000
--- a/src/main/java/use_case/login/LoginInteractor.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package use_case.login;
-
-import entity.User;
-
-/**
- * The Login Interactor.
- */
-public class LoginInteractor implements LoginInputBoundary {
- private final LoginUserDataAccessInterface userDataAccessObject;
- private final LoginOutputBoundary loginPresenter;
-
- public LoginInteractor(LoginUserDataAccessInterface userDataAccessInterface,
- LoginOutputBoundary loginOutputBoundary) {
- this.userDataAccessObject = userDataAccessInterface;
- this.loginPresenter = loginOutputBoundary;
- }
-
- @Override
- public void execute(LoginInputData loginInputData) {
- final String username = loginInputData.getUsername();
- final String password = loginInputData.getPassword();
- if (!userDataAccessObject.existsByName(username)) {
- loginPresenter.prepareFailView(username + ": Account does not exist.");
- }
- else {
- final String pwd = userDataAccessObject.get(username).getPassword();
- if (!password.equals(pwd)) {
- loginPresenter.prepareFailView("Incorrect password for \"" + username + "\".");
- }
- else {
-
- final User user = userDataAccessObject.get(loginInputData.getUsername());
-
- userDataAccessObject.setCurrentUsername(user.getName());
- final LoginOutputData loginOutputData = new LoginOutputData(user.getName(), false);
- loginPresenter.prepareSuccessView(loginOutputData);
- }
- }
- }
-}
diff --git a/src/main/java/use_case/login/LoginOutputBoundary.java b/src/main/java/use_case/login/LoginOutputBoundary.java
deleted file mode 100644
index 08bc4731f..000000000
--- a/src/main/java/use_case/login/LoginOutputBoundary.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package use_case.login;
-
-/**
- * The output boundary for the Login Use Case.
- */
-public interface LoginOutputBoundary {
- /**
- * Prepares the success view for the Login Use Case.
- * @param outputData the output data
- */
- void prepareSuccessView(LoginOutputData outputData);
-
- /**
- * Prepares the failure view for the Login Use Case.
- * @param errorMessage the explanation of the failure
- */
- void prepareFailView(String errorMessage);
-}
diff --git a/src/main/java/use_case/login/LoginOutputData.java b/src/main/java/use_case/login/LoginOutputData.java
deleted file mode 100644
index 3ea119a8f..000000000
--- a/src/main/java/use_case/login/LoginOutputData.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package use_case.login;
-
-/**
- * Output Data for the Login Use Case.
- */
-public class LoginOutputData {
-
- private final String username;
- private final boolean useCaseFailed;
-
- public LoginOutputData(String username, boolean useCaseFailed) {
- this.username = username;
- this.useCaseFailed = useCaseFailed;
- }
-
- public String getUsername() {
- return username;
- }
-
-}
diff --git a/src/main/java/use_case/login/LoginUserDataAccessInterface.java b/src/main/java/use_case/login/LoginUserDataAccessInterface.java
deleted file mode 100644
index 681e8a52e..000000000
--- a/src/main/java/use_case/login/LoginUserDataAccessInterface.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package use_case.login;
-
-import entity.User;
-
-/**
- * DAO for the Login Use Case.
- */
-public interface LoginUserDataAccessInterface {
-
- /**
- * Checks if the given username exists.
- * @param username the username to look for
- * @return true if a user with the given username exists; false otherwise
- */
- boolean existsByName(String username);
-
- /**
- * Saves the user.
- * @param user the user to save
- */
- void save(User user);
-
- /**
- * Returns the user with the given username.
- * @param username the username to look up
- * @return the user with the given username
- */
- User get(String username);
-
- /**
- * Returns the username of the curren user of the application.
- * @return the username of the current user; null indicates that no one is logged into the application.
- */
- String getCurrentUsername();
-
- /**
- * Sets the username indicating who is the current user of the application.
- * @param username the new current username; null to indicate that no one is currently logged into the application.
- */
- void setCurrentUsername(String username);
-}
diff --git a/src/main/java/use_case/logout/LogoutInputBoundary.java b/src/main/java/use_case/logout/LogoutInputBoundary.java
deleted file mode 100644
index 189f50168..000000000
--- a/src/main/java/use_case/logout/LogoutInputBoundary.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package use_case.logout;
-
-/**
- * Input Boundary for actions which are related to logging in.
- */
-public interface LogoutInputBoundary {
-
- /**
- * Executes the Logout use case.
- * @param LogoutInputData the input data
- */
- void execute(LogoutInputData LogoutInputData);
-}
diff --git a/src/main/java/use_case/logout/LogoutInputData.java b/src/main/java/use_case/logout/LogoutInputData.java
deleted file mode 100644
index 56a33b375..000000000
--- a/src/main/java/use_case/logout/LogoutInputData.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package use_case.logout;
-
-/**
- * The Input Data for the Logout Use Case.
- */
-public class LogoutInputData {
-
- public LogoutInputData(String username) {
- // TODO: save the current username in an instance variable and add a getter.
- }
-
-}
diff --git a/src/main/java/use_case/logout/LogoutInteractor.java b/src/main/java/use_case/logout/LogoutInteractor.java
deleted file mode 100644
index 1ca93b44e..000000000
--- a/src/main/java/use_case/logout/LogoutInteractor.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package use_case.logout;
-
-/**
- * The Logout Interactor.
- */
-public class LogoutInteractor implements LogoutInputBoundary {
- private LogoutUserDataAccessInterface userDataAccessObject;
- private LogoutOutputBoundary logoutPresenter;
-
- public LogoutInteractor(LogoutUserDataAccessInterface userDataAccessInterface,
- LogoutOutputBoundary logoutOutputBoundary) {
- // TODO: save the DAO and Presenter in the instance variables.
- // Which parameter is the DAO and which is the presenter?
- }
-
- @Override
- public void execute(LogoutInputData logoutInputData) {
- // TODO: implement the logic of the Logout Use Case (depends on the LogoutInputData.java TODO)
- // * get the username out of the input data,
- // * set the username to null in the DAO
- // * instantiate the `LogoutOutputData`, which needs to contain the username.
- // * tell the presenter to prepare a success view.
- }
-}
-
diff --git a/src/main/java/use_case/logout/LogoutOutputBoundary.java b/src/main/java/use_case/logout/LogoutOutputBoundary.java
deleted file mode 100644
index 935a06bdc..000000000
--- a/src/main/java/use_case/logout/LogoutOutputBoundary.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package use_case.logout;
-
-/**
- * The output boundary for the Login Use Case.
- */
-public interface LogoutOutputBoundary {
- /**
- * Prepares the success view for the Login Use Case.
- * @param outputData the output data
- */
- void prepareSuccessView(LogoutOutputData outputData);
-
- /**
- * Prepares the failure view for the Login Use Case.
- * @param errorMessage the explanation of the failure
- */
- void prepareFailView(String errorMessage);
-}
diff --git a/src/main/java/use_case/logout/LogoutOutputData.java b/src/main/java/use_case/logout/LogoutOutputData.java
deleted file mode 100644
index 974279155..000000000
--- a/src/main/java/use_case/logout/LogoutOutputData.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package use_case.logout;
-
-/**
- * Output Data for the Logout Use Case.
- */
-public class LogoutOutputData {
-
- private String username;
- private boolean useCaseFailed;
-
- public LogoutOutputData(String username, boolean useCaseFailed) {
- // TODO: save the parameters in the instance variables.
- }
-
- public String getUsername() {
- return username;
- }
-
- public boolean isUseCaseFailed() {
- return useCaseFailed;
- }
-}
diff --git a/src/main/java/use_case/logout/LogoutUserDataAccessInterface.java b/src/main/java/use_case/logout/LogoutUserDataAccessInterface.java
deleted file mode 100644
index 8263700e2..000000000
--- a/src/main/java/use_case/logout/LogoutUserDataAccessInterface.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package use_case.logout;
-
-/**
- * DAO for the Logout Use Case.
- */
-public interface LogoutUserDataAccessInterface {
-
- /**
- * Returns the username of the curren user of the application.
- * @return the username of the current user
- */
- String getCurrentUsername();
-
- /**
- * Sets the username indicating who is the current user of the application.
- * @param username the new current username
- */
- void setCurrentUsername(String username);
-}
diff --git a/src/main/java/use_case/nutrition_information/NutritionInformationAccessInterface.java b/src/main/java/use_case/nutrition_information/NutritionInformationAccessInterface.java
new file mode 100644
index 000000000..955df4d9a
--- /dev/null
+++ b/src/main/java/use_case/nutrition_information/NutritionInformationAccessInterface.java
@@ -0,0 +1,5 @@
+package use_case.nutrition_information;
+
+public interface NutritionInformationAccessInterface {
+ String fetchNutritionInformation(int recipeId);
+}
diff --git a/src/main/java/use_case/nutrition_information/NutritionInformationInputBoundary.java b/src/main/java/use_case/nutrition_information/NutritionInformationInputBoundary.java
new file mode 100644
index 000000000..3d71484e5
--- /dev/null
+++ b/src/main/java/use_case/nutrition_information/NutritionInformationInputBoundary.java
@@ -0,0 +1,9 @@
+package use_case.nutrition_information;
+
+import entity.Nutrition;
+
+import java.util.List;
+
+public interface NutritionInformationInputBoundary {
+ List NutritionInformation(NutritionInformationInputData inputData);
+}
diff --git a/src/main/java/use_case/nutrition_information/NutritionInformationInputData.java b/src/main/java/use_case/nutrition_information/NutritionInformationInputData.java
new file mode 100644
index 000000000..fb2798278
--- /dev/null
+++ b/src/main/java/use_case/nutrition_information/NutritionInformationInputData.java
@@ -0,0 +1,13 @@
+package use_case.nutrition_information;
+
+public class NutritionInformationInputData {
+ private final int recipeId;
+
+ public NutritionInformationInputData(int recipeId) {
+ this.recipeId = recipeId;
+ }
+
+ public int getRecipeId() {
+ return recipeId;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/use_case/nutrition_information/NutritionInformationInteractor.java b/src/main/java/use_case/nutrition_information/NutritionInformationInteractor.java
new file mode 100644
index 000000000..0c2a7c5ac
--- /dev/null
+++ b/src/main/java/use_case/nutrition_information/NutritionInformationInteractor.java
@@ -0,0 +1,84 @@
+package use_case.nutrition_information;
+
+import entity.Nutrition;
+
+import data_access.NutritionInformationDAO;
+import interface_adapter.nutrition_information.NutritionInformationPresenter;
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Interactor for handling nutrition information use case.
+ * This class acts as the intermediary between the data access layer and the presenter,
+ * orchestrating the logic to fetch and process nutrition information for a given recipe.
+ */
+public class NutritionInformationInteractor implements NutritionInformationInputBoundary {
+ // DAO to fetch nutrition data from the data source API
+ private final NutritionInformationDAO dataFetcher;
+
+ // Presenter to format and deliver the output data
+ private final NutritionInformationPresenter outputPresenter;
+
+ /**
+ * Constructor for the NutritionInformationInteractor.
+ * @param dataFetcher DAO responsible for fetching nutrition data.
+ * @param outputPresenter Presenter responsible for formatting the output data.
+ */
+ public NutritionInformationInteractor(NutritionInformationDAO dataFetcher
+ ,NutritionInformationPresenter outputPresenter) {
+ this.dataFetcher = dataFetcher;
+ this.outputPresenter = outputPresenter;
+ }
+
+ /**
+ * Fetches and processes nutrition information for a given recipe ID.
+ * @param inputData Input data containing the recipe ID.
+ * @return List of Nutrition objects representing the processed nutrition data.
+ */
+ @Override
+ public List NutritionInformation(NutritionInformationInputData inputData) {
+
+ // Step 1: Fetch raw JSON nutrition data using the DAO
+ String jsonData = dataFetcher.fetchNutritionInformation(inputData.getRecipeId());
+
+ // Step 2: Parse the raw JSON data into a list of Nutrition objects
+ List nutritionList = parseNutritionData(jsonData);
+
+ // Step 3: Wrap the parsed data into the output data object
+ NutritionInformationOutputData outputData = new NutritionInformationOutputData(nutritionList);
+
+ // Step 4: Use the presenter to format and deliver the result
+ return outputPresenter.presentNutritionInformation(outputData);
+ }
+
+ /**
+ * Helper Function:Parses raw JSON data into a list of Nutrition objects.
+ * @param jsonData Raw JSON string containing nutrition information.
+ * @return List of Nutrition objects parsed from the JSON data.
+ */
+ private List parseNutritionData(String jsonData) {
+ List nutritionList = new ArrayList<>();
+
+ JSONObject jsonObject = new JSONObject(jsonData);
+ if (jsonObject.has("nutrients")) {
+ JSONArray nutrients = jsonObject.getJSONArray("nutrients");
+ for (int i = 0; i < nutrients.length(); i++) {
+ JSONObject nutrientJson = nutrients.getJSONObject(i);
+
+ String name = nutrientJson.getString("name");
+ double amount = nutrientJson.getDouble("amount");
+ String unit = nutrientJson.getString("unit");
+ double percentOfDailyNeeds = nutrientJson.optDouble("percentOfDailyNeeds", 0);
+
+ Nutrition nutrition = new Nutrition(name, amount, unit, percentOfDailyNeeds);
+ nutritionList.add(nutrition);
+ }
+ }
+ return nutritionList;
+ }
+}
+
+
diff --git a/src/main/java/use_case/nutrition_information/NutritionInformationOutputBoundary.java b/src/main/java/use_case/nutrition_information/NutritionInformationOutputBoundary.java
new file mode 100644
index 000000000..8b5145787
--- /dev/null
+++ b/src/main/java/use_case/nutrition_information/NutritionInformationOutputBoundary.java
@@ -0,0 +1,9 @@
+package use_case.nutrition_information;
+
+import entity.Nutrition;
+
+import java.util.List;
+
+public interface NutritionInformationOutputBoundary {
+ List presentNutritionInformation(NutritionInformationOutputData outputData);
+}
diff --git a/src/main/java/use_case/nutrition_information/NutritionInformationOutputData.java b/src/main/java/use_case/nutrition_information/NutritionInformationOutputData.java
new file mode 100644
index 000000000..4498e7dd4
--- /dev/null
+++ b/src/main/java/use_case/nutrition_information/NutritionInformationOutputData.java
@@ -0,0 +1,16 @@
+package use_case.nutrition_information;
+
+import java.util.List;
+import entity.Nutrition;
+
+public class NutritionInformationOutputData {
+ private final List nutritionList;
+
+ public NutritionInformationOutputData(List nutritionList) {
+ this.nutritionList = nutritionList;
+ }
+
+ public List getNutritionList() {
+ return nutritionList;
+ }
+}
diff --git a/src/main/java/use_case/search_recipe/SearchRecipeInputBoundary.java b/src/main/java/use_case/search_recipe/SearchRecipeInputBoundary.java
new file mode 100644
index 000000000..3e74b455a
--- /dev/null
+++ b/src/main/java/use_case/search_recipe/SearchRecipeInputBoundary.java
@@ -0,0 +1,5 @@
+package use_case.search_recipe;
+
+public interface SearchRecipeInputBoundary {
+ void searchRecipes(SearchRecipeInputData inputData);
+}
diff --git a/src/main/java/use_case/search_recipe/SearchRecipeInputData.java b/src/main/java/use_case/search_recipe/SearchRecipeInputData.java
new file mode 100644
index 000000000..548c18ee0
--- /dev/null
+++ b/src/main/java/use_case/search_recipe/SearchRecipeInputData.java
@@ -0,0 +1,15 @@
+package use_case.search_recipe;
+
+import java.util.List;
+
+public class SearchRecipeInputData {
+ private final List ingredients;
+
+ public SearchRecipeInputData(List ingredients) {
+ this.ingredients = ingredients;
+ }
+
+ public List getIngredients() {
+ return ingredients;
+ }
+}
diff --git a/src/main/java/use_case/search_recipe/SearchRecipeInteractor.java b/src/main/java/use_case/search_recipe/SearchRecipeInteractor.java
new file mode 100644
index 000000000..20158c8d6
--- /dev/null
+++ b/src/main/java/use_case/search_recipe/SearchRecipeInteractor.java
@@ -0,0 +1,26 @@
+package use_case.search_recipe;
+
+import data_access.RecipeDAO;
+import entity.Recipe;
+
+import java.util.List;
+
+public class SearchRecipeInteractor implements SearchRecipeInputBoundary {
+ private final SearchRecipeOutputBoundary presenter;
+ private final RecipeDAO recipeDAO;
+
+ public SearchRecipeInteractor(SearchRecipeOutputBoundary presenter, RecipeDAO recipeDAO) {
+ this.presenter = presenter;
+ this.recipeDAO = recipeDAO;
+ }
+
+ @Override
+ public void searchRecipes(SearchRecipeInputData inputData) {
+ // Fetch recipes from DAO
+ List recipes = recipeDAO.getRecipesByIngredients(inputData.getIngredients());
+
+ // Prepare output data and pass to presenter
+ use_case.search_recipe.SearchRecipeOutputData outputData = new SearchRecipeOutputData(recipes);
+ presenter.presentRecipes(outputData);
+ }
+}
diff --git a/src/main/java/use_case/search_recipe/SearchRecipeOutputBoundary.java b/src/main/java/use_case/search_recipe/SearchRecipeOutputBoundary.java
new file mode 100644
index 000000000..d5fd14148
--- /dev/null
+++ b/src/main/java/use_case/search_recipe/SearchRecipeOutputBoundary.java
@@ -0,0 +1,5 @@
+package use_case.search_recipe;
+
+public interface SearchRecipeOutputBoundary {
+ void presentRecipes(use_case.search_recipe.SearchRecipeOutputData outputData);
+}
diff --git a/src/main/java/use_case/search_recipe/SearchRecipeOutputData.java b/src/main/java/use_case/search_recipe/SearchRecipeOutputData.java
new file mode 100644
index 000000000..7118dfeeb
--- /dev/null
+++ b/src/main/java/use_case/search_recipe/SearchRecipeOutputData.java
@@ -0,0 +1,17 @@
+package use_case.search_recipe;
+
+import entity.Recipe;
+
+import java.util.List;
+
+public class SearchRecipeOutputData {
+ private final List recipes;
+
+ public SearchRecipeOutputData(List recipes) {
+ this.recipes = recipes;
+ }
+
+ public List getRecipes() {
+ return recipes;
+ }
+}
diff --git a/src/main/java/use_case/search_recipe/SearchRecipeUseCase.java b/src/main/java/use_case/search_recipe/SearchRecipeUseCase.java
new file mode 100644
index 000000000..3b2f52029
--- /dev/null
+++ b/src/main/java/use_case/search_recipe/SearchRecipeUseCase.java
@@ -0,0 +1,29 @@
+package use_case.search_recipe;
+
+import data_access.RecipeDAO;
+import entity.Recipe;
+import interface_adapter.SearchRecipePresenter;
+
+import java.util.List;
+
+public class SearchRecipeUseCase implements use_case.search_recipe.SearchRecipeInputBoundary {
+ private final RecipeDAO recipeDAO;
+ private final use_case.search_recipe.SearchRecipeOutputBoundary presenter;
+
+ public SearchRecipeUseCase(RecipeDAO recipeDAO, SearchRecipePresenter presenter) {
+ this.recipeDAO = recipeDAO;
+ this.presenter = presenter;
+ }
+
+ @Override
+ public void searchRecipes(SearchRecipeInputData inputData) {
+ List ingredients = inputData.getIngredients();
+
+ // Fetch recipes from DAO
+ List recipes = recipeDAO.getRecipesByIngredients(ingredients);
+
+ // Pass results to presenter
+ use_case.search_recipe.SearchRecipeOutputData outputData = new use_case.search_recipe.SearchRecipeOutputData(recipes);
+ presenter.presentRecipes(outputData);
+ }
+}
diff --git a/src/main/java/use_case/search_recipe_list_by_ingredient/SearchRecipeListByIngredientDataAccessInterface.java b/src/main/java/use_case/search_recipe_list_by_ingredient/SearchRecipeListByIngredientDataAccessInterface.java
new file mode 100644
index 000000000..edd14b326
--- /dev/null
+++ b/src/main/java/use_case/search_recipe_list_by_ingredient/SearchRecipeListByIngredientDataAccessInterface.java
@@ -0,0 +1,23 @@
+package use_case.search_recipe_list_by_ingredient;
+
+import entity.Recipe;
+import entity.User;
+
+import java.util.List;
+
+/**
+ * The interface of the DAO for the Search Recipe List By Ingredient Use Case.
+ */
+public interface SearchRecipeListByIngredientDataAccessInterface {
+
+ /**
+ * Searches recipes by ingredient.
+ * @param ingredients the ingredients entered.
+ * @param user the user.
+ * @param folder the folder to search in.
+ * @return search results.
+ */
+ List searchRecipeListByIngredient(List ingredients, User user, String folder);
+
+ boolean addUser(User user);
+}
diff --git a/src/main/java/use_case/search_recipe_list_by_ingredient/SearchRecipeListByIngredientInputBoundary.java b/src/main/java/use_case/search_recipe_list_by_ingredient/SearchRecipeListByIngredientInputBoundary.java
new file mode 100644
index 000000000..5dbc7cb96
--- /dev/null
+++ b/src/main/java/use_case/search_recipe_list_by_ingredient/SearchRecipeListByIngredientInputBoundary.java
@@ -0,0 +1,14 @@
+package use_case.search_recipe_list_by_ingredient;
+
+/**
+ * The Search Recipe List By Ingredient Use Case.
+ */
+public interface SearchRecipeListByIngredientInputBoundary {
+
+ /**
+ * Execute the Search Recipe List By Ingredient Use Case.
+ * @param searchRecipeListByIngredientInputData the input data for this use case
+ */
+ void execute(SearchRecipeListByIngredientInputData searchRecipeListByIngredientInputData);
+
+}
diff --git a/src/main/java/use_case/search_recipe_list_by_ingredient/SearchRecipeListByIngredientInputData.java b/src/main/java/use_case/search_recipe_list_by_ingredient/SearchRecipeListByIngredientInputData.java
new file mode 100644
index 000000000..35d1a0abe
--- /dev/null
+++ b/src/main/java/use_case/search_recipe_list_by_ingredient/SearchRecipeListByIngredientInputData.java
@@ -0,0 +1,33 @@
+package use_case.search_recipe_list_by_ingredient;
+
+import entity.User;
+
+import java.util.List;
+
+/**
+ * The input data for the Search Recipe List By Ingredient Use Case.
+ */
+public class SearchRecipeListByIngredientInputData {
+
+ private final List ingredients;
+ private final User user;
+ private final String folder;
+
+ public SearchRecipeListByIngredientInputData(List ingredients, User user, String folder) {
+ this.ingredients = ingredients;
+ this.user = user;
+ this.folder = folder;
+ }
+
+ public List getIngredients() {
+ return ingredients;
+ }
+
+ public User getUser() {
+ return user;
+ }
+
+ public String getFolder() {
+ return folder;
+ }
+}
diff --git a/src/main/java/use_case/search_recipe_list_by_ingredient/SearchRecipeListByIngredientInteractor.java b/src/main/java/use_case/search_recipe_list_by_ingredient/SearchRecipeListByIngredientInteractor.java
new file mode 100644
index 000000000..06b21206f
--- /dev/null
+++ b/src/main/java/use_case/search_recipe_list_by_ingredient/SearchRecipeListByIngredientInteractor.java
@@ -0,0 +1,41 @@
+package use_case.search_recipe_list_by_ingredient;
+
+import java.util.List;
+
+import entity.Recipe;
+import entity.User;
+
+/**
+ * The Interactor for the Search Recipe List By Name Use Case.
+ */
+public class SearchRecipeListByIngredientInteractor implements SearchRecipeListByIngredientInputBoundary {
+
+ /**
+ * The Search Recipe List By Ingredient Interactor.
+ */
+ private final SearchRecipeListByIngredientDataAccessInterface userDataAccessObject;
+ private final SearchRecipeListByIngredientOutputBoundary userPresenter;
+
+ public SearchRecipeListByIngredientInteractor(
+ SearchRecipeListByIngredientDataAccessInterface searchRecipeListByIngredientDataAccessInterface,
+ SearchRecipeListByIngredientOutputBoundary searchRecipeListByIngredientOutputBoundary) {
+ this.userDataAccessObject = searchRecipeListByIngredientDataAccessInterface;
+ this.userPresenter = searchRecipeListByIngredientOutputBoundary;
+ }
+
+ @Override
+ public void execute(SearchRecipeListByIngredientInputData searchRecipeListByIngredientInputData) {
+ // Obtain the parameters for the search from the input data object
+ final List ingredients = searchRecipeListByIngredientInputData.getIngredients();
+ final User user = searchRecipeListByIngredientInputData.getUser();
+ final String folder = searchRecipeListByIngredientInputData.getFolder();
+ // Obtain the results of the search from the userDAO
+ final List recipes = userDataAccessObject.searchRecipeListByIngredient(ingredients, user, folder);
+ // Put the results in the output data object
+ final SearchRecipeListByIngredientOutputData searchRecipeListByIngredientOutputData =
+ new SearchRecipeListByIngredientOutputData(recipes,
+ false);
+ // Pass the output data to the presenter
+ userPresenter.prepareSuccessView(searchRecipeListByIngredientOutputData);
+ }
+}
diff --git a/src/main/java/use_case/search_recipe_list_by_ingredient/SearchRecipeListByIngredientOutputBoundary.java b/src/main/java/use_case/search_recipe_list_by_ingredient/SearchRecipeListByIngredientOutputBoundary.java
new file mode 100644
index 000000000..1b2d4f027
--- /dev/null
+++ b/src/main/java/use_case/search_recipe_list_by_ingredient/SearchRecipeListByIngredientOutputBoundary.java
@@ -0,0 +1,18 @@
+package use_case.search_recipe_list_by_ingredient;
+
+/**
+ * The output boundary for the Search Recipe List by Ingredient Use Case.
+ */
+public interface SearchRecipeListByIngredientOutputBoundary {
+ /**
+ * Prepares the success view for the Search Recipe List by Ingredient Use Case.
+ * @param outputData the output data
+ */
+ void prepareSuccessView(SearchRecipeListByIngredientOutputData outputData);
+
+ /**
+ * Prepares the failure view for the Search Recipe List by Ingredient Use Case.
+ * @param errorMessage the explanation of the failure
+ */
+ void prepareFailView(String errorMessage);
+}
diff --git a/src/main/java/use_case/search_recipe_list_by_ingredient/SearchRecipeListByIngredientOutputData.java b/src/main/java/use_case/search_recipe_list_by_ingredient/SearchRecipeListByIngredientOutputData.java
new file mode 100644
index 000000000..83586d997
--- /dev/null
+++ b/src/main/java/use_case/search_recipe_list_by_ingredient/SearchRecipeListByIngredientOutputData.java
@@ -0,0 +1,28 @@
+package use_case.search_recipe_list_by_ingredient;
+
+import entity.Recipe;
+
+import java.util.List;
+
+/**
+ * Output Data for the Search Recipe List By Ingredient Use Case.
+ */
+public class SearchRecipeListByIngredientOutputData {
+
+ private final List recipes;
+
+ private final boolean useCaseFailed;
+
+ public SearchRecipeListByIngredientOutputData(List recipes, boolean useCaseFailed) {
+ this.recipes = recipes;
+ this.useCaseFailed = useCaseFailed;
+ }
+
+ public List getRecipes() {
+ return recipes;
+ }
+
+ public boolean isUseCaseFailed() {
+ return useCaseFailed;
+ }
+}
diff --git a/src/main/java/use_case/search_recipe_list_by_name/SearchRecipeListByNameDataAccessInterface.java b/src/main/java/use_case/search_recipe_list_by_name/SearchRecipeListByNameDataAccessInterface.java
new file mode 100644
index 000000000..8608d3f16
--- /dev/null
+++ b/src/main/java/use_case/search_recipe_list_by_name/SearchRecipeListByNameDataAccessInterface.java
@@ -0,0 +1,23 @@
+package use_case.search_recipe_list_by_name;
+
+import entity.Recipe;
+import entity.User;
+
+import java.util.List;
+
+/**
+ * The interface of the DAO for the Search Recipe List By Name Use Case.
+ */
+public interface SearchRecipeListByNameDataAccessInterface {
+
+ /**
+ * Searches recipes by name.
+ * @param recipeName the recipe name entered.
+ * @param user the user.
+ * @param folder the folder to search in.
+ * @return search results.
+ */
+ List searchRecipeListByName(String recipeName, User user, String folder);
+
+ boolean addUser(User user);
+}
diff --git a/src/main/java/use_case/search_recipe_list_by_name/SearchRecipeListByNameInputBoundary.java b/src/main/java/use_case/search_recipe_list_by_name/SearchRecipeListByNameInputBoundary.java
new file mode 100644
index 000000000..89ac8e5d0
--- /dev/null
+++ b/src/main/java/use_case/search_recipe_list_by_name/SearchRecipeListByNameInputBoundary.java
@@ -0,0 +1,14 @@
+package use_case.search_recipe_list_by_name;
+
+/**
+ * The Search Recipe List By Name Use Case.
+ */
+public interface SearchRecipeListByNameInputBoundary {
+
+ /**
+ * Execute the Search Recipe List By Name Use Case.
+ * @param searchRecipeListByNameInputData the input data for this use case
+ */
+ void execute(SearchRecipeListByNameInputData searchRecipeListByNameInputData);
+
+}
diff --git a/src/main/java/use_case/search_recipe_list_by_name/SearchRecipeListByNameInputData.java b/src/main/java/use_case/search_recipe_list_by_name/SearchRecipeListByNameInputData.java
new file mode 100644
index 000000000..170f4a23d
--- /dev/null
+++ b/src/main/java/use_case/search_recipe_list_by_name/SearchRecipeListByNameInputData.java
@@ -0,0 +1,31 @@
+package use_case.search_recipe_list_by_name;
+
+import entity.User;
+
+/**
+ * The input data for the Search Recipe List By Name Use Case.
+ */
+public class SearchRecipeListByNameInputData {
+
+ private final String recipeName;
+ private final User user;
+ private final String folder;
+
+ public SearchRecipeListByNameInputData(String recipeName, User user, String folder) {
+ this.recipeName = recipeName;
+ this.user = user;
+ this.folder = folder;
+ }
+
+ public String getRecipeName() {
+ return recipeName;
+ }
+
+ public User getUser() {
+ return user;
+ }
+
+ public String getFolder() {
+ return folder;
+ }
+}
diff --git a/src/main/java/use_case/search_recipe_list_by_name/SearchRecipeListByNameInteractor.java b/src/main/java/use_case/search_recipe_list_by_name/SearchRecipeListByNameInteractor.java
new file mode 100644
index 000000000..23d840924
--- /dev/null
+++ b/src/main/java/use_case/search_recipe_list_by_name/SearchRecipeListByNameInteractor.java
@@ -0,0 +1,41 @@
+package use_case.search_recipe_list_by_name;
+
+import java.util.List;
+
+import entity.Recipe;
+import entity.User;
+
+/**
+ * The Interactor for the Search Recipe List By Name Use Case.
+ */
+public class SearchRecipeListByNameInteractor implements SearchRecipeListByNameInputBoundary {
+
+ /**
+ * The Search Recipe List By Name Interactor.
+ */
+ private final SearchRecipeListByNameDataAccessInterface userDataAccessObject;
+ private final SearchRecipeListByNameOutputBoundary userPresenter;
+
+ public SearchRecipeListByNameInteractor(
+ SearchRecipeListByNameDataAccessInterface searchRecipeListByNameDataAccessInterface,
+ SearchRecipeListByNameOutputBoundary searchRecipeListByNameOutputBoundary) {
+ this.userDataAccessObject = searchRecipeListByNameDataAccessInterface;
+ this.userPresenter = searchRecipeListByNameOutputBoundary;
+ }
+
+ @Override
+ public void execute(SearchRecipeListByNameInputData searchRecipeListByNameInputData) {
+ // Obtain the parameters for the search from the input data object
+ final String recipeName = searchRecipeListByNameInputData.getRecipeName();
+ final User user = searchRecipeListByNameInputData.getUser();
+ final String folder = searchRecipeListByNameInputData.getFolder();
+ // Obtain the results of the search from the userDAO
+ final List recipes = userDataAccessObject.searchRecipeListByName(recipeName, user, folder);
+ // Put the results in the output data object
+ final SearchRecipeListByNameOutputData searchRecipeListByNameOutputData =
+ new SearchRecipeListByNameOutputData(recipes,
+ false);
+ // Pass the output data to the presenter
+ userPresenter.prepareSuccessView(searchRecipeListByNameOutputData);
+ }
+}
diff --git a/src/main/java/use_case/search_recipe_list_by_name/SearchRecipeListByNameOutputBoundary.java b/src/main/java/use_case/search_recipe_list_by_name/SearchRecipeListByNameOutputBoundary.java
new file mode 100644
index 000000000..f448ad38d
--- /dev/null
+++ b/src/main/java/use_case/search_recipe_list_by_name/SearchRecipeListByNameOutputBoundary.java
@@ -0,0 +1,18 @@
+package use_case.search_recipe_list_by_name;
+
+/**
+ * The output boundary for the Search Recipe List by Name Use Case.
+ */
+public interface SearchRecipeListByNameOutputBoundary {
+ /**
+ * Prepares the success view for the Search Recipe List by Name Use Case.
+ * @param outputData the output data
+ */
+ void prepareSuccessView(SearchRecipeListByNameOutputData outputData);
+
+ /**
+ * Prepares the failure view for the Search Recipe List by Name Use Case.
+ * @param errorMessage the explanation of the failure
+ */
+ void prepareFailView(String errorMessage);
+}
diff --git a/src/main/java/use_case/search_recipe_list_by_name/SearchRecipeListByNameOutputData.java b/src/main/java/use_case/search_recipe_list_by_name/SearchRecipeListByNameOutputData.java
new file mode 100644
index 000000000..1b8593247
--- /dev/null
+++ b/src/main/java/use_case/search_recipe_list_by_name/SearchRecipeListByNameOutputData.java
@@ -0,0 +1,28 @@
+package use_case.search_recipe_list_by_name;
+
+import entity.Recipe;
+
+import java.util.List;
+
+/**
+ * Output Data for the Search Recipe List By Name Use Case.
+ */
+public class SearchRecipeListByNameOutputData {
+
+ private final List recipes;
+
+ private final boolean useCaseFailed;
+
+ public SearchRecipeListByNameOutputData(List recipes, boolean useCaseFailed) {
+ this.recipes = recipes;
+ this.useCaseFailed = useCaseFailed;
+ }
+
+ public List getRecipes() {
+ return recipes;
+ }
+
+ public boolean isUseCaseFailed() {
+ return useCaseFailed;
+ }
+}
diff --git a/src/main/java/use_case/signup/SignupInputBoundary.java b/src/main/java/use_case/signup/SignupInputBoundary.java
deleted file mode 100644
index 1cb69e02e..000000000
--- a/src/main/java/use_case/signup/SignupInputBoundary.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package use_case.signup;
-
-/**
- * Input Boundary for actions which are related to signing up.
- */
-public interface SignupInputBoundary {
-
- /**
- * Executes the signup use case.
- * @param signupInputData the input data
- */
- void execute(SignupInputData signupInputData);
-
- /**
- * Executes the switch to login view use case.
- */
- void switchToLoginView();
-}
diff --git a/src/main/java/use_case/signup/SignupInputData.java b/src/main/java/use_case/signup/SignupInputData.java
deleted file mode 100644
index 86c5e8abc..000000000
--- a/src/main/java/use_case/signup/SignupInputData.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package use_case.signup;
-
-/**
- * The Input Data for the Signup Use Case.
- */
-public class SignupInputData {
-
- private final String username;
- private final String password;
- private final String repeatPassword;
-
- public SignupInputData(String username, String password, String repeatPassword) {
- this.username = username;
- this.password = password;
- this.repeatPassword = repeatPassword;
- }
-
- String getUsername() {
- return username;
- }
-
- String getPassword() {
- return password;
- }
-
- public String getRepeatPassword() {
- return repeatPassword;
- }
-}
diff --git a/src/main/java/use_case/signup/SignupInteractor.java b/src/main/java/use_case/signup/SignupInteractor.java
deleted file mode 100644
index 3fd6560c7..000000000
--- a/src/main/java/use_case/signup/SignupInteractor.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package use_case.signup;
-
-import entity.User;
-import entity.UserFactory;
-
-/**
- * The Signup Interactor.
- */
-public class SignupInteractor implements SignupInputBoundary {
- private final SignupUserDataAccessInterface userDataAccessObject;
- private final SignupOutputBoundary userPresenter;
- private final UserFactory userFactory;
-
- public SignupInteractor(SignupUserDataAccessInterface signupDataAccessInterface,
- SignupOutputBoundary signupOutputBoundary,
- UserFactory userFactory) {
- this.userDataAccessObject = signupDataAccessInterface;
- this.userPresenter = signupOutputBoundary;
- this.userFactory = userFactory;
- }
-
- @Override
- public void execute(SignupInputData signupInputData) {
- if (userDataAccessObject.existsByName(signupInputData.getUsername())) {
- userPresenter.prepareFailView("User already exists.");
- }
- else if (!signupInputData.getPassword().equals(signupInputData.getRepeatPassword())) {
- userPresenter.prepareFailView("Passwords don't match.");
- }
- else {
- final User user = userFactory.create(signupInputData.getUsername(), signupInputData.getPassword());
- userDataAccessObject.save(user);
-
- final SignupOutputData signupOutputData = new SignupOutputData(user.getName(), false);
- userPresenter.prepareSuccessView(signupOutputData);
- }
- }
-
- @Override
- public void switchToLoginView() {
- userPresenter.switchToLoginView();
- }
-}
diff --git a/src/main/java/use_case/signup/SignupOutputBoundary.java b/src/main/java/use_case/signup/SignupOutputBoundary.java
deleted file mode 100644
index 314376b93..000000000
--- a/src/main/java/use_case/signup/SignupOutputBoundary.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package use_case.signup;
-
-/**
- * The output boundary for the Signup Use Case.
- */
-public interface SignupOutputBoundary {
-
- /**
- * Prepares the success view for the Signup Use Case.
- * @param outputData the output data
- */
- void prepareSuccessView(SignupOutputData outputData);
-
- /**
- * Prepares the failure view for the Signup Use Case.
- * @param errorMessage the explanation of the failure
- */
- void prepareFailView(String errorMessage);
-
- /**
- * Switches to the Login View.
- */
- void switchToLoginView();
-}
diff --git a/src/main/java/use_case/signup/SignupOutputData.java b/src/main/java/use_case/signup/SignupOutputData.java
deleted file mode 100644
index 6dc74d2fb..000000000
--- a/src/main/java/use_case/signup/SignupOutputData.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package use_case.signup;
-
-/**
- * Output Data for the Signup Use Case.
- */
-public class SignupOutputData {
-
- private final String username;
-
- private final boolean useCaseFailed;
-
- public SignupOutputData(String username, boolean useCaseFailed) {
- this.username = username;
- this.useCaseFailed = useCaseFailed;
- }
-
- public String getUsername() {
- return username;
- }
-
- public boolean isUseCaseFailed() {
- return useCaseFailed;
- }
-}
diff --git a/src/main/java/use_case/signup/SignupUserDataAccessInterface.java b/src/main/java/use_case/signup/SignupUserDataAccessInterface.java
deleted file mode 100644
index b9d60f585..000000000
--- a/src/main/java/use_case/signup/SignupUserDataAccessInterface.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package use_case.signup;
-
-import entity.User;
-
-/**
- * DAO for the Signup Use Case.
- */
-public interface SignupUserDataAccessInterface {
-
- /**
- * Checks if the given username exists.
- * @param username the username to look for
- * @return true if a user with the given username exists; false otherwise
- */
- boolean existsByName(String username);
-
- /**
- * Saves the user.
- * @param user the user to save
- */
- void save(User user);
-}
diff --git a/src/main/java/view/BookmarkView.java b/src/main/java/view/BookmarkView.java
new file mode 100644
index 000000000..843d1fbd9
--- /dev/null
+++ b/src/main/java/view/BookmarkView.java
@@ -0,0 +1,72 @@
+package view;
+
+import java.awt.*;
+import java.util.List;
+
+import javax.swing.*;
+
+import interface_adapter.RecipeListViewModel;
+import org.jetbrains.annotations.NotNull;
+
+import entity.Recipe;
+import entity.User;
+
+public class BookmarkView extends RecipeListView {
+
+ public BookmarkView(User user, String folderName, RecipeListViewModel recipeListViewModel) {
+ super(user, folderName, recipeListViewModel);
+ setTitle(user.getUsername() + "'s Bookmarks");
+
+ final JPanel folderPanel = new JPanel();
+ folderPanel.setLayout(new BoxLayout(folderPanel, BoxLayout.Y_AXIS));
+
+ final JComboBox dropdown = getDropdown(user);
+ folderPanel.add(dropdown);
+
+ final JPanel createFolderPanel = getCreateFolderPanel(user);
+ folderPanel.add(createFolderPanel);
+
+ add(folderPanel);
+ pack();
+ }
+
+ @NotNull
+ private JComboBox getDropdown(User user) {
+ String[] options = user.getFolders().keySet().toArray(new String[0]);
+ final JComboBox dropdown = new JComboBox<>(options);
+ final JLabel dropdownLabel = new JLabel("Select a folder:");
+ dropdown.add(dropdownLabel);
+
+ dropdown.addActionListener(event -> {
+ final String selectedOption = (String) dropdown.getSelectedItem();
+ new FolderView(user, selectedOption, new RecipeListViewModel());
+ });
+ return dropdown;
+ }
+
+ @NotNull
+ private JPanel getCreateFolderPanel(User user) {
+ final JPanel createFolderPanel = new JPanel();
+ createFolderPanel.setLayout(new BoxLayout(createFolderPanel, BoxLayout.X_AXIS));
+ final JTextField textField = new JTextField(20);
+ final JButton addFolderButton = new JButton("Create Folder");
+ addFolderButton.addActionListener(event -> {
+ final String folderName = textField.getText();
+ userDAO.addFolderToFile(user.getUsername(), folderName);
+ JOptionPane.showMessageDialog(null, "Folder created successfully");
+ });
+ createFolderPanel.add(textField);
+ createFolderPanel.add(addFolderButton);
+ return createFolderPanel;
+ }
+
+ @Override
+ protected List getRecipeList(User user1) {
+ return userDAO.getBookmarksFromFile(user1.getUsername());
+ }
+
+ @Override
+ protected List getRecipeList(User user1, String folderName) {
+ return userDAO.getRecentlyViewedFromFile(user1.getUsername());
+ }
+}
diff --git a/src/main/java/view/FolderView.java b/src/main/java/view/FolderView.java
new file mode 100644
index 000000000..d04bc7283
--- /dev/null
+++ b/src/main/java/view/FolderView.java
@@ -0,0 +1,25 @@
+package view;
+
+import java.util.List;
+
+import entity.Recipe;
+import entity.User;
+import interface_adapter.RecipeListViewModel;
+
+public class FolderView extends RecipeListView {
+
+ public FolderView(User user, String folderName, RecipeListViewModel recipeListViewModel) {
+ super(user, folderName, recipeListViewModel);
+ setTitle(folderName);
+ }
+
+ @Override
+ protected List getRecipeList(User user1) {
+ return List.of();
+ }
+
+ @Override
+ protected List getRecipeList(User user1, String folder) {
+ return userDAO.getFolderFromFile(user1.getUsername(), folder);
+ }
+}
diff --git a/src/main/java/view/HomePage.java b/src/main/java/view/HomePage.java
new file mode 100644
index 000000000..84641a7f7
--- /dev/null
+++ b/src/main/java/view/HomePage.java
@@ -0,0 +1,94 @@
+package view;
+
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.*;
+
+import data_access.AppConstants;
+import data_access.SpoonacularAPI;
+import data_access.SpoonacularRecipeDAO;
+import entity.User;
+import interface_adapter.RecipeController;
+import interface_adapter.RecipeListViewModel;
+import interface_adapter.SearchRecipePresenter;
+import interface_adapter.ShoppingListController;
+import interface_adapter.filter_recipes.FilterRecipesController;
+import use_case.ShoppingListUseCase;
+import use_case.filter_recipes.FilterRecipesInteractor;
+import use_case.search_recipe.SearchRecipeUseCase;
+
+public class HomePage extends JFrame {
+ private final User user;
+
+ public HomePage(User user) {
+ this.user = user;
+ setTitle("Welcome to the Recipe Finder");
+ // setSize(400, 300);
+ setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ setLayout(new BorderLayout());
+
+ final JLabel welcomeLabel = new JLabel("Welcome to the Recipe Finder!", JLabel.CENTER);
+ welcomeLabel.setFont(new Font(AppConstants.FONT, Font.BOLD, AppConstants.WELCOME_FONT_SIZE));
+ add(welcomeLabel, BorderLayout.CENTER);
+
+ final JButton startButton = new JButton("Start Searching Recipes");
+ startButton.setFont(new Font(AppConstants.FONT, Font.PLAIN, AppConstants.BUTTON_FONT_SIZE));
+ startButton.addActionListener(e -> {
+ // Create the presenter
+ final SearchRecipePresenter presenter = new SearchRecipePresenter();
+ // Create the use case interactor
+ final SearchRecipeUseCase interactor = new SearchRecipeUseCase(new SpoonacularRecipeDAO(), presenter);
+ // Create the controller
+ final RecipeController controller = new RecipeController(interactor);
+ // Open the RecipeView and pass dependencies
+ // new RecipeView(controller, presenter, user, new SpoonacularRecipeDAO());
+ final FilterRecipesInteractor filterInteractor = new FilterRecipesInteractor(new SpoonacularRecipeDAO());
+ final FilterRecipesController filterController = new FilterRecipesController(filterInteractor);
+ new RecipeView(controller, presenter, user, filterController, new SpoonacularRecipeDAO());
+ });
+
+ // Shopping List Button
+ final JButton shoppingListButton = new JButton("Shopping List");
+ shoppingListButton.setFont(new Font(AppConstants.FONT, Font.PLAIN, AppConstants.BUTTON_FONT_SIZE));
+// Action Listener for Shopping List Button
+ shoppingListButton.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ // Launch Shopping List GUI
+ SpoonacularAPI api = new SpoonacularAPI();
+ ShoppingListUseCase useCase = new ShoppingListUseCase(api);
+ ShoppingListController controller = new ShoppingListController(useCase);
+ ShoppingListGUI gui = new ShoppingListGUI(controller);
+ gui.run();
+ }
+ });
+
+ // Bookmarks Button
+ final JButton bookmarksButton = new JButton("Bookmarks");
+ bookmarksButton.setFont(new Font(AppConstants.FONT, Font.PLAIN, AppConstants.BUTTON_FONT_SIZE));
+ bookmarksButton.addActionListener(event -> {
+ new BookmarkView(this.user, "bookmarks", new RecipeListViewModel());
+ });
+
+ // Recently Viewed Button
+ final JButton recentlyViewedButton = new JButton("Recently Viewed");
+ recentlyViewedButton.setFont(new Font(AppConstants.FONT, Font.PLAIN, AppConstants.BUTTON_FONT_SIZE));
+ recentlyViewedButton.addActionListener(event -> {
+ new RecentlyViewedView(this.user, "recentlyViewed", new RecipeListViewModel());
+ });
+
+ // Putting the buttons together
+ final JPanel panel = new JPanel();
+ panel.setLayout(new GridLayout(2, 2, 10, 10));
+ panel.add(startButton);
+ panel.add(bookmarksButton);
+ panel.add(recentlyViewedButton);
+ panel.add(shoppingListButton);
+ add(panel, BorderLayout.SOUTH);
+ pack();
+ setVisible(true);
+ }
+}
+
diff --git a/src/main/java/view/IndividualRecipeView.java b/src/main/java/view/IndividualRecipeView.java
new file mode 100644
index 000000000..a96548e4a
--- /dev/null
+++ b/src/main/java/view/IndividualRecipeView.java
@@ -0,0 +1,159 @@
+package view;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+import data_access.UserDAOImpl;
+import entity.*;
+
+public class IndividualRecipeView extends JFrame implements ActionListener {
+ private final JButton nutritionButton;
+ private final JButton bookmarkButton;
+ private final JButton urlButton;
+ private JList ingredientsJLIst;
+ private final Recipe recipe;
+ private URL imageUrl;
+ private ImageIcon imageIcon;
+ private User user;
+ private UserDAOImpl userDAO;
+
+ public IndividualRecipeView(Recipe recipe, User user) {
+ this.recipe = recipe;
+ this.imageUrl = null;
+ this.user = user;
+ this.userDAO = new UserDAOImpl();
+
+ // Add this recipe to the user's recently viewed list upon the page's opening
+ userDAO.addRecentlyViewedToFile(this.user.getUsername(), this.recipe);
+
+ // Set up JFrame properties
+ setTitle(recipe.getName());
+ setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+
+ // Initialize main panel with vertical BoxLayout
+ final JPanel mainPanel = new JPanel();
+ mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
+ setContentPane(mainPanel);
+
+ // Initialize buttons
+ nutritionButton = new JButton("Nutrition");
+ bookmarkButton = new JButton("Bookmark");
+ urlButton = new JButton("Open Recipe in Browser");
+
+ // Initialize image
+ try {
+ // Specify the image URL
+ if (recipe.getImage() == null) {
+ throw new IllegalArgumentException("URL string cannot be null");
+ }
+ this.imageUrl = new URL(recipe.getImage()); // Replace with your image URL
+
+ // Load the image
+ this.imageIcon = new ImageIcon(imageUrl);
+ int originalWidth = this.imageIcon.getIconWidth();
+ int originalHeight = this.imageIcon.getIconHeight();
+ int desiredHeight = 200;
+ int scaledWidth = (desiredHeight * originalWidth) / originalHeight;
+
+ // Scale the image
+ final Image scaledImage = imageIcon.getImage().getScaledInstance(scaledWidth, desiredHeight, Image.SCALE_SMOOTH);
+ imageIcon = new ImageIcon(scaledImage);
+
+ // Add the image to a JLabel
+ final JLabel imageLabel = new JLabel(imageIcon);
+ imageLabel.setHorizontalAlignment(SwingConstants.CENTER);
+ imageLabel.setVerticalAlignment(SwingConstants.CENTER);
+
+ // Add the label to the main panel
+ mainPanel.add(imageLabel, BorderLayout.CENTER);
+
+ } catch (Exception e) {
+ // Handle exceptions, e.g., invalid URL or connection error
+ final JLabel errorLabel = new JLabel("Failed to load image.");
+ errorLabel.setHorizontalAlignment(SwingConstants.CENTER);
+ mainPanel.add(errorLabel, BorderLayout.CENTER);
+ e.printStackTrace();
+ }
+
+ // Initialize ingredient list
+ final DefaultListModel listModel = new DefaultListModel<>();
+ for (Ingredient ingredient : recipe.getIngredients()) {
+ listModel.addElement(ingredient.getName() + ", Amount: " + ingredient.getAmount() + " " + ingredient.getUnit());
+ }
+ ingredientsJLIst = new JList<>(listModel);
+ final JScrollPane scrollPane = new JScrollPane(ingredientsJLIst);
+ scrollPane.setPreferredSize(ingredientsJLIst.getPreferredScrollableViewportSize());
+ mainPanel.add(scrollPane);
+
+ // Initialize button panel with horizontal BoxLayout
+ final JPanel buttonPanel = new JPanel();
+ buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
+ buttonPanel.add(nutritionButton);
+ buttonPanel.add(Box.createRigidArea(new Dimension(10, 0))); // Add spacing
+ buttonPanel.add(bookmarkButton);
+ buttonPanel.add(Box.createRigidArea(new Dimension(10, 0)));
+ buttonPanel.add(urlButton);
+ mainPanel.add(buttonPanel);
+
+ // Add action listeners to buttons
+ nutritionButton.addActionListener(this);
+ bookmarkButton.addActionListener(this);
+ urlButton.addActionListener(this);
+
+ // Initialize dropdown menu for adding the recipe to folders
+ String[] options = user.getFolders().keySet().toArray(new String[0]);
+ final JComboBox dropdown = new JComboBox<>(options);
+ final JLabel dropdownLabel = new JLabel("Add this recipe to a folder");
+ dropdown.add(dropdownLabel);
+ dropdown.addActionListener(folderEvent -> {
+ final String selectedOption = (String) dropdown.getSelectedItem();
+ userDAO.addRecipeToFolderInFile(this.user.getUsername(), selectedOption, this.recipe);
+ JOptionPane.showMessageDialog(this, "Recipe added to " + selectedOption + "!");
+ });
+ mainPanel.add(dropdown);
+
+ // Display the frame
+ pack();
+ setVisible(true);
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent event) {
+// if (event.getSource() == nutritionButton) {
+// RecipeIdDAO getRecipeIdDAO = new RecipeIdDAO();
+// int recipeId = getRecipeIdDAO.getRecipeIdByName(recipe.getName());
+// new NutritionInformationView(recipeId);
+// }
+ if (event.getSource() == bookmarkButton) {
+ if (!user.getBookmarks().contains(recipe)) {
+ userDAO.addBookmarkToFile(user.getUsername(), recipe);
+ JOptionPane.showMessageDialog(this, "Recipe added to bookmarks!");
+ } else {
+ JOptionPane.showMessageDialog(this, "Recipe is already bookmarked.");
+ }
+ }
+ else if (event.getSource() == urlButton) {
+ try {
+ // Create a URI for the URL
+ URI uri = new URI(recipe.getUrl());
+ // Use the Desktop class to open the default browser
+ if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
+ Desktop.getDesktop().browse(uri);
+ } else {
+ JOptionPane.showMessageDialog(this, "Desktop browsing is not supported on your system.");
+ }
+ } catch (URISyntaxException | IOException ex) {
+ ex.printStackTrace();
+ JOptionPane.showMessageDialog(this, "Failed to open the URL: " + ex.getMessage());
+ }
+ }
+ }
+}
diff --git a/src/main/java/view/LabelTextPanel.java b/src/main/java/view/LabelTextPanel.java
deleted file mode 100644
index e7138555f..000000000
--- a/src/main/java/view/LabelTextPanel.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package view;
-
-import javax.swing.JLabel;
-import javax.swing.JPanel;
-import javax.swing.JTextField;
-
-/**
- * A panel containing a label and a text field.
- */
-class LabelTextPanel extends JPanel {
- LabelTextPanel(JLabel label, JTextField textField) {
- this.add(label);
- this.add(textField);
- }
-}
diff --git a/src/main/java/view/LoggedInView.java b/src/main/java/view/LoggedInView.java
deleted file mode 100644
index 76d40c0f1..000000000
--- a/src/main/java/view/LoggedInView.java
+++ /dev/null
@@ -1,143 +0,0 @@
-package view;
-
-import java.awt.Component;
-import java.beans.PropertyChangeEvent;
-import java.beans.PropertyChangeListener;
-
-import javax.swing.BoxLayout;
-import javax.swing.JButton;
-import javax.swing.JLabel;
-import javax.swing.JOptionPane;
-import javax.swing.JPanel;
-import javax.swing.JTextField;
-import javax.swing.event.DocumentEvent;
-import javax.swing.event.DocumentListener;
-
-import interface_adapter.change_password.ChangePasswordController;
-import interface_adapter.change_password.LoggedInState;
-import interface_adapter.change_password.LoggedInViewModel;
-import interface_adapter.logout.LogoutController;
-
-/**
- * The View for when the user is logged into the program.
- */
-public class LoggedInView extends JPanel implements PropertyChangeListener {
-
- private final String viewName = "logged in";
- private final LoggedInViewModel loggedInViewModel;
- private final JLabel passwordErrorField = new JLabel();
- private ChangePasswordController changePasswordController;
- private LogoutController logoutController;
-
- private final JLabel username;
-
- private final JButton logOut;
-
- private final JTextField passwordInputField = new JTextField(15);
- private final JButton changePassword;
-
- public LoggedInView(LoggedInViewModel loggedInViewModel) {
- this.loggedInViewModel = loggedInViewModel;
- this.loggedInViewModel.addPropertyChangeListener(this);
-
- final JLabel title = new JLabel("Logged In Screen");
- title.setAlignmentX(Component.CENTER_ALIGNMENT);
-
- final LabelTextPanel passwordInfo = new LabelTextPanel(
- new JLabel("Password"), passwordInputField);
-
- final JLabel usernameInfo = new JLabel("Currently logged in: ");
- username = new JLabel();
-
- final JPanel buttons = new JPanel();
- logOut = new JButton("Log Out");
- buttons.add(logOut);
-
- changePassword = new JButton("Change Password");
- buttons.add(changePassword);
-
- this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
-
- passwordInputField.getDocument().addDocumentListener(new DocumentListener() {
-
- private void documentListenerHelper() {
- final LoggedInState currentState = loggedInViewModel.getState();
- currentState.setPassword(passwordInputField.getText());
- loggedInViewModel.setState(currentState);
- }
-
- @Override
- public void insertUpdate(DocumentEvent e) {
- documentListenerHelper();
- }
-
- @Override
- public void removeUpdate(DocumentEvent e) {
- documentListenerHelper();
- }
-
- @Override
- public void changedUpdate(DocumentEvent e) {
- documentListenerHelper();
- }
- });
-
- changePassword.addActionListener(
- // This creates an anonymous subclass of ActionListener and instantiates it.
- evt -> {
- if (evt.getSource().equals(changePassword)) {
- final LoggedInState currentState = loggedInViewModel.getState();
-
- this.changePasswordController.execute(
- currentState.getUsername(),
- currentState.getPassword()
- );
- }
- }
- );
-
- logOut.addActionListener(
- // This creates an anonymous subclass of ActionListener and instantiates it.
- evt -> {
- if (evt.getSource().equals(logOut)) {
- // TODO: execute the logout use case through the Controller
- // 1. get the state out of the loggedInViewModel. It contains the username.
- // 2. Execute the logout Controller.
- }
- }
- );
-
- this.add(title);
- this.add(usernameInfo);
- this.add(username);
-
- this.add(passwordInfo);
- this.add(passwordErrorField);
- this.add(buttons);
- }
-
- @Override
- public void propertyChange(PropertyChangeEvent evt) {
- if (evt.getPropertyName().equals("state")) {
- final LoggedInState state = (LoggedInState) evt.getNewValue();
- username.setText(state.getUsername());
- }
- else if (evt.getPropertyName().equals("password")) {
- final LoggedInState state = (LoggedInState) evt.getNewValue();
- JOptionPane.showMessageDialog(null, "password updated for " + state.getUsername());
- }
-
- }
-
- public String getViewName() {
- return viewName;
- }
-
- public void setChangePasswordController(ChangePasswordController changePasswordController) {
- this.changePasswordController = changePasswordController;
- }
-
- public void setLogoutController(LogoutController logoutController) {
- // TODO: save the logout controller in the instance variable.
- }
-}
diff --git a/src/main/java/view/LoginSignupPage.java b/src/main/java/view/LoginSignupPage.java
new file mode 100644
index 000000000..d258253a4
--- /dev/null
+++ b/src/main/java/view/LoginSignupPage.java
@@ -0,0 +1,113 @@
+package view;
+
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.*;
+
+import data_access.UserDAO;
+import data_access.UserDAOImpl;
+import entity.User;
+
+public class LoginSignupPage extends JFrame {
+ private final UserDAO userDao;
+
+ // UI components
+ private final JTextField usernameField;
+ private final JPasswordField passwordField;
+
+ public LoginSignupPage() {
+ // Initialize with JSON-backed DAO
+ userDao = new UserDAOImpl();
+ setTitle("Login or Sign Up");
+ setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ setLayout(new GridLayout(4, 1, 10, 10));
+
+ // Username field
+ final JPanel usernamePanel = new JPanel();
+ usernamePanel.add(new JLabel("Username:"));
+ usernameField = new JTextField(15);
+ usernamePanel.add(usernameField);
+ add(usernamePanel);
+
+ // Password field
+ final JPanel passwordPanel = new JPanel();
+ passwordPanel.add(new JLabel("Password:"));
+ passwordField = new JPasswordField(15);
+ passwordPanel.add(passwordField);
+ add(passwordPanel);
+
+ // Login button
+ final JButton loginButton = new JButton("Login");
+ loginButton.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ handleLogin();
+ }
+ });
+
+ // Signup button
+ final JButton signupButton = new JButton("Sign Up");
+ signupButton.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ handleSignup();
+ }
+ });
+
+ // Button panel
+ JPanel buttonPanel = new JPanel();
+ buttonPanel.add(loginButton);
+ buttonPanel.add(signupButton);
+ add(buttonPanel);
+
+ // Display the frame
+ pack();
+ setVisible(true);
+ }
+
+ private void handleLogin() {
+ final String username = usernameField.getText();
+ final String password = new String(passwordField.getPassword());
+
+ if (userDao.validateUser(username, password)) {
+ JOptionPane.showMessageDialog(this, "Login successful!");
+ openHomePage(userDao.findUserByUsername(username));
+ dispose();
+ }
+ else {
+ JOptionPane.showMessageDialog(this, "Invalid credentials. Please try again.");
+ }
+ }
+
+ private void handleSignup() {
+ final String username = usernameField.getText();
+ final String password = new String(passwordField.getPassword());
+
+ // Check for empty username or password
+ if (username.trim().isEmpty() || password.trim().isEmpty()) {
+ JOptionPane.showMessageDialog(this, "Username and password cannot be empty.");
+ }
+
+ if (userDao.findUserByUsername(username) != null) {
+ JOptionPane.showMessageDialog(this, "Username already exists. Please choose another.");
+ }
+ else {
+ final boolean success = userDao.addUser(new User(username, password));
+ if (success) {
+ JOptionPane.showMessageDialog(this, "Signup successful! You can now log in.");
+ }
+ else {
+ JOptionPane.showMessageDialog(this, "Signup failed. Please try again.");
+ }
+ }
+ }
+
+ private void openHomePage(User user) {
+ // Open the HomePage
+ new HomePage(user);
+ // Close the LoginSignupPage
+ dispose();
+ }
+}
diff --git a/src/main/java/view/LoginView.java b/src/main/java/view/LoginView.java
deleted file mode 100644
index 96d4f3845..000000000
--- a/src/main/java/view/LoginView.java
+++ /dev/null
@@ -1,160 +0,0 @@
-package view;
-
-import java.awt.Component;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.beans.PropertyChangeEvent;
-import java.beans.PropertyChangeListener;
-
-import javax.swing.BoxLayout;
-import javax.swing.JButton;
-import javax.swing.JLabel;
-import javax.swing.JPanel;
-import javax.swing.JPasswordField;
-import javax.swing.JTextField;
-import javax.swing.event.DocumentEvent;
-import javax.swing.event.DocumentListener;
-
-import interface_adapter.login.LoginController;
-import interface_adapter.login.LoginState;
-import interface_adapter.login.LoginViewModel;
-
-/**
- * The View for when the user is logging into the program.
- */
-public class LoginView extends JPanel implements ActionListener, PropertyChangeListener {
-
- private final String viewName = "log in";
- private final LoginViewModel loginViewModel;
-
- private final JTextField usernameInputField = new JTextField(15);
- private final JLabel usernameErrorField = new JLabel();
-
- private final JPasswordField passwordInputField = new JPasswordField(15);
- private final JLabel passwordErrorField = new JLabel();
-
- private final JButton logIn;
- private final JButton cancel;
- private LoginController loginController;
-
- public LoginView(LoginViewModel loginViewModel) {
-
- this.loginViewModel = loginViewModel;
- this.loginViewModel.addPropertyChangeListener(this);
-
- final JLabel title = new JLabel("Login Screen");
- title.setAlignmentX(Component.CENTER_ALIGNMENT);
-
- final LabelTextPanel usernameInfo = new LabelTextPanel(
- new JLabel("Username"), usernameInputField);
- final LabelTextPanel passwordInfo = new LabelTextPanel(
- new JLabel("Password"), passwordInputField);
-
- final JPanel buttons = new JPanel();
- logIn = new JButton("log in");
- buttons.add(logIn);
- cancel = new JButton("cancel");
- buttons.add(cancel);
-
- logIn.addActionListener(
- new ActionListener() {
- public void actionPerformed(ActionEvent evt) {
- if (evt.getSource().equals(logIn)) {
- final LoginState currentState = loginViewModel.getState();
-
- loginController.execute(
- currentState.getUsername(),
- currentState.getPassword()
- );
- }
- }
- }
- );
-
- cancel.addActionListener(this);
-
- usernameInputField.getDocument().addDocumentListener(new DocumentListener() {
-
- private void documentListenerHelper() {
- final LoginState currentState = loginViewModel.getState();
- currentState.setUsername(usernameInputField.getText());
- loginViewModel.setState(currentState);
- }
-
- @Override
- public void insertUpdate(DocumentEvent e) {
- documentListenerHelper();
- }
-
- @Override
- public void removeUpdate(DocumentEvent e) {
- documentListenerHelper();
- }
-
- @Override
- public void changedUpdate(DocumentEvent e) {
- documentListenerHelper();
- }
- });
-
- this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
-
- passwordInputField.getDocument().addDocumentListener(new DocumentListener() {
-
- private void documentListenerHelper() {
- final LoginState currentState = loginViewModel.getState();
- currentState.setPassword(new String(passwordInputField.getPassword()));
- loginViewModel.setState(currentState);
- }
-
- @Override
- public void insertUpdate(DocumentEvent e) {
- documentListenerHelper();
- }
-
- @Override
- public void removeUpdate(DocumentEvent e) {
- documentListenerHelper();
- }
-
- @Override
- public void changedUpdate(DocumentEvent e) {
- documentListenerHelper();
- }
- });
-
- this.add(title);
- this.add(usernameInfo);
- this.add(usernameErrorField);
- this.add(passwordInfo);
- this.add(buttons);
- }
-
- /**
- * React to a button click that results in evt.
- * @param evt the ActionEvent to react to
- */
- public void actionPerformed(ActionEvent evt) {
- System.out.println("Click " + evt.getActionCommand());
- }
-
- @Override
- public void propertyChange(PropertyChangeEvent evt) {
- final LoginState state = (LoginState) evt.getNewValue();
- setFields(state);
- usernameErrorField.setText(state.getLoginError());
- }
-
- private void setFields(LoginState state) {
- usernameInputField.setText(state.getUsername());
- passwordInputField.setText(state.getPassword());
- }
-
- public String getViewName() {
- return viewName;
- }
-
- public void setLoginController(LoginController loginController) {
- this.loginController = loginController;
- }
-}
diff --git a/src/main/java/view/NutritionInformationView.java b/src/main/java/view/NutritionInformationView.java
new file mode 100644
index 000000000..967a10606
--- /dev/null
+++ b/src/main/java/view/NutritionInformationView.java
@@ -0,0 +1,52 @@
+package view;
+
+import data_access.NutritionInformationDAO;
+import entity.Nutrition;
+import interface_adapter.nutrition_information.NutritionInformationPresenter;
+import use_case.nutrition_information.NutritionInformationInputData;
+import use_case.nutrition_information.NutritionInformationInteractor;
+
+import javax.swing.*;
+import java.util.List;
+
+public class NutritionInformationView extends JFrame {
+ private final int recipeId;
+ private final NutritionInformationInteractor interactor;
+ private final JList nutritionList;
+
+ public NutritionInformationView(int recipeId) {
+ this.recipeId = recipeId;
+
+ nutritionList = new JList<>();
+ setTitle("Nutrition Information");
+ setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ setSize(800, 300);
+ setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
+ add(new JScrollPane(nutritionList));
+
+ NutritionInformationPresenter outputPresenter = new NutritionInformationPresenter();
+ NutritionInformationDAO dataFetcher = new NutritionInformationDAO();
+ interactor = new NutritionInformationInteractor(dataFetcher, outputPresenter);
+
+ loadNutritionData();
+
+ setVisible(true);
+ }
+
+ private void loadNutritionData() {
+ NutritionInformationInputData inputData = new NutritionInformationInputData(recipeId);
+
+ List nutritionData = interactor.NutritionInformation(inputData);
+
+ DefaultListModel listModel = new DefaultListModel<>();
+ for (Nutrition nutrition : nutritionData) {
+ String displayText = String.format("%s: %.2f %s (%.2f%% of daily needs)",
+ nutrition.getName(),
+ nutrition.getAmount(),
+ nutrition.getUnit(),
+ nutrition.getPercentDailyNeeds());
+ listModel.addElement(displayText);
+ }
+ nutritionList.setModel(listModel);
+ }
+}
diff --git a/src/main/java/view/RecentlyViewedView.java b/src/main/java/view/RecentlyViewedView.java
new file mode 100644
index 000000000..7a03516a2
--- /dev/null
+++ b/src/main/java/view/RecentlyViewedView.java
@@ -0,0 +1,33 @@
+package view;
+
+import java.util.List;
+
+import javax.swing.*;
+
+import entity.Recipe;
+import entity.User;
+import interface_adapter.RecipeListViewModel;
+
+public class RecentlyViewedView extends RecipeListView {
+
+ public RecentlyViewedView(User user, String folderName, RecipeListViewModel recipeListViewModel) {
+ super(user, folderName, recipeListViewModel);
+ setTitle(user.getUsername() + "'s Recently Viewed");
+ final JButton clearButton = new JButton("Clear Recently Viewed");
+ clearButton.addActionListener(event -> {
+ userDAO.clearRecentlyViewed(user.getUsername());
+ recipeList.setModel(listModel);
+ });
+ add(clearButton);
+ }
+
+ @Override
+ protected List getRecipeList(User user1) {
+ return userDAO.getRecentlyViewedFromFile(user1.getUsername());
+ }
+
+ @Override
+ protected List getRecipeList(User user1, String folderName) {
+ return userDAO.getRecentlyViewedFromFile(user1.getUsername());
+ }
+}
diff --git a/src/main/java/view/RecipeListView.java b/src/main/java/view/RecipeListView.java
new file mode 100644
index 000000000..44e351d7c
--- /dev/null
+++ b/src/main/java/view/RecipeListView.java
@@ -0,0 +1,281 @@
+package view;
+
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.swing.*;
+
+import data_access.SpoonacularRecipeDAO;
+import data_access.UserDAOImpl;
+import entity.Recipe;
+import entity.User;
+import interface_adapter.RecipeListState;
+import interface_adapter.RecipeListViewModel;
+import interface_adapter.search_recipe_list_by_ingredient.SearchRecipeListByIngredientController;
+import interface_adapter.search_recipe_list_by_ingredient.SearchRecipeListByIngredientPresenter;
+import interface_adapter.search_recipe_list_by_name.SearchRecipeListByNameController;
+import interface_adapter.search_recipe_list_by_name.SearchRecipeListByNamePresenter;
+import use_case.search_recipe_list_by_ingredient.SearchRecipeListByIngredientInputBoundary;
+import use_case.search_recipe_list_by_ingredient.SearchRecipeListByIngredientInteractor;
+import use_case.search_recipe_list_by_ingredient.SearchRecipeListByIngredientOutputBoundary;
+import use_case.search_recipe_list_by_name.SearchRecipeListByNameInputBoundary;
+import use_case.search_recipe_list_by_name.SearchRecipeListByNameInteractor;
+import use_case.search_recipe_list_by_name.SearchRecipeListByNameOutputBoundary;
+
+public abstract class RecipeListView extends JFrame implements ActionListener, PropertyChangeListener {
+ protected static UserDAOImpl userDAO;
+ protected final JList recipeList;
+ protected final DefaultListModel listModel;
+ private final User user;
+
+ private JTextField ingredientSearchField;
+ private JButton ingredientSearchButton;
+ private JTextField recipeSearchField;
+ private JButton recipeSearchButton;
+ private JButton clearSearchButton;
+
+ private JComboBox dietComboBox;
+ private JComboBox cuisineComboBox;
+ private SpoonacularRecipeDAO spoonacularRecipeDAO;
+
+ private final String folderName;
+
+ private final RecipeListViewModel recipeListViewModel;
+ private SearchRecipeListByIngredientController searchRecipeListByIngredientController;
+ private SearchRecipeListByNameController searchRecipeListByNameController;
+
+ /*
+ Generates the default view of a list of recipes associated with a User.
+ */
+ public RecipeListView(User user, String folderName, RecipeListViewModel recipeListViewModel) {
+ this.user = user;
+ this.folderName = folderName;
+ this.userDAO = new UserDAOImpl();
+ this.recipeList = new JList<>();
+ this.listModel = new DefaultListModel<>();
+ this.spoonacularRecipeDAO = new SpoonacularRecipeDAO();
+
+ this.recipeListViewModel = recipeListViewModel;
+ this.recipeListViewModel.addPropertyChangeListener(this);
+
+ final SearchRecipeListByIngredientOutputBoundary searchRecipeListByIngredientOutputBoundary =
+ new SearchRecipeListByIngredientPresenter(recipeListViewModel);
+ final SearchRecipeListByIngredientInputBoundary searchRecipeListByIngredientInteractor =
+ new SearchRecipeListByIngredientInteractor(
+ userDAO, searchRecipeListByIngredientOutputBoundary);
+ this.searchRecipeListByIngredientController =
+ new SearchRecipeListByIngredientController(searchRecipeListByIngredientInteractor);
+
+ final SearchRecipeListByNameOutputBoundary searchRecipeListByNameOutputBoundary =
+ new SearchRecipeListByNamePresenter(recipeListViewModel);
+ final SearchRecipeListByNameInputBoundary searchRecipeListByNameInteractor =
+ new SearchRecipeListByNameInteractor(
+ userDAO, searchRecipeListByNameOutputBoundary);
+ this.searchRecipeListByNameController =
+ new SearchRecipeListByNameController(searchRecipeListByNameInteractor);
+
+ // Initialize recipe list to display
+ List recipes = new ArrayList<>();
+ // Split into cases: BookmarkView/RecentlyViewedView and FolderView
+ if (folderName == "bookmarks" || folderName == "recentlyViewed") {
+ recipes = getRecipeList(userDAO.findUserByUsername(user.getUsername()));
+ }
+ else {
+ recipes = getRecipeList(userDAO.findUserByUsername(user.getUsername()), folderName);
+ }
+ for (Recipe recipe : recipes) {
+ this.listModel.addElement(recipe);
+ }
+ this.recipeList.setModel(listModel);
+ // Wrap the JList in a JScrollPane
+ final JScrollPane scrollPane = new JScrollPane(recipeList);
+ scrollPane.setPreferredSize(recipeList.getPreferredScrollableViewportSize());
+ add(scrollPane, BorderLayout.SOUTH);
+
+ // Make the recipe list clickable
+ recipeList.addMouseListener(new MouseAdapter() {
+ public void mouseClicked(MouseEvent event) {
+ if (event.getClickCount() == 2) {
+ // Get index of clicked item
+ final int index = recipeList.locationToIndex(event.getPoint());
+ // Ensure valid index
+ if (index >= 0) {
+ // Get selected item
+ final Recipe selectedItem = recipeList.getModel().getElementAt(index);
+ // Open a new window
+ new IndividualRecipeView(selectedItem, user);
+ }
+ }
+ }
+ });
+
+ // Initialize and display the two search bars in a panel
+ final JPanel searchPanel = new JPanel();
+ searchPanel.setLayout(new BoxLayout(searchPanel, BoxLayout.Y_AXIS));
+ // Search by ingredient
+ ingredientSearchField = new JTextField(20);
+ ingredientSearchButton = new JButton("Search recipes by ingredients");
+ final JPanel ingredientSearchPanel = new JPanel();
+ ingredientSearchPanel.add(new JLabel("Enter ingredients (comma-separated):"));
+ ingredientSearchPanel.add(ingredientSearchField);
+ ingredientSearchPanel.add(ingredientSearchButton);
+ ingredientSearchButton.addActionListener(this);
+ searchPanel.add(ingredientSearchPanel);
+ // Search by recipe name
+ recipeSearchField = new JTextField(20);
+ recipeSearchButton = new JButton("Search recipes by name");
+ final JPanel recipeSearchPanel = new JPanel();
+ recipeSearchPanel.add(new JLabel("Enter recipe name:"));
+ recipeSearchPanel.add(recipeSearchField);
+ recipeSearchPanel.add(recipeSearchButton);
+ recipeSearchButton.addActionListener(this);
+ searchPanel.add(recipeSearchPanel);
+
+ // Clear search button
+ clearSearchButton = new JButton("Clear Search");
+ clearSearchButton.addActionListener(this);
+ add(clearSearchButton, BorderLayout.WEST);
+
+ // Add the search panel to the frame
+ add(searchPanel, BorderLayout.NORTH);
+
+ // Search filters
+ final JPanel filterPanel = new JPanel(new FlowLayout());
+ dietComboBox = new JComboBox<>();
+ cuisineComboBox = new JComboBox<>();
+ filterPanel.add(new JLabel("Diet:"));
+ filterPanel.add(dietComboBox);
+ filterPanel.add(new JLabel("Cuisine:"));
+ filterPanel.add(cuisineComboBox);
+
+ add(filterPanel, BorderLayout.EAST);
+
+ dietComboBox.addActionListener(e -> applyFilters());
+ cuisineComboBox.addActionListener(e -> applyFilters());
+
+ populateDropdowns();
+
+ // dietComboBox.addActionListener(new ActionListener() {
+ // @Override
+ // public void actionPerformed(ActionEvent e) {
+ // applyFilters();
+ // }
+ // });
+
+ // cuisineComboBox.addActionListener(new ActionListener() {
+ // @Override
+ // public void actionPerformed(ActionEvent e) {
+ // applyFilters();
+ // }
+ // });
+
+ setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ pack();
+ setVisible(true);
+ }
+
+ public void actionPerformed(ActionEvent event) {
+ if (event.getSource() == ingredientSearchButton) {
+ final String userInput = ingredientSearchField.getText();
+ final List ingredients = Arrays.asList(userInput.split(","));
+ final RecipeListState currentState = recipeListViewModel.getState();
+ currentState.setFolder(folderName);
+ currentState.setUser(user);
+ this.searchRecipeListByIngredientController.execute(
+ ingredients, currentState.getUser(), currentState.getFolder());
+ recipeListViewModel.setState(currentState);
+ final DefaultListModel recipeSearchListModel = new DefaultListModel<>();
+ for (Recipe recipe : currentState.getRecipeList()) {
+ recipeSearchListModel.addElement(recipe);
+ }
+ recipeList.setModel(recipeSearchListModel);
+ }
+ if (event.getSource() == recipeSearchButton) {
+ final String userInput = recipeSearchField.getText();
+ final RecipeListState currentState = recipeListViewModel.getState();
+ currentState.setFolder(folderName);
+ currentState.setUser(user);
+ this.searchRecipeListByNameController.execute(
+ userInput, currentState.getUser(), currentState.getFolder());
+ recipeListViewModel.setState(currentState);
+ final DefaultListModel recipeSearchListModel = new DefaultListModel<>();
+ for (Recipe recipe : currentState.getRecipeList()) {
+ recipeSearchListModel.addElement(recipe);
+ }
+ recipeList.setModel(recipeSearchListModel);
+ }
+ if (event.getSource() == clearSearchButton) {
+ recipeList.setModel(listModel);
+ }
+ }
+
+ // populating diet and cuisine dropdown menus
+ private void populateDropdowns() {
+ try {
+ final List diets = spoonacularRecipeDAO.getAvailableDiets();
+ dietComboBox.addItem("Any");
+ for (String diet : diets) {
+ dietComboBox.addItem(diet);
+ }
+
+ final List cuisines = spoonacularRecipeDAO.getAvailableCuisines();
+ cuisineComboBox.addItem("Any");
+ for (String cuisine : cuisines) {
+ cuisineComboBox.addItem(cuisine);
+ }
+ } catch (Exception exception) {
+ JOptionPane.showMessageDialog(this,
+ "Error loading filters: " + exception.getMessage());
+ }
+ }
+
+ // applying the filters
+ private void applyFilters() {
+ final String enteredIngredients = ingredientSearchField.getText();
+ final List ingredients = List.of(enteredIngredients.split(","));
+
+ // final String selectedDiet = dietComboBox.getSelectedItem().toString();
+ // final String selectedCuisine = cuisineComboBox.getSelectedItem().toString();
+
+ // final List recipesFiltered =
+ // spoonacularRecipeDAO.filterSearchRecipes(ingredients, selectedDiet, selectedCuisine);
+
+ // listModel.clear();
+ // for (Recipe recipe : recipesFiltered) {
+ // listModel.addElement(recipe);
+ // }
+ // recipeList.setModel(listModel);
+ }
+
+ protected abstract List getRecipeList(User user1);
+
+ protected abstract List getRecipeList(User user1, String folderName);
+
+ @Override
+ public void propertyChange(PropertyChangeEvent event) {
+ if (event.getPropertyName().equals("state")) {
+ final RecipeListState state = (RecipeListState) event.getNewValue();
+ // TODO recipeList.setModel(ingredientSearchListModel);
+ }
+ // TODO add other cases
+ }
+
+ // TODO only need these if there's an appbuilder
+ public void setSearchRecipeListByIngredientController(
+ SearchRecipeListByIngredientController SearchRecipeListByIngredientController) {
+ this.searchRecipeListByIngredientController = SearchRecipeListByIngredientController;
+ }
+
+ public void setSearchRecipeListByNameController(
+ SearchRecipeListByNameController SearchRecipeListByNameController) {
+ this.searchRecipeListByNameController = SearchRecipeListByNameController;
+ }
+}
diff --git a/src/main/java/view/RecipeView.java b/src/main/java/view/RecipeView.java
new file mode 100644
index 000000000..6452eecd0
--- /dev/null
+++ b/src/main/java/view/RecipeView.java
@@ -0,0 +1,154 @@
+package view;
+
+import java.awt.*;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.swing.*;
+
+import entity.Recipe;
+import entity.User;
+import interface_adapter.RecipeController;
+import interface_adapter.SearchRecipePresenter;
+import interface_adapter.filter_recipes.FilterRecipesController;
+import use_case.filter_recipes.FilterRecipesDataAccessInterface;
+
+public class RecipeView extends JFrame {
+ private static final int TEXTFIELD_COLUMNS = 20;
+
+ private final JTextField ingredientInput;
+ private final JList recipeList;
+ private final DefaultListModel listModel;
+ private final RecipeController controller;
+ private final FilterRecipesController filterController;
+ private final SearchRecipePresenter presenter;
+ private final User user;
+
+ private final FilterRecipesDataAccessInterface frDataAccessInterface;
+ private final JComboBox dietComboBox;
+ private final JComboBox cuisineComboBox;
+ private final String defaultFilter;
+
+// public RecipeView(RecipeController controller, SearchRecipePresenter presenter, User user,
+// FilterRecipesDataAccessInterface frDataAccessInterface) {
+// this.controller = controller;
+// this.presenter = presenter;
+// this.user = user;
+// this.frDataAccessInterface = frDataAccessInterface;
+// this.defaultFilter = "Any";
+
+ public RecipeView(RecipeController controller, SearchRecipePresenter presenter, User user,
+ FilterRecipesController filterController,
+ FilterRecipesDataAccessInterface frDataAccessInterface) {
+ this.controller = controller;
+ this.presenter = presenter;
+ this.user = user;
+ this.filterController = filterController;
+ this.frDataAccessInterface = frDataAccessInterface;
+ this.defaultFilter = "Any";
+
+ setTitle("Recipe Generator");
+ setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+
+ ingredientInput = new JTextField(TEXTFIELD_COLUMNS);
+ final JButton searchButton = new JButton("Find Recipes");
+ recipeList = new JList<>();
+ listModel = new DefaultListModel<>();
+ recipeList.setModel(listModel);
+
+ searchButton.addActionListener(event -> handleSearch());
+ recipeList.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent event) {
+ if (event.getClickCount() == 2) {
+ // Get index of clicked item
+ final int index = recipeList.locationToIndex(event.getPoint());
+ // Ensure valid index
+ if (index >= 0) {
+ // Get selected item
+ final Recipe selectedItem = recipeList.getModel().getElementAt(index);
+ // Open a new window
+ new IndividualRecipeView(selectedItem, user);
+ }
+ }
+ }
+ });
+
+ final JPanel panel = new JPanel();
+ panel.add(new JLabel("Enter ingredients (comma-separated):"));
+ panel.add(ingredientInput);
+ panel.add(searchButton);
+ add(panel, BorderLayout.NORTH);
+ add(new JScrollPane(recipeList), BorderLayout.CENTER);
+
+ // search filters
+ final JPanel filterPanel = new JPanel(new FlowLayout());
+ dietComboBox = new JComboBox<>();
+ cuisineComboBox = new JComboBox<>();
+
+ filterPanel.add(new JLabel("Diet:"));
+ final List diets = frDataAccessInterface.getAvailableDiets();
+ populateDropdown(dietComboBox, diets);
+ filterPanel.add(dietComboBox);
+
+ filterPanel.add(new JLabel("Cuisine:"));
+ final List cuisines = frDataAccessInterface.getAvailableCuisines();
+ populateDropdown(cuisineComboBox, cuisines);
+ filterPanel.add(cuisineComboBox);
+
+ add(filterPanel, BorderLayout.SOUTH);
+
+ dietComboBox.addActionListener(event -> applyFilters());
+ cuisineComboBox.addActionListener(event -> applyFilters());
+
+ pack();
+ setVisible(true);
+ }
+
+ private void handleSearch() {
+ final String ingredientsText = ingredientInput.getText();
+ final List ingredients = Arrays.asList(ingredientsText.split(","));
+ listModel.clear();
+ // Delegate search to the controller when default filters applied
+ if (defaultFilter.equals(dietComboBox.getSelectedItem())
+ && defaultFilter.equals(cuisineComboBox.getSelectedItem())) {
+ controller.searchRecipes(ingredients);
+ // Retrieve results from the presenter
+ for (Recipe recipe : presenter.getRecipes()) {
+ listModel.addElement(recipe);
+ }
+ recipeList.setModel(listModel);
+ }
+ else {
+ // else: filters have been chosen, so using search function specific to filters
+ final List recipes = applyFilters();
+ for (Recipe recipe : recipes) {
+ listModel.addElement(recipe);
+ }
+ }
+ recipeList.setModel(listModel);
+ }
+
+ private void populateDropdown(JComboBox dropdown, List stringList) {
+ dropdown.addItem(defaultFilter);
+ for (String item : stringList) {
+ dropdown.addItem(item);
+ }
+ }
+
+ // applying the filters
+ private List applyFilters() {
+ final String enteredIngredients = ingredientInput.getText();
+ final List ingredients = List.of(enteredIngredients.split(","));
+ // get selected value from dropdown menu
+ final String selectedDiet = String.valueOf(dietComboBox.getSelectedItem());
+ final String selectedCuisine = String.valueOf(cuisineComboBox.getSelectedItem());
+ // call the data access object and api to return a list of recipes
+ final List recipesFiltered =
+ frDataAccessInterface.filterSearchRecipes(ingredients, selectedDiet, selectedCuisine);
+ return recipesFiltered;
+ }
+
+}
diff --git a/src/main/java/view/ShoppingListGUI.java b/src/main/java/view/ShoppingListGUI.java
new file mode 100644
index 000000000..029faa8d8
--- /dev/null
+++ b/src/main/java/view/ShoppingListGUI.java
@@ -0,0 +1,133 @@
+package view;
+
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.swing.*;
+
+import interface_adapter.ShoppingListController;
+
+public class ShoppingListGUI {
+ private final ShoppingListController controller;
+
+ public ShoppingListGUI(ShoppingListController controller) {
+ this.controller = controller;
+ }
+
+ public void run() {
+ // Main Frame
+ final JFrame frame = new JFrame("Shopping List Generator");
+ frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+
+ // Panel Layout
+ final JPanel panel = new JPanel();
+ panel.setLayout(new BorderLayout());
+
+ // Top Input Section
+ final JPanel inputPanel = new JPanel(new FlowLayout());
+ final JLabel ingredientLabel = new JLabel("Enter Ingredients (comma-separated):");
+ final JTextField ingredientField = new JTextField(20);
+ final JButton searchButton = new JButton("Search Recipes");
+ inputPanel.add(ingredientLabel);
+ inputPanel.add(ingredientField);
+ inputPanel.add(searchButton);
+
+ // Center Recipe List Section
+ final DefaultListModel recipeListModel = new DefaultListModel<>();
+ final JList recipeList = new JList<>(recipeListModel);
+ final JScrollPane recipeScrollPane = new JScrollPane(recipeList);
+ recipeScrollPane.setBorder(BorderFactory.createTitledBorder("Select Recipes"));
+
+ // TODO Make the recipe list clickable - will be complicated...
+// recipeList.addMouseListener(new MouseAdapter() {
+// public void mouseClicked(MouseEvent event) {
+// if (event.getClickCount() == 2) {
+// // Get index of clicked item
+// final int index = recipeList.locationToIndex(event.getPoint());
+// // Ensure valid index
+// if (index >= 0) {
+// // Get selected item
+// final String selectedItem = recipeList.getModel().getElementAt(index);
+//
+// // Convert it to a Recipe object
+// final Recipe selectedRecipe = api.nameToRecipe(selectedItem);
+//
+// // Open a new window
+// new IndividualRecipeView(selectedRecipe, user);
+// }
+// }
+// }
+// });
+
+ // Bottom Generate Section
+ final JButton generateButton = new JButton("Generate Shopping List");
+ final JTextArea shoppingListArea = new JTextArea(10, 50);
+ shoppingListArea.setEditable(false);
+ final JScrollPane shoppingListScrollPane = new JScrollPane(shoppingListArea);
+ shoppingListScrollPane.setBorder(BorderFactory.createTitledBorder("Shopping List"));
+
+ // Add components to panel
+ panel.add(inputPanel, BorderLayout.NORTH);
+ panel.add(recipeScrollPane, BorderLayout.CENTER);
+ panel.add(generateButton, BorderLayout.WEST);
+ panel.add(shoppingListScrollPane, BorderLayout.SOUTH);
+
+ // Add panel to frame
+ frame.add(panel);
+
+ // Search Button Action Listener
+ searchButton.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ String ingredientInput = ingredientField.getText().trim();
+ if (ingredientInput.isEmpty()) {
+ JOptionPane.showMessageDialog(frame, "Please enter ingredients!", "Error", JOptionPane.ERROR_MESSAGE);
+ }
+
+ // Parse the user-provided ingredients
+ final List availableIngredients = Arrays.asList(ingredientInput.split(","));
+
+ // Fetch recipes
+ final List recipes = controller.getRecipesContaining(availableIngredients);
+ if (recipes.isEmpty()) {
+ JOptionPane.showMessageDialog(frame, "No recipes found!", "Info", JOptionPane.INFORMATION_MESSAGE);
+ }
+ else {
+ recipeListModel.clear();
+ for (String recipe : recipes) {
+ recipeListModel.addElement(recipe);
+ }
+ }
+ }
+ });
+
+ // Generate Button Action Listener
+ generateButton.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ final List selectedRecipes = recipeList.getSelectedValuesList();
+ final String ingredientInput = ingredientField.getText().trim();
+
+ if (ingredientInput.isEmpty()) {
+ JOptionPane.showMessageDialog(frame, "Please enter ingredients!", "Error", JOptionPane.ERROR_MESSAGE);
+ }
+
+ List availableIngredients = Arrays.asList(ingredientInput.split(","));
+ if (selectedRecipes.isEmpty()) {
+ JOptionPane.showMessageDialog(frame, "Please select at least one recipe!", "Error", JOptionPane.ERROR_MESSAGE);
+ }
+
+ // Generate the shopping list
+ final String shoppingList = controller.generateShoppingList(selectedRecipes, availableIngredients);
+ shoppingListArea.setText(shoppingList);
+ }
+ });
+
+ // Show Frame
+ frame.pack();
+ frame.setVisible(true);
+ }
+}
diff --git a/src/main/java/view/SignupView.java b/src/main/java/view/SignupView.java
deleted file mode 100644
index f98570d62..000000000
--- a/src/main/java/view/SignupView.java
+++ /dev/null
@@ -1,199 +0,0 @@
-package view;
-
-import java.awt.Component;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.beans.PropertyChangeEvent;
-import java.beans.PropertyChangeListener;
-
-import javax.swing.BoxLayout;
-import javax.swing.JButton;
-import javax.swing.JLabel;
-import javax.swing.JOptionPane;
-import javax.swing.JPanel;
-import javax.swing.JPasswordField;
-import javax.swing.JTextField;
-import javax.swing.event.DocumentEvent;
-import javax.swing.event.DocumentListener;
-
-import interface_adapter.signup.SignupController;
-import interface_adapter.signup.SignupState;
-import interface_adapter.signup.SignupViewModel;
-
-/**
- * The View for the Signup Use Case.
- */
-public class SignupView extends JPanel implements ActionListener, PropertyChangeListener {
- private final String viewName = "sign up";
-
- private final SignupViewModel signupViewModel;
- private final JTextField usernameInputField = new JTextField(15);
- private final JPasswordField passwordInputField = new JPasswordField(15);
- private final JPasswordField repeatPasswordInputField = new JPasswordField(15);
- private SignupController signupController;
-
- private final JButton signUp;
- private final JButton cancel;
- private final JButton toLogin;
-
- public SignupView(SignupViewModel signupViewModel) {
- this.signupViewModel = signupViewModel;
- signupViewModel.addPropertyChangeListener(this);
-
- final JLabel title = new JLabel(SignupViewModel.TITLE_LABEL);
- title.setAlignmentX(Component.CENTER_ALIGNMENT);
-
- final LabelTextPanel usernameInfo = new LabelTextPanel(
- new JLabel(SignupViewModel.USERNAME_LABEL), usernameInputField);
- final LabelTextPanel passwordInfo = new LabelTextPanel(
- new JLabel(SignupViewModel.PASSWORD_LABEL), passwordInputField);
- final LabelTextPanel repeatPasswordInfo = new LabelTextPanel(
- new JLabel(SignupViewModel.REPEAT_PASSWORD_LABEL), repeatPasswordInputField);
-
- final JPanel buttons = new JPanel();
- toLogin = new JButton(SignupViewModel.TO_LOGIN_BUTTON_LABEL);
- buttons.add(toLogin);
- signUp = new JButton(SignupViewModel.SIGNUP_BUTTON_LABEL);
- buttons.add(signUp);
- cancel = new JButton(SignupViewModel.CANCEL_BUTTON_LABEL);
- buttons.add(cancel);
-
- signUp.addActionListener(
- // This creates an anonymous subclass of ActionListener and instantiates it.
- new ActionListener() {
- public void actionPerformed(ActionEvent evt) {
- if (evt.getSource().equals(signUp)) {
- final SignupState currentState = signupViewModel.getState();
-
- signupController.execute(
- currentState.getUsername(),
- currentState.getPassword(),
- currentState.getRepeatPassword()
- );
- }
- }
- }
- );
-
- toLogin.addActionListener(
- new ActionListener() {
- public void actionPerformed(ActionEvent evt) {
- signupController.switchToLoginView();
- }
- }
- );
-
- cancel.addActionListener(this);
-
- addUsernameListener();
- addPasswordListener();
- addRepeatPasswordListener();
-
- this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
-
- this.add(title);
- this.add(usernameInfo);
- this.add(passwordInfo);
- this.add(repeatPasswordInfo);
- this.add(buttons);
- }
-
- private void addUsernameListener() {
- usernameInputField.getDocument().addDocumentListener(new DocumentListener() {
-
- private void documentListenerHelper() {
- final SignupState currentState = signupViewModel.getState();
- currentState.setUsername(usernameInputField.getText());
- signupViewModel.setState(currentState);
- }
-
- @Override
- public void insertUpdate(DocumentEvent e) {
- documentListenerHelper();
- }
-
- @Override
- public void removeUpdate(DocumentEvent e) {
- documentListenerHelper();
- }
-
- @Override
- public void changedUpdate(DocumentEvent e) {
- documentListenerHelper();
- }
- });
- }
-
- private void addPasswordListener() {
- passwordInputField.getDocument().addDocumentListener(new DocumentListener() {
-
- private void documentListenerHelper() {
- final SignupState currentState = signupViewModel.getState();
- currentState.setPassword(new String(passwordInputField.getPassword()));
- signupViewModel.setState(currentState);
- }
-
- @Override
- public void insertUpdate(DocumentEvent e) {
- documentListenerHelper();
- }
-
- @Override
- public void removeUpdate(DocumentEvent e) {
- documentListenerHelper();
- }
-
- @Override
- public void changedUpdate(DocumentEvent e) {
- documentListenerHelper();
- }
- });
- }
-
- private void addRepeatPasswordListener() {
- repeatPasswordInputField.getDocument().addDocumentListener(new DocumentListener() {
-
- private void documentListenerHelper() {
- final SignupState currentState = signupViewModel.getState();
- currentState.setRepeatPassword(new String(repeatPasswordInputField.getPassword()));
- signupViewModel.setState(currentState);
- }
-
- @Override
- public void insertUpdate(DocumentEvent e) {
- documentListenerHelper();
- }
-
- @Override
- public void removeUpdate(DocumentEvent e) {
- documentListenerHelper();
- }
-
- @Override
- public void changedUpdate(DocumentEvent e) {
- documentListenerHelper();
- }
- });
- }
-
- @Override
- public void actionPerformed(ActionEvent evt) {
- JOptionPane.showMessageDialog(this, "Cancel not implemented yet.");
- }
-
- @Override
- public void propertyChange(PropertyChangeEvent evt) {
- final SignupState state = (SignupState) evt.getNewValue();
- if (state.getUsernameError() != null) {
- JOptionPane.showMessageDialog(this, state.getUsernameError());
- }
- }
-
- public String getViewName() {
- return viewName;
- }
-
- public void setSignupController(SignupController controller) {
- this.signupController = controller;
- }
-}
diff --git a/src/test/java/filter_recipes_test/FilterRecipesInteractorTest.java b/src/test/java/filter_recipes_test/FilterRecipesInteractorTest.java
new file mode 100644
index 000000000..8f3a82a6e
--- /dev/null
+++ b/src/test/java/filter_recipes_test/FilterRecipesInteractorTest.java
@@ -0,0 +1,54 @@
+package filter_recipes_test;
+
+import data_access.InMemoryFilterRecipesDataAccessObject;
+import org.junit.jupiter.api.Test;
+import use_case.filter_recipes.*;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+class FilterRecipesInteractorTest {
+
+ @Test
+ void getAvailableDiets() {
+ FilterRecipesDataAccessInterface testRepo = new InMemoryFilterRecipesDataAccessObject();
+ FilterRecipesInputBoundary interactor = new FilterRecipesInteractor(testRepo);
+ List diets = interactor.getAvailableDiets();
+ assertNotNull(diets);
+ assertTrue(diets.contains("vegetarian"));
+ assertTrue(diets.contains("vegan"));
+ }
+
+ @Test
+ void getAvailableCuisines() {
+ FilterRecipesDataAccessInterface testRepo = new InMemoryFilterRecipesDataAccessObject();
+ FilterRecipesInputBoundary interactor = new FilterRecipesInteractor(testRepo);
+ List cuisines = interactor.getAvailableCuisines();
+ assertNotNull(cuisines);
+ assertTrue(cuisines.contains("italian"));
+ assertTrue(cuisines.contains("chinese"));
+ }
+
+ @Test
+ void filterSearchRecipes() {
+ FilterRecipesDataAccessInterface testRepo = new InMemoryFilterRecipesDataAccessObject();
+
+ FilterRecipesOutputBoundary successPresenter = new FilterRecipesOutputBoundary() {
+ @Override
+ public void prepareSuccessView(FilterRecipesOutputData data) {
+ assertNotNull(data.getRecipes());
+ assertEquals(data.getRecipes().size(), 1);
+ assertEquals("vegan pasta", data.getRecipes().get(0).getName());
+ }
+ @Override
+ public void prepareFailView(String error) {
+ fail("Use case failure is unexpected");
+ }
+ };
+
+ FilterRecipesInputData inputData = new FilterRecipesInputData(
+ List.of("tomato", "cheese"), "vegan", "italian");
+ FilterRecipesInputBoundary interactor = new FilterRecipesInteractor(testRepo, successPresenter);
+ interactor.filterSearchRecipes(inputData);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/nutrition_information_test/NutritionInformationInteractorTset.java b/src/test/java/nutrition_information_test/NutritionInformationInteractorTset.java
new file mode 100644
index 000000000..8c4ce8ef0
--- /dev/null
+++ b/src/test/java/nutrition_information_test/NutritionInformationInteractorTset.java
@@ -0,0 +1,11 @@
+package nutrition_information_test;
+
+import org.junit.jupiter.api.Test;
+import use_case.nutrition_information.NutritionInformationInputData;
+import use_case.nutrition_information.NutritionInformationInteractor;
+
+public class NutritionInformationInteractorTset {
+ @Test
+ void testNutritionInformation() {
+ }
+}
diff --git a/src/test/java/use_case/login/LoginInteractorTest.java b/src/test/java/use_case/login/LoginInteractorTest.java
deleted file mode 100644
index 9fc1bfc89..000000000
--- a/src/test/java/use_case/login/LoginInteractorTest.java
+++ /dev/null
@@ -1,124 +0,0 @@
-package use_case.login;
-
-import data_access.InMemoryUserDataAccessObject;
-import entity.CommonUserFactory;
-import entity.User;
-import entity.UserFactory;
-import org.junit.jupiter.api.Test;
-
-import java.time.LocalDateTime;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-class LoginInteractorTest {
-
- @Test
- void successTest() {
- LoginInputData inputData = new LoginInputData("Paul", "password");
- LoginUserDataAccessInterface userRepository = new InMemoryUserDataAccessObject();
-
- // For the success test, we need to add Paul to the data access repository before we log in.
- UserFactory factory = new CommonUserFactory();
- User user = factory.create("Paul", "password");
- userRepository.save(user);
-
- // This creates a successPresenter that tests whether the test case is as we expect.
- LoginOutputBoundary successPresenter = new LoginOutputBoundary() {
- @Override
- public void prepareSuccessView(LoginOutputData user) {
- assertEquals("Paul", user.getUsername());
- }
-
- @Override
- public void prepareFailView(String error) {
- fail("Use case failure is unexpected.");
- }
- };
-
- LoginInputBoundary interactor = new LoginInteractor(userRepository, successPresenter);
- interactor.execute(inputData);
- }
-
- @Test
- void successUserLoggedInTest() {
- LoginInputData inputData = new LoginInputData("Paul", "password");
- LoginUserDataAccessInterface userRepository = new InMemoryUserDataAccessObject();
-
- // For the success test, we need to add Paul to the data access repository before we log in.
- UserFactory factory = new CommonUserFactory();
- User user = factory.create("Paul", "password");
- userRepository.save(user);
-
- // This creates a successPresenter that tests whether the test case is as we expect.
- LoginOutputBoundary successPresenter = new LoginOutputBoundary() {
- @Override
- public void prepareSuccessView(LoginOutputData user) {
- assertEquals("Paul", userRepository.getCurrentUsername());
- }
-
- @Override
- public void prepareFailView(String error) {
- fail("Use case failure is unexpected.");
- }
- };
-
- LoginInputBoundary interactor = new LoginInteractor(userRepository, successPresenter);
- assertEquals(null, userRepository.getCurrentUsername());
-
- interactor.execute(inputData);
- }
-
- @Test
- void failurePasswordMismatchTest() {
- LoginInputData inputData = new LoginInputData("Paul", "wrong");
- LoginUserDataAccessInterface userRepository = new InMemoryUserDataAccessObject();
-
- // For this failure test, we need to add Paul to the data access repository before we log in, and
- // the passwords should not match.
- UserFactory factory = new CommonUserFactory();
- User user = factory.create("Paul", "password");
- userRepository.save(user);
-
- // This creates a presenter that tests whether the test case is as we expect.
- LoginOutputBoundary failurePresenter = new LoginOutputBoundary() {
- @Override
- public void prepareSuccessView(LoginOutputData user) {
- // this should never be reached since the test case should fail
- fail("Use case success is unexpected.");
- }
-
- @Override
- public void prepareFailView(String error) {
- assertEquals("Incorrect password for \"Paul\".", error);
- }
- };
-
- LoginInputBoundary interactor = new LoginInteractor(userRepository, failurePresenter);
- interactor.execute(inputData);
- }
-
- @Test
- void failureUserDoesNotExistTest() {
- LoginInputData inputData = new LoginInputData("Paul", "password");
- LoginUserDataAccessInterface userRepository = new InMemoryUserDataAccessObject();
-
- // Add Paul to the repo so that when we check later they already exist
-
- // This creates a presenter that tests whether the test case is as we expect.
- LoginOutputBoundary failurePresenter = new LoginOutputBoundary() {
- @Override
- public void prepareSuccessView(LoginOutputData user) {
- // this should never be reached since the test case should fail
- fail("Use case success is unexpected.");
- }
-
- @Override
- public void prepareFailView(String error) {
- assertEquals("Paul: Account does not exist.", error);
- }
- };
-
- LoginInputBoundary interactor = new LoginInteractor(userRepository, failurePresenter);
- interactor.execute(inputData);
- }
-}
\ No newline at end of file
diff --git a/src/test/java/use_case/logout/LogoutInteractorTest.java b/src/test/java/use_case/logout/LogoutInteractorTest.java
deleted file mode 100644
index b005a402c..000000000
--- a/src/test/java/use_case/logout/LogoutInteractorTest.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package use_case.logout;
-
-import data_access.InMemoryUserDataAccessObject;
-import entity.CommonUserFactory;
-import entity.User;
-import entity.UserFactory;
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-class LogoutInteractorTest {
-
- @Test
- void successTest() {
- LogoutInputData inputData = new LogoutInputData("Paul");
- InMemoryUserDataAccessObject userRepository = new InMemoryUserDataAccessObject();
-
- // For the success test, we need to add Paul to the data access repository before we log in.
- UserFactory factory = new CommonUserFactory();
- User user = factory.create("Paul", "password");
- userRepository.save(user);
- userRepository.setCurrentUsername("Paul");
-
- // This creates a successPresenter that tests whether the test case is as we expect.
- LogoutOutputBoundary successPresenter = new LogoutOutputBoundary() {
- @Override
- public void prepareSuccessView(LogoutOutputData user) {
- // check that the output data contains the username of who logged out
- assertEquals("Paul", user.getUsername());
- }
-
- @Override
- public void prepareFailView(String error) {
- fail("Use case failure is unexpected.");
- }
- };
-
- LogoutInputBoundary interactor = new LogoutInteractor(userRepository, successPresenter);
- interactor.execute(inputData);
- // check that the user was logged out
- assertNull(userRepository.getCurrentUsername());
- }
-
-}
\ No newline at end of file
diff --git a/src/test/java/use_case/search_recipe_list_by_ingredient/SearchRecipeListByIngredientInteractorTest.java b/src/test/java/use_case/search_recipe_list_by_ingredient/SearchRecipeListByIngredientInteractorTest.java
new file mode 100644
index 000000000..dc4c84fb1
--- /dev/null
+++ b/src/test/java/use_case/search_recipe_list_by_ingredient/SearchRecipeListByIngredientInteractorTest.java
@@ -0,0 +1,185 @@
+package use_case.search_recipe_list_by_ingredient;
+
+import data_access.InMemorySearchRecipeListDataAccessObject;
+import entity.Ingredient;
+import entity.Recipe;
+import entity.User;
+import org.junit.jupiter.api.Test;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class SearchRecipeListByIngredientInteractorTest {
+ private static final Recipe recipe1 = new Recipe(
+ "recipe1",
+ "url",
+ List.of(
+ new Ingredient("chicken", 1.0, "unit"),
+ new Ingredient("mushroom", 1.0, "unit")),
+ "image");
+ private static final Recipe recipe2 = new Recipe(
+ "recipe2",
+ "url",
+ List.of(
+ new Ingredient("tomato", 1.0, "unit"),
+ new Ingredient("onion", 1.0, "unit")),
+ "image");
+ private static final Recipe recipe3 = new Recipe(
+ "recipe3",
+ "url",
+ List.of(
+ new Ingredient("lettuce", 1.0, "unit"),
+ new Ingredient("pork", 1.0, "unit")),
+ "image");
+ private static final List bookmarks = List.of(recipe1, recipe2, recipe3);
+ private static final List recentlyViewed = List.of(recipe1, recipe2, recipe3);
+
+ @Test
+ void successTestForBookmarks() {
+ final Map> folders = new HashMap<>();
+ final User user = new User("cindy", "123", bookmarks, recentlyViewed, folders);
+ final List ingredients = List.of("chicken", "mushroom", "tomato");
+
+ SearchRecipeListByIngredientInputData inputData = new SearchRecipeListByIngredientInputData(
+ ingredients, user, "bookmarks");
+ SearchRecipeListByIngredientDataAccessInterface userRepository = new InMemorySearchRecipeListDataAccessObject();
+ userRepository.addUser(user);
+
+ SearchRecipeListByIngredientOutputBoundary successPresenter =
+ new SearchRecipeListByIngredientOutputBoundary() {
+ @Override
+ public void prepareSuccessView(SearchRecipeListByIngredientOutputData searchResults) {
+ assertEquals(List.of(recipe1, recipe2), searchResults.getRecipes());
+ }
+
+ @Override
+ public void prepareFailView(String error) {
+ fail("Use case failure is unexpected.");
+ }
+ };
+
+ SearchRecipeListByIngredientInputBoundary interactor =
+ new SearchRecipeListByIngredientInteractor(userRepository, successPresenter);
+ interactor.execute(inputData);
+ }
+
+ @Test
+ void successTestForRecentlyViewed() {
+ final Map> folders = new HashMap<>();
+ final User user = new User("cindy", "123", bookmarks, recentlyViewed, folders);
+ final List ingredients = List.of("chicken", "mushroom", "tomato");
+
+ SearchRecipeListByIngredientInputData inputData = new SearchRecipeListByIngredientInputData(
+ ingredients, user, "recentlyViewed");
+ SearchRecipeListByIngredientDataAccessInterface userRepository = new InMemorySearchRecipeListDataAccessObject();
+ userRepository.addUser(user);
+
+ SearchRecipeListByIngredientOutputBoundary successPresenter =
+ new SearchRecipeListByIngredientOutputBoundary() {
+ @Override
+ public void prepareSuccessView(SearchRecipeListByIngredientOutputData searchResults) {
+ assertEquals(List.of(recipe1, recipe2), searchResults.getRecipes());
+ }
+
+ @Override
+ public void prepareFailView(String error) {
+ fail("Use case failure is unexpected.");
+ }
+ };
+
+ SearchRecipeListByIngredientInputBoundary interactor =
+ new SearchRecipeListByIngredientInteractor(userRepository, successPresenter);
+ interactor.execute(inputData);
+ }
+
+ @Test
+ void successTestForFolder() {
+ final Map> folders = new HashMap<>();
+ folders.put("dinner", bookmarks);
+ folders.put("lunch", List.of());
+ final User user = new User("cindy", "123", bookmarks, recentlyViewed, folders);
+ final List ingredients = List.of("chicken", "mushroom", "tomato");
+
+ SearchRecipeListByIngredientInputData inputData = new SearchRecipeListByIngredientInputData(
+ ingredients, user, "dinner");
+ SearchRecipeListByIngredientDataAccessInterface userRepository = new InMemorySearchRecipeListDataAccessObject();
+ userRepository.addUser(user);
+
+ SearchRecipeListByIngredientOutputBoundary successPresenter =
+ new SearchRecipeListByIngredientOutputBoundary() {
+ @Override
+ public void prepareSuccessView(SearchRecipeListByIngredientOutputData searchResults) {
+ assertEquals(List.of(recipe1, recipe2), searchResults.getRecipes());
+ }
+
+ @Override
+ public void prepareFailView(String error) {
+ fail("Use case failure is unexpected.");
+ }
+ };
+
+ SearchRecipeListByIngredientInputBoundary interactor =
+ new SearchRecipeListByIngredientInteractor(userRepository, successPresenter);
+ interactor.execute(inputData);
+ }
+
+ @Test
+ void emptyQueryTest() {
+ final Map> folders = new HashMap<>();
+ final User user = new User("cindy", "123", bookmarks, recentlyViewed, folders);
+
+ SearchRecipeListByIngredientInputData inputData = new SearchRecipeListByIngredientInputData(
+ List.of(), user, "bookmarks");
+ SearchRecipeListByIngredientDataAccessInterface userRepository = new InMemorySearchRecipeListDataAccessObject();
+ userRepository.addUser(user);
+
+ SearchRecipeListByIngredientOutputBoundary successPresenter =
+ new SearchRecipeListByIngredientOutputBoundary() {
+ @Override
+ public void prepareSuccessView(SearchRecipeListByIngredientOutputData searchResults) {
+ assertEquals(List.of(), searchResults.getRecipes());
+ }
+
+ @Override
+ public void prepareFailView(String error) {
+ fail("Use case failure is unexpected.");
+ }
+ };
+
+ SearchRecipeListByIngredientInputBoundary interactor =
+ new SearchRecipeListByIngredientInteractor(userRepository, successPresenter);
+ interactor.execute(inputData);
+ }
+
+ @Test
+ void noMatchingIngredientTest() {
+ final Map> folders = new HashMap<>();
+ final User user = new User("cindy", "123", bookmarks, recentlyViewed, folders);
+ final List ingredients = List.of("triangle", "tree");
+
+ SearchRecipeListByIngredientInputData inputData = new SearchRecipeListByIngredientInputData(
+ ingredients, user, "bookmarks");
+ SearchRecipeListByIngredientDataAccessInterface userRepository = new InMemorySearchRecipeListDataAccessObject();
+ userRepository.addUser(user);
+
+ SearchRecipeListByIngredientOutputBoundary successPresenter =
+ new SearchRecipeListByIngredientOutputBoundary() {
+ @Override
+ public void prepareSuccessView(SearchRecipeListByIngredientOutputData searchResults) {
+ assertEquals(List.of(), searchResults.getRecipes());
+ }
+
+ @Override
+ public void prepareFailView(String error) {
+ fail("Use case failure is unexpected.");
+ }
+ };
+
+ SearchRecipeListByIngredientInputBoundary interactor =
+ new SearchRecipeListByIngredientInteractor(userRepository, successPresenter);
+ interactor.execute(inputData);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/use_case/search_recipe_list_by_name/SearchRecipeListByNameInteractorTest.java b/src/test/java/use_case/search_recipe_list_by_name/SearchRecipeListByNameInteractorTest.java
new file mode 100644
index 000000000..96e00eb50
--- /dev/null
+++ b/src/test/java/use_case/search_recipe_list_by_name/SearchRecipeListByNameInteractorTest.java
@@ -0,0 +1,187 @@
+package use_case.search_recipe_list_by_name;
+
+import data_access.InMemorySearchRecipeListDataAccessObject;
+import entity.Ingredient;
+import entity.Recipe;
+import entity.User;
+import org.junit.jupiter.api.Test;
+import use_case.search_recipe_list_by_ingredient.*;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+
+class SearchRecipeListByNameInteractorTest {
+ private static final Recipe recipe1 = new Recipe(
+ "recipe1 roasted in oven",
+ "url",
+ List.of(
+ new Ingredient("chicken", 1.0, "unit"),
+ new Ingredient("mushroom", 1.0, "unit")),
+ "image");
+ private static final Recipe recipe2 = new Recipe(
+ "recipe2 roasted on grill",
+ "url",
+ List.of(
+ new Ingredient("tomato", 1.0, "unit"),
+ new Ingredient("onion", 1.0, "unit")),
+ "image");
+ private static final Recipe recipe3 = new Recipe(
+ "recipe3 microwaved",
+ "url",
+ List.of(
+ new Ingredient("lettuce", 1.0, "unit"),
+ new Ingredient("pork", 1.0, "unit")),
+ "image");
+ private static final List bookmarks = List.of(recipe1, recipe2, recipe3);
+ private static final List recentlyViewed = List.of(recipe1, recipe2, recipe3);
+
+ @Test
+ void successTestForBookmarks() {
+ final Map> folders = new HashMap<>();
+ final User user = new User("cindy", "123", bookmarks, recentlyViewed, folders);
+ final String recipeName = "roasted";
+
+ SearchRecipeListByNameInputData inputData = new SearchRecipeListByNameInputData(
+ recipeName, user, "bookmarks");
+ SearchRecipeListByNameDataAccessInterface userRepository = new InMemorySearchRecipeListDataAccessObject();
+ userRepository.addUser(user);
+
+ SearchRecipeListByNameOutputBoundary successPresenter =
+ new SearchRecipeListByNameOutputBoundary() {
+ @Override
+ public void prepareSuccessView(SearchRecipeListByNameOutputData searchResults) {
+ assertEquals(List.of(recipe1, recipe2), searchResults.getRecipes());
+ }
+
+ @Override
+ public void prepareFailView(String error) {
+ fail("Use case failure is unexpected.");
+ }
+ };
+
+ SearchRecipeListByNameInputBoundary interactor =
+ new SearchRecipeListByNameInteractor(userRepository, successPresenter);
+ interactor.execute(inputData);
+ }
+
+ @Test
+ void successTestForRecentlyViewed() {
+ final Map> folders = new HashMap<>();
+ final User user = new User("cindy", "123", bookmarks, recentlyViewed, folders);
+ final String recipeName = "roasted";
+
+ SearchRecipeListByNameInputData inputData = new SearchRecipeListByNameInputData(
+ recipeName, user, "recentlyViewed");
+ SearchRecipeListByNameDataAccessInterface userRepository = new InMemorySearchRecipeListDataAccessObject();
+ userRepository.addUser(user);
+
+ SearchRecipeListByNameOutputBoundary successPresenter =
+ new SearchRecipeListByNameOutputBoundary() {
+ @Override
+ public void prepareSuccessView(SearchRecipeListByNameOutputData searchResults) {
+ assertEquals(List.of(recipe1, recipe2), searchResults.getRecipes());
+ }
+
+ @Override
+ public void prepareFailView(String error) {
+ fail("Use case failure is unexpected.");
+ }
+ };
+
+ SearchRecipeListByNameInputBoundary interactor =
+ new SearchRecipeListByNameInteractor(userRepository, successPresenter);
+ interactor.execute(inputData);
+ }
+
+ @Test
+ void successTestForFolder() {
+ final Map> folders = new HashMap<>();
+ folders.put("dinner", bookmarks);
+ folders.put("lunch", List.of());
+ final User user = new User("cindy", "123", bookmarks, recentlyViewed, folders);
+ final String recipeName = "roasted";
+
+ SearchRecipeListByNameInputData inputData = new SearchRecipeListByNameInputData(
+ recipeName, user, "dinner");
+ SearchRecipeListByNameDataAccessInterface userRepository = new InMemorySearchRecipeListDataAccessObject();
+ userRepository.addUser(user);
+
+ SearchRecipeListByNameOutputBoundary successPresenter =
+ new SearchRecipeListByNameOutputBoundary() {
+ @Override
+ public void prepareSuccessView(SearchRecipeListByNameOutputData searchResults) {
+ assertEquals(List.of(recipe1, recipe2), searchResults.getRecipes());
+ }
+
+ @Override
+ public void prepareFailView(String error) {
+ fail("Use case failure is unexpected.");
+ }
+ };
+
+ SearchRecipeListByNameInputBoundary interactor =
+ new SearchRecipeListByNameInteractor(userRepository, successPresenter);
+ interactor.execute(inputData);
+ }
+
+ @Test
+ void emptyQueryTest() {
+ final Map> folders = new HashMap<>();
+ final User user = new User("cindy", "123", bookmarks, recentlyViewed, folders);
+
+ SearchRecipeListByNameInputData inputData = new SearchRecipeListByNameInputData(
+ "", user, "bookmarks");
+ SearchRecipeListByNameDataAccessInterface userRepository = new InMemorySearchRecipeListDataAccessObject();
+ userRepository.addUser(user);
+
+ SearchRecipeListByNameOutputBoundary successPresenter =
+ new SearchRecipeListByNameOutputBoundary() {
+ @Override
+ public void prepareSuccessView(SearchRecipeListByNameOutputData searchResults) {
+ assertEquals(List.of(), searchResults.getRecipes());
+ }
+
+ @Override
+ public void prepareFailView(String error) {
+ fail("Use case failure is unexpected.");
+ }
+ };
+
+ SearchRecipeListByNameInputBoundary interactor =
+ new SearchRecipeListByNameInteractor(userRepository, successPresenter);
+ interactor.execute(inputData);
+ }
+
+ @Test
+ void noMatchingNameTest() {
+ final Map> folders = new HashMap<>();
+ final User user = new User("cindy", "123", bookmarks, recentlyViewed, folders);
+ final String recipeName = "steamed";
+
+ SearchRecipeListByNameInputData inputData = new SearchRecipeListByNameInputData(
+ recipeName, user, "bookmarks");
+ SearchRecipeListByNameDataAccessInterface userRepository = new InMemorySearchRecipeListDataAccessObject();
+ userRepository.addUser(user);
+
+ SearchRecipeListByNameOutputBoundary successPresenter =
+ new SearchRecipeListByNameOutputBoundary() {
+ @Override
+ public void prepareSuccessView(SearchRecipeListByNameOutputData searchResults) {
+ assertEquals(List.of(), searchResults.getRecipes());
+ }
+
+ @Override
+ public void prepareFailView(String error) {
+ fail("Use case failure is unexpected.");
+ }
+ };
+
+ SearchRecipeListByNameInputBoundary interactor =
+ new SearchRecipeListByNameInteractor(userRepository, successPresenter);
+ interactor.execute(inputData);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/use_case/signup/SignupInteractorTest.java b/src/test/java/use_case/signup/SignupInteractorTest.java
deleted file mode 100644
index 00f757c1c..000000000
--- a/src/test/java/use_case/signup/SignupInteractorTest.java
+++ /dev/null
@@ -1,104 +0,0 @@
-package use_case.signup;
-
-import data_access.InMemoryUserDataAccessObject;
-import entity.CommonUserFactory;
-import entity.User;
-import entity.UserFactory;
-import org.junit.jupiter.api.Test;
-
-import java.time.LocalDateTime;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-class SignupInteractorTest {
-
- @Test
- void successTest() {
- SignupInputData inputData = new SignupInputData("Paul", "password", "password");
- SignupUserDataAccessInterface userRepository = new InMemoryUserDataAccessObject();
-
- // This creates a successPresenter that tests whether the test case is as we expect.
- SignupOutputBoundary successPresenter = new SignupOutputBoundary() {
- @Override
- public void prepareSuccessView(SignupOutputData user) {
- // 2 things to check: the output data is correct, and the user has been created in the DAO.
- assertEquals("Paul", user.getUsername());
- assertTrue(userRepository.existsByName("Paul"));
- }
-
- @Override
- public void prepareFailView(String error) {
- fail("Use case failure is unexpected.");
- }
-
- @Override
- public void switchToLoginView() {
- // This is expected
- }
- };
-
- SignupInputBoundary interactor = new SignupInteractor(userRepository, successPresenter, new CommonUserFactory());
- interactor.execute(inputData);
- }
-
- @Test
- void failurePasswordMismatchTest() {
- SignupInputData inputData = new SignupInputData("Paul", "password", "wrong");
- SignupUserDataAccessInterface userRepository = new InMemoryUserDataAccessObject();
-
- // This creates a presenter that tests whether the test case is as we expect.
- SignupOutputBoundary failurePresenter = new SignupOutputBoundary() {
- @Override
- public void prepareSuccessView(SignupOutputData user) {
- // this should never be reached since the test case should fail
- fail("Use case success is unexpected.");
- }
-
- @Override
- public void prepareFailView(String error) {
- assertEquals("Passwords don't match.", error);
- }
-
- @Override
- public void switchToLoginView() {
- // This is expected
- }
- };
-
- SignupInputBoundary interactor = new SignupInteractor(userRepository, failurePresenter, new CommonUserFactory());
- interactor.execute(inputData);
- }
-
- @Test
- void failureUserExistsTest() {
- SignupInputData inputData = new SignupInputData("Paul", "password", "wrong");
- SignupUserDataAccessInterface userRepository = new InMemoryUserDataAccessObject();
-
- // Add Paul to the repo so that when we check later they already exist
- UserFactory factory = new CommonUserFactory();
- User user = factory.create("Paul", "pwd");
- userRepository.save(user);
-
- // This creates a presenter that tests whether the test case is as we expect.
- SignupOutputBoundary failurePresenter = new SignupOutputBoundary() {
- @Override
- public void prepareSuccessView(SignupOutputData user) {
- // this should never be reached since the test case should fail
- fail("Use case success is unexpected.");
- }
-
- @Override
- public void prepareFailView(String error) {
- assertEquals("User already exists.", error);
- }
-
- @Override
- public void switchToLoginView() {
- // This is expected
- }
- };
-
- SignupInputBoundary interactor = new SignupInteractor(userRepository, failurePresenter, new CommonUserFactory());
- interactor.execute(inputData);
- }
-}
\ No newline at end of file
diff --git a/users.json b/users.json
new file mode 100644
index 000000000..71e6f3304
--- /dev/null
+++ b/users.json
@@ -0,0 +1,37 @@
+[{
+ "bookmarks": [],
+ "recentlyViewed": [{
+ "image": "https://img.spoonacular.com/recipes/648368-312x231.jpg",
+ "name": "Jalapeno Queso With Goat Cheese",
+ "ingredients": [
+ {
+ "unit": "oz",
+ "amount": 8,
+ "name": "canned tomatoes",
+ "id": 0
+ },
+ {
+ "unit": "oz",
+ "amount": 5.3,
+ "name": "goat cheese",
+ "id": 0
+ },
+ {
+ "unit": "",
+ "amount": 1,
+ "name": "ea. jalapeno pepper",
+ "id": 0
+ },
+ {
+ "unit": "teaspoons",
+ "amount": 2,
+ "name": "hot sauce",
+ "id": 0
+ }
+ ],
+ "id": 0,
+ "url": "https://spoonacular.com/jalapeno-queso-with-goat-cheese-648368"
+ }],
+ "password": "hhcgoodluck",
+ "username": "hhc"
+}]
\ No newline at end of file