diff --git a/src/main/java/diagram.puml b/src/main/java/diagram.puml new file mode 100644 index 0000000..132fc1c --- /dev/null +++ b/src/main/java/diagram.puml @@ -0,0 +1,77 @@ +@startuml + +Class FitnessApp{ ++ startApp(): void +} + +Class Person{ +- name: String +- email: String +- workouts: ArrayList + ++ getName(): String ++ setName(name: String): void + ++ getEmail(): String ++ setEmail(email: String): void + ++ getWorkouts(): ArrayList ++ setWorkouts(workouts: ArrayList): void +} + +Class Workout{ +- duration: String +- date: Date +- completed: boolean +- exercise: ArrayList + ++ getDuration(): String ++ setDuration(duration: String): void + ++ getDate(): Date ++ setDate(date: Date): void + ++ getCompleted(): boolean ++ setCompleted(completed: boolean): void + ++ getExercise(): ArrayList ++ setExercise(exercise: ArrayList): void + ++ displayWorkoutInfo(): void +} + +Class Exercise{ +- type: WorkoutType +- name: String +- caloriesBurned: double + ++ getType(): WorkoutType ++ setType(type: WorkoutType): void + ++ getCaloriesBurned(): double ++ setCaloriesBurned(caloriesBurned: double): void +} + +enum WorkoutType{ +CHEST +BACK +SHOULDER +ARMS +CARDIO +LEGS +} + +' Relationships +FitnessApp "1" --> "*" Person : manages users +' One fitness app can contain multiple people + +Person "1" --> "*" Workout : performs +' A person can complete many workouts + +Workout "1" --> "*" Exercise : contains +' A workout is made up of multiple exercises + +Exercise --> WorkoutType : categorized by +' Each exercise belongs to one workout type + +@enduml \ No newline at end of file diff --git a/src/main/java/org/codedifferently/Exercise.java b/src/main/java/org/codedifferently/Exercise.java new file mode 100644 index 0000000..55a6819 --- /dev/null +++ b/src/main/java/org/codedifferently/Exercise.java @@ -0,0 +1,61 @@ +package org.codedifferently; + +// Class that represents a single exercise +public class Exercise { + + // The type of exercise (ex: cardio, strength, etc.) + private ExerciseType type; + + // Name of the exercise (ex: Pushups, Running) + private String name; + + // Number of calories burned during the exercise + private int caloriesBurned; + + // Constructor used to create a new Exercise object + public Exercise(ExerciseType type, String name, int caloriesBurned) { + this.type = type; // set exercise type + this.name = name; // set exercise name + this.caloriesBurned = caloriesBurned; // set calories burned + } + + // Returns the exercise type + public ExerciseType getType() { + return type; + } + + // Updates the exercise type + public void setType(ExerciseType type) { + this.type = type; + } + + // Returns the name of the exercise + public String getName() { + return name; + } + + // Updates the name of the exercise + public void setName(String name) { + this.name = name; + } + + // Returns the number of calories burned + public int getCaloriesBurned() { + return caloriesBurned; + } + + // Updates the number of calories burned + public void setCaloriesBurned(int caloriesBurned) { + this.caloriesBurned = caloriesBurned; + } + + // Displays the exercise information to the console + public void displayExerciseInfo() { + System.out.println("--------------------"); + System.out.println("Exercise Information"); + System.out.println("Name: " + name); // print exercise name + System.out.println("Type: " + type); // print exercise type + System.out.println("Calories Burned: " + caloriesBurned); // print calories burned + System.out.println(); + } +} \ No newline at end of file diff --git a/src/main/java/org/codedifferently/ExerciseType.java b/src/main/java/org/codedifferently/ExerciseType.java new file mode 100644 index 0000000..c5b233e --- /dev/null +++ b/src/main/java/org/codedifferently/ExerciseType.java @@ -0,0 +1,23 @@ +package org.codedifferently; + +// Enum representing the different categories of exercises a workout can include +public enum ExerciseType { + + // Exercises that target chest muscles + CHEST, + + // Exercises that target back muscles + BACK, + + // Exercises that target shoulder muscles + SHOULDERS, + + // Exercises that target arm muscles + ARMS, + + // Exercises focused on cardiovascular endurance + CARDIO, + + // Exercises that target leg muscles + LEGS +} \ No newline at end of file diff --git a/src/main/java/org/codedifferently/FitnessApp.java b/src/main/java/org/codedifferently/FitnessApp.java new file mode 100644 index 0000000..fb5e1c1 --- /dev/null +++ b/src/main/java/org/codedifferently/FitnessApp.java @@ -0,0 +1,471 @@ +package org.codedifferently; + +import java.util.ArrayList; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Scanner; + +// Manages the main workflow of the LiftLogic fitness application +public class FitnessApp { + + // Stores all registered users in the system + private static ArrayList users = new ArrayList<>(); + + // Starts the application and displays the main menu + public static void startApp(Scanner sc) { + System.out.println("Welcome to LiftLogic!"); + int input; + + do { + // Displays main menu options + System.out.println("\n=== LiftLogic ==="); + System.out.println("1) Signup"); + System.out.println("2) Login"); + System.out.println("0) Quit"); + System.out.print("Choose: "); + + // Validates numeric menu input + if (sc.hasNextInt()) { + input = sc.nextInt(); + } else { + input = -1; + } + sc.nextLine(); + + switch (input) { + + // Creates a new user account + case 1: + signup(sc); + break; + + // Attempts user login + case 2: + Person toLogin = validateLogin(sc); + if (toLogin != null) { + login(toLogin, sc); + } else { + System.out.println("Invalid username or password. Try again."); + } + break; + + // Exits the application + case 0: + System.out.println("\nExiting system..."); + break; + + // Handles invalid selections + default: + System.out.println("\nPlease select a valid option."); + } + + } while (input != 0); + } + + // Registers a new user and logs them in + private static void signup(Scanner sc) { + String name = validateName(sc); + String email = validateEmail(sc); + System.out.print("Enter a password: "); + String password = sc.nextLine(); + + // Creates a new Person object and stores it in the users list + Person user = new Person(name, email, password); + users.add(user); + + // Logs the new user into the system + FitnessApp.login(user, sc); + } + + // Displays the logged-in user menu + public static void login(Person person, Scanner sc) { + System.out.println("Welcome, " + person.getName() + "!"); + int input; + + do { + // Displays user dashboard menu + System.out.println("\n=== LiftLogic ==="); + System.out.println("1) Log a workout"); + System.out.println("2) Display Leaderboard"); + System.out.println("3) Settings"); + System.out.println("4) Display all workouts"); + System.out.println("0) Logout"); + System.out.print("Choose: "); + + // Validates numeric input + if (sc.hasNextInt()) { + input = sc.nextInt(); + } else { + input = -1; + } + sc.nextLine(); + + switch (input) { + + // Creates and stores a workout for the user + case 1: + Workout workout = createWorkout(sc); + + // Adds workout to the user's workout list + ArrayList workouts = person.getWorkouts(); + workouts.add(workout); + person.setWorkouts(workouts); + + // Marks this workout as the user's active workout + person.setActiveWorkout(workout); + break; + + // Displays leaderboard rankings + case 2: + displayLeaderboard(getUsers()); + break; + + // Opens user settings menu + case 3: + displaySettings(person, sc); + break; + + // Displays all workouts the user has done + case 4: + displayAllWorkouts(person); + break; + + // Logs the user out + case 0: + System.out.println("\nLogging out..."); + break; + + // Handles invalid selections + default: + System.out.println("\nPlease select a valid option."); + } + + } while (input != 0); + } + + // Displays and processes user settings + public static void displaySettings(Person person, Scanner sc) { + int input; + + do { + // Displays settings menu + System.out.println("\n=== Settings ==="); + System.out.println("1) Change Name"); + System.out.println("2) Reset Password"); + System.out.println("0) Exit"); + System.out.print("Choose: "); + + // Validates numeric input + if (sc.hasNextInt()) { + input = sc.nextInt(); + } else { + input = -1; + } + sc.nextLine(); + + switch (input) { + + // Updates the user's name + case 1: + String name = validateName(sc); + person.setName(name); + break; + + // Updates the user's password after verification + case 2: + System.out.print("Enter your current password: "); + String password = sc.nextLine(); + + // Confirms the current password matches + if (person.getPassword().equals(password)) { + + // Prompts for new password twice + System.out.print("Enter your new password: "); + String pass1 = sc.nextLine(); + System.out.print("Enter your new password again: "); + String pass2 = sc.nextLine(); + + // Ensures both new passwords match + if (pass1.equals(pass2)) { + person.setPassword(pass1); + } else { + System.out.println("Passwords did not match."); + } + + } else { + System.out.println("Invalid password."); + } + break; + + // Exits settings menu + case 0: + System.out.println("\nExiting settings..."); + break; + + // Handles invalid selections + default: + System.out.println("\nPlease select a valid option."); + } + + } while (input != 0); + } + + // Creates a workout and collects exercise information from the user + public static Workout createWorkout(Scanner scanner) { + + System.out.println("Create New Workout"); + + // Collects workout duration + System.out.print("Enter workout duration: "); + String duration = scanner.nextLine(); + + // Records workout date automatically + Date date = new Date(); + + // Initializes workout object + Workout workout = new Workout(duration); + + // Asks how many exercises to log + System.out.print("How many exercises are in this workout? "); + int exerciseCount = scanner.nextInt(); + scanner.nextLine(); + + // Loops through and collects each exercise + for (int i = 0; i < exerciseCount; i++) { + + // Collects exercise name + System.out.println("\nExercise " + (i + 1)); + System.out.print("Enter exercise name: "); + String name = scanner.nextLine(); + + // Collects calories burned + System.out.print("Enter calories burned: "); + int calories = scanner.nextInt(); + scanner.nextLine(); + + // Displays exercise type options + System.out.println("Select Exercise Type:"); + ExerciseType[] types = ExerciseType.values(); + + for (int j = 0; j < types.length; j++) { + System.out.println((j + 1) + ". " + types[j]); + } + + // Converts user selection to enum value + int typeChoice = scanner.nextInt(); + scanner.nextLine(); + ExerciseType selectedType = types[typeChoice - 1]; + + // Creates and adds exercise to workout + Exercise exercise = new Exercise(selectedType, name, calories); + workout.addExercise(exercise); + } + + return workout; + } + + // Validates login credentials and returns the matching user + public static Person validateLogin(Scanner sc) { + + // Requests email from user + String email = validateEmail(sc); + + // Requests password from user + System.out.print("Enter your password: "); + String password = sc.nextLine(); + + // Searches through registered users + for (Person user : FitnessApp.getUsers()) { + if (user.getEmail().equals(email) && user.getPassword().equals(password)) { + return user; + } + } + + // Returns null if login fails + return null; + } + + // Validates name input using a regex pattern + public static String validateName(Scanner sc) { + + while (true) { + + System.out.print("\nEnter a name: "); + String name = sc.nextLine(); + + // Checks name formatting rules + if (name.matches("^[a-zA-Z]+([ '-][a-zA-Z]+)*$")) { + return name; + } else { + System.out.println("\nInvalid name. Please use letters only."); + } + } + } + + // Validates email format using regex + public static String validateEmail(Scanner sc) { + + while (true) { + + System.out.print("Enter your email address?: "); + String email = sc.nextLine(); + + try { + // Validates email pattern + if (!email.matches("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$")) { + throw new IllegalArgumentException(); + } else { + return email; + } + + } catch (IllegalArgumentException e) { + System.out.println("\nInvalid email address. Try again.\n"); + } + } + } + + // Returns the list of registered users + public static ArrayList getUsers() { + return users; + } + + // Displays all the workouts the user has done + public static void displayAllWorkouts(Person person){ + // If there are no workouts completed display a message + if (person.getWorkouts().size() ==0){ + System.out.println("No workouts have been completed"); + } + + // Cycles through the list of workouts to display the data of each workout + for (Workout workout: person.getWorkouts()){ + workout.displayWorkoutInfo(); + } + } + + // Finds the person with the highest value in the map + public static Person getMaxKey(Map map) { + int maxExercises = -1; + Person maxPerson = null; + + for (Person person : map.keySet()) { + if (map.get(person) > maxExercises) { + maxExercises = map.get(person); + maxPerson = person; + } + } + + return maxPerson; + } + + // Counts total number of exercises completed by a person + public static int countExercises(Person person) { + int count = 0; + + for (Workout workout : person.getWorkouts()) { + count += workout.getExercises().size(); + } + + return count; + } + + // Counts total calories burned by a person across all workouts + public static int countCaloriesBurned(Person person) { + int count = 0; + + // Retrieves the user's workouts + ArrayList workouts = person.getWorkouts(); + + for (Workout workout : workouts) { + for (Exercise exercise : workout.getExercises()) { + count += exercise.getCaloriesBurned(); + } + } + + return count; + } + + // Builds a sorted leaderboard from a map of people and scores + public static ArrayList buildLeaderboard(Map map) { + ArrayList leaderboard = new ArrayList<>(); + + // Repeatedly finds the max value and removes it + while (!map.isEmpty()) { + Person maxPerson = getMaxKey(map); + leaderboard.add(maxPerson); + map.remove(maxPerson); + } + + return leaderboard; + } + + // Displays leaderboard ranked by total number of exercises + public static void rankByTotalExercises(ArrayList people){ + + // Stores people and their exercise totals + Map map = new LinkedHashMap<>(); + + for (Person person : people) { + int totalExercises = countExercises(person); + map.put(person, totalExercises); + } + + // Copies map so original data is preserved + Map copy = new LinkedHashMap<>(map); + + // Builds sorted leaderboard + ArrayList leaderboard = buildLeaderboard(copy); + + int rank = 1; + + System.out.println("\n****Leaderboard****"); + System.out.println("Ranked by total amount of exercises: "); + + // Displays ranked results + for (Person person : leaderboard) { + System.out.println(rank + ". " + person.getName() + + " completed " + map.get(person) + " exercises"); + rank++; + } + } + + // Displays leaderboard ranked by total calories burned + public static void rankByTotalCaloriesBurned(ArrayList people){ + + // Stores people and their calorie totals + Map map = new LinkedHashMap<>(); + + for (Person person : people) { + int totalCalories = countCaloriesBurned(person); + map.put(person, totalCalories); + } + + // Copies map so original data remains unchanged + Map copy = new LinkedHashMap<>(map); + + // Builds sorted leaderboard + ArrayList leaderboard = buildLeaderboard(copy); + + int rank = 1; + + System.out.println("\n****Leaderboard****"); + System.out.println("Ranked by total amount of calories burned: "); + + // Displays ranked results + for (Person person : leaderboard) { + System.out.println(rank + ". " + person.getName() + + " burned " + map.get(person) + " calories"); + rank++; + } + } + + // Displays both leaderboard rankings + public static void displayLeaderboard(ArrayList people) { + + // Shows ranking by total exercises + rankByTotalExercises(people); + + // Shows ranking by calories burned + rankByTotalCaloriesBurned(people); + } +} \ No newline at end of file diff --git a/src/main/java/org/codedifferently/Main.java b/src/main/java/org/codedifferently/Main.java index 435139b..f588bad 100644 --- a/src/main/java/org/codedifferently/Main.java +++ b/src/main/java/org/codedifferently/Main.java @@ -1,17 +1,8 @@ package org.codedifferently; +import java.util.Scanner; -//TIP To Run code, press or -// click the icon in the gutter. public class Main { public static void main(String[] args) { - //TIP Press with your caret at the highlighted text - // to see how IntelliJ IDEA suggests fixing it. - System.out.printf("Hello and welcome!"); - - for (int i = 1; i <= 5; i++) { - //TIP Press to start debugging your code. We have set one breakpoint - // for you, but you can always add more by pressing . - System.out.println("i = " + i); - } + FitnessApp.startApp(new Scanner(System.in)); } } \ No newline at end of file diff --git a/src/main/java/org/codedifferently/Person.java b/src/main/java/org/codedifferently/Person.java new file mode 100644 index 0000000..ed03d7b --- /dev/null +++ b/src/main/java/org/codedifferently/Person.java @@ -0,0 +1,80 @@ +package org.codedifferently; + +import java.util.ArrayList; + +// Represents a user of the fitness application +public class Person { + + // Stores the person's name + private String name; + + // Stores the person's email used for identification/login + private String email; + + // Stores the person's password for authentication + private String password; + + // Tracks the workout the user is currently performing (null if none) + private Workout activeWorkout = null; + + // Stores all workouts associated with this user using a dynamic list + private ArrayList workouts; + + // Constructor initializes user information and an empty workout list + public Person(String name, String email, String password) { + this.name = name; + this.email = email; + this.password = password; + this.workouts = new ArrayList<>(); + } + + // Returns the person's name + public String getName() { + return name; + } + + // Updates the person's name + public void setName(String name) { + this.name = name; + } + + // Returns the person's email + public String getEmail() { + return email; + } + + // Updates the person's email + public void setEmail(String email) { + this.email = email; + } + + // Returns the list of workouts associated with the user + public ArrayList getWorkouts() { + return workouts; + } + + // Replaces the user's workout list + public void setWorkouts(ArrayList workouts) { + this.workouts = workouts; + } + + // Returns the user's password + public String getPassword() { + return password; + } + + // Updates the user's password + public void setPassword(String password) { + this.password = password; + } + + // Returns the workout currently being performed + public Workout getActiveWorkout() { + return activeWorkout; + } + + // Sets the user's current active workout + public void setActiveWorkout(Workout activeWorkout) { + this.activeWorkout = activeWorkout; + } +} \ No newline at end of file diff --git a/src/main/java/org/codedifferently/README.md b/src/main/java/org/codedifferently/README.md new file mode 100644 index 0000000..39bb9cc --- /dev/null +++ b/src/main/java/org/codedifferently/README.md @@ -0,0 +1,31 @@ +# Sprint Documentation + +## Sprint 1 — The Plan +Our project was originally designed to be a real-time workout tracking application that would allow users to track their exercises as they performed them. The goal was to help individuals monitor their workouts live, organize their routines, and keep track of their physical activity as it happened. +To support this functionality, we planned several core features. + +Users would be able to create workouts, add exercises to those workouts, and track their progress in real time. Exercises would be categorized using an enumeration to organize different types of workouts and improve the structure of the application. +The program was designed to include several main classes: FitnessApplication, Exercise, Person, and Workout. Each class represents a different part of the system, helping to organize user data, exercises, and workout sessions. Additionally, an ExerciseType enumeration was created to represent different categories of exercises. + +Work for this sprint was divided between both team members. Jayden implemented the Person class, FitnessApplication menu system and created the ExerciseType enumeration. Bryant implemented the Exercise, and Workout classes. + +## Sprint 2 - The Build + +During this sprint, we implemented the core structure of the fitness application. The program allows users to log workouts through a menu-based interface where they can create workout entries and associate exercises with them. Key classes such as FitnessApplication, Exercise, Person, and Workout were developed, along with the ExerciseType enumeration to categorize exercises. + +One major change from our original plan was shifting from a real-time workout tracker to a workout logging application. The initial concept required features like timers and more complex state management. Due to time constraints, we simplified the scope so users could log workouts after completing them rather than track them live. + +The main challenge was attempting to implement the more complex tracking functionality within the limited sprint timeframe. After recognizing that it would require significantly more time and testing, we adjusted the project scope and focused on building a reliable workout logger. + +By narrowing the scope, we were able to complete a functional application while still achieving the core goal of helping users organize and record workouts. This adjustment reflects a common reality in software development, where project plans often evolve to ensure a working product is delivered within constraints. + +## Sprint 3 — The Reflection +Several aspects of our program work well. The code is well organized and not overly complicated, with logic divided into multiple functions to keep responsibilities clear and manageable. The application also handles different types of user input without crashing, which helps maintain stability during use. + +With more time, we would improve the leaderboard system. Currently, it only ranks user progress in two ways, but we would expand it to include additional metrics to provide a more detailed view of user performance. + +The Java concepts we used most frequently were encapsulation, conditionals, and loops. Conditionals and loops were especially important for building the menu system and iterating through collections to manage workouts and exercises. + +One major takeaway from this project was the importance of coordination and communication when working in a team. Creating a clear plan before coding helped keep everyone aligned and reduced misunderstandings. We also learned that tools like diagrams are helpful for outlining the structure of a program before implementation. + +Overall, this experience helped us better understand both collaborative development and practical Java programming. \ No newline at end of file diff --git a/src/main/java/org/codedifferently/Workout.java b/src/main/java/org/codedifferently/Workout.java new file mode 100644 index 0000000..8e3e306 --- /dev/null +++ b/src/main/java/org/codedifferently/Workout.java @@ -0,0 +1,57 @@ +package org.codedifferently; + +import java.util.ArrayList; +import java.util.Date; + +// Class that represents a workout session +public class Workout { + + // How long the workout lasted (ex: "30 minutes") + private String duration; + + // List that stores all exercises done in this workout + private ArrayList exercises; + + // Constructor used to create a new Workout object + public Workout(String duration) { + this.duration = duration; // set workout duration + exercises = new ArrayList<>(); // create an empty list of exercises + } + + // Returns the workout duration + public String getDuration() { + return duration; + } + + // Updates the workout duration + public void setDuration(String duration) { + this.duration = duration; + } + + // Returns the list of exercises in this workout + public ArrayList getExercises() { + return exercises; + } + + // Adds a new exercise to the workout + public void addExercise(Exercise exercise){ + exercises.add(exercise); + } + + // Displays the workout information and all exercises in it + public void displayWorkoutInfo(){ + + System.out.println("******************************"); + + // Print workout duration + System.out.println("Duration: " + duration); + + // Loop through all exercises and print them + for (Exercise exercise : exercises) { + exercise.displayExerciseInfo(); + } + + System.out.println("******************************"); + } + +} \ No newline at end of file