Skip to content

Commit

Permalink
Merge pull request #14 from voynow/13-mid-week-update-productionization
Browse files Browse the repository at this point in the history
13 mid week update productionization
  • Loading branch information
voynow authored Aug 18, 2024
2 parents 8b69fb1 + e57b8d0 commit 9c3bfb0
Show file tree
Hide file tree
Showing 8 changed files with 223 additions and 160 deletions.
30 changes: 30 additions & 0 deletions src/activities.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from stravalib.client import Client
from stravalib.model import Activity

from src.types.activity_summary import ActivitySummary
from src.types.day_of_week_summary import DayOfWeekSummary
from src.types.week_summary import WeekSummary

Expand Down Expand Up @@ -80,6 +81,7 @@ def get_activities_df(strava_client: Client, num_weeks: int = 8) -> pl.DataFrame
cleansed and processed
:param athlete_id: The Strava athlete ID
:param num_weeks: The number of weeks to fetch activities for
:return: A cleaned and processed DataFrame of the athlete's activities
"""
timedela_x_weeks = datetime.now() - timedelta(weeks=num_weeks)
Expand All @@ -90,6 +92,34 @@ def get_activities_df(strava_client: Client, num_weeks: int = 8) -> pl.DataFrame
return preprocess_activities_df(raw_df)


def get_activity_summaries(strava_client, num_weeks=8) -> List[ActivitySummary]:
"""
Fetches and returns activity summaries for a given athlete ID
:param athlete_id: The Strava athlete ID
:param num_weeks: The number of weeks to fetch activities for
:return: A list of ActivitySummary objects, lighter weight than the full get_activities_df DataFrame
"""
df = get_activities_df(strava_client, num_weeks)
concise_activities_df = df.with_columns(
pl.col("start_date_local")
.apply(lambda x: x.strftime("%A, %B %d, %Y %I:%M %p"), return_dtype=pl.Utf8)
.alias("date_and_time"),
pl.col("distance_in_miles").apply(lambda x: round(x, 2)),
pl.col("elevation_gain_in_feet").apply(lambda x: round(x, 2)),
pl.col("pace_minutes_per_mile").apply(lambda x: round(x, 2)),
).drop(
"id",
"name",
"day_of_week",
"week_of_year",
"year",
"moving_time_in_minutes",
"start_date_local",
)
return [ActivitySummary(**row) for row in concise_activities_df.rows(named=True)]


def get_day_of_week_summaries(activities_df: pl.DataFrame) -> List[DayOfWeekSummary]:
"""
Aggregate activities DataFrame by day of the week and calculate summary statistics
Expand Down
35 changes: 8 additions & 27 deletions src/email_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
)


def space(n: int = 1):
return " " * n


