diff --git a/.gitignore b/.gitignore
index 650c91720..e00dc3664 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,7 +3,10 @@ out/
!**/src/main/**/out/
!**/src/test/**/out/
.idea/easycode.ignore
+.idea/
+homework-5.iml
+src/keys
target/
### Eclipse ###
diff --git a/.idea/.gitignore b/.idea/.gitignore
index 13566b81b..650c91720 100644
--- a/.idea/.gitignore
+++ b/.idea/.gitignore
@@ -1,8 +1,32 @@
-# Default ignored files
-/shelf/
-/workspace.xml
-# Editor-based HTTP Client requests
-/httpRequests/
-# Datasource local storage ignored files
-/dataSources/
-/dataSources.local.xml
+### IntelliJ IDEA ###
+out/
+!**/src/main/**/out/
+!**/src/test/**/out/
+.idea/easycode.ignore
+
+target/
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+bin/
+!**/src/main/**/bin/
+!**/src/test/**/bin/
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store
\ No newline at end of file
diff --git a/.idea/checkstyle-idea.xml b/.idea/checkstyle-idea.xml
index aa01ae2cb..0a0561d2c 100644
--- a/.idea/checkstyle-idea.xml
+++ b/.idea/checkstyle-idea.xml
@@ -1,7 +1,7 @@
- 10.18.0
+ 10.18.1
JavaOnly
\ No newline at end of file
diff --git a/src/main/java/Constants.java b/src/main/java/Constants.java
new file mode 100644
index 000000000..ce3a5c882
--- /dev/null
+++ b/src/main/java/Constants.java
@@ -0,0 +1,17 @@
+package main.java;
+
+import java.awt.Dimension;
+import java.awt.Toolkit;
+
+/**
+ * Constants used in this program.
+ */
+public class Constants {
+ // App Size
+ private static final Dimension SCREEN_SIZE = Toolkit.getDefaultToolkit().getScreenSize();
+ public static final int APP_WIDTH = SCREEN_SIZE.width;
+ public static final int APP_HEIGHT = SCREEN_SIZE.height;
+ public static final int MIN_HEIGHT = 400;
+ public static final int MIN_WIDTH = 400;
+}
+
diff --git a/src/main/java/app/AppBuilder.java b/src/main/java/app/AppBuilder.java
index e9eef5c81..c1f0b4ae3 100644
--- a/src/main/java/app/AppBuilder.java
+++ b/src/main/java/app/AppBuilder.java
@@ -1,42 +1,64 @@
package app;
-import java.awt.CardLayout;
+import java.awt.*;
-import javax.swing.JFrame;
-import javax.swing.JPanel;
-import javax.swing.WindowConstants;
+import javax.swing.*;
-import data_access.InMemoryUserDataAccessObject;
+import data_access.*;
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.keyword.KeywordController;
+import interface_adapter.keyword.KeywordPresenter;
+import interface_adapter.keyword.KeywordViewModel;
+import interface_adapter.logged_in.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 interface_adapter.recommend.RecommendController;
+import interface_adapter.recommend.RecommendPresenter;
+import interface_adapter.recommend.RecommendViewModel;
+import interface_adapter.search.SearchController;
+import interface_adapter.search.SearchPresenter;
+import interface_adapter.search.SearchViewModel;
+import interface_adapter.similar_listeners.SimilarListenersController;
+import interface_adapter.similar_listeners.SimilarListenersPresenter;
+import interface_adapter.similar_listeners.SimilarListenersViewModel;
+import interface_adapter.top_items.TopItemsController;
+import interface_adapter.top_items.TopItemsPresenter;
+import interface_adapter.top_items.TopItemsViewModel;
+import use_case.keyword.KeywordInputBoundary;
+import use_case.keyword.KeywordInteractor;
+import use_case.keyword.KeywordOutputBoundary;
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 use_case.recommend.RecommendInputBoundary;
+import use_case.recommend.RecommendInteractor;
+import use_case.recommend.RecommendOutputBoundary;
+import use_case.search.SearchInputBoundary;
+import use_case.search.SearchInteractor;
+import use_case.search.SearchOutputBoundary;
+import use_case.similar_listeners.SimilarListenersInputBoundary;
+import use_case.similar_listeners.SimilarListenersInteractor;
+import use_case.similar_listeners.SimilarListenersOutputBoundary;
+import use_case.top_items.TopItemsInputBoundary;
+import use_case.top_items.TopItemsInteractor;
+import use_case.top_items.TopItemsOutputBoundary;
import view.LoggedInView;
import view.LoginView;
-import view.SignupView;
+import view.RecommendationsView;
+import view.SearchView;
+import view.TopItemsView;
+import view.SimilarListenersView;
+
import view.ViewManager;
+import view.*;
/**
* The AppBuilder class is responsible for putting together the pieces of
@@ -59,37 +81,66 @@ public class AppBuilder {
// thought question: is the hard dependency below a problem?
private final InMemoryUserDataAccessObject userDataAccessObject = new InMemoryUserDataAccessObject();
+ private final LanguageModelDataAccessObject languageModelDataAccessObject = new LanguageModelDataAccessObject();
+ private final RecommendUserDataAccessObject recommendUserDataAccessObject = new RecommendUserDataAccessObject();
+ private final SpotifyDataAccessObject spotifyDataAccessObject = new SpotifyDataAccessObject();
- private SignupView signupView;
- private SignupViewModel signupViewModel;
private LoginViewModel loginViewModel;
private LoggedInViewModel loggedInViewModel;
+ private TopItemsViewModel topTracksAndArtistsViewModel;
+ private RecommendViewModel recommendViewModel;
+ private SearchViewModel searchViewModel;
+ private SimilarListenersViewModel similarListenersViewModel;
private LoggedInView loggedInView;
private LoginView loginView;
+ private SearchView searchView;
+ private TopItemsView topItemsView;
+ private SimilarListenersView similarListenersView;
+ private RecommendationsView recommendationsView;
+ private KeywordView keywordView;
+ private KeywordViewModel keywordViewModel;
+
public AppBuilder() {
cardPanel.setLayout(cardLayout);
}
/**
- * Adds the Signup View to the application.
+ * Adds the Login View to the application.
* @return this builder
*/
- public AppBuilder addSignupView() {
- signupViewModel = new SignupViewModel();
- signupView = new SignupView(signupViewModel);
- cardPanel.add(signupView, signupView.getViewName());
+ public AppBuilder addLoginView() {
+ loginViewModel = new LoginViewModel();
+ loginView = new LoginView(loginViewModel);
+ cardPanel.add(loginView, loginView.getViewName());
return this;
}
/**
- * Adds the Login View to the application.
+ * Adds the Search View to the application.
* @return this builder
*/
- public AppBuilder addLoginView() {
- loginViewModel = new LoginViewModel();
- loginView = new LoginView(loginViewModel);
- cardPanel.add(loginView, loginView.getViewName());
+ public AppBuilder addSearchView() {
+ searchViewModel = new SearchViewModel();
+ searchView = new SearchView(searchViewModel);
+ cardPanel.add(searchView, searchView.getViewName());
+ final LoginOutputBoundary loginOutputBoundary = new LoginPresenter(viewManagerModel,
+ loggedInViewModel, loginViewModel);
+ final LoginInputBoundary loginInteractor = new LoginInteractor(
+ userDataAccessObject,spotifyDataAccessObject, loginOutputBoundary);
+ final LoginController loginController = new LoginController(loginInteractor);
+ searchView.setLoginController(loginController);
+ return this;
+ }
+
+ /**
+ * Adds the Recommendation View to the application.
+ * @return this builder
+ */
+ public AppBuilder addRecommendationsView() {
+ recommendViewModel = new RecommendViewModel();
+ recommendationsView = new RecommendationsView(recommendViewModel);
+ cardPanel.add(recommendationsView, recommendationsView.getViewName());
return this;
}
@@ -105,49 +156,56 @@ public AppBuilder addLoggedInView() {
}
/**
- * Adds the Signup Use Case to the application.
+ * Adds the Top Tracks and Artists View 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);
+ public AppBuilder addTopItemsView() {
+ topTracksAndArtistsViewModel = new TopItemsViewModel();
+ topItemsView = new TopItemsView(topTracksAndArtistsViewModel);
+ cardPanel.add(topItemsView, topItemsView.getViewName());
return this;
}
/**
- * Adds the Login Use Case to the application.
+ * Adds the SimilarListeners View to the application.
* @return this builder
*/
- public AppBuilder addLoginUseCase() {
+ public AppBuilder addSimilarListenersView() {
+ similarListenersViewModel = new SimilarListenersViewModel();
+ similarListenersView = new SimilarListenersView(similarListenersViewModel);
+ cardPanel.add(similarListenersView, similarListenersView.getViewName());
+ return this;
+ }
+
+ /**
+ * Adds the Keyword View to the application.
+ * @return this builder
+ */
+ public AppBuilder addKeywordView() {
+ keywordViewModel = new KeywordViewModel();
+ keywordView = new KeywordView(keywordViewModel);
+ cardPanel.add(keywordView, keywordView.getViewName());
final LoginOutputBoundary loginOutputBoundary = new LoginPresenter(viewManagerModel,
loggedInViewModel, loginViewModel);
final LoginInputBoundary loginInteractor = new LoginInteractor(
- userDataAccessObject, loginOutputBoundary);
-
+ userDataAccessObject,spotifyDataAccessObject, loginOutputBoundary);
final LoginController loginController = new LoginController(loginInteractor);
- loginView.setLoginController(loginController);
+ keywordView.setLoginController(loginController);
return this;
}
/**
- * Adds the Change Password Use Case to the application.
+ * Adds the Login 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);
+ public AppBuilder addLoginUseCase() {
+ final LoginOutputBoundary loginOutputBoundary = new LoginPresenter(viewManagerModel,
+ loggedInViewModel, loginViewModel);
+ final LoginInputBoundary loginInteractor = new LoginInteractor(
+ userDataAccessObject,spotifyDataAccessObject, loginOutputBoundary);
- final ChangePasswordController changePasswordController =
- new ChangePasswordController(changePasswordInteractor);
- loggedInView.setChangePasswordController(changePasswordController);
+ final LoginController loginController = new LoginController(loginInteractor);
+ loginView.setLoginController(loginController);
return this;
}
@@ -166,18 +224,136 @@ public AppBuilder addLogoutUseCase() {
loggedInView.setLogoutController(logoutController);
return this;
}
+ /**
+ * Adds the Keyword Search Use Case to the application.
+ * @return this builder
+ */
+ public AppBuilder addKeywordUseCase() {
+ final LoginOutputBoundary loginOutputBoundary = new LoginPresenter(viewManagerModel,
+ loggedInViewModel, loginViewModel);
+ final LoginInputBoundary loginInteractor = new LoginInteractor(
+ userDataAccessObject,spotifyDataAccessObject, loginOutputBoundary);
+
+ final LoginController loginController = new LoginController(loginInteractor);
+ keywordView.setLoginController(loginController);
+
+ final KeywordOutputBoundary keywordOutputBoundary = new KeywordPresenter(viewManagerModel,
+ loggedInViewModel, keywordViewModel);
+
+ final KeywordInputBoundary keywordInteractor =
+ new KeywordInteractor(spotifyDataAccessObject, keywordOutputBoundary);
+
+ final KeywordController keywordController = new KeywordController(keywordInteractor);
+ keywordView.setKeywordController(keywordController);
+ loggedInView.setKeywordController(keywordController);
+ return this;
+ }
+ /**
+ * Adds the Search Use Case to the application.
+ * @return this builder
+ */
+ public AppBuilder addSearchUseCase() {
+ final LoginOutputBoundary loginOutputBoundary = new LoginPresenter(viewManagerModel,
+ loggedInViewModel, loginViewModel);
+ final LoginInputBoundary loginInteractor = new LoginInteractor(
+ userDataAccessObject,spotifyDataAccessObject, loginOutputBoundary);
+
+ final LoginController loginController = new LoginController(loginInteractor);
+ searchView.setLoginController(loginController);
+
+ final SearchOutputBoundary searchOutputBoundary = new SearchPresenter(viewManagerModel,
+ loggedInViewModel, searchViewModel);
+
+ final SearchInputBoundary searchInteractor =
+ new SearchInteractor(languageModelDataAccessObject, searchOutputBoundary);
+
+ final SearchController searchController = new SearchController(searchInteractor);
+ searchView.setSearchController(searchController);
+ loggedInView.setSearchController(searchController);
+ return this;
+ }
+
+ /**
+ * Adds the Top Tracks and Artists Use Case to the application.
+ * @return this builder
+ */
+ public AppBuilder addTopItemsUseCase() {
+
+ final LoginOutputBoundary loginOutputBoundary = new LoginPresenter(viewManagerModel,
+ loggedInViewModel, loginViewModel);
+ final LoginInputBoundary loginInteractor = new LoginInteractor(
+ userDataAccessObject,spotifyDataAccessObject, loginOutputBoundary);
+
+ final LoginController loginController = new LoginController(loginInteractor);
+ topItemsView.setLoginController(loginController);
+
+ final TopItemsOutputBoundary topItemsOutputBoundary = new TopItemsPresenter(viewManagerModel,
+ topTracksAndArtistsViewModel);
+
+ final TopItemsInputBoundary topItemsInputBoundary =
+ new TopItemsInteractor(spotifyDataAccessObject, topItemsOutputBoundary);
+
+ final TopItemsController topItemsController = new TopItemsController(topItemsInputBoundary);
+ loggedInView.setTopTracksController(topItemsController);
+ return this;
+ }
+
+ /**
+ * Adds the Recommend Use Case to the application.
+ * @return this builder
+ */
+ public AppBuilder addRecommendUseCase() {
+ final LoginOutputBoundary loginOutputBoundary = new LoginPresenter(viewManagerModel,
+ loggedInViewModel, loginViewModel);
+ final LoginInputBoundary loginInteractor = new LoginInteractor(
+ userDataAccessObject,spotifyDataAccessObject, loginOutputBoundary);
+
+ final LoginController loginController = new LoginController(loginInteractor);
+ recommendationsView.setLoginController(loginController);
+
+ final RecommendOutputBoundary recommendOutputBoundary =
+ new RecommendPresenter(viewManagerModel, recommendViewModel);
+ final RecommendInputBoundary recommendInputBoundary =
+ new RecommendInteractor(recommendUserDataAccessObject, recommendOutputBoundary);
+
+ final RecommendController recommendController = new RecommendController(recommendInputBoundary);
+ loggedInView.setRecommendController(recommendController);
+ return this;
+ }
+
+ public AppBuilder addSimilarListenersUseCase() {
+ final LoginOutputBoundary loginOutputBoundary = new LoginPresenter(viewManagerModel,
+ loggedInViewModel, loginViewModel);
+ final LoginInputBoundary loginInteractor = new LoginInteractor(
+ userDataAccessObject,spotifyDataAccessObject, loginOutputBoundary);
+
+ final LoginController loginController = new LoginController(loginInteractor);
+ similarListenersView.setLoginController(loginController);
+
+ final SimilarListenersOutputBoundary similarListenersOutputBoundary =
+ new SimilarListenersPresenter(similarListenersViewModel, viewManagerModel);
+ final SimilarListenersInputBoundary similarListenersInputBoundary =
+ new SimilarListenersInteractor(spotifyDataAccessObject, similarListenersOutputBoundary);
+ final SimilarListenersController similarListenersController =
+ new SimilarListenersController(similarListenersInputBoundary);
+ loggedInView.setSimilarListenersController(similarListenersController);
+ 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");
+ final JFrame application = new JFrame("CSC207 Project: Spotify Companion App");
application.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
+// Trying to set default size to app, but its not working
+// application.setSize(Constants.APP_WIDTH, Constants.APP_HEIGHT);
+
application.add(cardPanel);
- viewManagerModel.setState(signupView.getViewName());
+ viewManagerModel.setState(loginViewModel.getViewName());
viewManagerModel.firePropertyChanged();
return application;
diff --git a/src/main/java/app/Main.java b/src/main/java/app/Main.java
index bef63ad7a..18f98cc65 100644
--- a/src/main/java/app/Main.java
+++ b/src/main/java/app/Main.java
@@ -1,6 +1,8 @@
package app;
-import javax.swing.JFrame;
+import javax.swing.*;
+//import java.Constants;
+import java.awt.*;
/**
* The Main class of our application.
@@ -11,16 +13,24 @@ public class Main {
* @param args unused arguments
*/
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();
+ .addLoginView()
+ .addRecommendationsView()
+ .addSearchView()
+ .addTopItemsView()
+ .addKeywordView()
+ .addLoggedInView()
+ .addSimilarListenersView()
+ .addLoginUseCase()
+ .addLogoutUseCase()
+ .addRecommendUseCase()
+ .addSearchUseCase()
+ .addTopItemsUseCase()
+ .addSimilarListenersUseCase()
+ .addKeywordUseCase()
+ .build();
application.pack();
application.setVisible(true);
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/InMemoryUserDataAccessObject.java b/src/main/java/data_access/InMemoryUserDataAccessObject.java
index 71f00862c..73dd25511 100644
--- a/src/main/java/data_access/InMemoryUserDataAccessObject.java
+++ b/src/main/java/data_access/InMemoryUserDataAccessObject.java
@@ -4,20 +4,16 @@
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,
+public class InMemoryUserDataAccessObject implements
LoginUserDataAccessInterface,
- ChangePasswordUserDataAccessInterface,
LogoutUserDataAccessInterface {
-
private final Map users = new HashMap<>();
private String currentUsername;
@@ -29,7 +25,7 @@ public boolean existsByName(String identifier) {
@Override
public void save(User user) {
- users.put(user.getName(), user);
+ users.put(user.getAccessToken(), user);
}
@Override
@@ -38,18 +34,12 @@ public User get(String 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) {
+ public void setCurrentAccessToken(String name) {
this.currentUsername = name;
}
@Override
- public String getCurrentUsername() {
+ public String getCurrentAccessToken() {
return this.currentUsername;
}
}
diff --git a/src/main/java/data_access/KeywordTestDataAccessObject.java b/src/main/java/data_access/KeywordTestDataAccessObject.java
new file mode 100644
index 000000000..12b5520d3
--- /dev/null
+++ b/src/main/java/data_access/KeywordTestDataAccessObject.java
@@ -0,0 +1,18 @@
+package data_access;
+
+import use_case.keyword.KeywordDataAccessInterface;
+import java.util.ArrayList;
+import java.util.List;
+
+public class KeywordTestDataAccessObject implements KeywordDataAccessInterface {
+
+ @Override
+ public List searchSongs(String artistName, String keyword) {
+ if ("Taylor Swift".equals(artistName) && "Love".equals(keyword)) {
+ List results = new ArrayList<>();
+ results.add("Love Story");
+ return results;
+ }
+ return new ArrayList<>();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/data_access/LanguageModelDataAccessObject.java b/src/main/java/data_access/LanguageModelDataAccessObject.java
new file mode 100644
index 000000000..47bd57ae8
--- /dev/null
+++ b/src/main/java/data_access/LanguageModelDataAccessObject.java
@@ -0,0 +1,149 @@
+package data_access;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.*;
+
+import com.azure.ai.openai.OpenAIClient;
+import com.azure.ai.openai.OpenAIClientBuilder;
+import com.azure.ai.openai.models.ChatCompletions;
+import com.azure.ai.openai.models.ChatCompletionsOptions;
+import com.azure.ai.openai.models.ChatRequestAssistantMessage;
+import com.azure.ai.openai.models.ChatRequestMessage;
+import com.azure.ai.openai.models.ChatRequestSystemMessage;
+import com.azure.ai.openai.models.ChatRequestUserMessage;
+import com.azure.core.credential.AzureKeyCredential;
+import org.jetbrains.annotations.NotNull;
+import use_case.recommend.RecommendLanguageModelDataAccessInterface;
+import use_case.search.SearchLanguageModelDataAccessInterface;
+
+/**
+ * Data access object to connect to Azure openAI.
+ */
+public class LanguageModelDataAccessObject implements RecommendLanguageModelDataAccessInterface,
+ SearchLanguageModelDataAccessInterface {
+
+ final private String endpoint = "https://spotifycompanion.openai.azure.com/";
+ final private String accessToken;
+ private boolean keyExists;
+ final private String errorMessage ="The file which should contain the " +
+ "azure access token does not exist in the correct place. Either create the file and place a valid api key in it or move the file to the src folder";
+
+ public LanguageModelDataAccessObject() {
+ accessToken = getKey();
+ }
+
+ /**
+ * This is a helper method to get the Azure API key.
+ * @return the access token stored in the local keys.txt file
+ */
+ private String getKey() {
+ final File file;
+ file = new File("src/keys");
+ String key = "not found";
+ try {
+ this.keyExists = file.exists();
+ final BufferedReader br = new BufferedReader(new FileReader(file));
+ key = br.readLine();
+
+ br.close();
+ }
+ catch (FileNotFoundException ex) {
+ this.keyExists = false;
+ System.out.println(errorMessage);
+ return key;
+ }
+ catch (IOException ex) {
+ this.keyExists = false;
+ System.out.println(errorMessage);
+ System.out.println("It appears the file does not exist");
+ return key;
+ }
+ return key;
+ }
+
+ @Override
+ public String query(String prompt) {
+ if (!this.keyExists) {
+ return errorMessage;
+ }
+ final OpenAIClient client = new OpenAIClientBuilder()
+ .credential(new AzureKeyCredential(accessToken))
+ .endpoint(endpoint)
+ .buildClient();
+ final List chatMessages = new ArrayList<>();
+ chatMessages.add(new ChatRequestSystemMessage("You are a Spotify analyst, you will either send back a "
+ + "list of songs based on a description or a list of songs which are similar to a provided list."));
+ chatMessages.add(new ChatRequestUserMessage("Can you help me find songs similar to Driver's License"));
+ chatMessages.add(new ChatRequestAssistantMessage(" Similar songs include Femininomenon by Chappell Roan"
+ + ", Birds of A Feather by Billie Eilish and Part of Me by Noah Kahan"));
+ chatMessages.add(new ChatRequestUserMessage("Is there a song about a house in the south of"
+ + " the US and includes something about the sun?"));
+ chatMessages.add(new ChatRequestUserMessage("House of the Rising Sun by The Animals."));
+ chatMessages.add(new ChatRequestUserMessage(prompt));
+
+ final ChatCompletions chatCompletions = client.getChatCompletions("gpt-4",
+ new ChatCompletionsOptions(chatMessages));
+ final int back = chatCompletions.getChoices().size() - 1;
+ return chatCompletions.getChoices().get(back).getMessage().getContent();
+ }
+
+ @Override
+ public String getRecommendations(List songs, String topArtists) {
+ if (accessToken.isEmpty()) {
+ return "Error getting access token";
+ }
+ if (topArtists.isEmpty()) {
+ return "Error getting artists from Spotify";
+ }
+ if (songs.isEmpty()) {
+ return "Error getting tracks from Spotify";
+ }
+
+ final OpenAIClient client = new OpenAIClientBuilder()
+ .credential(new AzureKeyCredential(accessToken))
+ .endpoint(endpoint)
+ .buildClient();
+ final List chatMessages = new ArrayList<>();
+ chatMessages.add(new ChatRequestSystemMessage("You are a Spotify analyst, you will send back a "
+ + "list of songs which are similar to a given list of songs and artists that a user has listened to."));
+ chatMessages.add(new ChatRequestUserMessage("A user's top artists are" + topArtists
+ + ". Their top tracks are" + songs + ". What are some song recommendations, based on these?"));
+ chatMessages.add(new ChatRequestUserMessage(String.valueOf(songs)));
+
+ final ChatCompletions chatCompletions = client.getChatCompletions("gpt-4",
+ new ChatCompletionsOptions(chatMessages));
+ StringBuilder result = getStringBuilder(chatCompletions);
+
+ // Return the result as a string
+ return result.toString();
+ }
+
+ @NotNull
+ private static StringBuilder getStringBuilder(ChatCompletions chatCompletions) {
+ final int back = chatCompletions.getChoices().size() - 1;
+ String response = chatCompletions.getChoices().get(back).getMessage().getContent();
+
+ // Removes extra characters placed by the AI
+ response = response.replace("*", "");
+
+ String[] lines = response.split("\n");
+
+ // Create a new StringBuilder to hold the result
+ StringBuilder result = new StringBuilder();
+
+ // Append lines from the second to the second last (index 1 to length-2)
+ for (int i = 1; i < lines.length - 1; i++) {
+ if (!Objects.equals(lines[i], "\n")) {
+ result.append(lines[i]);
+ }
+ if (i < lines.length - 2) {
+ result.append("\n"); // Add a newline character if not the last line
+ }
+ }
+ return result;
+ }
+}
diff --git a/src/main/java/data_access/LoginTestDataAccessObject.java b/src/main/java/data_access/LoginTestDataAccessObject.java
new file mode 100644
index 000000000..05df81fa7
--- /dev/null
+++ b/src/main/java/data_access/LoginTestDataAccessObject.java
@@ -0,0 +1,10 @@
+package data_access;
+
+import use_case.login.LoginDataAccessInterface;
+
+public class LoginTestDataAccessObject implements LoginDataAccessInterface {
+ @Override
+ public void setAccessToken(String newToken) {
+
+ }
+}
diff --git a/src/main/java/data_access/RecommendTestDataAccessObject.java b/src/main/java/data_access/RecommendTestDataAccessObject.java
new file mode 100644
index 000000000..3d8a16992
--- /dev/null
+++ b/src/main/java/data_access/RecommendTestDataAccessObject.java
@@ -0,0 +1,50 @@
+package data_access;
+
+import use_case.recommend.RecommendSpotifyDataAccessInterface;
+import use_case.recommend.RecommendUserDataAccessInterface;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class RecommendTestDataAccessObject implements RecommendSpotifyDataAccessInterface,
+ RecommendUserDataAccessInterface {
+ private List topTracks;
+ private String topArtists;
+
+ public RecommendTestDataAccessObject(){
+ topTracks = new ArrayList<>();
+ topTracks.add("(sic)");
+ topTracks.add("Left Behind");
+ topTracks.add("Spit It Out");
+ topTracks.add("People = Shit");
+ topTracks.add("Metabolic");
+ topTracks.add("Everything Ends");
+
+ topArtists = "Slipknot, Soulfly, Korn, Sepultura, System Of A Down";
+ }
+
+ @Override
+ public List getTopTracks() {
+ return topTracks;
+ }
+
+ @Override
+ public void setTopTracks(List topTracks) {
+ this.topTracks = topTracks;
+ }
+
+ @Override
+ public String getTopArtists() {
+ return topArtists;
+ }
+
+ @Override
+ public void setTopArtists(String topArtists) {
+ this.topArtists = topArtists;
+ }
+
+ @Override
+ public String getRecommendations(List songs, String topArtists) {
+ return "";
+ }
+}
diff --git a/src/main/java/data_access/RecommendUserDataAccessObject.java b/src/main/java/data_access/RecommendUserDataAccessObject.java
new file mode 100644
index 000000000..33c961d69
--- /dev/null
+++ b/src/main/java/data_access/RecommendUserDataAccessObject.java
@@ -0,0 +1,45 @@
+package data_access;
+
+import use_case.recommend.RecommendUserDataAccessInterface;
+import data_access.LanguageModelDataAccessObject;
+import data_access.SpotifyDataAccessObject;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class RecommendUserDataAccessObject implements RecommendUserDataAccessInterface {
+ private List topTracks;
+ private String topArtists;
+
+ public RecommendUserDataAccessObject() {
+ final SpotifyDataAccessObject spotifyDataAccessObject = new SpotifyDataAccessObject();
+ topTracks = spotifyDataAccessObject.getCurrentTopTracks();
+ topArtists = spotifyDataAccessObject.getTopArtists();
+ }
+
+ @Override
+ public List getTopTracks() {
+ return topTracks;
+ }
+
+ @Override
+ public void setTopTracks(List topTracks) {
+ this.topTracks = topTracks;
+ }
+
+ @Override
+ public String getTopArtists() {
+ return topArtists;
+ }
+
+ @Override
+ public void setTopArtists(String artists) {
+ this.topArtists = artists;
+ }
+
+ @Override
+ public String getRecommendations(List songs, String topArtists) {
+ final LanguageModelDataAccessObject languageModel = new LanguageModelDataAccessObject();
+ return languageModel.getRecommendations(songs, topArtists);
+ }
+}
diff --git a/src/main/java/data_access/SearchTestDataAccessObject.java b/src/main/java/data_access/SearchTestDataAccessObject.java
new file mode 100644
index 000000000..afed8707d
--- /dev/null
+++ b/src/main/java/data_access/SearchTestDataAccessObject.java
@@ -0,0 +1,13 @@
+package data_access;
+
+import use_case.search.SearchLanguageModelDataAccessInterface;
+
+public class SearchTestDataAccessObject implements SearchLanguageModelDataAccessInterface {
+
+ public SearchTestDataAccessObject(){}
+
+ @Override
+ public String query(String prompt) {
+ return "One Piece at a Time by Johnny Cash";
+ }
+}
diff --git a/src/main/java/data_access/SimilarListenersTestDataAccessObject.java b/src/main/java/data_access/SimilarListenersTestDataAccessObject.java
new file mode 100644
index 000000000..fa3733ed8
--- /dev/null
+++ b/src/main/java/data_access/SimilarListenersTestDataAccessObject.java
@@ -0,0 +1,21 @@
+package data_access;
+
+import use_case.similar_listeners.SimilarListenersDataAccessInterface;
+
+import java.util.List;
+
+public class SimilarListenersTestDataAccessObject implements SimilarListenersDataAccessInterface {
+
+ private List followedArtists;
+
+ @Override
+ public List getFollowedArtists() {
+ return this.followedArtists;
+ }
+
+ @Override
+ public void setCurrentFollowedArtists(List artists) {
+ this.followedArtists = artists;
+
+ }
+}
diff --git a/src/main/java/data_access/SpotifyDataAccessObject.java b/src/main/java/data_access/SpotifyDataAccessObject.java
new file mode 100644
index 000000000..428aeb83b
--- /dev/null
+++ b/src/main/java/data_access/SpotifyDataAccessObject.java
@@ -0,0 +1,314 @@
+package data_access;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import entity.CommonUserFactory;
+import entity.User;
+import entity.UserFactory;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import interface_adapter.login.LoginState;
+import okhttp3.HttpUrl;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import org.apache.hc.core5.http.ParseException;
+import se.michaelthelin.spotify.SpotifyApi;
+import se.michaelthelin.spotify.enums.ModelObjectType;
+import se.michaelthelin.spotify.exceptions.SpotifyWebApiException;
+import se.michaelthelin.spotify.model_objects.specification.*;
+import se.michaelthelin.spotify.requests.data.browse.GetRecommendationsRequest;
+import se.michaelthelin.spotify.requests.data.follow.GetUsersFollowedArtistsRequest;
+import se.michaelthelin.spotify.requests.data.personalization.simplified.GetUsersTopArtistsRequest;
+import se.michaelthelin.spotify.requests.data.personalization.simplified.GetUsersTopTracksRequest;
+import use_case.login.LoginDataAccessInterface;
+import use_case.recommend.RecommendUserDataAccessInterface;
+import use_case.keyword.KeywordDataAccessInterface;
+import use_case.similar_listeners.SimilarListenersDataAccessInterface;
+import use_case.top_items.TopItemsDataAccessInterface;
+
+/**
+ * DAO for getting relevant information from Spotify API.
+ */
+
+public class SpotifyDataAccessObject implements TopItemsDataAccessInterface,
+ SimilarListenersDataAccessInterface, RecommendUserDataAccessInterface, KeywordDataAccessInterface, LoginDataAccessInterface {
+ public static final int OFFSET = 4;
+ public static final int OFFSET2 = 0;
+
+ private LoginState loginState = new LoginState();
+ private String accessToken;
+ private List currentTopTracks;
+ private List currentTopArtists;
+ private List currentSearchResults;
+ private SpotifyApi spotifyApi;
+ private GetUsersTopTracksRequest getTopTracksRequest;
+ private GetUsersTopArtistsRequest getTopArtistsRequest;
+
+ private GetUsersFollowedArtistsRequest getUsersFollowedArtistsRequest;
+ private static final ModelObjectType type = ModelObjectType.ARTIST;
+ private List currFollowedArtists;
+
+ private GetRecommendationsRequest getRecommendationsRequest;
+
+ public SpotifyDataAccessObject() {
+ this.accessToken = "this should never be reached";
+ this.spotifyApi = new SpotifyApi.Builder()
+ .setAccessToken(accessToken)
+ .build();
+ this.getTopTracksRequest = spotifyApi.getUsersTopTracks()
+ .offset(OFFSET)
+ .time_range("medium_term")
+ .build();
+ this.getTopArtistsRequest = spotifyApi.getUsersTopArtists()
+ .offset(OFFSET)
+ .time_range("medium_term")
+ .build();
+ this.currentTopTracks = getUsersTopTracksSync();
+ this.currentTopArtists = getUsersTopArtistsSync();
+
+ this.getUsersFollowedArtistsRequest = spotifyApi.getUsersFollowedArtists(type).build();
+ this.currFollowedArtists = getUsersFollowedArtistsSync();
+
+ this.getRecommendationsRequest = spotifyApi.getRecommendations()
+ .build();
+ }
+
+ // Constructor used for testing.
+ public SpotifyDataAccessObject(String accessToken) {
+ this.accessToken = accessToken;
+ this.spotifyApi = new SpotifyApi.Builder()
+ .setAccessToken(accessToken)
+ .build();
+ this.getTopTracksRequest = spotifyApi.getUsersTopTracks()
+ .offset(OFFSET)
+ .time_range("medium_term")
+ .build();
+ this.getTopArtistsRequest = spotifyApi.getUsersTopArtists()
+ .offset(OFFSET2)
+ .time_range("medium_term")
+ .build();
+ this.currentTopTracks = getUsersTopTracksSync();
+ this.currentTopArtists = getUsersTopArtistsSync();
+
+ this.getUsersFollowedArtistsRequest = spotifyApi.getUsersFollowedArtists(type).build();
+ this.currFollowedArtists = getUsersFollowedArtistsSync();
+
+ this.getRecommendationsRequest = spotifyApi.getRecommendations()
+ .build();
+ }
+
+ /**
+ * A helper method to get the current users top tracks.
+ *
+ * @return a list of the track names.
+ */
+ private List getUsersTopTracksSync() {
+ try {
+ // Using Spotify Wrapper to get the given users top tracks from API
+ final Paging