From 110e5e8163493dd9b92cc48177c4cc07f0342a44 Mon Sep 17 00:00:00 2001 From: Rithvik Sriram Date: Wed, 21 Jan 2026 22:48:07 +0100 Subject: [PATCH] added a Dialog sequence for when the server is not up, which was previously just a log msg. Now the user gets to provide their own server. --- client/src/main/java/client/UI.java | 12 ++- .../scenes/ServerConnectionDialogCtrl.java | 94 ++++++++++++++++ .../java/client/utils/server/ServerUtils.java | 9 +- locc.sh | 102 ++++++++++++++++++ 4 files changed, 208 insertions(+), 9 deletions(-) create mode 100644 client/src/main/java/client/scenes/ServerConnectionDialogCtrl.java create mode 100644 locc.sh diff --git a/client/src/main/java/client/UI.java b/client/src/main/java/client/UI.java index 752dbaf..da3a2b3 100644 --- a/client/src/main/java/client/UI.java +++ b/client/src/main/java/client/UI.java @@ -2,6 +2,7 @@ package client; import client.scenes.FoodpalApplicationCtrl; import client.scenes.MainCtrl; +import client.scenes.ServerConnectionDialogCtrl; import client.utils.server.ServerUtils; import com.google.inject.Injector; import javafx.application.Application; @@ -27,9 +28,14 @@ public class UI extends Application { var serverUtils = INJECTOR.getInstance(ServerUtils.class); if (!serverUtils.isServerAvailable()) { - var msg = "Server needs to be started before the client, but it does not seem to be available. Shutting down."; - System.err.println(msg); - return; + var connectionHandler = INJECTOR.getInstance(ServerConnectionDialogCtrl.class); + boolean serverConnected = connectionHandler.promptForURL(); + + if(!serverConnected){ + var msg = "User Cancelled Server connection. Shutting down"; + System.err.print(msg); + return; + } } var foodpal = FXML.load(FoodpalApplicationCtrl.class, "client", "scenes", "FoodpalApplication.fxml"); diff --git a/client/src/main/java/client/scenes/ServerConnectionDialogCtrl.java b/client/src/main/java/client/scenes/ServerConnectionDialogCtrl.java new file mode 100644 index 0000000..4283d01 --- /dev/null +++ b/client/src/main/java/client/scenes/ServerConnectionDialogCtrl.java @@ -0,0 +1,94 @@ +package client.scenes; + + +import client.utils.ConfigService; +import client.utils.server.ServerUtils; +import com.google.inject.Inject; +import javafx.scene.control.Alert; +import javafx.scene.control.ButtonType; +import javafx.scene.control.TextInputDialog; + +import java.util.Optional; + + +public class ServerConnectionDialogCtrl { + + private final ConfigService configService; + private final ServerUtils serverUtils; + + + + @Inject + public ServerConnectionDialogCtrl(ConfigService configService, ServerUtils serverUtils) { + this.configService = configService; + this.serverUtils = serverUtils; + + } + + /** + * + * @return a boolean for if the user got connected to server or + */ + public boolean promptForURL(){ + Alert error = new Alert(Alert.AlertType.ERROR); //creates an error alert + error.setTitle("Server is Unavailable"); + error.setHeaderText("Unable to connect to Server"); + error.setContentText("The server at " + configService.getConfig().getServerUrl() + + " is not available\n\n Would you like to try a different Server URL?"); + + error.getButtonTypes().setAll(ButtonType.YES, ButtonType.NO); + Optional userChoice = error.showAndWait(); //asks if user wants to input server URL + + if(userChoice.isEmpty() || userChoice.get() == ButtonType.NO){ + return false; + } + + while(true){ // Keeps asking the user until either a valid url is provided or the user exits + TextInputDialog dialog = + new TextInputDialog(configService.getConfig().getServerUrl()); + dialog.setTitle("Enter new server URL"); + dialog.setContentText("Server URL:"); + Optional userRes = dialog.showAndWait(); + if(userRes.isEmpty()){ + return false; //user cancelled the operation + } + String newServer = userRes.get().trim(); + + if(newServer.isEmpty()){ + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("Invalid Input"); + alert.setHeaderText("Invalid server URL"); + alert.setContentText("Please enter a valid URL"); + alert.showAndWait(); + continue; + } + configService.getConfig().setServerUrl(newServer); + if(serverUtils.isServerAvailable()){ + configService.save(); + Alert success = new Alert(Alert.AlertType.INFORMATION); + success.setTitle("Success"); + success.setHeaderText("Connected to Server"); + success.setContentText("Successfully connected to the server!"); + success.showAndWait(); + return true; + } + else{ + Alert retry = new Alert(Alert.AlertType.ERROR); + retry.setTitle("Failed"); + retry.setHeaderText("Failed to connect to Server"); + retry.setContentText("Would you like to try another URL?"); + retry.getButtonTypes().setAll(ButtonType.YES, ButtonType.NO); + Optional result = retry.showAndWait(); + if(result.isEmpty() || result.get() == ButtonType.NO){ + return false; + } + + } + + } + } + + + + +} diff --git a/client/src/main/java/client/utils/server/ServerUtils.java b/client/src/main/java/client/utils/server/ServerUtils.java index 5330fdd..b82d478 100644 --- a/client/src/main/java/client/utils/server/ServerUtils.java +++ b/client/src/main/java/client/utils/server/ServerUtils.java @@ -7,13 +7,11 @@ import com.google.inject.Inject; import commons.Ingredient; import commons.Recipe; import commons.RecipeIngredient; -import jakarta.ws.rs.ProcessingException; import jakarta.ws.rs.client.ClientBuilder; import org.glassfish.jersey.client.ClientConfig; import java.io.IOException; -import java.net.ConnectException; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; @@ -171,10 +169,9 @@ public class ServerUtils { .target(this.endpoints.baseUrl()) // .request(APPLICATION_JSON) // .get(); - } catch (ProcessingException e) { - if (e.getCause() instanceof ConnectException) { - return false; - } + } + catch(Exception e){ + return false; //any exception caught will return false, not just processing exception. } return true; } diff --git a/locc.sh b/locc.sh new file mode 100644 index 0000000..93a0fea --- /dev/null +++ b/locc.sh @@ -0,0 +1,102 @@ +#!/usr/bin/env bash +set -u + +# 1. Check for git repo +if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then + echo "Error: current directory is not a git repository." >&2 + exit 1 +fi + +# 2. Define path patterns +PATH_CLIENT='client/src/**/*.java' +PATH_SERVER='server/src/**/*.java' +PATH_COMMONS='commons/src/**/*.java' +PATH_PROD='*/src/main/*.java' +PATH_TEST='*/src/test/*.java' +PATH_ALL='*.java' + +# 3. Helper functions + +# Standard count: Includes imports, respects WS changes +count_lines_standard() { + git show --format="" --patch "$1" -- "$2" \ + | grep -E '^\+[^+/][^/]+$' \ + | grep -v '+ *[*@]' \ + | wc -l +} + +# Strict count: Ignores imports, ignores pure WS changes +count_lines_strict() { + git show --format="" --patch -w "$1" -- "$2" \ + | grep -E '^\+[^+/][^/]+$' \ + | grep -v 'import' \ + | grep -v '+ *[*@]' \ + | wc -l +} + +echo "Analyzing commits on 'main'..." >&2 + +# 4. Main Loop +# Use %ae for Author Email +git log --no-merges --pretty=format:'%H %ae' main | { + + declare -A client_count + declare -A server_count + declare -A commons_count + declare -A prod_count + declare -A test_count + declare -A total_count + declare -A strict_count + declare -A seen_users + + while read -r hash raw_email; do + # Normalize email to lowercase + email=$(echo "$raw_email" | tr '[:upper:]' '[:lower:]') + seen_users["$email"]=1 + + # Run counts (Standard) + c_add=$(count_lines_standard "$hash" "$PATH_CLIENT") + s_add=$(count_lines_standard "$hash" "$PATH_SERVER") + k_add=$(count_lines_standard "$hash" "$PATH_COMMONS") + p_add=$(count_lines_standard "$hash" "$PATH_PROD") + t_add=$(count_lines_standard "$hash" "$PATH_TEST") + all_add=$(count_lines_standard "$hash" "$PATH_ALL") + + # Run count (Strict) + strict_add=$(count_lines_strict "$hash" "$PATH_ALL") + + # Accumulate + client_count["$email"]=$(( ${client_count["$email"]:-0} + c_add )) + server_count["$email"]=$(( ${server_count["$email"]:-0} + s_add )) + commons_count["$email"]=$(( ${commons_count["$email"]:-0} + k_add )) + prod_count["$email"]=$(( ${prod_count["$email"]:-0} + p_add )) + test_count["$email"]=$(( ${test_count["$email"]:-0} + t_add )) + total_count["$email"]=$(( ${total_count["$email"]:-0} + all_add )) + strict_count["$email"]=$(( ${strict_count["$email"]:-0} + strict_add )) + + printf "." >&2 + done + + echo "" >&2 + echo "Done." >&2 + + # 5. Print Table + # Widths: Email=40, Others=10, Strict=13 + printf "%-40s | %-10s | %-10s | %-10s | %-10s | %-10s | %-10s | %-13s\n" \ + "User Email" "Client" "Server" "Commons" "Prod" "Test" "Total" "Total (Strict)" + printf "%s\n" "-----------------------------------------|------------|------------|------------|------------|------------|------------|----------------" + + for email in "${!seen_users[@]}"; do + echo "$email" + done | sort | while read -r e; do + printf "%-40s | %-10d | %-10d | %-10d | %-10d | %-10d | %-10d | %-13d\n" \ + "$e" \ + "${client_count[$e]:-0}" \ + "${server_count[$e]:-0}" \ + "${commons_count[$e]:-0}" \ + "${prod_count[$e]:-0}" \ + "${test_count[$e]:-0}" \ + "${total_count[$e]:-0}" \ + "${strict_count[$e]:-0}" + done +}