Skip to content
Tracking Strength & Running Progress: A Personal Fitness Data Project
Introduction
Project Overview
Tracking fitness progress requires consistent data collection and meaningful analysis. This project automates the process of monitoring both strength training and running performance by extracting workout data from the Fitbod app, storing it in an Azure SQL database, and analyzing trends using Python. The goal is to track benchmark strength exercises, total training volume, and running pace over time, allowing for data-driven improvements.
Objectives
This project aims to:
- Track key benchmark exercises to measure strength gains.
 - Analyze total workout volume on a monthly basis.
 - Monitor running performance, specifically pace improvements.
 - Identify trends and patterns in training progress.
 - Present insights in an intuitive format for quick progress evaluation.
 
Infrastructure & Data Collection
Azure Deployment
- Azure Resource Group: Deployed an Azure resource group to manage cloud infrastructure.
 - SQL Server & Database: Configured an Azure SQL Server and SQL Database for secure data storage.
 - Security & Access Control: Implemented authentication and access rules to ensure data privacy.
 - Connected this notenook to the Azure SQL database.
 
Data Source & ETL Process
- Workout and running data is exported from Fitbod in CSV format.
 - The data includes exercises, sets, reps, weight, timestamps, distance, duration, and pace.
 - Data is uploaded to the Azure SQL Database using SSMS and transformed using SQL queries before analysis.
 - Additonal columns were added, such as converting kg to lb and calculating total volume.
 
Data Analysis & Transformation
Using Jupyter Notebook (Datacamp) for SQL and Python Queries
- SQL queries extract relevant workout and running metrics.
 - Data is converted into Pandas DataFrames for further analysis and visualization.
 
Weight Lifting Analysis
Prepare Initial Weight Lifting Dataframe
- Converted kg to lb
 - Removed rowing, hiking, walking, and running
 - Filters date by >= 2025-01-01
 
DataFrameas
weight_lifting_df
variable
DECLARE @lb FLOAT = 2.20462;
SELECT
	FORMAT(wo.Date, 'yyyy-MM-dd') AS Date
	, wo.Exercise AS Exercise
	, wo.Reps AS Reps
	, ROUND((wo.Weight_kg * @lb), 1) AS Weight_lb -- convert kg to lb
	, ROUND((wo.Weight_kg * @lb) * wo.Reps, 1) AS Total_volume_lb -- calculate total volume
	, wo.multiplier AS Multiplier
	
FROM
	dbo.WorkoutExport AS wo
	
WHERE
	wo.Exercise NOT IN ('Hiking', 'Rowing', 'Running', 'Walking')
	AND wo.Date >= '2025-01-01'Get Total Volume Lifted Summaries By Month
import pandas as pd
# Ensure the 'Date' column is in datetime format
weight_lifting_df['Date'] = pd.to_datetime(weight_lifting_df['Date'])
# Extract year and month from the 'Date' column
weight_lifting_df['YearMonth'] = weight_lifting_df['Date'].dt.to_period('M')
# Group by 'YearMonth' and sum the 'Total_volume_lb'
monthly_volume = weight_lifting_df.groupby('YearMonth')['Total_volume_lb'].sum().reset_index()
# Convert 'YearMonth' back to string for better readability
monthly_volume['YearMonth'] = monthly_volume['YearMonth'].astype(str)
# Display dataframe
monthly_volumeimport matplotlib.pyplot as plt
import matplotlib.colors as mcolors
import numpy as np
from matplotlib.patches import PathPatch
from matplotlib.path import Path
# Get tab10 color palette
colors = mcolors.TABLEAU_COLORS
# Create plot with increased width
fig, ax = plt.subplots(figsize=(12, 6))  # Increased width to 12 inches
ax.plot(monthly_volume['YearMonth'], monthly_volume['Total_volume_lb'], linewidth=2, marker='o', color=list(colors.values())[1], markerfacecolor=list(colors.values())[1], markeredgewidth=0)
# Update layout
title = 'Total Volume Lifted Over Time'
subtitle = 'Monthly Aggregated Data'
ax.set_title(f'{title}\n{subtitle}', loc='left', color='white', fontsize=12)
ax.title.set_fontsize(12)  # Title font size
ax.set_xlabel('', color='white')
ax.set_ylabel('Total Volume (lb)', color='white')
ax.yaxis.tick_right()
ax.yaxis.set_label_position("right")
# Remove all ticks on the y and x
ax.tick_params(axis='both', which='both', length=0, colors='white')
# Update grid and axis lines
ax.grid(True, color='#3A3F4A', linestyle='dashed', linewidth=0.5, alpha=0.5)  # Corrected the grid parameters
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['left'].set_visible(False)
ax.spines['bottom'].set_visible(False)
#ax.set_ylim(bottom=0)
#ax.set_ylim(top=160000)
# Add legend
legend = ax.legend(['Total Volume (lb)'])
legend.set_visible(False)
for text in legend.get_texts():
    text.set_fontsize(8)
    text.set_color('white')
# Set legend background color
legend.get_frame().set_facecolor('#1E1E2E')
legend.get_frame().set_edgecolor('#1E1E2E')
# Set background color
fig.patch.set_facecolor('#1E1E2E')
ax.set_facecolor('#1E1E2E')
# Adjust layout for better spacing
plt.tight_layout()
# Show plot
plt.show()Get Total Volume By Benchmark Exercise
Back squat, bench press, deadlift, barbell row, and military press.