Added graphs to show workouts & PR's per week on dashboard. However there is no tooltip on hover and I duplicated the svg spark line template (May combine the two)
This commit is contained in:
139
utils.py
139
utils.py
@@ -1,7 +1,8 @@
|
||||
import colorsys
|
||||
from datetime import datetime, date, timedelta
|
||||
import random
|
||||
import numpy as np
|
||||
import json
|
||||
|
||||
import pandas as pd
|
||||
|
||||
def get_workouts(topsets):
|
||||
# Get all unique workout_ids (No duplicates)
|
||||
@@ -305,4 +306,136 @@ def get_exercise_graph_model(title, estimated_1rm, repetitions, weight, start_da
|
||||
'plots': [repetitions, weight, estimated_1rm],
|
||||
'best_fit_points': best_fit_points,
|
||||
'plot_labels': plot_labels
|
||||
}
|
||||
}
|
||||
|
||||
def get_workout_counts(workouts, period='week'):
|
||||
# Convert to DataFrame
|
||||
df = pd.DataFrame(workouts)
|
||||
|
||||
# Convert 'StartDate' to datetime
|
||||
df['StartDate'] = pd.to_datetime(df['StartDate'])
|
||||
|
||||
# Determine the range of periods to cover
|
||||
min_date = df['StartDate'].min()
|
||||
max_date = pd.Timestamp(datetime.now())
|
||||
|
||||
# Generate a complete range of periods
|
||||
freq = 'W-MON' if period == 'week' else 'MS'
|
||||
period_range = pd.date_range(start=min_date, end=max_date, freq=freq)
|
||||
|
||||
# Initialize a dictionary to store workout counts and person names
|
||||
workout_counts = {
|
||||
person_id: {
|
||||
"PersonName": person_name,
|
||||
"PRCounts": {p: 0 for p in period_range}
|
||||
} for person_id, person_name in df[['PersonId', 'PersonName']].drop_duplicates().values
|
||||
}
|
||||
|
||||
# Process the workouts
|
||||
for person_id, person_data in workout_counts.items():
|
||||
person_df = df[df['PersonId'] == person_id]
|
||||
|
||||
for period_start in person_data["PRCounts"]:
|
||||
period_end = period_start + pd.DateOffset(weeks=1) if period == 'week' else period_start + pd.DateOffset(months=1)
|
||||
period_workouts = person_df[(person_df['StartDate'] >= period_start) & (person_df['StartDate'] < period_end)]
|
||||
person_data["PRCounts"][period_start] = len(period_workouts)
|
||||
|
||||
return workout_counts
|
||||
|
||||
def count_prs_over_time(workouts, period='week'):
|
||||
# Convert to DataFrame
|
||||
df = pd.DataFrame(workouts)
|
||||
|
||||
# Convert 'StartDate' to datetime
|
||||
df['StartDate'] = pd.to_datetime(df['StartDate'])
|
||||
|
||||
# Determine the range of periods to cover
|
||||
min_date = df['StartDate'].min()
|
||||
max_date = pd.Timestamp(datetime.now())
|
||||
|
||||
# Generate a complete range of periods
|
||||
period_range = pd.date_range(start=min_date, end=max_date, freq='W-MON' if period == 'week' else 'MS')
|
||||
|
||||
# Initialize a dictionary to store PR counts and names
|
||||
pr_counts = {
|
||||
person_id: {
|
||||
"PersonName": person_name,
|
||||
"PRCounts": {p: 0 for p in period_range}
|
||||
} for person_id, person_name in df[['PersonId', 'PersonName']].drop_duplicates().values
|
||||
}
|
||||
|
||||
# Process the workouts
|
||||
for person_id, person_data in pr_counts.items():
|
||||
person_df = df[df['PersonId'] == person_id]
|
||||
|
||||
for period_start in person_data["PRCounts"]:
|
||||
period_end = period_start + pd.DateOffset(weeks=1) if period == 'week' else period_start + pd.DateOffset(months=1)
|
||||
period_workouts = person_df[(person_df['StartDate'] >= period_start) & (person_df['StartDate'] < period_end)]
|
||||
|
||||
for exercise_id in period_workouts['ExerciseId'].unique():
|
||||
exercise_max = period_workouts[period_workouts['ExerciseId'] == exercise_id]['Estimated1RM'].max()
|
||||
|
||||
# Check if this is a PR
|
||||
previous_max = person_df[(person_df['StartDate'] < period_start) &
|
||||
(person_df['ExerciseId'] == exercise_id)]['Estimated1RM'].max()
|
||||
|
||||
if pd.isna(previous_max) or exercise_max > previous_max:
|
||||
person_data["PRCounts"][period_start] += 1
|
||||
|
||||
return pr_counts
|
||||
|
||||
|
||||
def get_weekly_pr_graph_model(title, weekly_pr_data):
|
||||
# Assuming weekly_pr_data is in the format {1: {"PersonName": "Alice", "PRCounts": {Timestamp('2022-01-01', freq='W-MON'): 0, ...}}, 2: {...}, ...}
|
||||
|
||||
# Find the overall date range for all users
|
||||
all_dates = [date for user_data in weekly_pr_data.values() for date in user_data["PRCounts"].keys()]
|
||||
min_date, max_date = min(all_dates), max(all_dates)
|
||||
total_span = (max_date - min_date).days or 1
|
||||
relative_positions = [(date - min_date).days / total_span for date in all_dates]
|
||||
|
||||
# Calculate viewBox dimensions
|
||||
max_pr_count = max(max(user_data["PRCounts"].values()) for user_data in weekly_pr_data.values()) or 1
|
||||
vb_width, vb_height = total_span, max_pr_count
|
||||
vb_width *= 200 / vb_width # Scale to 200px width
|
||||
vb_height *= 75 / vb_height # Scale to 75px height
|
||||
|
||||
plots = []
|
||||
colors = get_distinct_colors(len(weekly_pr_data.items()))
|
||||
for count, (user_id, user_data) in enumerate(weekly_pr_data.items()):
|
||||
pr_counts = user_data["PRCounts"]
|
||||
person_name = user_data["PersonName"]
|
||||
|
||||
values = pr_counts.values()
|
||||
min_value, max_value = min(values), max(values)
|
||||
value_range = (max_value - min_value) or 1
|
||||
|
||||
values_scaled = [((value - min_value) / value_range) * vb_height for value in values]
|
||||
plot_points = list(zip(values_scaled, relative_positions))
|
||||
|
||||
# Create a plot for each user
|
||||
plot = {
|
||||
'label': person_name, # Use PersonName instead of User ID
|
||||
'color': colors[count],
|
||||
'points': plot_points
|
||||
}
|
||||
plots.append(plot)
|
||||
|
||||
# Return workout data with SVG dimensions and data points
|
||||
return {
|
||||
'title': title,
|
||||
'vb_width': vb_width,
|
||||
'vb_height': vb_height,
|
||||
'plots': plots
|
||||
}
|
||||
|
||||
def get_distinct_colors(n):
|
||||
colors = []
|
||||
for i in range(n):
|
||||
# Divide the color wheel into n parts
|
||||
hue = i / n
|
||||
# Convert HSL (Hue, Saturation, Lightness) to RGB and then to a Hex string
|
||||
rgb = colorsys.hls_to_rgb(hue, 0.6, 0.4) # Fixed lightness and saturation
|
||||
hex_color = '#{:02x}{:02x}{:02x}'.format(int(rgb[0]*255), int(rgb[1]*255), int(rgb[2]*255))
|
||||
colors.append(hex_color)
|
||||
return colors
|
||||
Reference in New Issue
Block a user