Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -5,7 +5,6 @@ import plotly.express as px
|
|
| 5 |
from stravalib.client import Client
|
| 6 |
import google.generativeai as genai
|
| 7 |
from dotenv import load_dotenv
|
| 8 |
-
from datetime import datetime, timedelta
|
| 9 |
|
| 10 |
# Load environment variables
|
| 11 |
load_dotenv()
|
|
@@ -104,10 +103,9 @@ if 'access_token' in st.session_state:
|
|
| 104 |
df = pd.DataFrame([{
|
| 105 |
'name': activity.name,
|
| 106 |
'distance': float(activity.distance) if activity.distance is not None else 0,
|
| 107 |
-
'moving_time':
|
| 108 |
'elevation_gain': float(activity.total_elevation_gain) if activity.total_elevation_gain is not None else 0,
|
| 109 |
'type': str(activity.type),
|
| 110 |
-
'start_date': activity.start_date.replace(tzinfo=None) if activity.start_date is not None else None
|
| 111 |
} for activity in activities])
|
| 112 |
return df
|
| 113 |
|
|
@@ -128,7 +126,7 @@ if 'access_token' in st.session_state:
|
|
| 128 |
# Basic stats
|
| 129 |
st.subheader("π
Activity Stats")
|
| 130 |
total_distance = df['distance'].sum() / 1000 # Convert to km
|
| 131 |
-
total_time = df['moving_time'].sum()
|
| 132 |
total_elevation = df['elevation_gain'].sum()
|
| 133 |
|
| 134 |
col1, col2, col3 = st.columns(3)
|
|
@@ -139,24 +137,12 @@ if 'access_token' in st.session_state:
|
|
| 139 |
# Visualizations
|
| 140 |
st.subheader("π Activity Visualizations")
|
| 141 |
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
title="Total Distance by Activity Type")
|
| 149 |
-
fig_distance.update_layout(xaxis_title="Activity Type", yaxis_title="Distance (m)")
|
| 150 |
-
st.plotly_chart(fig_distance, use_container_width=True)
|
| 151 |
-
|
| 152 |
-
with col2:
|
| 153 |
-
# Activity count over time
|
| 154 |
-
df['start_date'] = pd.to_datetime(df['start_date'])
|
| 155 |
-
activity_counts = df.resample('D', on='start_date').size().reset_index(name='count')
|
| 156 |
-
fig_timeline = px.bar(activity_counts, x='start_date', y='count',
|
| 157 |
-
title="Activity Count Over Time")
|
| 158 |
-
fig_timeline.update_layout(xaxis_title="Date", yaxis_title="Number of Activities")
|
| 159 |
-
st.plotly_chart(fig_timeline, use_container_width=True)
|
| 160 |
|
| 161 |
# Training Plan Generation
|
| 162 |
st.subheader("ποΈββοΈ Generate Training Plan")
|
|
@@ -179,51 +165,12 @@ if 'access_token' in st.session_state:
|
|
| 179 |
except Exception as e:
|
| 180 |
st.markdown(f'<div class="error-message">Error generating training plan: {str(e)}. Please try again later.</div>', unsafe_allow_html=True)
|
| 181 |
|
| 182 |
-
#
|
| 183 |
-
st.subheader("π‘
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
context = f"""Based on the runner's data:
|
| 189 |
-
Total distance: {total_distance:.2f} km
|
| 190 |
-
Total time: {total_time:.2f} hours
|
| 191 |
-
Total elevation gain: {total_elevation:.0f} m
|
| 192 |
-
Number of activities: {len(df)}
|
| 193 |
-
Date range: {df['start_date'].min().date()} to {df['start_date'].max().date()}
|
| 194 |
-
"""
|
| 195 |
-
prompt = f"{context}\n\nUser question: {user_question}\n\nProvide a concise, insightful answer:"
|
| 196 |
-
response = model.generate_content(prompt)
|
| 197 |
-
st.write(response.text)
|
| 198 |
-
except Exception as e:
|
| 199 |
-
st.markdown(f'<div class="error-message">Error generating insights: {str(e)}. Please try again later.</div>', unsafe_allow_html=True)
|
| 200 |
-
|
| 201 |
-
# Performance Analysis
|
| 202 |
-
st.subheader("π Performance Analysis")
|
| 203 |
-
|
| 204 |
-
# Calculate pace
|
| 205 |
-
df['pace'] = df['moving_time'] / (df['distance'] / 1000) # hours/km
|
| 206 |
-
df['pace'] = df['pace'] * 60 # convert to minutes/km
|
| 207 |
-
|
| 208 |
-
# Filter for runs only
|
| 209 |
-
runs_df = df[df['type'] == 'Run']
|
| 210 |
-
|
| 211 |
-
if not runs_df.empty:
|
| 212 |
-
fig_pace = px.scatter(runs_df, x='start_date', y='pace',
|
| 213 |
-
title="Running Pace Over Time",
|
| 214 |
-
labels={'pace': 'Pace (min/km)', 'start_date': 'Date'})
|
| 215 |
-
fig_pace.update_layout(yaxis_title="Pace (min/km)")
|
| 216 |
-
st.plotly_chart(fig_pace, use_container_width=True)
|
| 217 |
-
|
| 218 |
-
# Weekly mileage
|
| 219 |
-
weekly_mileage = runs_df.resample('W', on='start_date')['distance'].sum() / 1000
|
| 220 |
-
fig_weekly = px.bar(weekly_mileage.reset_index(), x='start_date', y='distance',
|
| 221 |
-
title="Weekly Running Mileage",
|
| 222 |
-
labels={'distance': 'Distance (km)', 'start_date': 'Week'})
|
| 223 |
-
fig_weekly.update_layout(xaxis_title="Week", yaxis_title="Distance (km)")
|
| 224 |
-
st.plotly_chart(fig_weekly, use_container_width=True)
|
| 225 |
-
else:
|
| 226 |
-
st.info("No running data available for performance analysis.")
|
| 227 |
|
| 228 |
else:
|
| 229 |
st.info("Please complete the authorization process to view your Strava data and analytics.")
|
|
|
|
| 5 |
from stravalib.client import Client
|
| 6 |
import google.generativeai as genai
|
| 7 |
from dotenv import load_dotenv
|
|
|
|
| 8 |
|
| 9 |
# Load environment variables
|
| 10 |
load_dotenv()
|
|
|
|
| 103 |
df = pd.DataFrame([{
|
| 104 |
'name': activity.name,
|
| 105 |
'distance': float(activity.distance) if activity.distance is not None else 0,
|
| 106 |
+
'moving_time': float(activity.moving_time) if activity.moving_time is not None else 0,
|
| 107 |
'elevation_gain': float(activity.total_elevation_gain) if activity.total_elevation_gain is not None else 0,
|
| 108 |
'type': str(activity.type),
|
|
|
|
| 109 |
} for activity in activities])
|
| 110 |
return df
|
| 111 |
|
|
|
|
| 126 |
# Basic stats
|
| 127 |
st.subheader("π
Activity Stats")
|
| 128 |
total_distance = df['distance'].sum() / 1000 # Convert to km
|
| 129 |
+
total_time = df['moving_time'].sum() / 3600 # Convert to hours
|
| 130 |
total_elevation = df['elevation_gain'].sum()
|
| 131 |
|
| 132 |
col1, col2, col3 = st.columns(3)
|
|
|
|
| 137 |
# Visualizations
|
| 138 |
st.subheader("π Activity Visualizations")
|
| 139 |
|
| 140 |
+
# Distance by activity type
|
| 141 |
+
fig_distance = px.bar(df.groupby('type').sum().reset_index(),
|
| 142 |
+
x='type', y='distance',
|
| 143 |
+
title="Total Distance by Activity Type")
|
| 144 |
+
fig_distance.update_layout(xaxis_title="Activity Type", yaxis_title="Distance (m)")
|
| 145 |
+
st.plotly_chart(fig_distance, use_container_width=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 146 |
|
| 147 |
# Training Plan Generation
|
| 148 |
st.subheader("ποΈββοΈ Generate Training Plan")
|
|
|
|
| 165 |
except Exception as e:
|
| 166 |
st.markdown(f'<div class="error-message">Error generating training plan: {str(e)}. Please try again later.</div>', unsafe_allow_html=True)
|
| 167 |
|
| 168 |
+
# Simple Insights
|
| 169 |
+
st.subheader("π‘ Activity Insights")
|
| 170 |
+
activity_types = df['type'].value_counts()
|
| 171 |
+
st.write(f"You have {len(df)} recorded activities.")
|
| 172 |
+
st.write(f"Your most common activity type is {activity_types.index[0]} with {activity_types.iloc[0]} activities.")
|
| 173 |
+
st.write(f"Your average activity distance is {df['distance'].mean() / 1000:.2f} km.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 174 |
|
| 175 |
else:
|
| 176 |
st.info("Please complete the authorization process to view your Strava data and analytics.")
|