def training_week_update_to_html(
mid_week_analysis: MidWeekAnalysis,
training_week_update_with_planning: TrainingWeekWithPlanning,
Expand Down Expand Up @@ -125,17 +129,6 @@ def training_week_update_to_html(
color: #ffffff;
margin-bottom: 5px;
}
.miles-value {
font-size: 28px;
font-weight: bold;
color: #ffffff;
}
.separator {
width: 2px;
background-color: #ddd;
height: 50px;
margin: 0 20px;
}
.footer {
background-color: #f1f1f1;
text-align: center;
Expand Down Expand Up @@ -180,15 +173,9 @@ def training_week_update_to_html(

html_content += f"""
</ul>
<div class="miles-summary">
<div class="miles-info">
<span class="miles-label">Total Miles Planned </span>
<span class="miles-value">{total_miles}</span>
</div>
<div class="separator"></div>
<div class="miles-info">
<span class="miles-label">Miles Remaining </span>
<span class="miles-value">{miles_remaining}</span>
<div class="miles-summary" style="text-align: center;">
<div class="miles-info" style="margin: 0 auto;">
<span class="miles-label">Total Miles Planned: {total_miles}{space(8)}Miles Remaining: {miles_remaining}</span>
</div>
</div>
</div>
Expand Down Expand Up @@ -291,11 +278,6 @@ def training_week_to_html(training_week_with_coaching: TrainingWeekWithCoaching)
color: #ffffff;
margin-bottom: 5px;
}
.miles-value {
font-size: 28px;
font-weight: bold;
color: #ffffff;
}
.mileage-target-section {
margin-top: 30px;
padding: 20px;
Expand Down Expand Up @@ -333,8 +315,7 @@ def training_week_to_html(training_week_with_coaching: TrainingWeekWithCoaching)
</ul>
<div class="miles-summary" style="text-align: center;">
<div class="miles-info" style="margin: 0 auto;">
<span class="miles-label">Total Miles Planned </span>
<span class="miles-value">{total_miles}</span>
<span class="miles-label">Total Miles Planned: {total_miles}</span>
</div>
</div>
<div class="mileage-target-section">
Expand Down
91 changes: 91 additions & 0 deletions src/lambda_function.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import os
from datetime import datetime

from stravalib.client import Client

from src.activities import (
get_activities_df,
get_activity_summaries,
get_day_of_week_summaries,
get_weekly_summaries,
)
from src.auth_manager import get_strava_client
from src.constants import COACH_ROLE
from src.email_manager import (
send_email,
training_week_to_html,
training_week_update_to_html,
)
from src.supabase_client import (
get_training_week_with_coaching,
upsert_training_week_update,
upsert_training_week_with_coaching,
)
from src.training_week import generate_training_week
from src.training_week_update import get_updated_training_week
from src.types.mid_week_analysis import MidWeekAnalysis


def run_gen_training_week_process(
strava_client: Client, sysmsg_base: str, athlete_id: str
):
activities_df = get_activities_df(strava_client)
day_of_week_summaries = get_day_of_week_summaries(activities_df)
weekly_summaries = get_weekly_summaries(activities_df)
training_week_with_coaching = generate_training_week(
sysmsg_base=sysmsg_base,
weekly_summaries=weekly_summaries,
day_of_week_summaries=day_of_week_summaries,
)

upsert_training_week_with_coaching(
athlete_id=athlete_id, training_week_with_coaching=training_week_with_coaching
)
send_email(
subject="Training Schedule Just Dropped 🏃",
html_content=training_week_to_html(training_week_with_coaching),
)


def run_update_training_week_process(
strava_client: Client, sysmsg_base: str, athlete_id: str
):
training_week_with_coaching = get_training_week_with_coaching(athlete_id)
current_weeks_activity_summaries = get_activity_summaries(
strava_client, num_weeks=1
)
mid_week_analysis = MidWeekAnalysis(
activities=current_weeks_activity_summaries,
training_week=training_week_with_coaching.training_week,
)
training_week_update_with_planning = get_updated_training_week(
sysmsg_base=sysmsg_base, mid_week_analysis=mid_week_analysis
)

upsert_training_week_update(
athlete_id=athlete_id,
mid_week_analysis=mid_week_analysis,
training_week_update_with_planning=training_week_update_with_planning,
)
send_email(
subject="Training Schedule Update 🏃",
html_content=training_week_update_to_html(
mid_week_analysis=mid_week_analysis,
training_week_update_with_planning=training_week_update_with_planning,
),
)


def lambda_handler(event, context):
client_preferences = "A) Training for a marathon B) This will be my second marathon C) Prefer workouts on Wednesdays and long runs on Saturdays"
sysmsg_base = f"{COACH_ROLE}\nYour client has included the following preferences: {client_preferences}\n"

# activities setup
athlete_id = os.environ["JAMIES_ATHLETE_ID"]
strava_client = get_strava_client(athlete_id)

# weekday 6 is Sunday
if datetime.now().weekday() == 6:
run_gen_training_week_process(strava_client, sysmsg_base, athlete_id)
else:
run_update_training_week_process(strava_client, sysmsg_base, athlete_id)
File renamed without changes.
Empty file.
39 changes: 0 additions & 39 deletions src/training_week_generation/lambda_function.py

This file was deleted.

26 changes: 26 additions & 0 deletions src/training_week_update.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from src.llm import get_completion_json
from src.types.mid_week_analysis import MidWeekAnalysis
from src.types.training_week import TrainingWeekWithPlanning


def get_updated_training_week(
sysmsg_base: str,
mid_week_analysis: MidWeekAnalysis,
) -> TrainingWeekWithPlanning:
msg = (
sysmsg_base
+ f"""\nThe original plan for this week targeted {mid_week_analysis.miles_target} miles. Your client has run {mid_week_analysis.miles_ran} miles so far (and they have {mid_week_analysis.miles_remaining} miles left to run this week), but the plan for the rest of the week includes {mid_week_analysis.future_miles_planned} miles. Adjust the plan accordingly given the following information:
Here is your client's progress so far this week:
{mid_week_analysis.activities}
Here is the plan for the rest of the week:
{mid_week_analysis.training_week_future}
What changes need to be made so that your client runs {mid_week_analysis.miles_remaining} more miles this week?"""
)

return get_completion_json(
message=msg,
response_model=TrainingWeekWithPlanning,
)
Loading

0 comments on commit 9c3bfb0

Please sign in to comment.