├── .mvn ├── maven.config └── jvm.config ├── .github ├── workflows └── ci.yml ├── .gitignore ├── tests ├── placeholder.txt ├── coverage_report.md ├── CreationalPatternsTest.java └── InMemoryRepositoryTest.java ├── factories ├── placeholder.txt └── RepositoryFactory.java ├── repositories ├── placeholder.txt ├── inmemory │ ├── placeholder.txt │ ├── InMemoryUserRepository.java │ └── InMemoryWorkoutRepository.java ├── WorkoutRepository.java ├── UserRepository.java ├── Repository.java └── database │ └── DatabaseUserRepository.java ├── creational_patterns ├── placeholder.txt ├── DatabaseConnection.java ├── WorkoutFactory.java ├── DataPrototype.java ├── DataProcessorFactory.java ├── GoalBuilder.java └── UIFactory.java ├── test ├── main │ └── java │ │ └── com │ │ └── fitness │ │ └── tracker │ │ └── api │ │ └── UserController.java └── java │ └── com │ └── fitness │ └── tracker │ ├── api │ ├── HelloWorldTest.java │ └── UserControllerTest.java │ └── service │ ├── GoalServiceTest.java │ ├── WorkoutServiceTest.java │ └── UserServiceTest.java ├── .vscode ├── launch.json └── Main.java ├── src ├── test │ └── java │ │ └── com │ │ └── fitness │ │ └── tracker │ │ ├── HealthCheckTest.java │ │ └── api │ │ ├── UserControllerTest.java │ │ ├── GoalControllerTest.java │ │ └── WorkoutControllerTest.java └── main │ ├── java │ └── com │ │ └── fitness │ │ └── tracker │ │ ├── repository │ │ ├── UserRepository.java │ │ ├── GoalRepository.java │ │ └── WorkoutRepository.java │ │ ├── RealTimeFitnessTrackerApplication.java │ │ ├── service │ │ ├── UserService.java │ │ ├── GoalService.java │ │ ├── WorkoutService.java │ │ └── UserServiceImpl.java │ │ ├── model │ │ └── User.java │ │ ├── config │ │ └── WebConfig.java │ │ ├── domain │ │ ├── Goal.java │ │ └── Workout.java │ │ └── api │ │ ├── GoalController.java │ │ └── WorkoutController.java │ ├── webapp │ └── WebConfig.java │ └── resources │ └── application.properties ├── docs ├── ROADMAP.md ├── CHANGELOG.md ├── LICENSE.md ├── assignment8_reflection.md ├── CONTRIBUTING.md ├── domain_model.md ├── template_analysis.md ├── SPECIFICATION.md ├── STAKEHOLDER_ANALYSIS.md ├── kanban_explanation.md ├── ARCHITECTURE.md ├── assignment9_reflection.md ├── REFLECTION.md ├── activity_diagrams.md ├── SYSTEM_REQUIREMENTS.md ├── state_transition_diagrams.md ├── class_diagram.md ├── openapi.yaml ├── README.md ├── USE_CASES_AND_TESTS.md └── AGILE_PLANNING.md ├── com.fitness.tracker └── pom.xml ├── pom.xml └── README.md /.mvn/maven.config: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/workflows: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | -------------------------------------------------------------------------------- /tests/placeholder.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /factories/placeholder.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /repositories/placeholder.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /creational_patterns/placeholder.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /repositories/inmemory/placeholder.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.mvn/jvm.config: -------------------------------------------------------------------------------- 1 | --enable-native-access=ALL-UNNAMED 2 | -------------------------------------------------------------------------------- /repositories/WorkoutRepository.java: -------------------------------------------------------------------------------- 1 | package com.fitness.tracker.repositories; 2 | 3 | import com.fitness.tracker.Workout; 4 | 5 | public interface WorkoutRepository extends Repository { 6 | // Add custom methods if needed 7 | } 8 | -------------------------------------------------------------------------------- /repositories/UserRepository.java: -------------------------------------------------------------------------------- 1 | package com.fitness.tracker.repositories; 2 | 3 | import com.fitness.tracker.User; 4 | 5 | public interface UserRepository extends Repository { 6 | // Add custom methods if needed (e.g., findByEmail) 7 | } 8 | -------------------------------------------------------------------------------- /test/main/java/com/fitness/tracker/api/UserController.java: -------------------------------------------------------------------------------- 1 | public class UserControllerTest { 2 | @org.junit.jupiter.api.Test 3 | void helloWorldTest() { 4 | org.junit.jupiter.api.Assertions.assertEquals("Hello, World!", "Hello, World!"); 5 | } 6 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "java", 6 | "name": "Launch Main", 7 | "request": "launch", 8 | "mainClass": "com.fitness.tracker.Main" 9 | "projectName": "RealTimeFitnessTracker" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /src/test/java/com/fitness/tracker/HealthCheckTest.java: -------------------------------------------------------------------------------- 1 | import org.junit.jupiter.api.Test; 2 | import org.springframework.boot.test.context.SpringBootTest; 3 | 4 | @SpringBootTest 5 | public class HealthCheckTest { 6 | @Test 7 | void applicationStartsCorrectly() { 8 | // Context loading test 9 | } 10 | } -------------------------------------------------------------------------------- /repositories/Repository.java: -------------------------------------------------------------------------------- 1 | package com.fitness.tracker.repositories; 2 | 3 | import java.util.List; 4 | import java.util.Optional; 5 | 6 | public interface Repository { 7 | void save(T entity); // Create/Update 8 | Optional findById(ID id); // Read 9 | List findAll(); // Read All 10 | void delete(ID id); // Delete 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/fitness/tracker/repository/UserRepository.java: -------------------------------------------------------------------------------- 1 | // File: src/main/java/com/fitness/tracker/repository/UserRepository.java 2 | package com.fitness.tracker.repository; 3 | 4 | import com.fitness.tracker.model.User; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | 7 | public interface UserRepository extends JpaRepository { 8 | } -------------------------------------------------------------------------------- /test/java/com/fitness/tracker/api/HelloWorldTest.java: -------------------------------------------------------------------------------- 1 | import org.junit.jupiter.api.Test; 2 | import static org.junit.jupiter.api.Assertions.assertEquals; 3 | 4 | public class HelloWorldTest { 5 | 6 | @Test 7 | public void testHelloWorld() { 8 | HelloWorld helloWorld = new HelloWorld(); 9 | assertEquals("Hello, World!", helloWorld.greet()); 10 | } 11 | } -------------------------------------------------------------------------------- /src/main/java/com/fitness/tracker/RealTimeFitnessTrackerApplication.java: -------------------------------------------------------------------------------- 1 | package com.fitness.tracker; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class RealTimeFitnessTrackerApplication { 8 | public static void main(String[] args) { 9 | SpringApplication.run(RealTimeFitnessTrackerApplication.class, args); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/fitness/tracker/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.fitness.tracker.service; 2 | 3 | import com.fitness.tracker.model.User; 4 | import com.fitness.tracker.service.UserService; 5 | import java.util.List; 6 | 7 | public interface UserService { 8 | User createUser(User user); 9 | List getAllUsers(); 10 | User getUserById(Long id); 11 | User updateUser(User user); 12 | void deleteUser(Long id); // This was likely missing in your implementation 13 | } -------------------------------------------------------------------------------- /creational_patterns/DatabaseConnection.java: -------------------------------------------------------------------------------- 1 | package com.fitness.tracker.patterns; 2 | 3 | public class DatabaseConnection { 4 | private static DatabaseConnection instance; 5 | 6 | private DatabaseConnection() {} 7 | 8 | public static synchronized DatabaseConnection getInstance() { 9 | if (instance == null) { 10 | instance = new DatabaseConnection(); 11 | } 12 | return instance; 13 | } 14 | 15 | public void connect() { /* Connect to DB */ } 16 | } 17 | -------------------------------------------------------------------------------- /tests/coverage_report.md: -------------------------------------------------------------------------------- 1 | # coverage_report.md 2 | Simulated JaCoCo Report: 3 | - WorkoutFactory: 90% (createWorkout tested, edge case missing) 4 | - DataProcessorFactory: 85% (HeartRateProcessor tested, CalorieProcessor partial) 5 | - UIFactory: 100% (Mobile/Web factories fully tested) 6 | - GoalBuilder: 95% (All attributes tested, null deadline not covered) 7 | - HeartRate (Prototype): 90% (Clone tested, timestamp edge case missing) 8 | - DatabaseConnection: 100% (Singleton instance verified) 9 | Overall: ~93% 10 | -------------------------------------------------------------------------------- /test/java/com/fitness/tracker/api/UserControllerTest.java: -------------------------------------------------------------------------------- 1 | package com.fitness.tracker.api; 2 | 3 | import org.springframework.web.bind.annotation.*; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | 6 | @RestController 7 | @RequestMapping("/api/users") 8 | public class UserController { 9 | 10 | // Controller methods go here inside the class 11 | 12 | @GetMapping 13 | public String getAllUsers() { 14 | return "All users"; 15 | } 16 | 17 | // Other endpoint methods... 18 | } -------------------------------------------------------------------------------- /docs/ROADMAP.md: -------------------------------------------------------------------------------- 1 | # Roadmap for RealTime FitnessTracker 2 | 3 | This roadmap outlines future features for **RealTimeFitnessTracker**. 4 | 5 | ## Planned Features 6 | - **Q2 2025**: Integrate WebSocket for real-time heart rate updates. 7 | - **Q3 2025**: Add Fitbit API integration for syncing steps and sleep data. 8 | - **Q4 2025**: Implement calorie tracking with a food logging feature. 9 | - **TBD**: Support offline mode for workout tracking. 10 | 11 | ## Contributing 12 | See [CONTRIBUTING.md](CONTRIBUTING.md) to help implement these features! 13 | -------------------------------------------------------------------------------- /.vscode/Main.java: -------------------------------------------------------------------------------- 1 | package com.fitness.tracker; 2 | 3 | import com.fitness.tracker.patterns.WorkoutFactory; 4 | 5 | public class Main { 6 | public static void main(String[] args) { 7 | // Example: Use Simple Factory to create a Workout 8 | WorkoutFactory factory = new WorkoutFactory(); 9 | Workout workout = factory.createWorkout("cardio", "W1"); 10 | workout.start(); 11 | System.out.println("Workout started: " + workout.getWorkoutId()); 12 | 13 | // Add more test code (e.g., create User, Goal, etc.) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/fitness/tracker/repository/GoalRepository.java: -------------------------------------------------------------------------------- 1 | package com.fitness.tracker.repository; 2 | 3 | import com.fitness.tracker.domain.Goal; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | import java.util.Optional; 7 | 8 | public class GoalRepository { 9 | private final Map goals = new HashMap<>(); 10 | 11 | public Goal save(Goal goal) { 12 | goals.put(goal.getGoalId(), goal); 13 | return goal; 14 | } 15 | 16 | public Optional findById(String goalId) { 17 | return Optional.ofNullable(goals.get(goalId)); 18 | } 19 | } -------------------------------------------------------------------------------- /src/main/java/com/fitness/tracker/repository/WorkoutRepository.java: -------------------------------------------------------------------------------- 1 | package com.fitness.tracker.repository; 2 | 3 | import com.fitness.tracker.domain.Workout; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | import java.util.Optional; 7 | 8 | public class WorkoutRepository { 9 | private final Map workouts = new HashMap<>(); 10 | 11 | public Workout save(Workout workout) { 12 | workouts.put(workout.getWorkoutId(), workout); 13 | return workout; 14 | } 15 | 16 | public Optional findById(String workoutId) { 17 | return Optional.ofNullable(workouts.get(workoutId)); 18 | } 19 | } -------------------------------------------------------------------------------- /creational_patterns/WorkoutFactory.java: -------------------------------------------------------------------------------- 1 | package com.fitness.tracker.patterns; 2 | 3 | public class WorkoutFactory { 4 | public Workout createWorkout(String type, String workoutId) { 5 | switch (type.toLowerCase()) { 6 | case "cardio": return new CardioWorkout(workoutId); 7 | case "strength": return new StrengthWorkout(workoutId); 8 | default: throw new IllegalArgumentException("Unknown workout type"); 9 | } 10 | } 11 | } 12 | 13 | class CardioWorkout extends Workout { 14 | public CardioWorkout(String workoutId) { super(workoutId); } 15 | } 16 | 17 | class StrengthWorkout extends Workout { 18 | public StrengthWorkout(String workoutId) { super(workoutId); } 19 | } 20 | -------------------------------------------------------------------------------- /creational_patterns/DataPrototype.java: -------------------------------------------------------------------------------- 1 | package com.fitness.tracker.patterns; 2 | 3 | import java.time.LocalDateTime; 4 | 5 | public class HeartRate implements Cloneable { 6 | private String hrId; 7 | private int bpm; 8 | private LocalDateTime timestamp; 9 | 10 | public HeartRate(String hrId, int bpm) { 11 | this.hrId = hrId; 12 | this.bpm = bpm; 13 | this.timestamp = LocalDateTime.now(); 14 | } 15 | 16 | @Override 17 | public HeartRate clone() { 18 | try { 19 | return (HeartRate) super.clone(); 20 | } catch (CloneNotSupportedException e) { 21 | return null; 22 | } 23 | } 24 | 25 | // Getters 26 | public int getBPM() { return bpm; } 27 | } 28 | -------------------------------------------------------------------------------- /creational_patterns/DataProcessorFactory.java: -------------------------------------------------------------------------------- 1 | package com.fitness.tracker.patterns; 2 | 3 | public abstract class DataProcessorFactory { 4 | public abstract DataProcessor createProcessor(); 5 | } 6 | 7 | class HeartRateProcessorFactory extends DataProcessorFactory { 8 | @Override 9 | public DataProcessor createProcessor() { return new HeartRateProcessor(); } 10 | } 11 | 12 | class CalorieProcessorFactory extends DataProcessorFactory { 13 | @Override 14 | public DataProcessor createProcessor() { return new CalorieProcessor(); } 15 | } 16 | 17 | interface DataProcessor { 18 | void process(); 19 | } 20 | 21 | class HeartRateProcessor implements DataProcessor { 22 | public void process() { /* Process heart rate */ } 23 | } 24 | 25 | class CalorieProcessor implements DataProcessor { 26 | public void process() { /* Process calories */ } 27 | } 28 | -------------------------------------------------------------------------------- /creational_patterns/GoalBuilder.java: -------------------------------------------------------------------------------- 1 | package com.fitness.tracker.patterns; 2 | 3 | import java.util.Date; 4 | 5 | public class GoalBuilder { 6 | private String goalId; 7 | private float target; 8 | private String type; 9 | private Date deadline; 10 | 11 | public GoalBuilder setGoalId(String goalId) { 12 | this.goalId = goalId; 13 | return this; 14 | } 15 | 16 | public GoalBuilder setTarget(float target) { 17 | this.target = target; 18 | return this; 19 | } 20 | 21 | public GoalBuilder setType(String type) { 22 | this.type = type; 23 | return this; 24 | } 25 | 26 | public GoalBuilder setDeadline(Date deadline) { 27 | this.deadline = deadline; 28 | return this; 29 | } 30 | 31 | public Goal build() { 32 | return new Goal(goalId, target, type, deadline); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /creational_patterns/UIFactory.java: -------------------------------------------------------------------------------- 1 | package com.fitness.tracker.patterns; 2 | 3 | public interface UIFactory { 4 | Button createButton(); 5 | Display createDisplay(); 6 | } 7 | 8 | class MobileUIFactory implements UIFactory { 9 | public Button createButton() { return new MobileButton(); } 10 | public Display createDisplay() { return new MobileDisplay(); } 11 | } 12 | 13 | class WebUIFactory implements UIFactory { 14 | public Button createButton() { return new WebButton(); } 15 | public Display createDisplay() { return new WebDisplay(); } 16 | } 17 | 18 | interface Button { void render(); } 19 | interface Display { void show(); } 20 | 21 | class MobileButton implements Button { public void render() {} } 22 | class MobileDisplay implements Display { public void show() {} } 23 | class WebButton implements Button { public void render() {} } 24 | class WebDisplay implements Display { public void show() {} } 25 | -------------------------------------------------------------------------------- /src/main/java/com/fitness/tracker/model/User.java: -------------------------------------------------------------------------------- 1 | package com.fitness.tracker.model; 2 | 3 | public class User { 4 | private Long userId; 5 | private String name; 6 | private String email; 7 | 8 | public User() {} 9 | 10 | public User(Long userId, String name, String email) { 11 | this.userId = userId; 12 | this.name = name; 13 | this.email = email; 14 | } 15 | 16 | public Long getUserId() { 17 | return userId; 18 | } 19 | 20 | public String getName() { 21 | return name; 22 | } 23 | 24 | public String getEmail() { 25 | return email; 26 | } 27 | 28 | public void setUserId(Long userId) { 29 | this.userId = userId; 30 | } 31 | 32 | public void setName(String name) { 33 | this.name = name; 34 | } 35 | 36 | public void setEmail(String email) { 37 | this.email = email; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /docs/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG.md 2 | 3 | ## [Assignment 10] - 2025-04-18 4 | - Added /src with User, Workout, HeartRate, Calorie, Goal, Device, Log classes. 5 | - Implemented 6 creational patterns in /creational_patterns. 6 | - Added unit tests in /tests with ~93% coverage. 7 | - Created issues #15-#20 for pattern tasks and bugs. 8 | - Moved completed tasks to Done on Sprint 1 Kanban. 9 | 10 | - ## [Assignment 11] - 2025-04-22 11 | - Added /repositories with Repository, UserRepository, WorkoutRepository interfaces (#36-#38). 12 | - Implemented InMemoryUserRepository, InMemoryWorkoutRepository in /repositories/inmemory (#40-#41). 13 | - Added unit tests for in-memory repositories in /tests (#42). 14 | - Created RepositoryFactory in /factories for storage abstraction (#43). 15 | - Added DatabaseUserRepository stub and updated class_diagram.md (#44-#45). 16 | - Documented in README.md (#39). 17 | - Created issues #36-#45, moved to Done on Sprint 1 Kanban. 18 | -------------------------------------------------------------------------------- /repositories/inmemory/InMemoryUserRepository.java: -------------------------------------------------------------------------------- 1 | package com.fitness.tracker.repositories.inmemory; 2 | 3 | import com.fitness.tracker.User; 4 | import com.fitness.tracker.repositories.UserRepository; 5 | import java.util.ArrayList; 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | import java.util.Optional; 10 | 11 | public class InMemoryUserRepository implements UserRepository { 12 | private final Map storage = new HashMap<>(); 13 | 14 | @Override 15 | public void save(User user) { 16 | storage.put(user.getUserId(), user); 17 | } 18 | 19 | @Override 20 | public Optional findById(String id) { 21 | return Optional.ofNullable(storage.get(id)); 22 | } 23 | 24 | @Override 25 | public List findAll() { 26 | return new ArrayList<>(storage.values()); 27 | } 28 | 29 | @Override 30 | public void delete(String id) { 31 | storage.remove(id); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/test/java/com/fitness/tracker/api/UserControllerTest.java: -------------------------------------------------------------------------------- 1 | package com.fitness.tracker.api; 2 | 3 | import com.fitness.tracker.model.User; 4 | import com.fitness.tracker.service.UserService; 5 | 6 | import org.junit.jupiter.api.Test; 7 | import org.mockito.Mockito; 8 | 9 | import static org.junit.jupiter.api.Assertions.assertEquals; 10 | 11 | public class UserControllerTest { 12 | 13 | @Test 14 | public void testUserCreationAndServiceCall() { 15 | // Arrange 16 | UserService userService = Mockito.mock(UserService.class); 17 | 18 | User user = new User(123L, "John Doe", "john@example.com"); 19 | Mockito.when(userService.getUserById(123L)).thenReturn(user); 20 | 21 | // Act 22 | User result = userService.getUserById(123L); 23 | 24 | // Assert 25 | assertEquals(123L, result.getUserId()); 26 | assertEquals("John Doe", result.getName()); 27 | assertEquals("john@example.com", result.getEmail()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /repositories/inmemory/InMemoryWorkoutRepository.java: -------------------------------------------------------------------------------- 1 | package com.fitness.tracker.repositories.inmemory; 2 | 3 | import com.fitness.tracker.Workout; 4 | import com.fitness.tracker.repositories.WorkoutRepository; 5 | import java.util.ArrayList; 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.Optional; 9 | 10 | public class InMemoryWorkoutRepository implements WorkoutRepository { 11 | private final Map storage = new HashMap<>(); 12 | 13 | @Override 14 | public void save(Workout workout) { 15 | storage.put(workout.getWorkoutId(), workout); 16 | } 17 | 18 | @Override 19 | public Optional findById(String id) { 20 | return Optional.ofNullable(storage.get(id)); 21 | } 22 | 23 | @Override 24 | public List findAll() { 25 | return new ArrayList<>(storage.values()); 26 | } 27 | 28 | @Override 29 | public void delete(String id) { 30 | storage.remove(id); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /docs/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 [Noncedo Qanda] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | -------------------------------------------------------------------------------- /src/main/java/com/fitness/tracker/service/GoalService.java: -------------------------------------------------------------------------------- 1 | 2 | package com.fitness.tracker.service; 3 | 4 | import com.fitness.tracker.domain.Goal; 5 | import com.fitness.tracker.repository.GoalRepository; 6 | import org.springframework.stereotype.Service; 7 | import java.util.Optional; 8 | 9 | @Service 10 | public class GoalService { 11 | private final GoalRepository goalRepository; 12 | 13 | public GoalService(GoalRepository goalRepository) { 14 | this.goalRepository = goalRepository; 15 | } 16 | 17 | public Goal createGoal(Goal goal) { 18 | if (goal.getDescription() == null || goal.getDescription().isEmpty()) { 19 | throw new IllegalArgumentException("Goal description is required"); 20 | } 21 | return goalRepository.save(goal); 22 | } 23 | 24 | public Goal getGoalById(String goalId) { 25 | Optional goal = goalRepository.findById(goalId); 26 | return goal.orElse(null); 27 | } 28 | } -------------------------------------------------------------------------------- /repositories/database/DatabaseUserRepository.java: -------------------------------------------------------------------------------- 1 | package com.fitness.tracker.repositories.database; 2 | 3 | import com.fitness.tracker.User; 4 | import com.fitness.tracker.repositories.UserRepository; 5 | import java.util.List; 6 | import java.util.Optional; 7 | 8 | public class DatabaseUserRepository implements UserRepository { 9 | public DatabaseUserRepository() { 10 | // Initialize DB connection (stub) 11 | } 12 | 13 | @Override 14 | public void save(User user) { 15 | throw new UnsupportedOperationException("Database storage not implemented"); 16 | } 17 | 18 | @Override 19 | public Optional findById(String id) { 20 | throw new UnsupportedOperationException("Database storage not implemented"); 21 | } 22 | 23 | @Override 24 | public List findAll() { 25 | throw new UnsupportedOperationException("Database storage not implemented"); 26 | } 27 | 28 | @Override 29 | public void delete(String id) { 30 | throw new UnsupportedOperationException("Database storage not implemented"); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/webapp/WebConfig.java: -------------------------------------------------------------------------------- 1 | package com.fitness.tracker.config; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; 5 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 6 | 7 | @Configuration 8 | public class WebConfig implements WebMvcConfigurer { 9 | 10 | @Override 11 | public void addResourceHandlers(ResourceHandlerRegistry registry) { 12 | // Serve static resources from src/main/resources/static 13 | registry.addResourceHandler("/static/**") 14 | .addResourceLocations("classpath:/static/"); 15 | 16 | // Serve templates (e.g., Thymeleaf) from src/main/resources/templates 17 | registry.addResourceHandler("/templates/**") 18 | .addResourceLocations("classpath:/templates/"); 19 | 20 | // Optionally serve resources from src/main/webapp (uncomment if needed) 21 | // registry.addResourceHandler("/webapp/**") 22 | // .addResourceLocations("file:src/main/webapp/"); 23 | } 24 | } -------------------------------------------------------------------------------- /src/main/java/com/fitness/tracker/service/WorkoutService.java: -------------------------------------------------------------------------------- 1 | package com.fitness.tracker.service; 2 | 3 | import com.fitness.tracker.domain.Workout; 4 | import com.fitness.tracker.repository.WorkoutRepository; 5 | import org.springframework.stereotype.Service; 6 | import java.util.Optional; 7 | 8 | @Service 9 | public class WorkoutService { 10 | private final WorkoutRepository workoutRepository; 11 | 12 | public WorkoutService(WorkoutRepository workoutRepository) { 13 | this.workoutRepository = workoutRepository; 14 | } 15 | 16 | public Workout createWorkout(Workout workout) { 17 | if (workout.getDuration() <= 0) { 18 | throw new IllegalArgumentException("Workout duration must be positive"); 19 | } 20 | return workoutRepository.save(workout); 21 | } 22 | 23 | public Workout getWorkoutById(String workoutId) { 24 | Optional workout = workoutRepository.findById(workoutId); 25 | return workout.orElse(null); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/fitness/tracker/config/WebConfig.java: -------------------------------------------------------------------------------- 1 | package com.fitness.tracker.config; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; 5 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 6 | 7 | @Configuration 8 | public class WebConfig implements WebMvcConfigurer { 9 | 10 | @Override 11 | public void addResourceHandlers(ResourceHandlerRegistry registry) { 12 | // Serve static resources from src/main/resources/static 13 | registry.addResourceHandler("/static/**") 14 | .addResourceLocations("classpath:/static/"); 15 | 16 | // Serve templates (e.g., Thymeleaf) from src/main/resources/templates 17 | registry.addResourceHandler("/templates/**") 18 | .addResourceLocations("classpath:/templates/"); 19 | 20 | // Optionally serve resources from src/main/webapp (uncomment if needed) 21 | // registry.addResourceHandler("/webapp/**") 22 | // .addResourceLocations("file:src/main/webapp/"); 23 | } 24 | } -------------------------------------------------------------------------------- /src/test/java/com/fitness/tracker/api/GoalControllerTest.java: -------------------------------------------------------------------------------- 1 | package com.fitness.tracker.api; 2 | 3 | import com.fitness.tracker.RealTimeFitnessTrackerApplication; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | import org.springframework.test.web.servlet.MockMvc; 8 | import org.springframework.test.web.servlet.setup.MockMvcBuilders; 9 | 10 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 11 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 12 | 13 | @SpringBootTest(classes = RealTimeFitnessTrackerApplication.class) 14 | public class GoalControllerTest { 15 | 16 | @Autowired 17 | private GoalController goalController; 18 | 19 | private MockMvc mockMvc; 20 | 21 | @Test 22 | public void getGoals_returnsOk() throws Exception { 23 | mockMvc = MockMvcBuilders.standaloneSetup(goalController).build(); 24 | 25 | mockMvc.perform(get("/api/goals")) 26 | .andExpect(status().isOk()); 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /src/test/java/com/fitness/tracker/api/WorkoutControllerTest.java: -------------------------------------------------------------------------------- 1 | package com.fitness.tracker.api; 2 | 3 | import com.fitness.tracker.RealTimeFitnessTrackerApplication; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | import org.springframework.test.web.servlet.MockMvc; 8 | import org.springframework.test.web.servlet.setup.MockMvcBuilders; 9 | 10 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 11 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 12 | 13 | @SpringBootTest(classes = RealTimeFitnessTrackerApplication.class) 14 | public class WorkoutControllerTest { 15 | 16 | @Autowired 17 | private WorkoutController workoutController; 18 | 19 | private MockMvc mockMvc; 20 | 21 | @Test 22 | public void getWorkoutById_existingWorkout_returnsWorkout() throws Exception { 23 | mockMvc = MockMvcBuilders.standaloneSetup(workoutController).build(); 24 | 25 | mockMvc.perform(get("/api/workouts/1")) 26 | .andExpect(status().isOk()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/fitness/tracker/service/UserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.fitness.tracker.service; 2 | 3 | import com.fitness.tracker.model.User; 4 | import com.fitness.tracker.repository.UserRepository; 5 | import org.springframework.stereotype.Service; 6 | 7 | import java.util.List; 8 | 9 | @Service 10 | public class UserServiceImpl implements UserService { 11 | 12 | private final UserRepository userRepository; 13 | 14 | public UserServiceImpl(UserRepository userRepository) { 15 | this.userRepository = userRepository; 16 | } 17 | 18 | @Override 19 | public User createUser(User user) { 20 | return userRepository.save(user); 21 | } 22 | 23 | @Override 24 | public List getAllUsers() { 25 | return userRepository.findAll(); 26 | } 27 | 28 | @Override 29 | public User getUserById(Long id) { 30 | return userRepository.findById(id).orElse(null); 31 | } 32 | 33 | @Override 34 | public User updateUser(User user) { 35 | return userRepository.save(user); 36 | } 37 | 38 | @Override 39 | public void deleteUser(Long id) { 40 | userRepository.deleteById(id); 41 | } 42 | } -------------------------------------------------------------------------------- /src/main/java/com/fitness/tracker/domain/Goal.java: -------------------------------------------------------------------------------- 1 | package com.fitness.tracker.domain; 2 | 3 | public class Goal { 4 | private String goalId; 5 | private String userId; 6 | private String description; 7 | private boolean achieved; 8 | 9 | public Goal() {} 10 | 11 | public Goal(String goalId, String userId, String description, boolean achieved) { 12 | this.goalId = goalId; 13 | this.userId = userId; 14 | this.description = description; 15 | this.achieved = achieved; 16 | } 17 | 18 | // Getters and Setters 19 | public String getGoalId() { return goalId; } 20 | public void setGoalId(String goalId) { this.goalId = goalId; } 21 | public String getUserId() { return userId; } 22 | public void setUserId(String userId) { this.userId = userId; } 23 | public String getDescription() { return description; } 24 | public void setDescription(String description) { this.description = description; } 25 | public boolean isAchieved() { return achieved; } 26 | public void setAchieved(boolean achieved) { this.achieved = achieved; } 27 | } -------------------------------------------------------------------------------- /src/main/java/com/fitness/tracker/api/GoalController.java: -------------------------------------------------------------------------------- 1 | package com.fitness.tracker.api; 2 | 3 | import org.springframework.http.ResponseEntity; 4 | import org.springframework.web.bind.annotation.*; 5 | 6 | import java.util.*; 7 | 8 | @RestController 9 | @RequestMapping("/api/goals") 10 | public class GoalController { 11 | 12 | // Simulated in-memory store 13 | private static final Map goals = new HashMap<>(); 14 | 15 | static { 16 | goals.put(1L, "Run 5km daily"); 17 | goals.put(2L, "Cycle 20km weekly"); 18 | } 19 | 20 | @GetMapping 21 | public ResponseEntity> getAllGoals() { 22 | return ResponseEntity.ok(new ArrayList<>(goals.values())); 23 | } 24 | 25 | @GetMapping("/{id}") 26 | public ResponseEntity getGoalById(@PathVariable Long id) { 27 | String goal = goals.get(id); 28 | if (goal != null) { 29 | return ResponseEntity.ok(goal); 30 | } else { 31 | return ResponseEntity.notFound().build(); 32 | } 33 | } 34 | 35 | @PostMapping 36 | public ResponseEntity createGoal(@RequestBody String goal) { 37 | long id = goals.size() + 1L; 38 | goals.put(id, goal); 39 | return ResponseEntity.status(201).body(goal); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /test/java/com/fitness/tracker/service/GoalServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.fitness.tracker.service; 2 | 3 | import com.fitness.tracker.domain.Goal; 4 | import com.fitness.tracker.repository.GoalRepository; 5 | import org.junit.jupiter.api.BeforeEach; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import static org.junit.jupiter.api.Assertions.*; 9 | 10 | class GoalServiceTest { 11 | private GoalService goalService; 12 | private GoalRepository goalRepository; 13 | 14 | @BeforeEach 15 | void setUp() { 16 | goalRepository = new GoalRepository(); 17 | goalService = new GoalService(goalRepository); 18 | } 19 | 20 | @Test 21 | void createGoal_validGoal_savesGoal() { 22 | Goal goal = new Goal("g1", "u1", "Run 5km", false); 23 | Goal savedGoal = goalService.createGoal(goal); 24 | assertEquals("g1", savedGoal.getGoalId()); 25 | assertEquals("Run 5km", savedGoal.getDescription()); 26 | } 27 | 28 | @Test 29 | void createGoal_emptyDescription_throwsException() { 30 | Goal goal = new Goal("g1", "u1", "", false); 31 | assertThrows(IllegalArgumentException.class, () -> goalService.createGoal(goal)); 32 | } 33 | } -------------------------------------------------------------------------------- /src/main/java/com/fitness/tracker/api/WorkoutController.java: -------------------------------------------------------------------------------- 1 | package com.fitness.tracker.api; 2 | 3 | import org.springframework.http.ResponseEntity; 4 | import org.springframework.web.bind.annotation.*; 5 | 6 | import java.util.*; 7 | 8 | @RestController 9 | @RequestMapping("/api/workouts") 10 | public class WorkoutController { 11 | 12 | private static final Map workouts = new HashMap<>(); 13 | 14 | static { 15 | workouts.put(1L, "Morning Yoga"); 16 | workouts.put(2L, "Evening Run"); 17 | } 18 | 19 | @GetMapping 20 | public ResponseEntity> getAllWorkouts() { 21 | return ResponseEntity.ok(new ArrayList<>(workouts.values())); 22 | } 23 | 24 | @GetMapping("/{id}") 25 | public ResponseEntity getWorkoutById(@PathVariable Long id) { 26 | String workout = workouts.get(id); 27 | if (workout != null) { 28 | return ResponseEntity.ok(workout); 29 | } else { 30 | return ResponseEntity.notFound().build(); 31 | } 32 | } 33 | 34 | @PostMapping 35 | public ResponseEntity createWorkout(@RequestBody String workout) { 36 | long id = workouts.size() + 1L; 37 | workouts.put(id, workout); 38 | return ResponseEntity.status(201).body(workout); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /factories/RepositoryFactory.java: -------------------------------------------------------------------------------- 1 | package com.fitness.tracker.factories; 2 | 3 | import com.fitness.tracker.repositories.UserRepository; 4 | import com.fitness.tracker.repositories.WorkoutRepository; 5 | import com.fitness.tracker.repositories.inmemory.InMemoryUserRepository; 6 | import com.fitness.tracker.repositories.inmemory.InMemoryWorkoutRepository; 7 | 8 | public class RepositoryFactory { 9 | public static UserRepository getUserRepository(String storageType) { 10 | switch (storageType) { 11 | case "MEMORY": 12 | return new InMemoryUserRepository(); 13 | case "DATABASE": 14 | throw new UnsupportedOperationException("Database not implemented"); 15 | default: 16 | throw new IllegalArgumentException("Invalid storage type: " + storageType); 17 | } 18 | } 19 | 20 | public static WorkoutRepository getWorkoutRepository(String storageType) { 21 | switch (storageType) { 22 | case "MEMORY": 23 | return new InMemoryWorkoutRepository(); 24 | case "DATABASE": 25 | throw new UnsupportedOperationException("Database not implemented"); 26 | default: 27 | throw new IllegalArgumentException("Invalid storage type: " + storageType); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /docs/assignment8_reflection.md: -------------------------------------------------------------------------------- 1 | # assignment8_reflection.md 2 | 3 | ## Challenges 4 | - **Granularity**: Picking states like "Paused" for Workout Session was tricky—too many states (e.g., "Paused," "Resumed") cluttered the diagram, but too few (just "Active") lost detail needed for FR-003. For workflows, balancing actions like "Validate input" vs. broader steps was hard to keep readable. 5 | - **Agile Alignment**: Matching state diagrams to user stories (e.g., US-001’s "Tracking" state) required breaking tasks like T-001 into clear transitions, which felt forced at times since Agile focuses on deliverables, not object states. 6 | 7 | ## State vs. Activity Diagrams 8 | - **State Diagrams**: Showed how objects like "Heart Rate Data" change (Untracked → Tracking), great for understanding lifecycles but rigid for processes. They tied well to FR-001’s tracking need. 9 | - **Activity Diagrams**: Mapped workflows like "Track Heart Rate" with decisions (e.g., device connected?), making user actions clearer but less focused on object states. They better suited stakeholder needs, such as real-time updates. 10 | 11 | ## Lessons Learned 12 | Splitting diagrams into multiple files fixed GitHub’s rendering issue—a practical workaround I’ll reuse. State diagrams helped design object behavior, while activity diagrams clarified user-system interactions, both essential for implementation. 13 | -------------------------------------------------------------------------------- /docs/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to RealTimeFitnessTracker 2 | 3 | Thank you for contributing to **RealTimeFitnessTracker**, a real-time fitness tracking app! Follow these steps to get started. 4 | 5 | ## Setup 6 | - **Prerequisites**: Install Node.js v18+, npm, and Git. For backend, install Python 3.10+ and pip. 7 | - **Installation**: 8 | 1. Fork this repository. 9 | 2. Clone your fork: `git clone https://github.com/your-username/RealTimeFitnessTracker.git` 10 | 3. Install frontend dependencies: `cd frontend && npm install` 11 | 4. Install backend dependencies: `cd backend && pip install -r requirements.txt` 12 | 5. Start the app: `npm run start` (frontend) and `python manage.py runserver` (backend) 13 | 14 | ## Coding Standards 15 | - Run ESLint for JavaScript: `npm run lint`. 16 | - Write unit tests using Jest: `npm test`. 17 | - Ensure test coverage >80%. 18 | - For Python, follow PEP8 and use Flake8: `flake8 .`. 19 | 20 | ## How to Contribute 21 | 1. Pick an issue labeled `good-first-issue` (e.g., UI fixes or test cases). 22 | 2. Comment on the issue to claim it. 23 | 3. Create a branch: `git checkout -b feature/issue-number` 24 | 4. Write code and tests. 25 | 5. Commit with a clear message: `git commit -m "Fix #issue-number: added heart rate display"` 26 | 6. Push to your fork: `git push origin feature/issue-number` 27 | 7. Submit a PR with a description: 28 | -------------------------------------------------------------------------------- /src/main/java/com/fitness/tracker/domain/Workout.java: -------------------------------------------------------------------------------- 1 | package com.fitness.tracker.domain; 2 | 3 | import java.time.LocalDateTime; 4 | 5 | public class Workout { 6 | private String workoutId; 7 | private String userId; 8 | private String type; 9 | private int duration; 10 | private LocalDateTime date; 11 | 12 | public Workout() {} 13 | 14 | public Workout(String workoutId, String userId, String type, int duration, LocalDateTime date) { 15 | this.workoutId = workoutId; 16 | this.userId = userId; 17 | this.type = type; 18 | this.duration = duration; 19 | this.date = date; 20 | } 21 | 22 | // Getters and Setters 23 | public String getWorkoutId() { return workoutId; } 24 | public void setWorkoutId(String workoutId) { this.workoutId = workoutId; } 25 | public String getUserId() { return userId; } 26 | public void setUserId(String userId) { this.userId = userId; } 27 | public String getType() { return type; } 28 | public void setType(String type) { this.type = type; } 29 | public int getDuration() { return duration; } 30 | public void setDuration(int duration) { this.duration = duration; } 31 | public LocalDateTime getDate() { return date; } 32 | public void setDate(LocalDateTime date) { this.date = date; } 33 | } -------------------------------------------------------------------------------- /test/java/com/fitness/tracker/service/WorkoutServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.fitness.tracker.service; 2 | 3 | import com.fitness.tracker.domain.Workout; 4 | import com.fitness.tracker.repository.WorkoutRepository; 5 | import org.junit.jupiter.api.BeforeEach; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import java.time.LocalDateTime; 9 | 10 | import static org.junit.jupiter.api.Assertions.*; 11 | 12 | class WorkoutServiceTest { 13 | private WorkoutService workoutService; 14 | private WorkoutRepository workoutRepository; 15 | 16 | @BeforeEach 17 | void setUp() { 18 | workoutRepository = new WorkoutRepository(); 19 | workoutService = new WorkoutService(workoutRepository); 20 | } 21 | 22 | @Test 23 | void createWorkout_validWorkout_savesWorkout() { 24 | Workout workout = new Workout("w1", "u1", "Running", 30, LocalDateTime.now()); 25 | Workout savedWorkout = workoutService.createWorkout(workout); 26 | assertEquals("w1", savedWorkout.getWorkoutId()); 27 | assertEquals(30, savedWorkout.getDuration()); 28 | } 29 | 30 | @Test 31 | void createWorkout_invalidDuration_throwsException() { 32 | Workout workout = new Workout("w1", "u1", "Running", 0, LocalDateTime.now()); 33 | assertThrows(IllegalArgumentException.class, () -> workoutService.createWorkout(workout)); 34 | } 35 | } -------------------------------------------------------------------------------- /docs/domain_model.md: -------------------------------------------------------------------------------- 1 | # domain_model.md 2 | 3 | ## Domain Model for Real-Time Fitness Tracker 4 | 5 | | Entity | Attributes | Methods | Relationships | 6 | |------------|--------------------------------|-----------------------------|----------------------------------------| 7 | | User | userId: String, name: String, email: String | login(), logout(), setGoal() | 1 User has 0..* Workouts, 0..5 Goals | 8 | | Workout | workoutId: String, startTime: DateTime, endTime: DateTime, status: String | start(), pause(), stop() | 1 Workout contains 1 HeartRate, 1 Calorie; 1..* Workouts in 1 Log | 9 | | HeartRate | hrId: String, bpm: Integer, timestamp: DateTime | record(), getBPM() | 1 HeartRate in 1 Workout; 1 Device provides 0..* HeartRates | 10 | | Calorie | calId: String, value: Float, timestamp: DateTime | calculate(), getValue() | 1 Calorie in 1 Workout | 11 | | Goal | goalId: String, target: Float, type: String, deadline: Date | setTarget(), checkProgress() | 0..5 Goals belong to 1 User | 12 | | Device | deviceId: String, status: String | connect(), disconnect() | 1 Device provides 0..* HeartRates | 13 | | Log | logId: String, date: Date | addWorkout(), getHistory() | 1 Log contains 1..* Workouts | 14 | 15 | ## Business Rules 16 | - Max 5 Goals per User (FR-007). 17 | - Workout requires startTime for data (FR-003). 18 | - HeartRate needs Device connection (FR-012). 19 | - Logs store all Workouts (FR-004). 20 | -------------------------------------------------------------------------------- /docs/template_analysis.md: -------------------------------------------------------------------------------- 1 | # template_analysis.md 2 | 3 | ## GitHub Project Templates Comparison 4 | 5 | | Template | Columns/Workflows | Automation Features | Agile Suitability | 6 | |--------------------|----------------------------------------|-------------------------------------------------|--------------------------------------------| 7 | | Basic Kanban | To Do, In Progress, Done | None | Simple Agile workflows, no sprint automation | 8 | | Automated Kanban | To Do, In Progress, Done | Auto-moves Issues/PRs (e.g., closed → Done) | Sprint tracking, continuous delivery | 9 | | Bug Triage | Needs Triage, Triaged, In Progress, Done | Auto-moves based on labels (e.g., bug triaged) | Bug-focused, less suited for features | 10 | | Team Planning | Backlog, To Do, In Progress, Done | Optional automation (e.g., status updates) | Flexible for sprints and backlog management | 11 | 12 | ## Justification 13 | I chose "Automated Kanban" for the Real-Time Fitness Tracker because it aligns with our Sprint 1 plan (Assignment 6), which includes 5 user stories (US-001, US-002, US-003, US-007, US-012) focused on core functionality. Its simple workflow (To Do, In Progress, Done) mirrors our task progression, while automation—moving closed Issues to "Done"—supports Agile’s continuous delivery by minimizing manual effort. "Basic Kanban" lacks this efficiency, "Bug Triage" is too bug-specific, and "Team Planning" adds unnecessary backlog complexity for our current scope. 14 | -------------------------------------------------------------------------------- /.github/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI/CD Pipeline 2 | 3 | on: 4 | push: 5 | branches: 6 | - '**' 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | test: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - name: Checkout code 17 | uses: actions/checkout@v4 18 | 19 | - name: Set up JDK 17 20 | uses: actions/setup-java@v4 21 | with: 22 | java-version: '17' 23 | distribution: 'temurin' 24 | 25 | - name: Cache Maven dependencies 26 | uses: actions/cache@v4 27 | with: 28 | path: ~/.m2 29 | key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} 30 | restore-keys: ${{ runner.os }}-m2 31 | 32 | - name: Run tests 33 | run: mvn --batch-mode --update-snapshots test 34 | 35 | release: 36 | needs: test 37 | if: github.event_name == 'push' && github.ref == 'refs/heads/main' 38 | runs-on: ubuntu-latest 39 | 40 | steps: 41 | - name: Checkout code 42 | uses: actions/checkout@v4 43 | 44 | - name: Set up JDK 17 45 | uses: actions/setup-java@v4 46 | with: 47 | java-version: '17' 48 | distribution: 'temurin' 49 | 50 | - name: Cache Maven dependencies 51 | uses: actions/cache@v4 52 | with: 53 | path: ~/.m2 54 | key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} 55 | restore-keys: ${{ runner.os }}-m2 56 | 57 | - name: Build JAR 58 | run: mvn --batch-mode package -DskipTests 59 | 60 | - name: Upload artifact 61 | uses: actions/upload-artifact@v4 62 | with: 63 | name: project-jar 64 | path: target/*.jar 65 | -------------------------------------------------------------------------------- /docs/SPECIFICATION.md: -------------------------------------------------------------------------------- 1 | # Introduction # SPECIFICATION.md 2 | 3 | ### Project Name: Fitness Tracker in Real Time 4 | 5 | ### Domain 6 | Health & Fitness: With an emphasis on real-time physical activity monitoring and management, this system functions within the realm of individual health and wellness. It targets those who exercise, from infrequent walkers to serious athletes, and it connects with contemporary mobile technologies to deliver useful insights. 7 | 8 | ### Problem Statement 9 | During workouts, many fitness lovers do not receive prompt, precise feedback, which makes it challenging to adequately track progress or modify their efforts in real time. The usefulness of existing solutions is diminished since they frequently deliver delayed data or necessitate laborious manual input. In order to improve training effectiveness and motivation, the Real-Time Fitness Tracker provides users with immediate indicators, like heart rate, calorie burn, and activity time. 10 | 11 | 12 | ### Personal Scope 13 | An lone developer can complete this project within the allotted time. It eliminates the need for specialized hardware or intricate infrastructure by utilizing readily accessible APIs and sensors included in mobile devices, such as heart rate monitors and accelerometers. Basic programming knowledge and common development tools like GitHub, Markdown, and a mobile framework (like Flutter or React Native) can handle the system's requirements for a simple mobile app for data collecting and presentation and a lightweight backend for processing and storage. To ensure completion without taxing resources, the scope is restricted to essential fitness tracking elements. 14 | -------------------------------------------------------------------------------- /docs/STAKEHOLDER_ANALYSIS.md: -------------------------------------------------------------------------------- 1 | # STAKEHOLDER_ANALYSIS.md 2 | ## Stakeholder Analysis for Real-Time Fitness Tracker 3 | 4 | | Stakeholder | Role | Key Concerns | Pain Points | Success Metrics | 5 | |---------------------------|------------------------------------------|---------------------------------------|------------------------------------------|------------------------------------------| 6 | | Fitness Enthusiast | Uses app to track workouts | Accurate, real-time data | Lagging apps, manual entry | 95% heart rate accuracy, 1-sec updates | 7 | | Personal Trainer | Monitors client progress | Detailed workout logs | No integration, delayed sharing | Logs exportable in 5 min | 8 | | App Developer | Builds/maintains app and backend | Sensor/API integration, low bugs | Sensor compatibility issues | 90% device compatibility, <5 bugs | 9 | | Cloud Service Provider | Hosts data storage/syncing | Uptime, data integrity | Server overload during peaks | 99.9% uptime, zero data corruption | 10 | | Healthcare Professional | Uses data for health assessments | Secure, compliant data sharing | Unencrypted data, no exports | 100% compliance, CSV export | 11 | | Fitness Device Manufacturer | Supplies integrated sensors | Connectivity, user adoption | Inconsistent API support | 98% connection success, 20% adoption | 12 | -------------------------------------------------------------------------------- /tests/CreationalPatternsTest.java: -------------------------------------------------------------------------------- 1 | package com.fitness.tracker.tests; 2 | 3 | import com.fitness.tracker.patterns.*; 4 | import org.junit.Test; 5 | import static org.junit.Assert.*; 6 | 7 | public class CreationalPatternsTest { 8 | @Test 9 | public void testSimpleFactory() { 10 | WorkoutFactory factory = new WorkoutFactory(); 11 | Workout cardio = factory.createWorkout("cardio", "W1"); 12 | assertTrue(cardio instanceof CardioWorkout); 13 | } 14 | 15 | @Test 16 | public void testFactoryMethod() { 17 | DataProcessorFactory factory = new HeartRateProcessorFactory(); 18 | DataProcessor processor = factory.createProcessor(); 19 | assertTrue(processor instanceof HeartRateProcessor); 20 | } 21 | 22 | @Test 23 | public void testAbstractFactory() { 24 | UIFactory factory = new MobileUIFactory(); 25 | Button button = factory.createButton(); 26 | assertTrue(button instanceof MobileButton); 27 | } 28 | 29 | @Test 30 | public void testBuilder() { 31 | Goal goal = new GoalBuilder() 32 | .setGoalId("G1") 33 | .setTarget(1000.0f) 34 | .setType("Steps") 35 | .build(); 36 | assertEquals("G1", goal.getGoalId()); 37 | } 38 | 39 | @Test 40 | public void testPrototype() { 41 | HeartRate original = new HeartRate("HR1", 80); 42 | HeartRate clone = original.clone(); 43 | assertEquals(original.getBPM(), clone.getBPM()); 44 | } 45 | 46 | @Test 47 | public void testSingleton() { 48 | DatabaseConnection instance1 = DatabaseConnection.getInstance(); 49 | DatabaseConnection instance2 = DatabaseConnection.getInstance(); 50 | assertSame(instance1, instance2); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | # Server Configuration 2 | server.port=8080 3 | 4 | # Datasource Configuration (MySQL) 5 | spring.datasource.url=jdbc:mysql://localhost:3306/fitness_db 6 | spring.datasource.username=root 7 | spring.datasource.password=rootpassword 8 | spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver 9 | spring.jpa.hibernate.ddl-auto=update 10 | spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect 11 | spring.datasource.url=jdbc:h2:mem:testdb 12 | spring.datasource.driverClassName=org.h2.Driver 13 | spring.datasource.username=sa 14 | spring.datasource.password= 15 | spring.jpa.database-platform=org.hibernate.dialect.H2Dialect 16 | spring.jpa.hibernate.ddl-auto=create-drop 17 | 18 | # JPA & Hibernate Configuration 19 | spring.jpa.show-sql=true 20 | spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect 21 | spring.jpa.properties.hibernate.format_sql=true 22 | 23 | # Logging Configuration 24 | logging.level.org.springframework.web=DEBUG 25 | logging.level.com.fitness.tracker=DEBUG 26 | logging.level.root=INFO 27 | 28 | # Spring Security Configuration 29 | spring.security.user.name=admin 30 | spring.security.user.password=adminpassword 31 | spring.security.user.roles=ADMIN 32 | 33 | # Spring Profiles 34 | spring.profiles.active=dev 35 | 36 | # Thymeleaf Configuration (if using Thymeleaf templates) 37 | spring.thymeleaf.cache=false 38 | spring.thymeleaf.prefix=classpath:/templates/ 39 | spring.thymeleaf.suffix=.html 40 | 41 | # Actuator Endpoints 42 | management.endpoints.web.exposure.include=health,info 43 | 44 | # Email Configuration (if sending emails) 45 | spring.mail.host=smtp.gmail.com 46 | spring.mail.port=587 47 | spring.mail.username=youremail@gmail.com 48 | spring.mail.password=yourpassword 49 | spring.mail.properties.mail.smtp.auth=true 50 | spring.mail.properties.mail.smtp.starttls.enable=true 51 | -------------------------------------------------------------------------------- /docs/kanban_explanation.md: -------------------------------------------------------------------------------- 1 | # kanban_explanation.md 2 | 3 | ## What is a Kanban Board? 4 | A Kanban board is a visual project management tool that helps track tasks by organizing them into columns representing different stages of a workflow, such as "To Do," "In Progress," and "Done." It makes it easy to see what needs to be done, what’s being worked on, and what’s finished, while also limiting the number of tasks in progress to keep work manageable and efficient. 5 | 6 | ## How Our Board Works 7 | My "Sprint 1 Kanban" board, built for the Real-Time Fitness Tracker project, uses five columns to manage Sprint 1 tasks from Assignment 6: "To Do," "In Progress," "Done," "Testing," and "Blocked." Here’s how it functions: 8 | 9 | - **Visualizes Workflow**: The columns show the progress of our five user stories (US-001, US-002, US-003, US-007, US-012). For example, US-001 ("Track heart rate in real time") starts in "To Do" when opened, moves to "In Progress" with the `in-progress` label, shifts to "Testing" with `testing` for QA checks (like T-010’s accuracy test), and lands in "Done" when closed. If an issue stalls (e.g., API delays in US-012), it’s labeled `blocked` and moves to "Blocked." 10 | 11 | - **Limits Work-in-Progress (WIP)**: To avoid overwhelming the development process, I set an informal WIP limit of 3 tasks in the "In Progress" column. For instance, only tasks like T-001 (API connection), T-003 (calorie logic), and T-005 (start/stop button) can be active at once, ensuring focus and reducing bottlenecks. 12 | 13 | - **Supports Agile Principles**: The board supports Agile by enabling continuous delivery and adaptability. The `kanban-automation.yml` workflow automates movements—e.g., closing an Issue moves it to "Done"—mimicking a smooth sprint cycle. Custom columns like "Testing" adapt to our QA needs, while "Blocked" highlights issues for quick resolution, keeping the project flexible and iterative. 14 | -------------------------------------------------------------------------------- /test/java/com/fitness/tracker/service/UserServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.fitness.tracker.service; 2 | 3 | import com.fitness.tracker.domain.User; 4 | import com.fitness.tracker.repository.UserRepository; 5 | import org.junit.jupiter.api.BeforeEach; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import static org.junit.jupiter.api.Assertions.*; 9 | 10 | class UserServiceTest { 11 | private UserService userService; 12 | private UserRepository userRepository; 13 | 14 | @BeforeEach 15 | void setUp() { 16 | userRepository = new UserRepository(); 17 | userService = new UserService(userRepository); 18 | } 19 | 20 | @Test 21 | void createUser_validUser_savesUser() { 22 | User user = new User("u1", "Alice", "alice@example.com"); 23 | User savedUser = userService.createUser(user); 24 | assertEquals("u1", savedUser.getUserId()); 25 | assertEquals("Alice", savedUser.getName()); 26 | } 27 | 28 | @Test 29 | void createUser_nullUserId_throwsException() { 30 | User user = new User(null, "Alice", "alice@example.com"); 31 | assertThrows(IllegalArgumentException.class, () -> userService.createUser(user)); 32 | } 33 | 34 | @Test 35 | void getUserById_existingUser_returnsUser() { 36 | User user = new User("u1", "Alice", "alice@example.com"); 37 | userService.createUser(user); 38 | User foundUser = userService.getUserById("u1"); 39 | assertNotNull(foundUser); 40 | assertEquals("u1", foundUser.getUserId()); 41 | } 42 | 43 | @Test 44 | void getUserById_nonExistingUser_returnsNull() { 45 | User foundUser = userService.getUserById("u2"); 46 | assertNull(foundUser); 47 | } 48 | } -------------------------------------------------------------------------------- /tests/InMemoryRepositoryTest.java: -------------------------------------------------------------------------------- 1 | package com.fitness.tracker.tests; 2 | 3 | import com.fitness.tracker.User; 4 | import com.fitness.tracker.Workout; 5 | import com.fitness.tracker.repositories.inmemory.InMemoryUserRepository; 6 | import com.fitness.tracker.repositories.inmemory.InMemoryWorkoutRepository; 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | import java.util.List; 10 | import java.util.Optional; 11 | import static org.junit.Assert.*; 12 | 13 | public class InMemoryRepositoryTest { 14 | private InMemoryUserRepository userRepo; 15 | private InMemoryWorkoutRepository workoutRepo; 16 | private User user; 17 | private Workout workout; 18 | 19 | @Before 20 | public void setUp() { 21 | userRepo = new InMemoryUserRepository(); 22 | workoutRepo = new InMemoryWorkoutRepository(); 23 | user = new User("U1", "Alice", "alice@example.com"); 24 | workout = new Workout("W1"); 25 | } 26 | 27 | @Test 28 | public void testUserSaveAndFindById() { 29 | userRepo.save(user); 30 | Optional found = userRepo.findById("U1"); 31 | assertTrue(found.isPresent()); 32 | assertEquals("Alice", found.get().getName()); 33 | } 34 | 35 | @Test 36 | public void testUserFindAll() { 37 | userRepo.save(user); 38 | userRepo.save(new User("U2", "Bob", "bob@example.com")); 39 | List users = userRepo.findAll(); 40 | assertEquals(2, users.size()); 41 | } 42 | 43 | @Test 44 | public void testUserDelete() { 45 | userRepo.save(user); 46 | userRepo.delete("U1"); 47 | assertFalse(userRepo.findById("U1").isPresent()); 48 | } 49 | 50 | @Test 51 | public void testWorkoutSaveAndFindById() { 52 | workoutRepo.save(workout); 53 | Optional found = workoutRepo.findById("W1"); 54 | assertTrue(found.isPresent()); 55 | assertEquals("W1", found.get().getWorkoutId()); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /com.fitness.tracker/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | org.springframework.boot 9 | spring-boot-starter-parent 10 | 3.3.0 11 | 12 | 13 | 14 | com.fitness.tracker 15 | RealTimeFitnessTracker 16 | 1.0-SNAPSHOT 17 | 18 | 19 | 17 20 | UTF-8 21 | 22 | 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-web 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-test 31 | test 32 | 33 | 34 | 35 | 36 | 37 | 38 | org.springframework.boot 39 | spring-boot-maven-plugin 40 | 41 | 42 | 43 | repackage 44 | 45 | 46 | 47 | 48 | 49 | org.apache.maven.plugins 50 | maven-surefire-plugin 51 | 3.2.3 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /docs/ARCHITECTURE.md: -------------------------------------------------------------------------------- 1 | # ARCHITECTURE.md 2 | # ARCHITECTURE.md 3 | ## Real-Time Fitness Tracker 4 | ### Domain 5 | Health & Fitness – A system for monitoring physical activity in real time. 6 | 7 | ### Problem Statement 8 | Provide immediate feedback on workouts to improve training efficiency. 9 | 10 | ### Individual Scope 11 | Feasible using mobile sensors and a lightweight backend. 12 | 13 | ## Context Diagram 14 | The Real-Time Fitness Tracker allows users to monitor their fitness data and syncs with a cloud service for storage and analysis. 15 | 16 | ```mermaid 17 | graph TD; 18 | A[User] -->|Uses| B(Real-Time Fitness Tracker) 19 | B -->|Syncs with| C[Cloud Service] 20 | B -->|Integrates| D[External Fitness API]; 21 | ``` 22 | graph TD; 23 | A[User] -->|Interacts with| B[Mobile App] 24 | B -->|Reads| C[Mobile Device Sensors] 25 | B -->|Sends data to| D[Backend API] 26 | D -->|Stores/Retrieves| E[Database] 27 | D -->|Queries| F[External Fitness API]; 28 | ``` 29 | graph TD; 30 | A[User] -->|Interacts with| B[UI Layer] 31 | B -->|Calls| C[Data Processor] 32 | C -->|Fetches data from| D[Sensor Interface] 33 | D -->|Reads| E[Mobile Device Sensors] 34 | C -->|Sends to| F[Backend API]; 35 | ``` 36 | 37 | ## Container Diagram 38 | The system consists of a mobile app for user interaction, a backend API for processing, and a database for storage, leveraging mobile device sensors. 39 | 40 | ```mermaid 41 | graph TD 42 | A[User] -->|Interacts with| B[Mobile App] 43 | B -->|Reads| C[Mobile Device Sensors] 44 | B -->|Sends data to| D[Backend API] 45 | D -->|Stores/Retrieves| E[Database] 46 | D -->|Queries| F[External Fitness API] 47 | ``` 48 | 49 | ## Component Diagram 50 | Inside the Mobile App, key components handle user interaction, data processing, and sensor communication. 51 | 52 | ```mermaid 53 | graph TD 54 | A[User] -->|Interacts with| B[UI Layer] 55 | B -->|Calls| C[Data Processor] 56 | C -->|Fetches data from| D[Sensor Interface] 57 | D -->|Reads| E[Mobile Device Sensors] 58 | C -->|Sends to| F[Backend API] 59 | ``` 60 | -------------------------------------------------------------------------------- /docs/assignment9_reflection.md: -------------------------------------------------------------------------------- 1 | # assignment9_reflection.md 2 | 3 | ## Reflection on Domain Modeling and Class Diagram Development 4 | 5 | Creating the domain model and class diagram for the Real-Time Fitness Tracker was a deep dive into structuring a system. One challenge was abstraction—deciding entities like Workout vs. a vague “Session” took revisiting Assignment 6’s US-003 (start/stop workouts). Relationships were tough; I initially saw HeartRate as independent but realized it’s tied to Workout (Assignment 8’s state diagram helped clarify this). Defining methods like `pause()` for Workout felt ambiguous—should it change state or trigger logic? Mapping to Assignment 8’s “Paused” state settled it. 6 | 7 | The class diagram aligns tightly with prior work. User supports FR-001 (user-specific data) and Assignment 5’s login use case. Workout’s `start()` and `stop()` reflect US-003 and Assignment 8’s activity diagram. HeartRate and Calorie match FR-001, FR-002, and US-001, US-002, with states like “Tracking” from Assignment 8. Goal ties to US-007’s workflow, and Log fulfills FR-004’s logging, as seen in “Save Workout Log.” Device aligns with US-012’s sync, per Assignment 8. These links ensure sprint tasks (e.g., T-001 for heart rate) are code-ready. 8 | 9 | Trade-offs shaped the design. I used composition for Workout-to-HeartRate/Calorie (1-to-1) since they’re inseparable, unlike Log-to-Workout’s aggregation (1-to-1..*), where Workouts stand alone (FR-004). I skipped inheritance for HeartRate and Calorie to avoid a “Data” superclass—FR-001 and FR-002’s unique needs didn’t justify it, though it limits reuse. Capping Goals at 5 (FR-007) simplifies but may restrict users later. Exploring Java code alongside Mermaid.js was insightful—Java clarified implementation (e.g., `List` for User), but Mermaid’s visual relationships (1..*) were clearer for UML compliance. 10 | 11 | Lessons learned were profound. Domain modeling prioritizes core entities—User and Workout stood out, while Device was secondary but critical (FR-012). UML diagrams bridge requirements to code; `start()` and `record()` suggest clear methods. Iteration refined my model, aligning with Assignment 6’s Agile stories. Mermaid.js is great but finicky—splitting files in Assignment 8 taught me to test rendering early. Java code helped me see encapsulation (e.g., HeartRate’s `bpm`) and relationships as fields, but UML’s visual clarity won for design. This process showed me how object-oriented design turns ideas into systems, ready for coding. 12 | 13 | -------------------------------------------------------------------------------- /docs/REFLECTION.md: -------------------------------------------------------------------------------- 1 | # REFLECTION.md 2 | ## Challenges in Balancing Stakeholder Needs for Real-Time Fitness Tracker 3 | 4 | 1. **Real-Time Data vs. Scalability**: 5 | - *Challenge*: Fitness Enthusiasts demand instant data updates (1-second latency), but Cloud Service Providers need scalable infrastructure for 10,000 users during peak times. 6 | - *Resolution*: Prioritized efficient data processing (e.g., lightweight sync in FR4) over adding complex features, ensuring scalability (NFR5). 7 | 8 | 2. **Security vs. Usability**: 9 | - *Challenge*: Healthcare Professionals require encryption (NFR6) and MFA (NFR7), which could frustrate Fitness Enthusiasts wanting quick access (NFR2). 10 | - *Resolution*: Made MFA optional for non-medical users, balancing security with ease of use. 11 | 12 | 3. **Integration Complexity**: 13 | - *Challenge*: Personal Trainers and Fitness Device Manufacturers need API integration (FR6), but App Developers face compatibility issues across devices. 14 | - *Resolution*: Limited integrations to major APIs (e.g., Google Fit), keeping development feasible (NFR4). 15 | # Reflection on Assignment 14: RealTimeFitnessTracker 16 | 17 | Preparing **RealTimeFitnessTracker** for open-source collaboration was both challenging and rewarding. Peer feedback helped me improve my repository significantly. For instance, a classmate pointed out that my `CONTRIBUTING.md` lacked instructions for running tests, so I added a section explaining how to use `npm test` for Jest. Another peer suggested my `good-first-issue` descriptions were too technical, so I simplified tasks like “Fix step counter alignment” to include step-by-step CSS changes. 18 | 19 | Onboarding contributors presented several challenges. Writing beginner-friendly issues for a fitness tracker was difficult because features like heart rate visualization required understanding APIs. I had to break tasks into smaller steps, like “Add a loading spinner for API calls,” to make them accessible. Additionally, ensuring my setup instructions worked across platforms (e.g., Windows and macOS) required testing on multiple machines, which was time-consuming. Promoting my repo in the class WhatsApp group also felt awkward initially, but I learned to pitch it concisely: “A fitness app needing UI and API contributions.” 20 | 21 | This assignment taught me valuable lessons about open-source collaboration. Clear documentation, like a detailed `README.md` and `CONTRIBUTING.md`, is essential for lowering the barrier to entry. I also realized that engaging with peers’ repositories by starring and forking builds a collaborative community, which increased my own repo’s visibility. Finally, GitHub forks, especially, reflect a project’s potential, as they show contributors are willing to invest time in it. 22 | -------------------------------------------------------------------------------- /docs/activity_diagrams.md: -------------------------------------------------------------------------------- 1 | # activity_diagrams.md 2 | 3 | ## 1. Start Workout 4 | ```mermaid 5 | flowchart TD 6 | A[Start] --> B{User logged in?} 7 | B -->|Yes| C[User presses Start] 8 | B -->|No| D[Prompt login] --> C 9 | C --> E[System records start time] --> F[End] 10 | ``` 11 | 12 | **Explanation:** This workflow ensures a logged-in user starts a workout (US-003), addressing the fitness enthusiast’s need for seamless tracking (FR-003). 13 | 14 | ## 2. Track Heart Rate 15 | ```mermaid 16 | flowchart TD 17 | A[Start] --> B{Device connected?} 18 | B -->|Yes| C[Read heart rate] --> D[Display in real time] 19 | B -->|No| E[Notify user] --> F[End] 20 | D --> F[End] 21 | ``` 22 | **Explanation:** Real-time heart rate tracking (US-001, FR-001) meets the enthusiast’s monitoring needs, with a decision for connectivity. 23 | 24 | ## 3. Calculate Calories 25 | ```mermaid 26 | flowchart TD 27 | A[Start] --> B[Collect activity data] 28 | B --> C[Calculate calories] --> D{Valid data?} 29 | D -->|Yes| E[Update UI] --> F[End] 30 | D -->|No| G[Retry calculation] --> C 31 | ``` 32 | 33 | **Explanation:** Calorie calculation (US-002, FR-002) ensures accurate feedback for users. 34 | 35 | ## 4. Set Fitness Goal 36 | ```mermaid 37 | flowchart TD 38 | A[Start] --> B[/User enters goal/] --> C[Validate input] 39 | C --> D{Valid?} 40 | D -->|Yes| E[Save goal] --> F[End] 41 | D -->|No| G[Prompt correction] --> B 42 | ``` 43 | 44 | **Explanation:** Goal setting (US-007, FR-007) supports user motivation with validation. 45 | 46 | ## 5. User Registration 47 | ```mermaid 48 | flowchart TD 49 | A[Start] --> B[/Enter credentials/] --> C[Check uniqueness] 50 | C --> D{Unique?} 51 | D -->|Yes| E[Create account] --> F[Send confirmation] --> G[End] 52 | D -->|No| H[Notify user] --> B 53 | ``` 54 | 55 | **Explanation:** Registration ensures user-specific data (FR-001), addressing scalability. 56 | 57 | ## 6. Sync Device 58 | ```mermaid 59 | flowchart TD 60 | A[Start] --> B[Initiate sync] --> C{Connection successful?} 61 | C -->|Yes| D[Fetch data] --> E[End] 62 | C -->|No| F[Retry] --> C 63 | ``` 64 | 65 | **Explanation:** Device sync (US-012, FR-012) ensures data accuracy for the developer stakeholder. 66 | 67 | ## 7. Save Workout Log 68 | ```mermaid 69 | flowchart TD 70 | A[Start] --> B[Workout ends] 71 | B --> C[Process data] --> D[Save to log] 72 | D --> E[Notify user] & F[Update stats] 73 | E --> G[End] 74 | F --> G[End] 75 | ``` 76 | **Explanation:** Logging (FR-004) meets the enthusiast’s history tracking need with parallel actions. 77 | 78 | ## 8. Pause Workout 79 | ```mermaid 80 | flowchart TD 81 | A[Start] --> B[User presses Pause] 82 | B --> C[System halts tracking] --> D[End] 83 | ``` 84 | 85 | **Explanation:** Pausing (US-003) offers flexibility, addressing user control. 86 | 87 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 5 | 4.0.0 6 | 7 | com.fitness 8 | RealTimeFitnessTracker 9 | 1.0.0 10 | jar 11 | 12 | RealTimeFitnessTracker 13 | Spring Boot Fitness Tracker App 14 | 15 | 16 | org.springframework.boot 17 | spring-boot-starter-parent 18 | 3.2.5 19 | 20 | 21 | 22 | 23 | 17 24 | 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-web 31 | 32 | 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-starter-data-jpa 37 | 38 | 39 | 40 | 41 | com.h2database 42 | h2 43 | runtime 44 | 45 | 46 | 47 | 48 | org.projectlombok 49 | lombok 50 | true 51 | 52 | 53 | 54 | 55 | org.springframework.boot 56 | spring-boot-starter-test 57 | test 58 | 59 | 60 | 61 | 62 | org.junit.jupiter 63 | junit-jupiter 64 | 5.10.0 65 | test 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | org.apache.maven.plugins 74 | maven-compiler-plugin 75 | 3.11.0 76 | 77 | ${java.version} 78 | ${java.version} 79 | 80 | 81 | 82 | 83 | 84 | org.springframework.boot 85 | spring-boot-maven-plugin 86 | 3.2.5 87 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /docs/SYSTEM_REQUIREMENTS.md: -------------------------------------------------------------------------------- 1 | # SYSTEM_REQUIREMENTS.md 2 | ## System Requirements Document for Real-Time Fitness Tracker 3 | 4 | ### Functional Requirements 5 | 1. **FR1**: The system shall display real-time heart rate during workouts. 6 | - *Acceptance Criteria*: Updates every 1 second; 95% accuracy compared to medical-grade devices. 7 | - *Stakeholder*: Fitness Enthusiast. 8 | 2. **FR2**: The system shall calculate and display calories burned based on heart rate and activity type. 9 | - *Acceptance Criteria*: Matches standard MET formulas within a 5% margin. 10 | - *Stakeholder*: Fitness Enthusiast. 11 | 3. **FR3**: The system shall allow users to start and stop workout sessions via the app. 12 | - *Stakeholder*: Fitness Enthusiast. 13 | 4. **FR4**: The system shall sync workout data to the cloud after each session. 14 | - *Acceptance Criteria*: Sync completes within 10 seconds with stable internet. 15 | - *Stakeholder*: Cloud Service Provider, Fitness Enthusiast. 16 | 5. **FR5**: The system shall generate exportable workout logs in CSV format. 17 | - *Stakeholder*: Personal Trainer, Healthcare Professional. 18 | 6. **FR6**: The system shall integrate with external fitness APIs (e.g., Google Fit) for data import/export. 19 | - *Stakeholder*: Fitness Device Manufacturer, Personal Trainer. 20 | 7. **FR7**: The system shall allow users to set fitness goals (e.g., daily calorie burn target). 21 | - *Stakeholder*: Fitness Enthusiast. 22 | 8. **FR8**: The system shall notify users when fitness goals are met in real time. 23 | - *Acceptance Criteria*: Notification appears within 2 seconds of goal completion. 24 | - *Stakeholder*: Fitness Enthusiast. 25 | 9. **FR9**: The system shall support manual entry of workout details if sensors fail. 26 | - *Stakeholder*: Fitness Enthusiast, App Developer. 27 | 10. **FR10**: The system shall provide a summary dashboard of weekly fitness metrics (e.g., total calories, average heart rate). 28 | - *Stakeholder*: Personal Trainer, Healthcare Professional. 29 | 30 | ### Non-Functional Requirements 31 | 32 | #### Usability 33 | 1. **NFR1**: The interface shall comply with WCAG 2.1 accessibility standards (e.g., sufficient color contrast). 34 | - *Stakeholder*: Fitness Enthusiast. 35 | 2. **NFR2**: The app shall require no more than 3 taps to start a workout session. 36 | - *Stakeholder*: Fitness Enthusiast. 37 | 38 | #### Deployability 39 | 3. **NFR3**: The system shall be deployable on iOS and Android devices via app stores. 40 | - *Stakeholder*: App Developer. 41 | 42 | #### Maintainability 43 | 4. **NFR4**: The backend shall include an API guide updated with each release. 44 | - *Stakeholder*: App Developer. 45 | 46 | #### Scalability 47 | 5. **NFR5**: The system shall support 10,000 concurrent users during peak hours (e.g., post-New Year fitness surges). 48 | - *Stakeholder*: Cloud Service Provider. 49 | 50 | #### Security 51 | 6. **NFR6**: All user data shall be encrypted using AES-256 in transit and at rest. 52 | - *Stakeholder*: Healthcare Professional. 53 | 7. **NFR7**: The system shall require multi-factor authentication (MFA) for account access. 54 | - *Stakeholder*: Healthcare Professional. 55 | 56 | #### Performance 57 | 8. **NFR8**: Real-time data updates shall load within 1 second on 4G networks. 58 | - *Stakeholder*: Fitness Enthusiast. 59 | 9. **NFR9**: The app shall launch within 3 seconds on supported devices. 60 | - *Stakeholder*: Fitness Enthusiast. 61 | -------------------------------------------------------------------------------- /docs/state_transition_diagrams.md: -------------------------------------------------------------------------------- 1 | # state_transition_diagrams.md 2 | 3 | ## 1. Workout Session 4 | ```mermaid 5 | stateDiagram-v2 6 | [*] --> Inactive: App launched 7 | Inactive --> Active: User starts workout 8 | Active --> Paused: User pauses workout 9 | Paused --> Active: User resumes workout 10 | Active --> Inactive: User stops workout [Duration > 0] 11 | Inactive --> [*]: Workout saved 12 | ``` 13 | 14 | **Explanation:** The "Workout Session" object starts as "Inactive" when the app launches. It transitions to "Active" when the user starts a workout (US-003, FR-003: Start/stop functionality). "Paused" allows temporary breaks, and stopping (with a guard condition ensuring duration) returns it to "Inactive" before saving, aligning with FR-004 (log activities). 15 | 16 | 17 | ## 2. Heart Rate Data 18 | ```mermaid 19 | stateDiagram-v2 20 | [*] --> Untracked: No device connected 21 | Untracked --> Tracking: Device synced 22 | Tracking --> Untracked: Device disconnected 23 | Tracking --> Recorded: Workout ends 24 | Recorded --> [*]: Data saved 25 | ``` 26 | 27 | **Explanation:** "Heart Rate Data" begins "Untracked" until a device syncs (US-001, FR-001: Real-time tracking). It transitions to "Tracking" during a workout and to "Recorded" when it ends, saving data (FR-004), supporting accurate monitoring. 28 | 29 | ## 3. Calorie Data 30 | ```mermaid 31 | stateDiagram-v2 32 | [*] --> Idle: No workout 33 | Idle --> Calculating: Workout starts 34 | Calculating --> Updated: Data processed 35 | Updated --> Idle: Workout ends 36 | Updated --> [*]: Data logged [Calories > 0] 37 | ``` 38 | 39 | **Explanation:** "Calorie Data" is "Idle" until a workout begins (US-002, FR-002: Calculate calories). It calculates in real-time, updates, and logs when the workout ends, ensuring FR-004 (activity logging). 40 | 41 | ## 4. Fitness Goal 42 | ```mermaid 43 | stateDiagram-v2 44 | [*] --> Draft: User sets goal 45 | Draft --> Active: Goal confirmed 46 | Active --> Achieved: Target met 47 | Active --> Expired: Deadline passed 48 | Achieved --> [*]: Goal archived 49 | Expired --> [*]: Goal archived 50 | ``` 51 | 52 | **Explanation:** "Fitness Goal" starts as "Draft" (US-007, FR-007: Set goals), becomes "Active" when set, and ends as "Achieved" or "Expired," aligning with goal-tracking requirements. 53 | 54 | ## 5. User Account 55 | ```mermaid 56 | stateDiagram-v2 57 | [*] --> Unregistered: App opened 58 | Unregistered --> Registered: User signs up 59 | Registered --> LoggedIn: User logs in 60 | LoggedIn --> LoggedOut: User logs out 61 | LoggedOut --> LoggedIn: User logs in again 62 | LoggedIn --> [*]: Account deleted 63 | ``` 64 | **Explanation:** "User Account" supports registration and login (implied in FR-001 for user-specific data), transitioning between states for access control. 65 | 66 | ## 6. Device Connection 67 | ```mermaid 68 | stateDiagram-v2 69 | [*] --> Disconnected: App launched 70 | Disconnected --> Connecting: User initiates sync 71 | Connecting --> Connected: Sync successful 72 | Connected --> Disconnected: Sync fails [Timeout] 73 | Connected --> [*]: Workout ends 74 | ``` 75 | 76 | **Explanation:** "Device Connection" ensures accurate data (US-012, FR-012), moving to "Connected" only on a successful sync, with a timeout guard. 77 | 78 | ## 7. Activity Log 79 | ```mermaid 80 | stateDiagram-v2 81 | [*] --> Empty: No workouts 82 | Empty --> Pending: Workout ends 83 | Pending --> Saved: Data processed 84 | Saved --> [*]: Log accessed 85 | ``` 86 | 87 | **Explanation:** The "Activity Log" tracks history (FR-004), transitioning from "Empty" to "Saved" as workouts are completed. 88 | 89 | 90 | -------------------------------------------------------------------------------- /docs/class_diagram.md: -------------------------------------------------------------------------------- 1 | # class_diagram.md 2 | 3 | ## Class Diagram 4 | ```mermaid 5 | classDiagram 6 | class User { 7 | -userId: String 8 | -name: String 9 | -email: String 10 | +login() void 11 | +logout() void 12 | +setGoal() void 13 | } 14 | class Workout { 15 | -workoutId: String 16 | -startTime: DateTime 17 | -endTime: DateTime 18 | -status: String 19 | +start() void 20 | +pause() void 21 | +stop() void 22 | } 23 | class HeartRate { 24 | -hrId: String 25 | -bpm: Integer 26 | -timestamp: DateTime 27 | +record() void 28 | +getBPM() Integer 29 | } 30 | class Calorie { 31 | -calId: String 32 | -value: Float 33 | -timestamp: DateTime 34 | +calculate() void 35 | +getValue() Float 36 | } 37 | class Goal { 38 | -goalId: String 39 | -target: Float 40 | -type: String 41 | -deadline: Date 42 | +setTarget() void 43 | +checkProgress() Float 44 | } 45 | class Device { 46 | -deviceId: String 47 | -status: String 48 | +connect() void 49 | +disconnect() void 50 | } 51 | class Log { 52 | -logId: String 53 | -date: Date 54 | +addWorkout() void 55 | +getHistory() List~Workout~ 56 | } 57 | User "1" --> "0..*" Workout : has 58 | User "1" --> "0..5" Goal : sets 59 | Workout "1" *--> "1" HeartRate : contains 60 | Workout "1" *--> "1" Calorie : contains 61 | Device "1" --> "0..*" HeartRate : provides 62 | Log "1" o--> "1..*" Workout : contains 63 | ``` 64 | 65 | ## **Design Decisions** 66 | 67 | **Composition** (*-->): Workout contains HeartRate and Calorie (1-to-1), as they’re essential parts of a session (US-001, US-002, Assignment 8 state diagrams). 68 | 69 | **Aggregation** (o-->): Log contains Workouts (1-to-1..*), as Workouts can exist before being logged (FR-004). 70 | 71 | **Association** (-->): User has Workouts (1-to-0..*) and Goals (1-to-0..5, per business rule), aligning with FR-003, FR-007. 72 | 73 | **Multiplicity**: Device to HeartRate (1-to-0..*) supports real-time data (FR-012, US-012). 74 | 75 | # class_diagram.md 76 | 77 | ## Updated Class Diagram for Assignment 11 78 | ```mermaid 79 | classDiagram 80 | class User { 81 | -userId: String 82 | -name: String 83 | -email: String 84 | +login() void 85 | +logout() void 86 | +setGoal() void 87 | } 88 | class Workout { 89 | -workoutId: String 90 | -startTime: DateTime 91 | -status: String 92 | +start() void 93 | +pause() void 94 | } 95 | class Repository~T,ID~ { 96 | +save(entity) void 97 | +findById(id) Optional~T~ 98 | +findAll() List~T~ 99 | +delete(id) void 100 | } 101 | class UserRepository { 102 | <> 103 | } 104 | class WorkoutRepository { 105 | <> 106 | } 107 | class InMemoryUserRepository { 108 | -storage: Map~String,User~ 109 | } 110 | class InMemoryWorkoutRepository { 111 | -storage: Map~String,Workout~ 112 | } 113 | class DatabaseUserRepository { 114 | // Stub for future DB 115 | } 116 | class RepositoryFactory { 117 | +getUserRepository(storageType) UserRepository 118 | +getWorkoutRepository(storageType) WorkoutRepository 119 | } 120 | UserRepository <|.. InMemoryUserRepository 121 | UserRepository <|.. DatabaseUserRepository 122 | WorkoutRepository <|.. InMemoryWorkoutRepository 123 | Repository~User,String~ <|.. UserRepository 124 | Repository~Workout,String~ <|.. WorkoutRepository 125 | RepositoryFactory --> UserRepository 126 | RepositoryFactory --> WorkoutRepository 127 | ``` 128 | **No Inheritance**: Avoided a “Data” superclass for HeartRate and Calorie to keep it simple, matching Assignment 8’s distinct states. 129 | 130 | **Alignment**: Matches domain_model.md and Assignment 6 user stories (e.g., US-003 for Workout’s start()). 131 | -------------------------------------------------------------------------------- /docs/openapi.yaml: -------------------------------------------------------------------------------- 1 | openapi: 3.0.3 2 | info: 3 | title: Real-Time Fitness Tracker API 4 | version: 1.0.0 5 | paths: 6 | /api/users: 7 | post: 8 | summary: Create a new user 9 | requestBody: 10 | content: 11 | application/json: 12 | schema: 13 | $ref: '#/components/schemas/User' 14 | responses: 15 | '200': 16 | description: User created 17 | content: 18 | application/json: 19 | schema: 20 | $ref: '#/components/schemas/User' 21 | /api/users/{userId}: 22 | get: 23 | summary: Get user by ID 24 | parameters: 25 | - name: userId 26 | in: path 27 | required: true 28 | schema: 29 | type: string 30 | responses: 31 | '200': 32 | description: User found 33 | content: 34 | application/json: 35 | schema: 36 | $ref: '#/components/schemas/User' 37 | '404': 38 | description: User not found 39 | /api/workouts: 40 | post: 41 | summary: Create a new workout 42 | requestBody: 43 | content: 44 | application/json: 45 | schema: 46 | $ref: '#/components/schemas/Workout' 47 | responses: 48 | '200': 49 | description: Workout created 50 | content: 51 | application/json: 52 | schema: 53 | $ref: '#/components/schemas/Workout' 54 | /api/workouts/{workoutId}: 55 | get: 56 | summary: Get workout by ID 57 | parameters: 58 | - name: workoutId 59 | in: path 60 | required: true 61 | schema: 62 | type: string 63 | responses: 64 | '200': 65 | description: Workout found 66 | content: 67 | application/json: 68 | schema: 69 | $ref: '#/components/schemas/Workout' 70 | '404': 71 | description: Workout not found 72 | /api/goals: 73 | post: 74 | summary: Create a new goal 75 | requestBody: 76 | content: 77 | application/json: 78 | schema: 79 | $ref: '#/components/schemas/Goal' 80 | responses: 81 | '200': 82 | description: Goal created 83 | content: 84 | application/json: 85 | schema: 86 | $ref: '#/components/schemas/Goal' 87 | /api/goals/{goalId}: 88 | get: 89 | summary: Get goal by ID 90 | parameters: 91 | - name: goalId 92 | in: path 93 | required: true 94 | schema: 95 | type: string 96 | responses: 97 | '200': 98 | description: Goal found 99 | content: 100 | application/json: 101 | schema: 102 | $ref: '#/components/schemas/Goal' 103 | '404': 104 | description: Goal not found 105 | components: 106 | schemas: 107 | User: 108 | type: object 109 | properties: 110 | userId: 111 | type: string 112 | name: 113 | type: string 114 | email: 115 | type: string 116 | Workout: 117 | type: object 118 | properties: 119 | workoutId: 120 | type: string 121 | userId: 122 | type: string 123 | type: 124 | type: string 125 | duration: 126 | type: integer 127 | date: 128 | type: string 129 | format: date-time 130 | Goal: 131 | type: object 132 | properties: 133 | goalId: 134 | type: string 135 | userId: 136 | type: string 137 | description: 138 | type: string 139 | achieved: 140 | type: boolean -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Real-Time Fitness Tracker 2 | ## Introduction 3 | The Real-Time Fitness Tracker is a mobile application designed to monitor workouts, heart rate, and calorie burn in real time, helping users achieve their fitness goals. 4 | 5 | ## Links 6 | - [System Specification](SPECIFICATION.md) 7 | - [Architecture Diagrams](ARCHITECTURE.md) 8 | - [Stakeholder Analysis](STAKEHOLDER_ANALYSIS.md) 9 | - [System Requirements](SYSTEM_REQUIREMENTS.md) 10 | - [Use Cases and Tests](USE_CASES_AND_TESTS.md) 11 | - [Agile Planning](AGILE_PLANNING.md) 12 | - [Reflection](REFLECTION.md) 13 | 14 | - ## Assignment 7: Custom Kanban Board 15 | - **Template**: Adapted "Automated Kanban" using the new Projects "Board" layout (classic templates unavailable post-August 2024 sunset). 16 | - **Custom Columns**: Added "Testing" for QA validation (e.g., T-010 accuracy checks) and "Blocked" for stalled tasks (e.g., API delays). 17 | - **Tasks**: Linked Sprint 1 Issues from Assignment 6: US-001 (heart rate tracking), US-002 (calorie calculation), US-003 (start/stop workouts), US-007 (goal setting), US-012 (data accuracy). 18 | - **Automation**: Implemented via `.github/workflows/kanban-automation.yml` with a Personal Access Token (PAT), moving Issues based on events: 19 | - `opened` → "To Do" 20 | - `closed` → "Done" 21 | - `in-progress` → "In Progress" 22 | - `testing` → "Testing" 23 | - `blocked` → "Blocked" 24 | - **Screenshot**: [Kanban Board] 25 | - ![image](https://github.com/user-attachments/assets/a132b681-d07a-4e10-91d1-3d6c62233816) 26 | 27 | ## Assignment 8: Dynamic Modeling 28 | - **[State Transition Diagrams (Objects 1-7)](state_transition_diagrams.md)**: 29 | - Workout Session: FR-003, US-003, T-003 (start/stop). 30 | - Heart Rate Data: FR-001, US-001, T-001 (real-time tracking). 31 | - Calorie Data: FR-002, US-002, T-002 (calorie calculation). 32 | - Fitness Goal: FR-007, US-007, T-007 (goal setting). 33 | - User Account: FR-001 (implied user data). 34 | - Device Connection: FR-012, US-012, T-012 (accurate data). 35 | - Activity Log: FR-004 (history logging). 36 | - **[Activity Diagrams (Workflows 1-8)](activity_diagrams.md)**: 37 | - Start Workout: FR-003, US-003, T-003. 38 | - Track Heart Rate: FR-001, US-001, T-001. 39 | - Calculate Calories: FR-002, US-002, T-002. 40 | - Set Fitness Goal: FR-007, US-007, T-007. 41 | - User Registration: FR-001 (implied). 42 | - Sync Device: FR-012, US-012, T-012. 43 | - Save Workout Log: FR-004. 44 | - Pause Workout: FR-003, US-003 (extension). 45 | - [Reflection](assignment8_reflection.md): Lessons learned from state and activity modeling challenges. 46 | 47 | ## Assignment 9: Domain Modeling and Class Diagram 48 | - [Domain Model](domin_model.md) 49 | - [Class Diagram (UML)](class_diagrams.md) 50 | - [Reflection](assignment9_reflection.md) 51 | 52 | - ## Assignment 10: From Class Diagrams to Code 53 | - **Language Choice**: Java, chosen for familiarity (used in Assignment 9’s reference code) and strong support for object-oriented design, aligning with UML relationships. 54 | - **Design Decisions**: Implemented all classes from Assignment 9’s class diagram in /src. Used composition for Workout-to-HeartRate/Calorie (1-to-1) and aggregation for Log-to-Workout (1-to-1..*). Getters ensure encapsulation. 55 | - [Source Code](/src) 56 | 57 | - - **Creational Patterns** (/creational_patterns): 58 | - Simple Factory: WorkoutFactory creates Cardio/Strength workouts for flexible session types (US-003). 59 | - Factory Method: DataProcessorFactory delegates HeartRate/Calorie processing (US-001, US-002). 60 | - Abstract Factory: UIFactory builds Mobile/Web UI components for platform-specific displays. 61 | - Builder: GoalBuilder constructs Goals with optional attributes (US-007). 62 | - Prototype: HeartRate cloning avoids redundant initialization (US-001). 63 | - Singleton: DatabaseConnection ensures one DB instance (FR-004). 64 | 65 | - ## Assignment 10: From Class Diagrams to Code 66 | - **Language**: Java (familiar, supports OOP). 67 | - **Design**: Classes in /src match Assignment 9 UML. Composition for Workout-to-HeartRate/Calorie, aggregation for Log-to-Workout. 68 | - **Patterns** (/creational_patterns): Simple Factory (Workout types), Factory Method (Data processing), Abstract Factory (UI), Builder (Goal), Prototype (HeartRate), Singleton (DB). 69 | - [Source Code](/src) 70 | - [Creational Patterns](/creational_patterns) 71 | - [Tests](/tests) 72 | - [Changelog](CHANGELOG.md) 73 | 74 | - ## Assignment 11: Persistence Repository Layer 75 | - **Task 1: Repository Interfaces** (/repositories): 76 | - Used generics in `Repository` to avoid code duplication across entities (User, Workout). 77 | - Defined `UserRepository` and `WorkoutRepository` for specific CRUD operations, extensible for future methods (e.g., findByEmail). 78 | 79 | - - **Task 3: Storage Abstraction** (/factories): 80 | - Chose Factory Pattern over DI for simplicity in creating repository instances (e.g., InMemoryUserRepository). 81 | - RepositoryFactory supports switching storage types (MEMORY, DATABASE), ensuring decoupling. 82 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Real-Time Fitness Tracker 2 | ## Introduction 3 | The Real-Time Fitness Tracker is a mobile application designed to monitor workouts, heart rate, and calorie burn in real time, helping users achieve their fitness goals. 4 | 5 | ## Links 6 | - [System Specification](SPECIFICATION.md) 7 | - [Architecture Diagrams](ARCHITECTURE.md) 8 | - [Stakeholder Analysis](STAKEHOLDER_ANALYSIS.md) 9 | - [System Requirements](SYSTEM_REQUIREMENTS.md) 10 | - [Use Cases and Tests](USE_CASES_AND_TESTS.md) 11 | - [Agile Planning](AGILE_PLANNING.md) 12 | - [Reflection](REFLECTION.md) 13 | 14 | - ## Assignment 7: Custom Kanban Board 15 | - **Template**: Adapted "Automated Kanban" using the new Projects "Board" layout (classic templates unavailable post-August 2024 sunset). 16 | - **Custom Columns**: Added "Testing" for QA validation (e.g., T-010 accuracy checks) and "Blocked" for stalled tasks (e.g., API delays). 17 | - **Tasks**: Linked Sprint 1 Issues from Assignment 6: US-001 (heart rate tracking), US-002 (calorie calculation), US-003 (start/stop workouts), US-007 (goal setting), US-012 (data accuracy). 18 | - **Automation**: Implemented via `.github/workflows/kanban-automation.yml` with a Personal Access Token (PAT), moving Issues based on events: 19 | - `opened` → "To Do" 20 | - `closed` → "Done" 21 | - `in-progress` → "In Progress" 22 | - `testing` → "Testing" 23 | - `blocked` → "Blocked" 24 | - **Screenshot**: [Kanban Board] 25 | - ![image](https://github.com/user-attachments/assets/a132b681-d07a-4e10-91d1-3d6c62233816) 26 | 27 | ## Assignment 8: Dynamic Modeling 28 | - **[State Transition Diagrams (Objects 1-7)](state_transition_diagrams.md)**: 29 | - Workout Session: FR-003, US-003, T-003 (start/stop). 30 | - Heart Rate Data: FR-001, US-001, T-001 (real-time tracking). 31 | - Calorie Data: FR-002, US-002, T-002 (calorie calculation). 32 | - Fitness Goal: FR-007, US-007, T-007 (goal setting). 33 | - User Account: FR-001 (implied user data). 34 | - Device Connection: FR-012, US-012, T-012 (accurate data). 35 | - Activity Log: FR-004 (history logging). 36 | - **[Activity Diagrams (Workflows 1-8)](activity_diagrams.md)**: 37 | - Start Workout: FR-003, US-003, T-003. 38 | - Track Heart Rate: FR-001, US-001, T-001. 39 | - Calculate Calories: FR-002, US-002, T-002. 40 | - Set Fitness Goal: FR-007, US-007, T-007. 41 | - User Registration: FR-001 (implied). 42 | - Sync Device: FR-012, US-012, T-012. 43 | - Save Workout Log: FR-004. 44 | - Pause Workout: FR-003, US-003 (extension). 45 | - [Reflection](assignment8_reflection.md): Lessons learned from state and activity modeling challenges. 46 | 47 | ## Assignment 9: Domain Modeling and Class Diagram 48 | - [Domain Model](domin_model.md) 49 | - [Class Diagram (UML)](class_diagrams.md) 50 | - [Reflection](assignment9_reflection.md) 51 | 52 | - ## Assignment 10: From Class Diagrams to Code 53 | - **Language Choice**: Java, chosen for familiarity (used in Assignment 9’s reference code) and strong support for object-oriented design, aligning with UML relationships. 54 | - **Design Decisions**: Implemented all classes from Assignment 9’s class diagram in /src. Used composition for Workout-to-HeartRate/Calorie (1-to-1) and aggregation for Log-to-Workout (1-to-1..*). Getters ensure encapsulation. 55 | - [Source Code](/src) 56 | 57 | - - **Creational Patterns** (/creational_patterns): 58 | - Simple Factory: WorkoutFactory creates Cardio/Strength workouts for flexible session types (US-003). 59 | - Factory Method: DataProcessorFactory delegates HeartRate/Calorie processing (US-001, US-002). 60 | - Abstract Factory: UIFactory builds Mobile/Web UI components for platform-specific displays. 61 | - Builder: GoalBuilder constructs Goals with optional attributes (US-007). 62 | - Prototype: HeartRate cloning avoids redundant initialization (US-001). 63 | - Singleton: DatabaseConnection ensures one DB instance (FR-004). 64 | 65 | - ## Assignment 10: From Class Diagrams to Code 66 | - **Language**: Java (familiar, supports OOP). 67 | - **Design**: Classes in /src match Assignment 9 UML. Composition for Workout-to-HeartRate/Calorie, aggregation for Log-to-Workout. 68 | - **Patterns** (/creational_patterns): Simple Factory (Workout types), Factory Method (Data processing), Abstract Factory (UI), Builder (Goal), Prototype (HeartRate), Singleton (DB). 69 | - [Source Code](/src) 70 | - [Creational Patterns](/creational_patterns) 71 | - [Tests](/tests) 72 | - [Changelog](CHANGELOG.md) 73 | 74 | - ## Assignment 11: Persistence Repository Layer 75 | - **Task 1: Repository Interfaces** (/repositories): 76 | - Used generics in `Repository` to avoid code duplication across entities (User, Workout). 77 | - Defined `UserRepository` and `WorkoutRepository` for specific CRUD operations, extensible for future methods (e.g., findByEmail). 78 | 79 | - - **Task 3: Storage Abstraction** (/factories): 80 | - Chose Factory Pattern over DI for simplicity in creating repository instances (e.g., InMemoryUserRepository). 81 | - RepositoryFactory supports switching storage types (MEMORY, DATABASE), ensuring decoupling. 82 | # RealTimeFitnessTracker 83 | 84 | **RealTimeFitnessTracker** is a web/mobile app for tracking fitness metrics like steps, heart rate, and workouts in real-time. 85 | 86 | ## Getting Started 87 | 1. Clone the repo: `git clone https://github.com/your-username/RealTimeFitnessTracker.git` 88 | 2. Install frontend dependencies: `cd frontend && npm install` 89 | 3. Install backend dependencies: `cd backend && pip install -r requirements.txt` 90 | 4. Run the app: `npm run start` (frontend) and `python manage.py runserver` (backend) 91 | 92 | ## Features for Contribution 93 | | Feature | Issue | Status | 94 | |---------|-------|--------| 95 | | Add calories calculation | [#20](https://github.com/your-username/RealTimeFitnessTracker/issues/20) | Open | 96 | | Fix step counter alignment | [#18](https://github.com/your-username/RealTimeFitnessTracker/issues/18) | Open | 97 | | Write tests for heart rate API | [#15](https://github.com/your-username/RealTimeFitnessTracker/issues/15) | Open | 98 | 99 | See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute. 100 | -------------------------------------------------------------------------------- /docs/USE_CASES_AND_TESTS.md: -------------------------------------------------------------------------------- 1 | # USE_CASES_AND_TESTS.md 2 | ## Use Case Diagram for Real-Time Fitness Tracker 3 | 4 | # USE_CASES_AND_TESTS.md 5 | ## Use Case Diagram for Real-Time Fitness Tracker 6 | 7 | # USE_CASES_AND_TESTS.md 8 | ## Use Case Diagram for Real-Time Fitness Tracker 9 | 10 | ### Diagram 11 | ```mermaid 12 | graph TD 13 | A[Fitness Enthusiast] --> UC1[Track Heart Rate] 14 | A --> UC2[Calculate Calories] 15 | A --> UC3[Start/Stop Workout] 16 | A --> UC4[Sync Data] 17 | A --> UC7[Set Goals] 18 | A --> UC8[Notify Goal Completion] 19 | B[Personal Trainer] --> UC5[Export Logs] 20 | C[App Developer] --> UC6[Integrate APIs] 21 | D[Cloud Service Provider] --> UC4 22 | E[Healthcare Professional] --> UC5 23 | F[Fitness Device] --> UC1 24 | F --> UC2 25 | UC1 --> UC2 26 | %% Calculate Calories includes Track Heart Rate 27 | UC3 --> UC1 28 | %% Start/Stop Workout triggers Track Heart Rate 29 | ``` 30 | ### Explanation 31 | **Actors**: 32 | - *Fitness Enthusiast*: Primary user, tracks workouts and sets goals (FR1, FR7). 33 | - *Personal Trainer*: Exports logs to monitor clients (FR5 concern). 34 | - *App Developer*: Configures API integrations (FR6 concern). 35 | - *Cloud Service Provider*: Ensures data sync (FR4 concern). 36 | - *Healthcare Professional*: Uses logs for health insights (FR5 concern). 37 | - *Fitness Device*: Provides sensor data (FR1, FR2). 38 | - **Relationships**: 39 | - "Calculate Calories" includes "Track Heart Rate" (needs heart rate data). 40 | - "Start/Stop Workout" triggers "Track Heart Rate." 41 | - **Stakeholder Concerns**: 42 | - Fitness Enthusiast: Real-time feedback (UC1, UC8). 43 | - Personal Trainer: Data access (UC5). 44 | - Cloud Provider: Reliable sync (UC4). 45 | 46 | ### Use Case Specifications 47 | 48 | ### UC1: Track Heart Rate 49 | - **Description**: Displays the user’s heart rate in real time during a workout. 50 | - **Preconditions**: User is logged in, workout session is active, Fitness Device is connected. 51 | - **Postconditions**: Heart rate is displayed and logged in the system. 52 | - **Basic Flow**: 53 | 1. User starts a workout session (UC3). 54 | 2. System requests heart rate data from Fitness Device. 55 | 3. System displays heart rate, updating every 1 second. 56 | - **Alternative Flow**: 57 | - A1: If Fitness Device disconnects, system prompts user to reconnect within 30 seconds or use manual input. 58 | 59 | ### UC2: Calculate Calories 60 | - **Description**: Computes and displays calories burned based on heart rate and activity type. 61 | - **Preconditions**: Heart rate tracking is active (UC1). 62 | - **Postconditions**: Calories burned are displayed and logged. 63 | - **Basic Flow**: 64 | 1. System retrieves real-time heart rate data (UC1). 65 | 2. User selects activity type (e.g., running). 66 | 3. System calculates calories using the MET formula. 67 | 4. System displays the result. 68 | - **Alternative Flow**: 69 | - A1: If no activity type is selected, system defaults to “walking” and notifies user. 70 | 71 | ### UC3: Start/Stop Workout 72 | - **Description**: Allows the user to begin or end a workout session. 73 | - **Preconditions**: User is logged in. 74 | - **Postconditions**: Workout session is active or terminated, data is saved. 75 | - **Basic Flow**: 76 | 1. User taps “Start Workout.” 77 | 2. System initiates heart rate tracking (UC1). 78 | 3. User taps “Stop Workout.” 79 | 4. System ends session and saves data. 80 | - **Alternative Flow**: 81 | - A1: If “Stop” isn’t tapped, system auto-saves after 2 hours of inactivity. 82 | 83 | ### UC4: Sync Data 84 | - **Description**: Uploads workout data to the cloud for storage and access. 85 | - **Preconditions**: Workout session has ended, internet connection is available. 86 | - **Postconditions**: Data is stored in the cloud. 87 | - **Basic Flow**: 88 | 1. System detects workout end (UC3). 89 | 2. System uploads data to Cloud Service Provider. 90 | 3. System confirms sync completion with a notification. 91 | - **Alternative Flow**: 92 | - A1: If no internet, system queues data and retries sync when connected. 93 | 94 | ### UC5: Export Logs 95 | - **Description**: Generates a CSV file of workout logs for user or trainer review. 96 | - **Preconditions**: User has completed at least one workout, is logged in. 97 | - **Postconditions**: CSV file is generated and available for download. 98 | - **Basic Flow**: 99 | 1. User selects “Export Logs.” 100 | 2. System compiles workout data into a CSV format. 101 | 3. System provides download link or saves file. 102 | - **Alternative Flow**: 103 | - A1: If no data exists, system displays “No logs available.” 104 | 105 | ### UC6: Integrate APIs 106 | - **Description**: Connects the system with external fitness APIs (e.g., Google Fit). 107 | - **Preconditions**: App Developer has configured API keys, user enables integration. 108 | - **Postconditions**: Data is synced with the external API. 109 | - **Basic Flow**: 110 | 1. User enables API integration in settings. 111 | 2. System authenticates with the external API. 112 | 3. System syncs workout data bidirectionally. 113 | - **Alternative Flow**: 114 | - A1: If authentication fails, system prompts user to re-enter credentials. 115 | 116 | ### UC7: Set Goals 117 | - ** Description**: Allows user to define fitness targets (e.g., calorie burn). 118 | - **Preconditions**: User is logged in. 119 | - **Postconditions**: Goal is saved and active in the system. 120 | - **Basic Flow**: 121 | 1. User selects “Set Goal.” 122 | 2. User inputs target (e.g., burn 500 calories). 123 | 3. System validates and saves the goal. 124 | - **Alternative Flow**: 125 | - A1: If target is invalid (e.g., negative), system prompts correction. 126 | 127 | ### UC8: Notify Goal Completion 128 | - **Description**: Alerts user when a fitness goal is achieved. 129 | - **Preconditions**: Goal is set (UC7), workout is active. 130 | - **Postconditions**: User receives a notification, goal status updates. 131 | - **Basic Flow**: 132 | 1. System monitors calories burned (UC2). 133 | 2. System detects goal threshold is met (e.g., 500 calories). 134 | 3. System sends a notification within 2 seconds. 135 | - **Alternative Flow**: 136 | - A1: If notifications are disabled, system logs completion silently. 137 | ## Test Cases 138 | 139 | ### Functional Test Cases 140 | | Test Case ID | Requirement ID | Description | Steps | Expected Result | Actual Result | Status | 141 | |--------------|----------------|------------------------------|------------------------------------------|----------------------------------|---------------|--------| 142 | | TC-001 | FR1 | Verify heart rate display | 1. Start workout. 2. Check display. | Updates every 1 sec, 95% accurate| TBD | TBD | 143 | | TC-002 | FR2 | Verify calorie calculation | 1. Track heart rate. 2. Select “Running.” 3. Check calories. | Matches MET within 5% | TBD | TBD | 144 | | TC-003 | FR3 | Verify workout start/stop | 1. Tap “Start.” 2. Tap “Stop.” | Session starts, ends, data saved | TBD | TBD | 145 | | TC-004 | FR4 | Verify data sync | 1. End workout. 2. Check cloud. | Syncs within 10 sec | TBD | TBD | 146 | | TC-005 | FR5 | Verify log export | 1. Select “Export Logs.” 2. Download. | CSV file generated | TBD | TBD | 147 | | TC-006 | FR6 | Verify API integration | 1. Enable Google Fit. 2. Sync data. | Data shared with API | TBD | TBD | 148 | | TC-007 | FR7 | Verify goal setting | 1. Set 500-calorie goal. 2. Save. | Goal saved | TBD | TBD | 149 | | TC-008 | FR8 | Verify goal notification | 1. Set goal. 2. Burn 500 calories. | Notification within 2 sec | TBD | TBD | 150 | 151 | ### Non-Functional Test Cases 152 | | Test Case ID | Requirement ID | Description | Steps | Expected Result | Actual Result | Status | 153 | |--------------|----------------|------------------------------|------------------------------------------|----------------------------------|---------------|--------| 154 | | TC-NF-001 | NFR6 | Verify data security | 1. Sync data. 2. Inspect transmission. | AES-256 encryption used | TBD | TBD | 155 | | TC-NF-002 | NFR8 | Verify performance under load| 1. Simulate 10,000 users tracking HR. 2. Measure time. | Updates in 1 sec on 4G | TBD | TBD | 156 | -------------------------------------------------------------------------------- /docs/AGILE_PLANNING.md: -------------------------------------------------------------------------------- 1 | # AGILE_PLANNING.md 2 | 3 | ## User Stories 4 | 5 | | Story ID | User Story | Acceptance Criteria | Priority | 6 | |----------|--------------------------------------------------------|---------------------------------------------------------|----------| 7 | | US-001 | As a Fitness Enthusiast, I want to track my heart rate in real time so that I can monitor my workout intensity. | Display updates every 1 sec, 95% accurate. | High | 8 | | US-002 | As a Fitness Enthusiast, I want to calculate calories burned so that I can track my energy expenditure. | Matches MET within 5%, updates with heart rate. | High | 9 | | US-003 | As a Fitness Enthusiast, I want to start/stop workouts so that I can control my tracking sessions. | Session starts/stops, data saved on stop. | High | 10 | | US-004 | As a Fitness Enthusiast, I want to sync data to the cloud so that I can access it across devices. | Syncs within 10 sec when connected. | Medium | 11 | | US-005 | As a Personal Trainer, I want to export workout logs so that I can analyze my client’s progress. | CSV file generated, includes all session data. | Medium | 12 | | US-006 | As an App Developer, I want to integrate external APIs so that users can share data with other fitness apps. | Syncs with Google Fit, bidirectional data flow. | Medium | 13 | | US-007 | As a Fitness Enthusiast, I want to set fitness goals so that I can stay motivated. | Goal saved, validated (e.g., positive values). | High | 14 | | US-008 | As a Fitness Enthusiast, I want to be notified when I complete a goal so that I can celebrate my achievement. | Notification within 2 sec of goal met. | Medium | 15 | | US-009 | As a Healthcare Professional, I want to access workout logs so that I can provide health advice. | Logs available in CSV, secure access only. | Low | 16 | | US-010 | As a Cloud Service Provider, I want data synced securely so that user privacy is maintained. | AES-256 encryption during sync. | High | 17 | | US-011 | As a Fitness Enthusiast, I want manual input for heart rate so that I can track without a device. | Manual entry option if device disconnects. | Low | 18 | | US-012 | As a Fitness Device, I want to provide accurate data so that the app functions reliably. | Data sent every 1 sec, 95% accuracy. | High | 19 | 20 | ## Product Backlog 21 | 22 | | Story ID | User Story | Priority (MoSCoW) | Effort Estimate (Story Points) | Dependencies | 23 | |----------|--------------------------------------------------------|-------------------|-------------------------------|--------------| 24 | | US-001 | Track my heart rate in real time | Must-have | 3 | US-012 | 25 | | US-002 | Calculate calories burned | Must-have | 3 | US-001 | 26 | | US-003 | Start/stop workouts | Must-have | 2 | None | 27 | | US-007 | Set fitness goals | Must-have | 2 | None | 28 | | US-012 | Provide accurate data (Fitness Device) | Must-have | 5 | None | 29 | | US-010 | Sync data securely (Cloud Service Provider) | Should-have | 3 | US-004 | 30 | | US-004 | Sync data to the cloud | Should-have | 2 | US-003 | 31 | | US-008 | Notify when I complete a goal | Should-have | 2 | US-002, US-007 | 32 | | US-005 | Export workout logs (Personal Trainer) | Could-have | 3 | US-004 | 33 | | US-006 | Integrate external APIs | Could-have | 5 | US-004 | 34 | | US-009 | Access workout logs (Healthcare Professional) | Could-have | 2 | US-005 | 35 | | US-011 | Manual input for heart rate | Won’t-have | 1 | None | 36 | 37 | ### Prioritization Justification 38 | - **Must-have**: Core tracking (US-001, US-002, US-003, US-012) and goal setting (US-007) are essential for the MVP, addressing Fitness Enthusiast’s primary needs (usability, real-time feedback). 39 | - **Should-have**: Security (US-010), cloud sync (US-004), and notifications (US-008) enhance value but can wait beyond MVP. 40 | - **Could-have**: Trainer/healthcare features (US-005, US-009) and API integration (US-006) are valuable but not critical initially. 41 | - **Won’t-have**: Manual input (US-011) is low priority due to reliance on Fitness Device. 42 | 43 | ## Sprint 1 Planning (2 Weeks) 44 | 45 | ### Sprint Goal 46 | Deliver core workout tracking, goal-setting, and device integration functionality for the Fitness Enthusiast to enable reliable real-time monitoring and motivation. 47 | 48 | ### Sprint Backlog 49 | 50 | | Task ID | Task Description | Assigned To | Estimated Hours | Status | Acceptance Criteria | 51 | |---------|---------------------------------|-------------|-----------------|----------------|---------------------------------------------| 52 | | T-001 | Connect to Fitness Device API | Alice | 8 | To Do | Successfully connects and retrieves data | 53 | | T-002 | Display heart rate UI | Bob | 6 | To Do | Heart rate displayed in real-time | 54 | | T-003 | Implement calorie calculation logic | Carol | 6 | To Do | Calorie calculation matches MET within 5% | 55 | | T-004 | Design calorie display UI | Alice | 4 | To Do | Calorie display integrated with UI | 56 | | T-005 | Add start/stop button | Bob | 4 | To Do | Button starts/stops session, saves data | 57 | | T-006 | Save session data locally | Carol | 6 | To Do | Session data saved accurately on stop | 58 | | T-007 | Create goal-setting form | Alice | 4 | To Do | Form created and functional | 59 | | T-008 | Validate and save goal | Bob | 4 | To Do | Goals are validated and saved | 60 | | T-009 | Set up Fitness Device data stream | Carol | 8 | To Do | Data stream set up and operational | 61 | | T-010 | Test data accuracy (95% threshold) | Alice | 4 | To Do | Data accuracy meets 95% threshold | 62 | 63 | ## Risk Management 64 | | Risk ID | Risk Description | Mitigation Plan | 65 | |---------|---------------------------------------------------|--------------------------------------------------------| 66 | | R-001 | Delay in API integration | Allocate buffer time, prioritize critical APIs | 67 | | R-002 | Inaccurate heart rate data | Implement rigorous testing, use fallback mechanisms | 68 | | R-003 | Data sync issues | Regular sync tests, robust error handling | 69 | 70 | ## Sprint Review and Retrospective 71 | - **Sprint Review**: Review completed tasks, demonstrate to stakeholders, gather feedback. 72 | - **Retrospective**: Discuss what went well, what didn’t, and how to improve in the next sprint. 73 | 74 | ## Continuous Integration and Testing 75 | - **CI Setup**: Implement a CI pipeline for automated builds and tests. 76 | - **Automated Testing**: Write unit tests, integration tests, and end-to-end tests to ensure code quality and functionality. 77 | 78 | ## Reflection (500 Words) 79 | 80 | Transitioning to Agile for the Real-Time Fitness Tracker was a rollercoaster of excitement and self-doubt. As the sole stakeholder, I wrestled with breaking down Assignment 4’s Feature Requirements (FRs) into user stories that were clear, actionable, and aligned with the Agile principles. It was critical to ensure that each user story encapsulated a piece of functionality that delivered value to the user, while being feasible to complete within a single sprint. 81 | 82 | Estimating effort solo was daunting. Assigning 5 points to US-012 versus 2 to US-007 relied on gut feel—device APIs loomed larger than a simple form—but without a team, it was guesswork refined by past experience. I had to objectively assess the complexity and interdependencies of tasks, balancing them against the limited resources and time constraints typical of a solo project. 83 | 84 | Continuity with Assignments 3-5 was a strength—FR1-FR8 mapped cleanly to stories, and Use Cases (UC1-UC8) reinforced Must-haves like US-012. The real challenge was resisting scope creep; US-011’s manual input was a tempting addition, but it risked diverting focus from core functionalities. Prioritizing user stories using the MoSCoW method helped maintain clarity on what was essential for the Minimum Viable Product (MVP). 85 | 86 | Sprint planning was another area of learning. Setting a sprint goal to deliver core workout tracking, goal-setting, and device integration functionality was straightforward, but breaking it down into a detailed sprint backlog required careful thought. Each task had to be granular enough to track progress but substantial enough to avoid micromanagement. Assigning tasks to “Dev Team” initially seemed sufficient, but I quickly realized the importance of individual accountability. This led to specifying task assignments, ensuring clear ownership and responsibility. 87 | 88 | Incorporating acceptance criteria for each task brought additional clarity, defining the “done” state and ensuring that each deliverable met the required standards. This practice also facilitated better testing and validation processes, crucial for maintaining the app’s reliability and user satisfaction. 89 | 90 | Risk management was another critical aspect. Identifying potential risks, such as delays in API integration or data accuracy issues, and planning mitigation strategies helped in proactively addressing challenges. This not only ensured smoother project execution but also built resilience into the project plan. 91 | 92 | The importance of regular review and retrospective meetings became apparent as the project progressed. These sessions provided valuable insights into what was working well and what needed improvement. By discussing successes and challenges openly, I could continuously refine processes, enhancing efficiency and effectiveness for future sprints. 93 | 94 | Lastly, setting up continuous integration and testing was a game-changer. Implementing a CI pipeline for automated builds and tests ensured that code quality remained high and that any issues were identified and addressed promptly. Writing unit tests, integration tests, and end-to-end tests provided comprehensive coverage, ensuring that all functionalities worked as expected and reducing the risk of bugs and regressions. 95 | 96 | In conclusion, transitioning to Agile for the Real-Time Fitness Tracker project was a significant learning experience. It required balancing detailed planning with flexibility, maintaining a user-centric focus, and continuously improving processes through feedback and reflection. Despite the challenges, the structured approach of Agile methodologies enabled the efficient delivery of a functional, reliable, and user-friendly fitness tracking application. 97 | --------------------------------------------------------------------------------