diff --git a/.gitignore b/.gitignore index 7754426..8abe182 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,6 @@ replay_pid* # End of https://www.toptal.com/developers/gitignore/api/java *.wav *.properties -!.vscode/launch.json +.vscode/launch.json !gradle/wrapper/gradle-wrapper.jar !gradle/wrapper/gradle-wrapper.properties \ No newline at end of file diff --git a/.gradle/8.0.2/executionHistory/executionHistory.bin b/.gradle/8.0.2/executionHistory/executionHistory.bin index 5daca43..29f810d 100644 Binary files a/.gradle/8.0.2/executionHistory/executionHistory.bin and b/.gradle/8.0.2/executionHistory/executionHistory.bin differ diff --git a/.gradle/8.0.2/executionHistory/executionHistory.lock b/.gradle/8.0.2/executionHistory/executionHistory.lock index e9ddad0..cf21192 100644 Binary files a/.gradle/8.0.2/executionHistory/executionHistory.lock and b/.gradle/8.0.2/executionHistory/executionHistory.lock differ diff --git a/.gradle/8.0.2/fileHashes/fileHashes.bin b/.gradle/8.0.2/fileHashes/fileHashes.bin index 9053215..a88c362 100644 Binary files a/.gradle/8.0.2/fileHashes/fileHashes.bin and b/.gradle/8.0.2/fileHashes/fileHashes.bin differ diff --git a/.gradle/8.0.2/fileHashes/fileHashes.lock b/.gradle/8.0.2/fileHashes/fileHashes.lock index c779b18..28531de 100644 Binary files a/.gradle/8.0.2/fileHashes/fileHashes.lock and b/.gradle/8.0.2/fileHashes/fileHashes.lock differ diff --git a/.gradle/8.0.2/fileHashes/resourceHashesCache.bin b/.gradle/8.0.2/fileHashes/resourceHashesCache.bin index 003a407..3499d22 100644 Binary files a/.gradle/8.0.2/fileHashes/resourceHashesCache.bin and b/.gradle/8.0.2/fileHashes/resourceHashesCache.bin differ diff --git a/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/.gradle/buildOutputCleanup/buildOutputCleanup.lock index dc7c7da..14b567a 100644 Binary files a/.gradle/buildOutputCleanup/buildOutputCleanup.lock and b/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/.gradle/buildOutputCleanup/outputFiles.bin b/.gradle/buildOutputCleanup/outputFiles.bin new file mode 100644 index 0000000..f00c87a Binary files /dev/null and b/.gradle/buildOutputCleanup/outputFiles.bin differ diff --git a/.gradle/file-system.probe b/.gradle/file-system.probe index 101905a..a91aa64 100644 Binary files a/.gradle/file-system.probe and b/.gradle/file-system.probe differ diff --git a/.vscode/launch.json b/.vscode/launch.json index c603acd..58baf8d 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,14 +8,15 @@ "type": "java", "name": "Current File", "request": "launch", - "mainClass": "${file}" + "mainClass": "${file}", + "vmArgs": "--module-path 'C:/Users/allen/Desktop/CSE110/CSE110-MiniProject/javafx-sdk-21/lib' --add-modules javafx.controls,javafx.base,javafx.fxml,javafx.graphics,javafx.media,javafx.web --add-opens=javafx.graphics/javafx.scene=ALL-UNNAMED --add-exports javafx.base/com.sun.javafx.event=ALL-UNNAMED" }, { "type": "java", - "name": "App", + "name": "Main", "request": "launch", - "mainClass": "View.java", - //"vmArgs": "--module-path 'C:\\Users\\Christophian S\\Desktop\\javafx-sdk-21\\lib' --add-modules javafx.controls,javafx.fxml" + "mainClass": "Main.java", + //"vmArgs": "--module-path '/Users/kesler_anderson/cse-110-project-team-39/lib' --add-modules javafx.controls,javafx.fxml" }, { "type": "java", diff --git a/.vscode/settings.json b/.vscode/settings.json index fc8081a..f642e77 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,5 +3,6 @@ "java.project.referencedLibraries": [ "lib/**/*.jar" ], - "java.configuration.updateBuildConfiguration": "interactive" + "java.configuration.updateBuildConfiguration": "interactive", + "java.jdt.ls.vmargs": "-XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx2G -Xms100m -Xlog:disable" } \ No newline at end of file diff --git a/app/build/tmp/compileJava/compileTransaction/stash-dir/App.class.uniqueId3 b/app/build/tmp/compileJava/compileTransaction/stash-dir/App.class.uniqueId3 deleted file mode 100644 index f673865..0000000 Binary files a/app/build/tmp/compileJava/compileTransaction/stash-dir/App.class.uniqueId3 and /dev/null differ diff --git a/app/build/tmp/compileJava/compileTransaction/stash-dir/AppFrame.class.uniqueId1 b/app/build/tmp/compileJava/compileTransaction/stash-dir/AppFrame.class.uniqueId1 new file mode 100644 index 0000000..fd521cb Binary files /dev/null and b/app/build/tmp/compileJava/compileTransaction/stash-dir/AppFrame.class.uniqueId1 differ diff --git a/app/build/tmp/compileJava/compileTransaction/stash-dir/AppFrameMic.class.uniqueId12 b/app/build/tmp/compileJava/compileTransaction/stash-dir/AppFrameMic.class.uniqueId12 new file mode 100644 index 0000000..a38d75c Binary files /dev/null and b/app/build/tmp/compileJava/compileTransaction/stash-dir/AppFrameMic.class.uniqueId12 differ diff --git a/app/build/tmp/compileJava/compileTransaction/stash-dir/AudioRecorder$1.class.uniqueId4 b/app/build/tmp/compileJava/compileTransaction/stash-dir/AudioRecorder$1.class.uniqueId4 deleted file mode 100644 index 1ebdd1e..0000000 Binary files a/app/build/tmp/compileJava/compileTransaction/stash-dir/AudioRecorder$1.class.uniqueId4 and /dev/null differ diff --git a/app/build/tmp/compileJava/compileTransaction/stash-dir/AudioRecorder.class.uniqueId0 b/app/build/tmp/compileJava/compileTransaction/stash-dir/AudioRecorder.class.uniqueId0 deleted file mode 100644 index fc272ba..0000000 Binary files a/app/build/tmp/compileJava/compileTransaction/stash-dir/AudioRecorder.class.uniqueId0 and /dev/null differ diff --git a/app/build/tmp/compileJava/compileTransaction/stash-dir/Controller.class.uniqueId1 b/app/build/tmp/compileJava/compileTransaction/stash-dir/Controller.class.uniqueId1 deleted file mode 100644 index 8ccf111..0000000 Binary files a/app/build/tmp/compileJava/compileTransaction/stash-dir/Controller.class.uniqueId1 and /dev/null differ diff --git a/app/build/tmp/compileJava/compileTransaction/stash-dir/Footer.class.uniqueId6 b/app/build/tmp/compileJava/compileTransaction/stash-dir/Footer.class.uniqueId6 new file mode 100644 index 0000000..6074683 Binary files /dev/null and b/app/build/tmp/compileJava/compileTransaction/stash-dir/Footer.class.uniqueId6 differ diff --git a/app/build/tmp/compileJava/compileTransaction/stash-dir/GPTRecipe.class.uniqueId2 b/app/build/tmp/compileJava/compileTransaction/stash-dir/GPTRecipe.class.uniqueId2 new file mode 100644 index 0000000..2a19fd8 Binary files /dev/null and b/app/build/tmp/compileJava/compileTransaction/stash-dir/GPTRecipe.class.uniqueId2 differ diff --git a/app/build/tmp/compileJava/compileTransaction/stash-dir/Header.class.uniqueId10 b/app/build/tmp/compileJava/compileTransaction/stash-dir/Header.class.uniqueId10 new file mode 100644 index 0000000..732b775 Binary files /dev/null and b/app/build/tmp/compileJava/compileTransaction/stash-dir/Header.class.uniqueId10 differ diff --git a/app/build/tmp/compileJava/compileTransaction/stash-dir/HeaderMic.class.uniqueId11 b/app/build/tmp/compileJava/compileTransaction/stash-dir/HeaderMic.class.uniqueId11 new file mode 100644 index 0000000..f649118 Binary files /dev/null and b/app/build/tmp/compileJava/compileTransaction/stash-dir/HeaderMic.class.uniqueId11 differ diff --git a/app/build/tmp/compileJava/compileTransaction/stash-dir/ImageUploader.class.uniqueId13 b/app/build/tmp/compileJava/compileTransaction/stash-dir/ImageUploader.class.uniqueId13 new file mode 100644 index 0000000..4e0f026 Binary files /dev/null and b/app/build/tmp/compileJava/compileTransaction/stash-dir/ImageUploader.class.uniqueId13 differ diff --git a/app/build/tmp/compileJava/compileTransaction/stash-dir/IngredientsList.class.uniqueId8 b/app/build/tmp/compileJava/compileTransaction/stash-dir/IngredientsList.class.uniqueId8 new file mode 100644 index 0000000..fcf224f Binary files /dev/null and b/app/build/tmp/compileJava/compileTransaction/stash-dir/IngredientsList.class.uniqueId8 differ diff --git a/app/build/tmp/compileJava/compileTransaction/stash-dir/Main.class.uniqueId3 b/app/build/tmp/compileJava/compileTransaction/stash-dir/Main.class.uniqueId3 new file mode 100644 index 0000000..ab329b1 Binary files /dev/null and b/app/build/tmp/compileJava/compileTransaction/stash-dir/Main.class.uniqueId3 differ diff --git a/app/build/tmp/compileJava/compileTransaction/stash-dir/MealTypeSelection.class.uniqueId9 b/app/build/tmp/compileJava/compileTransaction/stash-dir/MealTypeSelection.class.uniqueId9 new file mode 100644 index 0000000..d2ae5f9 Binary files /dev/null and b/app/build/tmp/compileJava/compileTransaction/stash-dir/MealTypeSelection.class.uniqueId9 differ diff --git a/app/build/tmp/compileJava/compileTransaction/stash-dir/Model.class.uniqueId2 b/app/build/tmp/compileJava/compileTransaction/stash-dir/Model.class.uniqueId2 deleted file mode 100644 index bafbbb9..0000000 Binary files a/app/build/tmp/compileJava/compileTransaction/stash-dir/Model.class.uniqueId2 and /dev/null differ diff --git a/app/build/tmp/compileJava/compileTransaction/stash-dir/NewRecipeUI.class.uniqueId0 b/app/build/tmp/compileJava/compileTransaction/stash-dir/NewRecipeUI.class.uniqueId0 new file mode 100644 index 0000000..cb77fc7 Binary files /dev/null and b/app/build/tmp/compileJava/compileTransaction/stash-dir/NewRecipeUI.class.uniqueId0 differ diff --git a/app/build/tmp/compileJava/compileTransaction/stash-dir/Recipe.class.uniqueId4 b/app/build/tmp/compileJava/compileTransaction/stash-dir/Recipe.class.uniqueId4 new file mode 100644 index 0000000..ac69ae7 Binary files /dev/null and b/app/build/tmp/compileJava/compileTransaction/stash-dir/Recipe.class.uniqueId4 differ diff --git a/app/build/tmp/compileJava/compileTransaction/stash-dir/RecipeList$1.class.uniqueId5 b/app/build/tmp/compileJava/compileTransaction/stash-dir/RecipeList$1.class.uniqueId5 new file mode 100644 index 0000000..4a85eb3 Binary files /dev/null and b/app/build/tmp/compileJava/compileTransaction/stash-dir/RecipeList$1.class.uniqueId5 differ diff --git a/app/build/tmp/compileJava/compileTransaction/stash-dir/RecipeList.class.uniqueId7 b/app/build/tmp/compileJava/compileTransaction/stash-dir/RecipeList.class.uniqueId7 new file mode 100644 index 0000000..908a359 Binary files /dev/null and b/app/build/tmp/compileJava/compileTransaction/stash-dir/RecipeList.class.uniqueId7 differ diff --git a/app/build/tmp/compileJava/compileTransaction/stash-dir/View.class.uniqueId5 b/app/build/tmp/compileJava/compileTransaction/stash-dir/View.class.uniqueId5 deleted file mode 100644 index 2a4478d..0000000 Binary files a/app/build/tmp/compileJava/compileTransaction/stash-dir/View.class.uniqueId5 and /dev/null differ diff --git a/app/build/tmp/compileJava/previous-compilation-data.bin b/app/build/tmp/compileJava/previous-compilation-data.bin index 8c8e2ca..417b684 100644 Binary files a/app/build/tmp/compileJava/previous-compilation-data.bin and b/app/build/tmp/compileJava/previous-compilation-data.bin differ diff --git a/app/build/tmp/compileTestJava/compileTransaction/stash-dir/CreateRecipeTest.class.uniqueId1 b/app/build/tmp/compileTestJava/compileTransaction/stash-dir/CreateRecipeTest.class.uniqueId1 new file mode 100644 index 0000000..abe1b5b Binary files /dev/null and b/app/build/tmp/compileTestJava/compileTransaction/stash-dir/CreateRecipeTest.class.uniqueId1 differ diff --git a/app/build/tmp/compileTestJava/compileTransaction/stash-dir/TextToRecipeTest.class.uniqueId0 b/app/build/tmp/compileTestJava/compileTransaction/stash-dir/TextToRecipeTest.class.uniqueId0 new file mode 100644 index 0000000..3523cf2 Binary files /dev/null and b/app/build/tmp/compileTestJava/compileTransaction/stash-dir/TextToRecipeTest.class.uniqueId0 differ diff --git a/app/build/tmp/compileTestJava/compileTransaction/stash-dir/VoiceRetrievalTest.class.uniqueId0 b/app/build/tmp/compileTestJava/compileTransaction/stash-dir/VoiceRetrievalTest.class.uniqueId0 deleted file mode 100644 index 5726b18..0000000 Binary files a/app/build/tmp/compileTestJava/compileTransaction/stash-dir/VoiceRetrievalTest.class.uniqueId0 and /dev/null differ diff --git a/app/build/tmp/compileTestJava/previous-compilation-data.bin b/app/build/tmp/compileTestJava/previous-compilation-data.bin index ab9df8f..8c94aaf 100644 Binary files a/app/build/tmp/compileTestJava/previous-compilation-data.bin and b/app/build/tmp/compileTestJava/previous-compilation-data.bin differ diff --git a/app/src/main/java/code/client/Model/ITextToRecipe.java b/app/src/main/java/code/client/Model/ITextToRecipe.java index c76a572..95565b5 100644 --- a/app/src/main/java/code/client/Model/ITextToRecipe.java +++ b/app/src/main/java/code/client/Model/ITextToRecipe.java @@ -4,7 +4,9 @@ import java.net.URISyntaxException; public interface ITextToRecipe { + String buildPrompt(String input); + String getChatGPTResponse(String input) throws IOException, InterruptedException, URISyntaxException; + Recipe mapResponseToRecipe(String responseText); } - diff --git a/app/src/main/java/code/client/Model/IVoiceToText.java b/app/src/main/java/code/client/Model/IVoiceToText.java new file mode 100644 index 0000000..623fe62 --- /dev/null +++ b/app/src/main/java/code/client/Model/IVoiceToText.java @@ -0,0 +1,8 @@ +package code.client.Model; + +import java.io.IOException; +import java.net.URISyntaxException; + +public interface IVoiceToText { + String processAudio() throws IOException, URISyntaxException; +} diff --git a/app/src/main/java/code/client/Model/TextToRecipe.java b/app/src/main/java/code/client/Model/TextToRecipe.java index 8b177ee..13fc5ba 100644 --- a/app/src/main/java/code/client/Model/TextToRecipe.java +++ b/app/src/main/java/code/client/Model/TextToRecipe.java @@ -94,11 +94,11 @@ public Recipe mapResponseToRecipe(String responseText) { return recipe; } - private String buildPrompt(String input) { + public String buildPrompt(String input) { StringBuilder prompt = new StringBuilder(); - prompt.append("I am a student on a budget with a busy schedule and I need to quickly cook a meal.") + prompt.append("I am a student on a budget with a busy schedule and I need to quickly cook a meal. ") .append(input) - .append("Make a recipe using only these ingredients plus condiments.") + .append("Make a recipe using only these ingredients plus condiments. ") .append("Remember to first include a title, then a list of ingredients, and then a list of instructions."); return prompt.toString(); } diff --git a/app/src/main/java/code/client/Model/VoiceToText.java b/app/src/main/java/code/client/Model/VoiceToText.java new file mode 100644 index 0000000..cb8133a --- /dev/null +++ b/app/src/main/java/code/client/Model/VoiceToText.java @@ -0,0 +1,146 @@ +package code.client.Model; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import org.json.JSONObject; +import org.json.JSONException; + +public class VoiceToText implements IVoiceToText { + private static final String API_ENDPOINT = "https://api.openai.com/v1/audio/transcriptions"; + private static final String TOKEN = "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"; + private static final String MODEL = "whisper-1"; + + // https://stackoverflow.com/questions/25334139/how-to-mock-a-url-connection + @Override + public String processAudio() throws IOException, URISyntaxException { + // Create file object from file path + File file = new File("recording.wav"); + HttpURLConnection connection = sendHttpRequest(file); + + // Get response code + int responseCode = connection.getResponseCode(); + String response; + + // Check response code and handle response accordingly + if (responseCode == HttpURLConnection.HTTP_OK) { + response = handleSuccessResponse(connection); + } else { + response = handleErrorResponse(connection); + } + + // Disconnect connection + connection.disconnect(); + + return response; + } + + private HttpURLConnection sendHttpRequest(File file) throws IOException, URISyntaxException { + URL url = new URI(API_ENDPOINT).toURL(); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("POST"); + connection.setDoOutput(true); + + // Set up request headers + String boundary = "Boundary-" + System.currentTimeMillis(); + connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary); + connection.setRequestProperty("Authorization", "Bearer " + TOKEN); + + // Set up output stream to write request body + OutputStream outputStream = connection.getOutputStream(); + + // Write model parameter to request body + writeParameterToOutputStream(outputStream, "model", MODEL, boundary); + + // Write file parameter to request body + writeFileToOutputStream(outputStream, file, boundary); + + // Write closing boundary to request body + outputStream.write(("\r\n--" + boundary + "--\r\n").getBytes()); + + // Flush and close output stream + outputStream.flush(); + outputStream.close(); + + return connection; + } + + // Helper method to write a parameter to the output stream in multipart form + // data format + private static void writeParameterToOutputStream( + OutputStream outputStream, + String parameterName, + String parameterValue, + String boundary) throws IOException { + outputStream.write(("--" + boundary + "\r\n").getBytes()); + outputStream.write( + ("Content-Disposition: form-data; name=\"" + parameterName + "\"\r\n\r\n").getBytes()); + outputStream.write((parameterValue + "\r\n").getBytes()); + } + + // Helper method to write a file to the output stream in multipart form data + // format + private static void writeFileToOutputStream( + OutputStream outputStream, + File file, + String boundary) throws IOException { + outputStream.write(("--" + boundary + "\r\n").getBytes()); + outputStream.write( + ("Content-Disposition: form-data; name=\"file\"; filename=\"" + + file.getName() + + "\"\r\n").getBytes()); + outputStream.write(("Content-Type: audio/mpeg\r\n\r\n").getBytes()); + + FileInputStream fileInputStream = new FileInputStream(file); + byte[] buffer = new byte[1024]; + int bytesRead; + while ((bytesRead = fileInputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + fileInputStream.close(); + } + + // Helper method to handle a successful response + private static String handleSuccessResponse(HttpURLConnection connection) + throws IOException, JSONException { + BufferedReader in = new BufferedReader( + new InputStreamReader(connection.getInputStream())); + String inputLine; + StringBuilder response = new StringBuilder(); + while ((inputLine = in.readLine()) != null) { + response.append(inputLine); + } + in.close(); + + JSONObject responseJson = new JSONObject(response.toString()); + + String generatedText = responseJson.getString("text"); + System.out.println("Transcription Result: " + generatedText); + return generatedText; + + } + + // Helper method to handle an error response + private static String handleErrorResponse(HttpURLConnection connection) + throws IOException, JSONException { + BufferedReader errorReader = new BufferedReader( + new InputStreamReader(connection.getErrorStream())); + String errorLine; + StringBuilder errorResponse = new StringBuilder(); + while ((errorLine = errorReader.readLine()) != null) { + errorResponse.append(errorLine); + } + errorReader.close(); + String errorResult = errorResponse.toString(); + System.out.println("Error Result: " + errorResult); + return errorResult; + } + +} diff --git a/app/src/main/java/code/client/View/DetailsAppFrame.java b/app/src/main/java/code/client/View/DetailsAppFrame.java new file mode 100644 index 0000000..f3bf8ee --- /dev/null +++ b/app/src/main/java/code/client/View/DetailsAppFrame.java @@ -0,0 +1,57 @@ +package code.client.View; + +import java.util.ArrayList; + +import code.client.Model.Recipe; +import javafx.geometry.Pos; +import javafx.scene.Scene; +import javafx.scene.control.TextField; +import javafx.scene.layout.VBox; +import javafx.stage.Stage; + +public class DetailsAppFrame { + private ArrayList scenes; + private Stage primaryStage; + + public Scene getScene() { + VBox root = new VBox(); + root.setSpacing(20); + root.setAlignment(Pos.CENTER); + root.setStyle("-fx-background-color: #F0F8FF;"); + + VBox setupContainer = new VBox(); + setupContainer.setSpacing(10); + + // Hardcoded value for now, recipe value for it should be changing + Recipe temp = new Recipe("1", "Fried Chicken and Egg Fried Rice"); + temp.addIngredient("2 chicken breasts, diced"); + temp.addIngredient("2 large eggs"); + temp.addIngredient("2 cups cooked rice"); + temp.addIngredient("2 tablespoons vegetable oil"); + temp.addInstruction("1. Heat the vegetable oil in a large pan over medium-high heat."); + + RecipeDetailsUI details = new RecipeDetailsUI(temp); + + TextField title = details.getTitleField(); + title.setAlignment(Pos.CENTER); + title.setStyle("-fx-font-weight: bold; -fx-font-size: 20;"); + root.getChildren().add(title); + + setupContainer.getChildren().add(details); + root.getChildren().add(setupContainer); + + return new Scene(root, 700, 600); + } + + /** + * This method provides the UI holder with the different scenes that can be + * switched between. + * + * @param primaryStage - Main stage that has the window + * @param scenes - list of different scenes to switch between. + */ + public void setScenes(Stage primaryStage, ArrayList scenes) { + this.scenes = scenes; + this.primaryStage = primaryStage; + } +} diff --git a/app/src/main/java/code/client/View/Main.java b/app/src/main/java/code/client/View/Main.java index 13c931f..d914c4b 100644 --- a/app/src/main/java/code/client/View/Main.java +++ b/app/src/main/java/code/client/View/Main.java @@ -1,8 +1,6 @@ package code.client.View; -import javafx.scene.layout.StackPane; - import javafx.application.Application; import javafx.stage.Stage; import javafx.scene.Scene; @@ -124,7 +122,6 @@ public void toggleSelected() { } } - class RecipeList extends VBox { RecipeList() { @@ -168,8 +165,7 @@ public void loadRecipes() { this.getChildren().add(recipe); } reader.close(); - } - catch (IOException e) { + } catch (IOException e) { System.out.println("Recipes could not be loaded."); } this.updateRecipeIndices(); @@ -185,8 +181,7 @@ public void saveRecipes() { writer.newLine(); } writer.close(); - } - catch (IOException e) { + } catch (IOException e) { System.out.println("Recipes could not be saved."); } } @@ -283,6 +278,8 @@ class AppFrame extends BorderPane { private Footer footer; private RecipeList recipeList; private Button addButton, deleteButton, loadButton, saveButton, selectButton, sortButton, uploadButton; + private ArrayList scenes; + private Stage primaryStage; AppFrame() { header = new Header(); @@ -306,6 +303,18 @@ class AppFrame extends BorderPane { addListeners(); } + /** + * This method provides the UI holder with the different scenes that can be + * switched between. + * + * @param primaryStage - Main stage that has the window + * @param scenes - list of different scenes to switch between. + */ + public void setScenes(Stage primaryStage, ArrayList scenes) { + this.scenes = scenes; + this.primaryStage = primaryStage; + } + public void addListeners() { addButton.setOnAction(e -> { Recipe recipe = new Recipe(); @@ -322,6 +331,7 @@ public void addListeners() { }); recipeList.updateRecipeIndices(); + primaryStage.setScene(scenes.get(1)); }); deleteButton.setOnAction(e -> { @@ -347,9 +357,26 @@ public class Main extends Application { @Override public void start(Stage primaryStage) throws Exception { AppFrame root = new AppFrame(); + Scene home = new Scene(root, 700, 600); + + NewRecipeUI audioCapture = new NewRecipeUI(); + Scene speaking = audioCapture.getScene(); + + DetailsAppFrame chatGPTed = new DetailsAppFrame(); + Scene details = chatGPTed.getScene(); + + ArrayList scenes = new ArrayList(); + scenes.add(home); + scenes.add(speaking); + scenes.add(details); + + // Can create observer, subject interface here + root.setScenes(primaryStage, scenes); + audioCapture.setScenes(primaryStage, scenes); + chatGPTed.setScenes(primaryStage, scenes); primaryStage.setTitle("Recipe Management App"); - primaryStage.setScene(new Scene(root, 700, 600)); + primaryStage.setScene(home); primaryStage.setResizable(false); primaryStage.show(); } @@ -358,6 +385,3 @@ public static void main(String[] args) { launch(args); } } - - - diff --git a/app/src/main/java/code/client/View/NewRecipeUI.java b/app/src/main/java/code/client/View/NewRecipeUI.java index 0066027..7084e3b 100644 --- a/app/src/main/java/code/client/View/NewRecipeUI.java +++ b/app/src/main/java/code/client/View/NewRecipeUI.java @@ -1,6 +1,13 @@ package code.client.View; +import code.client.Controllers.*; +import code.client.Model.*; import javafx.geometry.Pos; +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.ArrayList; + import javafx.application.Application; import javafx.stage.Stage; import javafx.scene.Scene; @@ -9,68 +16,339 @@ import javafx.scene.image.ImageView; import javafx.scene.layout.*; import javafx.scene.text.*; +// import javax.sound.sampled.*; + +class MealTypeSelection extends GridPane { + private Label prompt; + private Button recordButton; + private ImageView microphone; + private TextField mealTypeField; + + // =================FIRST PROMPT=================// + MealTypeSelection() { + // Set the preferred vertical and horizontal gaps + this.setVgap(20); + this.setHgap(20); + // Get a picture of a microphone for the voice recording button + File file = new File("app/src/main/java/code/client/View/microphone.png"); + microphone = new ImageView(new Image(file.toURI().toString())); + // Set the size of the microphone image + microphone.setFitWidth(30); + microphone.setFitHeight(30); + // Create a recording button + recordButton = new Button(); + recordButton.setGraphic(microphone); + // Set the user prompt for meal type selection + prompt = new Label("Select Meal Type (Breakfast, Lunch, or Dinner)"); + prompt.setStyle("-fx-font-size: 16;"); + // Set a textField for the meal type that was selected + mealTypeField = new TextField(); + mealTypeField.setPromptText("Meal Type"); + // Add all of the elements to the MealTypeSelection + this.add(recordButton, 0, 0); + this.add(prompt, 1, 0); + this.add(mealTypeField, 0, 1); + } + + public TextField getMealType() { + return mealTypeField; + } + + public Button getRecordButton() { + return recordButton; + } +} + +class IngredientsList extends GridPane { + + private Label prompt; + private Button recordButton; + private ImageView microphone; + private TextField ingredientsField; + + // ==============SECOND PROMPT=================// + IngredientsList() { + // Set the preferred vertical and horizontal gaps + this.setVgap(20); + this.setHgap(20); + // Get a picture of a microphone for the voice recording button + File file = new File("app/src/main/java/code/client/View/microphone.png"); + microphone = new ImageView(new Image(file.toURI().toString())); + // Set the size of the microphone image + microphone.setFitWidth(30); + microphone.setFitHeight(30); + // Create a recording button + recordButton = new Button(); + recordButton.setGraphic(microphone); + // Set the user prompt for meal type selection + prompt = new Label("Please List Your Ingredients"); + prompt.setStyle("-fx-font-size: 16;"); + // Set a textField for the meal type that was selected + ingredientsField = new TextField(); + ingredientsField.setPromptText("Ingredients"); + // Add all of the elements to the MealTypeSelection + this.add(recordButton, 0, 0); + this.add(prompt, 1, 0); + this.add(ingredientsField, 0, 1); + } + + public TextField getIngredients() { + return ingredientsField; + } + + public Button getRecordButton() { + return recordButton; + } +} + +class GPTRecipe extends GridPane { + private Label recipeLabel; + private TextField recipeField; + + GPTRecipe() { + this.setVgap(20); + recipeLabel = new Label("Here Is Your Recipe"); + recipeField = new TextField(); + this.add(recipeLabel, 0, 0); + this.add(recipeField, 0, 1); + } +} + +class HeaderMic extends HBox { + HeaderMic() { + this.setPrefSize(700, 60); + this.setStyle("-fx-background-color: #F0F8FF;"); + + Text titleText = new Text("Recipe Creation"); + titleText.setStyle("-fx-font-weight: bold; -fx-font-size: 20;"); + this.getChildren().add(titleText); + this.setAlignment(Pos.CENTER); + } +} + +class AppFrameMic extends BorderPane { + // Inputs for WhisperHandler + private static final String API_ENDPOINT = "https://api.openai.com/v1/audio/transcriptions"; + private static final String TOKEN = "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"; + private static final String MODEL = "whisper-1"; + // Helper variables for button functionality + private boolean recording; // keeps track if the app is currently recording + private String mealType; // stores the meal type specified by the user + private String ingredients; // stores the ingredients listed out by the user + private String recipeSteps; // stores the recipe steps generated by ChatGPT + // AppFrameMic elements + private GridPane recipeCreationGrid; + private Header header; + private MealTypeSelection mealTypeSelection; + private IngredientsList ingredientsList; + private GPTRecipe gptrecipe; + private Button recordButton1, recordButton2, createButton, saveButton; + + // Scene Transitions + private ArrayList scenes; + private Stage primaryStage; + + AppFrameMic() { + header = new Header(); + mealTypeSelection = new MealTypeSelection(); + ingredientsList = new IngredientsList(); + gptrecipe = new GPTRecipe(); + + recipeCreationGrid = new GridPane(); + recipeCreationGrid.setAlignment(Pos.CENTER); + recipeCreationGrid.setVgap(20); + recipeCreationGrid.setHgap(20); + recipeCreationGrid.setStyle("-fx-background-color: #DAE5EA; -fx-border-width: 0;"); + + recipeCreationGrid.add(mealTypeSelection, 0, 0); + recipeCreationGrid.add(ingredientsList, 0, 1); + recipeCreationGrid.add(gptrecipe, 0, 2); + + this.setTop(header); + this.setCenter(recipeCreationGrid); + + recordButton1 = mealTypeSelection.getRecordButton(); + recordButton2 = ingredientsList.getRecordButton(); + + createButton = new Button("Create Recipe"); + recipeCreationGrid.add(createButton, 0, 3); -public class NewRecipeUI extends Application { - - @Override - public void start(Stage primaryStage) { - - VBox root = new VBox(); - root.setSpacing(20); - root.setAlignment(Pos.CENTER); - root.setStyle("-fx-background-color: #F0F8FF;"); - - - Text title = new Text("New Recipe Setup"); - title.setStyle("-fx-font-weight: bold; -fx-font-size: 20;"); - root.getChildren().add(title); - - - VBox setupContainer = new VBox(); - setupContainer.setSpacing(10); - - //PROMPT 1 - HBox option1Container = new HBox(10); - Label option1Label = new Label("1) Breakfast, Lunch, or Dinner?"); - option1Label.setStyle("-fx-font-size: 16;"); // Increase the font size - Button microphoneButton1 = new Button(); - Image microphoneImage = new Image(getClass().getResourceAsStream(".\\microphone.png")); - - ImageView microphoneImageView1 = new ImageView(microphoneImage); - microphoneImageView1.setFitHeight(30); - microphoneImageView1.setFitWidth(30); - microphoneButton1.setGraphic(microphoneImageView1); - option1Container.getChildren().addAll(option1Label, microphoneButton1); - - // PROMPT 2 - HBox option2Container = new HBox(10); - Label option2Label = new Label("2) List the ingredients you have"); - option2Label.setStyle("-fx-font-size: 16;"); // Increase the font size - Button microphoneButton2 = new Button(); - ImageView microphoneImageView2 = new ImageView(microphoneImage); - microphoneImageView2.setFitHeight(30); - microphoneImageView2.setFitWidth(30); - microphoneButton2.setGraphic(microphoneImageView2); - option2Container.getChildren().addAll(option2Label, microphoneButton2); - - setupContainer.getChildren().addAll(option1Container, option2Container); - - - root.getChildren().add(setupContainer); - - - Button saveButton = new Button("Save Setup"); - root.getChildren().add(saveButton); - - - Scene scene = new Scene(root, 700, 600); - primaryStage.setTitle("New Recipe Setup"); - primaryStage.setScene(scene); - primaryStage.show(); + saveButton = new Button("Save Setup"); + recipeCreationGrid.add(saveButton, 0, 4); + + addListeners(); + } + + /** + * This method provides the UI holder with the different scenes that can be + * switched between. + * + * @param primaryStage - Main stage that has the window + * @param scenes - list of different scenes to switch between. + */ + public void setScenes(Stage primaryStage, ArrayList scenes) { + this.scenes = scenes; + this.primaryStage = primaryStage; + } + + public void addListeners() { + + AudioRecorder recorder = new AudioRecorder(new Label("Recording...")); + WhisperHandler audioProcessor = new WhisperHandler(API_ENDPOINT, TOKEN, MODEL); + + recordButton1.setOnAction(e -> { + + if (!recording) { + recorder.startRecording(); + recording = true; + + } else { + recorder.stopRecording(); + recording = false; + try { + mealType = audioProcessor.processAudio(); + } catch (IOException | URISyntaxException e2) { + e2.printStackTrace(); + } + mealTypeSelection.getMealType().setText(mealType); + } + }); + + recordButton2.setOnAction(e -> { + + if (!recording) { + recorder.startRecording(); + recording = true; + + } else { + recorder.stopRecording(); + recording = false; + try { + ingredients = audioProcessor.processAudio(); + } catch (IOException | URISyntaxException e2) { + e2.printStackTrace(); + } + mealTypeSelection.getMealType().setText(ingredients); + } + }); + + // createButton.setOnAction(e -> { + + // }); + + // CHANGE SCENE TO DETAILED RECIPE DISPLAY + saveButton.setOnAction(e -> { + /* + * TextToRecipe caller = new TextToRecipe(); + * WhisperHandler audio = new WhisperHandler( + * "https://api.openai.com/v1/audio/transcriptions", + * "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", + * "whisper-1"); + * try { + * caller.getChatGPTResponse(audio.processAudio()); + * } catch (Exception e1) { + * // TODO Auto-generated catch block + * e1.printStackTrace(); + * } + */ + primaryStage.setScene(scenes.get(2)); + }); + } +} + +public class NewRecipeUI { + private ArrayList scenes; + private Stage primaryStage; + private AppFrameMic root; + + public Scene getScene() throws Exception { + + root = new AppFrameMic(); + Scene scene = new Scene(root, 700, 700); + return scene; } - public static void main(String[] args) { - launch(args); + /** + * This method provides the UI holder with the different scenes that can be + * switched between. + * + * @param primaryStage - Main stage that has the window + * @param scenes - list of different scenes to switch between. + */ + public void setScenes(Stage primaryStage, ArrayList scenes) { + root.setScenes(primaryStage, scenes); } } +// root.setScenes(primaryStage,scenes);// +// package code.client.View; + +// import javafx.geometry.Pos; +// import javafx.application.Application; +// import javafx.stage.Stage; +// import javafx.scene.Scene; +// import javafx.scene.control.*; +// import javafx.scene.image.Image; +// import javafx.scene.image.ImageView; +// import javafx.scene.layout.*; +// import javafx.scene.text.*; + +// public class NewRecipeUI extends Application { + +// @Override +// public void start(Stage primaryStage) { + +// VBox root = new VBox(); +// root.setSpacing(20); +// root.setAlignment(Pos.CENTER); +// root.setStyle("-fx-background-color: #F0F8FF;"); + +// Text title = new Text("New Recipe Setup"); +// title.setStyle("-fx-font-weight: bold; -fx-font-size: 20;"); +// root.getChildren().add(title); + +// VBox setupContainer = new VBox(); +// setupContainer.setSpacing(10); + +// //PROMPT 1 +// HBox option1Container = new HBox(10); +// Label option1Label = new Label("1) Breakfast, Lunch, or Dinner?"); +// option1Label.setStyle("-fx-font-size: 16;"); // Increase the font size +// Button microphoneButton1 = new Button(); +// Image microphoneImage = new +// Image(getClass().getResourceAsStream("microphone.png")); + +// ImageView microphoneImageView1 = new ImageView(microphoneImage); +// microphoneImageView1.setFitHeight(30); +// microphoneImageView1.setFitWidth(30); +// microphoneButton1.setGraphic(microphoneImageView1); +// option1Container.getChildren().addAll(option1Label, microphoneButton1); + +// // PROMPT 2 +// HBox option2Container = new HBox(10); +// Label option2Label = new Label("2) List the ingredients you have"); +// option2Label.setStyle("-fx-font-size: 16;"); // Increase the font size +// Button microphoneButton2 = new Button(); +// ImageView microphoneImageView2 = new ImageView(microphoneImage); +// microphoneImageView2.setFitHeight(30); +// microphoneImageView2.setFitWidth(30); +// microphoneButton2.setGraphic(microphoneImageView2); +// option2Container.getChildren().addAll(option2Label, microphoneButton2); + +// setupContainer.getChildren().addAll(option1Container, option2Container); + +// root.getChildren().add(setupContainer); + +// Button saveButton = new Button("Save Setup"); +// root.getChildren().add(saveButton); + +// Scene scene = new Scene(root, 700, 600); +// primaryStage.setTitle("New Recipe Setup"); +// primaryStage.setScene(scene); +// primaryStage.show(); +// } + +// public static void main(String[] args) { +// launch(args); +// } +// } diff --git a/app/src/main/java/code/client/View/RecipeDetailsUI.java b/app/src/main/java/code/client/View/RecipeDetailsUI.java new file mode 100644 index 0000000..40acbb7 --- /dev/null +++ b/app/src/main/java/code/client/View/RecipeDetailsUI.java @@ -0,0 +1,45 @@ +package code.client.View; + +import javafx.scene.control.TextArea; +import javafx.scene.control.TextField; +import javafx.scene.layout.HBox; + +import java.util.Iterator; + +import code.client.Model.Recipe; + +public class RecipeDetailsUI extends HBox { + private final TextField titleTextField; + private final TextArea ingredientTextArea; + private final TextArea instructionTextArea; + + public RecipeDetailsUI(Recipe recipe) { + titleTextField = new TextField(); + ingredientTextArea = new TextArea(); + instructionTextArea = new TextArea(); + + titleTextField.setText(recipe.getTitle()); + + StringBuilder ingredientBuilder = new StringBuilder(); + Iterator ingredientIterator = recipe.getIngredientIterator(); + while (ingredientIterator.hasNext()) { + ingredientBuilder.append(ingredientIterator.next() + "\n"); + } + ingredientTextArea.setText(ingredientBuilder.toString()); + + StringBuilder instructionBuilder = new StringBuilder(); + Iterator instructionIterator = recipe.getInstructionIterator(); + while (instructionIterator.hasNext()) { + instructionBuilder.append(instructionIterator.next() + "\n"); + } + instructionTextArea.setText(instructionBuilder.toString()); + + // getChildren().add(titleTextField); + getChildren().add(ingredientTextArea); + getChildren().add(instructionTextArea); + } + + public TextField getTitleField() { + return titleTextField; + } +} diff --git a/app/src/test/java/code/CreateRecipeTest.java b/app/src/test/java/code/CreateRecipeTest.java new file mode 100644 index 0000000..797b0b1 --- /dev/null +++ b/app/src/test/java/code/CreateRecipeTest.java @@ -0,0 +1,37 @@ +package code; + +import org.junit.jupiter.api.Test; + +import code.client.Model.ITextToRecipe; +import code.client.Model.Recipe; +import code.client.Model.TextToRecipe; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class CreateRecipeTest { + /** + * Unit Test for Create Recipe + */ + @Test + public void testCreateRecipe() { + Recipe recipe = new Recipe("1", "Fried Rice"); + + assertEquals("1", recipe.getId()); + assertEquals("Fried Rice", recipe.getTitle()); + recipe.addIngredient("Rice"); + recipe.addIngredient("Fried"); + recipe.addInstruction("A shrimp fried this rice?"); + + String recipeString = recipe.toString(); + String parsedResponse = """ + Title: Fried Rice + Ingredients: + Rice + Fried + Instructions: + A shrimp fried this rice? + """; + assertEquals(recipeString, parsedResponse); + } + +} diff --git a/app/src/test/java/code/TextToRecipeTest.java b/app/src/test/java/code/TextToRecipeTest.java index 8502297..ab41c7b 100644 --- a/app/src/test/java/code/TextToRecipeTest.java +++ b/app/src/test/java/code/TextToRecipeTest.java @@ -9,7 +9,22 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class TextToRecipeTest { + @Test + /** + * Integration test for provide recipe + */ + public void testPromptBuild() { + ITextToRecipe textToRecipe = new TextToRecipe(); + String prompt = "I am a student on a budget with a busy schedule and I need to quickly cook a meal. I have rice, shrimp, chicken, and eggs. Make a recipe using only these ingredients plus condiments. Remember to first include a title, then a list of ingredients, and then a list of instructions."; + String response = textToRecipe.buildPrompt("I have rice, shrimp, chicken, and eggs. "); + assertEquals(prompt, response); + } + + @Test + /** + * Unit tests for Recipe features + */ public void testParseJSON() { ITextToRecipe textToRecipe = new TextToRecipe(); String responseText = """ @@ -148,8 +163,8 @@ public void testParseNoIndentsAndNewLines() { public void testParseDifferentLineSpacing() { ITextToRecipe textToRecipe = new TextToRecipe(); String responseText = """ - - + + Savory Beef Pasta Bake Ingredients: - ½ pound of ground beef diff --git a/app/src/test/java/code/VoiceRetrievalTest.java b/app/src/test/java/code/VoiceRetrievalTest.java index 96daa4f..b7d881d 100644 --- a/app/src/test/java/code/VoiceRetrievalTest.java +++ b/app/src/test/java/code/VoiceRetrievalTest.java @@ -15,13 +15,18 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; + +/** + * Work In Progress Voice Retrieval Unit Test + */ public class VoiceRetrievalTest { private AudioRecorder audioRecorder; private Label recordingLabel; @BeforeAll static void initJfxRuntime() { - Platform.startup(() -> {}); + Platform.startup(() -> { + }); } @BeforeEach @@ -30,7 +35,6 @@ void setUp() { audioRecorder = new AudioRecorder(recordingLabel); } - void testAudioSave() throws InterruptedException { audioRecorder.startRecording(); Thread.sleep(1000); @@ -39,12 +43,11 @@ void testAudioSave() throws InterruptedException { assertTrue(file.exists()); } - void testLabelChanges() throws InterruptedException { audioRecorder.startRecording(); assertTrue(recordingLabel.isVisible()); Thread.sleep(1000); - + audioRecorder.stopRecording(); assertFalse(recordingLabel.isVisible()); }