From 8cdf50a98e818b6332e3fafd1c55234eac295dc5 Mon Sep 17 00:00:00 2001 From: voynow Date: Sat, 24 Aug 2024 14:39:06 -0400 Subject: [PATCH] Renaming & doing some QA --- src/lambda_function.py | 8 +- src/supabase_client.py | 32 +++-- src/training_week.py | 2 +- test.ipynb | 268 ++++++++++++++++++++++++++++------------- 4 files changed, 204 insertions(+), 106 deletions(-) diff --git a/src/lambda_function.py b/src/lambda_function.py index 44be8ec..b3427e0 100644 --- a/src/lambda_function.py +++ b/src/lambda_function.py @@ -35,7 +35,7 @@ def get_athlete_full_name(strava_client) -> str: return f"{athlete.firstname} {athlete.lastname}" -def run_gen_training_week_process( +def run_weekly_update_process( user: UserRow, upsert_fn: Callable[[str, TrainingWeekWithPlanning], None], email_fn: Callable[[Dict[str, str], str, str], None], @@ -60,7 +60,7 @@ def run_gen_training_week_process( ) -def run_update_training_week_process( +def run_mid_week_update_process( user: UserRow, upsert_fn: Callable[[str, MidWeekAnalysis, TrainingWeekWithPlanning], None], email_fn: Callable[[Dict[str, str], str, str], None], @@ -101,13 +101,13 @@ def core_executor(user: UserRow) -> None: # day 6 is Sunday if datetime_now_est.weekday() == 6: - run_gen_training_week_process( + run_weekly_update_process( user=user, upsert_fn=upsert_training_week_with_coaching, email_fn=send_email, ) else: - run_update_training_week_process( + run_mid_week_update_process( user=user, upsert_fn=upsert_training_week_update, email_fn=send_email, diff --git a/src/supabase_client.py b/src/supabase_client.py index b9241a0..45e0cd2 100644 --- a/src/supabase_client.py +++ b/src/supabase_client.py @@ -124,15 +124,15 @@ def mock_upsert_training_week_with_coaching( row_data = { "athlete_id": athlete_id, "training_week_planning": training_week_with_coaching.training_week_planning, - "training_week": json.dumps( - [session.dict() for session in training_week_with_coaching.training_week] - ), + "training_week": [ + session.dict() for session in training_week_with_coaching.training_week + ], "typical_week_training_review": training_week_with_coaching.typical_week_training_review, "weekly_mileage_target": training_week_with_coaching.weekly_mileage_target, } - # Log the data for testing purposes - print(f"Mock Upsert Data: {json.dumps(row_data, indent=2)}") + print(json.dumps(row_data, indent=4)) + print(f"{training_week_with_coaching.total_weekly_mileage=}") return APIResponse(data=[row_data], count=1) @@ -208,21 +208,17 @@ def mock_upsert_training_week_update( row_data = { "athlete_id": athlete_id, - "activities": json.dumps( - [activity.dict() for activity in mid_week_analysis.activities] - ), - "training_week": json.dumps( - [session.dict() for session in mid_week_analysis.training_week] - ), + "activities": [activity.dict() for activity in mid_week_analysis.activities], + "training_week": [ + session.dict() for session in mid_week_analysis.training_week + ], "planning": training_week_update_with_planning.planning, - "training_week_update": json.dumps( - [ - session.dict() - for session in training_week_update_with_planning.training_week - ] - ), + "training_week_update": [ + session.dict() + for session in training_week_update_with_planning.training_week + ], } - print(f"Mock Upsert Data: {json.dumps(row_data, indent=2)}") + print(json.dumps(row_data, indent=4)) return APIResponse(data=[row_data], count=1) diff --git a/src/training_week.py b/src/training_week.py index ea01134..1ec2a45 100644 --- a/src/training_week.py +++ b/src/training_week.py @@ -28,7 +28,7 @@ def get_weekly_mileage_target( sysmsg = f"""{sysmsg_base}\nYou will be provided summary statistics (aggregated by week) of your client's training over the past several weeks.""" usermsg = f"""Starting from earliest to most recent, here are the weekly summaries: {weekly_summaries} -As the coach, prescribe your client a target weekly mileage and long run range for next week. Be conservative when increasing volume & distance. Be specific and refer to the data provided. Write 2-3 sentences while being as concise as possible.""" +As the coach, prescribe your client a target weekly mileage and long run range for next week. Be conservative when increasing volume & distance, it's important that the goals are very achievable. Be specific and refer to the data provided. Write 3-4 sentences while being as concise as possible.""" return get_completion( [{"role": "assistant", "content": sysmsg}, {"role": "user", "content": usermsg}] ) diff --git a/test.ipynb b/test.ipynb index 32ec32e..d72f51a 100644 --- a/test.ipynb +++ b/test.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 40, + "execution_count": 21, "metadata": {}, "outputs": [ { @@ -21,7 +21,7 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 22, "metadata": {}, "outputs": [], "source": [ @@ -29,14 +29,15 @@ "from freezegun import freeze_time\n", "\n", "from src.email_manager import mock_send_email\n", - "from src.lambda_function import run_gen_training_week_process, run_update_training_week_process\n", + "from src.lambda_function import run_weekly_update_process, run_mid_week_update_process\n", "from src.supabase_client import mock_upsert_training_week_update, mock_upsert_training_week_with_coaching\n", - "from src.supabase_client import list_users" + "from src.supabase_client import list_users\n", + "from src.auth_manager import get_strava_client" ] }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 23, "metadata": {}, "outputs": [], "source": [ @@ -56,31 +57,35 @@ }, { "cell_type": "code", - "execution_count": 46, + "execution_count": 32, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Mock user: athlete_id=98390356 email='voynow99@gmail.com' preferences='A) Training for a marathon B) This will be my second marathon C) Prefer workouts on Wednesdays and long runs on Saturdays' created_at=datetime.datetime(2024, 8, 21, 21, 12, 55, 723179, tzinfo=datetime.timezone.utc)\n" + "Mock user: athlete_id=98390356 email='voynow99@gmail.com' preferences='A) Training for a marathon B) This will be my second marathon C) Prefer workouts on Wednesdays and long runs on Saturdays' created_at=datetime.datetime(2024, 8, 21, 21, 12, 55, 723179, tzinfo=datetime.timezone.utc)\n", + "athlete_id=98390356 token still valid until 2024-08-24 23:24:34+00:00\n" ] } ], "source": [ "mock_user = setup_mock_user()\n", "\n", - "@freeze_time(\"2024-08-21 20:00:00\")\n", - "def mock_mid_week_update():\n", - " run_gen_training_week_process(\n", + "# re-authenticate the user if necessary prior to freeze_time\n", + "get_strava_client(mock_user.athlete_id)\n", + "\n", + "@freeze_time(\"2024-08-18 20:00:00\")\n", + "def mock_weekly_update():\n", + " run_weekly_update_process(\n", " user=mock_user,\n", " upsert_fn=mock_upsert_training_week_with_coaching,\n", " email_fn=mock_send_email,\n", " )\n", "\n", - "@freeze_time(\"2024-08-18 20:00:00\")\n", - "def mock_weekly_update():\n", - " run_update_training_week_process(\n", + "@freeze_time(\"2024-08-21 20:00:00\")\n", + "def mock_mid_week_update():\n", + " run_mid_week_update_process(\n", " user=mock_user,\n", " upsert_fn=mock_upsert_training_week_update,\n", " email_fn=mock_send_email,\n", @@ -89,44 +94,74 @@ }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 36, "metadata": {}, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "No rates present in response headers\n" - ] - }, { "name": "stdout", "output_type": "stream", "text": [ - "athlete_id=98390356 token still valid until 2024-08-23 20:21:00+00:00\n" - ] - }, - { - "ename": "AccessUnauthorized", - "evalue": "Unauthorized: Authorization Error: [{'resource': 'Athlete', 'field': 'access_token', 'code': 'invalid'}]", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mAccessUnauthorized\u001b[0m Traceback (most recent call last)", - "\u001b[1;32m/Users/jamievoynow/Desktop/code/strava/test.ipynb Cell 5\u001b[0m line \u001b[0;36m1\n\u001b[0;32m----> 1\u001b[0m mock_weekly_update()\n", - "File \u001b[0;32m~/Desktop/code/strava/.venv/lib/python3.12/site-packages/freezegun/api.py:885\u001b[0m, in \u001b[0;36m_freeze_time.decorate_callable..wrapper\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 883\u001b[0m result \u001b[39m=\u001b[39m func(\u001b[39m*\u001b[39margs, \u001b[39m*\u001b[39m\u001b[39m*\u001b[39mkwargs)\n\u001b[1;32m 884\u001b[0m \u001b[39melse\u001b[39;00m:\n\u001b[0;32m--> 885\u001b[0m result \u001b[39m=\u001b[39m func(\u001b[39m*\u001b[39;49margs, \u001b[39m*\u001b[39;49m\u001b[39m*\u001b[39;49mkwargs)\n\u001b[1;32m 886\u001b[0m \u001b[39mreturn\u001b[39;00m result\n", - "\u001b[1;32m/Users/jamievoynow/Desktop/code/strava/test.ipynb Cell 5\u001b[0m line \u001b[0;36m1\n\u001b[1;32m 11\u001b[0m \u001b[39m@freeze_time\u001b[39m(\u001b[39m\"\u001b[39m\u001b[39m2024-08-18 20:00:00\u001b[39m\u001b[39m\"\u001b[39m)\n\u001b[1;32m 12\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39mmock_weekly_update\u001b[39m():\n\u001b[0;32m---> 13\u001b[0m run_update_training_week_process(\n\u001b[1;32m 14\u001b[0m user\u001b[39m=\u001b[39;49mmock_user,\n\u001b[1;32m 15\u001b[0m upsert_fn\u001b[39m=\u001b[39;49mmock_upsert_training_week_update,\n\u001b[1;32m 16\u001b[0m email_fn\u001b[39m=\u001b[39;49mmock_send_email,\n\u001b[1;32m 17\u001b[0m )\n", - "File \u001b[0;32m~/Desktop/code/strava/src/lambda_function.py:72\u001b[0m, in \u001b[0;36mrun_update_training_week_process\u001b[0;34m(user, upsert_fn, email_fn)\u001b[0m\n\u001b[1;32m 69\u001b[0m strava_client \u001b[39m=\u001b[39m get_strava_client(user\u001b[39m.\u001b[39mathlete_id)\n\u001b[1;32m 71\u001b[0m training_week_with_coaching \u001b[39m=\u001b[39m get_training_week_with_coaching(user\u001b[39m.\u001b[39mathlete_id)\n\u001b[0;32m---> 72\u001b[0m current_weeks_activity_summaries \u001b[39m=\u001b[39m get_activity_summaries(\n\u001b[1;32m 73\u001b[0m strava_client, num_weeks\u001b[39m=\u001b[39;49m\u001b[39m1\u001b[39;49m\n\u001b[1;32m 74\u001b[0m )\n\u001b[1;32m 75\u001b[0m mid_week_analysis \u001b[39m=\u001b[39m MidWeekAnalysis(\n\u001b[1;32m 76\u001b[0m activities\u001b[39m=\u001b[39mcurrent_weeks_activity_summaries,\n\u001b[1;32m 77\u001b[0m training_week\u001b[39m=\u001b[39mtraining_week_with_coaching\u001b[39m.\u001b[39mtraining_week,\n\u001b[1;32m 78\u001b[0m )\n\u001b[1;32m 79\u001b[0m training_week_update_with_planning \u001b[39m=\u001b[39m get_updated_training_week(\n\u001b[1;32m 80\u001b[0m sysmsg_base\u001b[39m=\u001b[39msysmsg_base, mid_week_analysis\u001b[39m=\u001b[39mmid_week_analysis\n\u001b[1;32m 81\u001b[0m )\n", - "File \u001b[0;32m~/Desktop/code/strava/src/activities.py:133\u001b[0m, in \u001b[0;36mget_activity_summaries\u001b[0;34m(strava_client, num_weeks)\u001b[0m\n\u001b[1;32m 125\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39mget_activity_summaries\u001b[39m(strava_client, num_weeks\u001b[39m=\u001b[39m\u001b[39m8\u001b[39m) \u001b[39m-\u001b[39m\u001b[39m>\u001b[39m List[ActivitySummary]:\n\u001b[1;32m 126\u001b[0m \u001b[39m \u001b[39m\u001b[39m\"\"\"\u001b[39;00m\n\u001b[1;32m 127\u001b[0m \u001b[39m Fetches and returns activity summaries for a given athlete ID\u001b[39;00m\n\u001b[1;32m 128\u001b[0m \n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 131\u001b[0m \u001b[39m :return: A list of ActivitySummary objects, lighter weight than the full get_activities_df DataFrame\u001b[39;00m\n\u001b[1;32m 132\u001b[0m \u001b[39m \"\"\"\u001b[39;00m\n\u001b[0;32m--> 133\u001b[0m df \u001b[39m=\u001b[39m get_activities_df(strava_client, num_weeks)\n\u001b[1;32m 134\u001b[0m concise_activities_df \u001b[39m=\u001b[39m df\u001b[39m.\u001b[39mwith_columns(\n\u001b[1;32m 135\u001b[0m pl\u001b[39m.\u001b[39mcol(\u001b[39m\"\u001b[39m\u001b[39mdate\u001b[39m\u001b[39m\"\u001b[39m)\u001b[39m.\u001b[39mapply(\n\u001b[1;32m 136\u001b[0m \u001b[39mlambda\u001b[39;00m x: x\u001b[39m.\u001b[39mstrftime(\u001b[39m\"\u001b[39m\u001b[39m%\u001b[39m\u001b[39mA, \u001b[39m\u001b[39m%\u001b[39m\u001b[39mB \u001b[39m\u001b[39m%d\u001b[39;00m\u001b[39m, \u001b[39m\u001b[39m%\u001b[39m\u001b[39mY\u001b[39m\u001b[39m\"\u001b[39m), return_dtype\u001b[39m=\u001b[39mpl\u001b[39m.\u001b[39mUtf8\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 147\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mmoving_time_in_minutes\u001b[39m\u001b[39m\"\u001b[39m,\n\u001b[1;32m 148\u001b[0m )\n\u001b[1;32m 149\u001b[0m \u001b[39mreturn\u001b[39;00m [ActivitySummary(\u001b[39m*\u001b[39m\u001b[39m*\u001b[39mrow) \u001b[39mfor\u001b[39;00m row \u001b[39min\u001b[39;00m concise_activities_df\u001b[39m.\u001b[39mrows(named\u001b[39m=\u001b[39m\u001b[39mTrue\u001b[39;00m)]\n", - "File \u001b[0;32m~/Desktop/code/strava/src/activities.py:118\u001b[0m, in \u001b[0;36mget_activities_df\u001b[0;34m(strava_client, num_weeks)\u001b[0m\n\u001b[1;32m 115\u001b[0m start_date \u001b[39m=\u001b[39m end_date \u001b[39m-\u001b[39m timedelta(weeks\u001b[39m=\u001b[39mnum_weeks)\n\u001b[1;32m 117\u001b[0m activities \u001b[39m=\u001b[39m strava_client\u001b[39m.\u001b[39mget_activities(after\u001b[39m=\u001b[39mstart_date, before\u001b[39m=\u001b[39mend_date)\n\u001b[0;32m--> 118\u001b[0m raw_df \u001b[39m=\u001b[39m activities_to_df(activities)\n\u001b[1;32m 119\u001b[0m all_dates_df \u001b[39m=\u001b[39m add_missing_dates(\n\u001b[1;32m 120\u001b[0m df\u001b[39m=\u001b[39mraw_df, start_date\u001b[39m=\u001b[39mstart_date, end_date\u001b[39m=\u001b[39mend_date\n\u001b[1;32m 121\u001b[0m )\n\u001b[1;32m 122\u001b[0m \u001b[39mreturn\u001b[39;00m preprocess(all_dates_df)\n", - "File \u001b[0;32m~/Desktop/code/strava/src/activities.py:34\u001b[0m, in \u001b[0;36mactivities_to_df\u001b[0;34m(activities)\u001b[0m\n\u001b[1;32m 24\u001b[0m df_schema \u001b[39m=\u001b[39m {\n\u001b[1;32m 25\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mid\u001b[39m\u001b[39m\"\u001b[39m: pl\u001b[39m.\u001b[39mUInt64,\n\u001b[1;32m 26\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mname\u001b[39m\u001b[39m\"\u001b[39m: pl\u001b[39m.\u001b[39mUtf8,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 30\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mstart_date_local\u001b[39m\u001b[39m\"\u001b[39m: pl\u001b[39m.\u001b[39mDatetime,\n\u001b[1;32m 31\u001b[0m }\n\u001b[1;32m 32\u001b[0m df_builder: Dict[\u001b[39mstr\u001b[39m, List] \u001b[39m=\u001b[39m {}\n\u001b[0;32m---> 34\u001b[0m \u001b[39mfor\u001b[39;49;00m activity \u001b[39min\u001b[39;49;00m activities:\n\u001b[1;32m 35\u001b[0m \u001b[39mfor\u001b[39;49;00m attribute \u001b[39min\u001b[39;49;00m df_schema:\n\u001b[1;32m 36\u001b[0m \u001b[39mif\u001b[39;49;00m attribute \u001b[39mnot\u001b[39;49;00m \u001b[39min\u001b[39;49;00m df_builder:\n", - "File \u001b[0;32m~/Desktop/code/strava/.venv/lib/python3.12/site-packages/stravalib/client.py:2064\u001b[0m, in \u001b[0;36mBatchedResultsIterator.__next__\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 2063\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39m__next__\u001b[39m(\u001b[39mself\u001b[39m) \u001b[39m-\u001b[39m\u001b[39m>\u001b[39m T:\n\u001b[0;32m-> 2064\u001b[0m \u001b[39mreturn\u001b[39;00m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49mnext()\n", - "File \u001b[0;32m~/Desktop/code/strava/.venv/lib/python3.12/site-packages/stravalib/client.py:2070\u001b[0m, in \u001b[0;36mBatchedResultsIterator.next\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 2068\u001b[0m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_eof()\n\u001b[1;32m 2069\u001b[0m \u001b[39mif\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_buffer:\n\u001b[0;32m-> 2070\u001b[0m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49m_fill_buffer()\n\u001b[1;32m 2071\u001b[0m \u001b[39massert\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_buffer \u001b[39mis\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mNone\u001b[39;00m\n\u001b[1;32m 2072\u001b[0m \u001b[39mtry\u001b[39;00m:\n", - "File \u001b[0;32m~/Desktop/code/strava/.venv/lib/python3.12/site-packages/stravalib/client.py:2032\u001b[0m, in \u001b[0;36mBatchedResultsIterator._fill_buffer\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 2030\u001b[0m \u001b[39mif\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_all_results_fetched:\n\u001b[1;32m 2031\u001b[0m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_eof()\n\u001b[0;32m-> 2032\u001b[0m raw_results \u001b[39m=\u001b[39m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49mresult_fetcher(\n\u001b[1;32m 2033\u001b[0m page\u001b[39m=\u001b[39;49m\u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49m_page, per_page\u001b[39m=\u001b[39;49m\u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49mper_page\n\u001b[1;32m 2034\u001b[0m )\n\u001b[1;32m 2036\u001b[0m entities \u001b[39m=\u001b[39m []\n\u001b[1;32m 2037\u001b[0m \u001b[39mfor\u001b[39;00m raw \u001b[39min\u001b[39;00m raw_results:\n", - "File \u001b[0;32m~/Desktop/code/strava/.venv/lib/python3.12/site-packages/stravalib/protocol.py:442\u001b[0m, in \u001b[0;36mApiV3.get\u001b[0;34m(self, url, check_for_errors, **kwargs)\u001b[0m\n\u001b[1;32m 440\u001b[0m url \u001b[39m=\u001b[39m url\u001b[39m.\u001b[39mformat(\u001b[39m*\u001b[39m\u001b[39m*\u001b[39mkwargs)\n\u001b[1;32m 441\u001b[0m params \u001b[39m=\u001b[39m {k: v \u001b[39mfor\u001b[39;00m k, v \u001b[39min\u001b[39;00m kwargs\u001b[39m.\u001b[39mitems() \u001b[39mif\u001b[39;00m k \u001b[39mnot\u001b[39;00m \u001b[39min\u001b[39;00m referenced}\n\u001b[0;32m--> 442\u001b[0m \u001b[39mreturn\u001b[39;00m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49m_request(\n\u001b[1;32m 443\u001b[0m url, params\u001b[39m=\u001b[39;49mparams, check_for_errors\u001b[39m=\u001b[39;49mcheck_for_errors\n\u001b[1;32m 444\u001b[0m )\n", - "File \u001b[0;32m~/Desktop/code/strava/.venv/lib/python3.12/site-packages/stravalib/protocol.py:327\u001b[0m, in \u001b[0;36mApiV3._request\u001b[0;34m(self, url, params, files, method, check_for_errors)\u001b[0m\n\u001b[1;32m 324\u001b[0m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mrate_limiter(raw\u001b[39m.\u001b[39mheaders, method)\n\u001b[1;32m 326\u001b[0m \u001b[39mif\u001b[39;00m check_for_errors:\n\u001b[0;32m--> 327\u001b[0m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49m_handle_protocol_error(raw)\n\u001b[1;32m 329\u001b[0m \u001b[39m# 204 = No content\u001b[39;00m\n\u001b[1;32m 330\u001b[0m \u001b[39mif\u001b[39;00m raw\u001b[39m.\u001b[39mstatus_code \u001b[39min\u001b[39;00m [\u001b[39m204\u001b[39m]:\n", - "File \u001b[0;32m~/Desktop/code/strava/.venv/lib/python3.12/site-packages/stravalib/protocol.py:371\u001b[0m, in \u001b[0;36mApiV3._handle_protocol_error\u001b[0;34m(self, response)\u001b[0m\n\u001b[1;32m 369\u001b[0m \u001b[39melif\u001b[39;00m response\u001b[39m.\u001b[39mstatus_code \u001b[39m==\u001b[39m \u001b[39m401\u001b[39m:\n\u001b[1;32m 370\u001b[0m msg \u001b[39m=\u001b[39m \u001b[39m\"\u001b[39m\u001b[39m{}\u001b[39;00m\u001b[39m: \u001b[39m\u001b[39m{}\u001b[39;00m\u001b[39m\"\u001b[39m\u001b[39m.\u001b[39mformat(response\u001b[39m.\u001b[39mreason, error_str)\n\u001b[0;32m--> 371\u001b[0m \u001b[39mraise\u001b[39;00m exc\u001b[39m.\u001b[39mAccessUnauthorized(msg, response\u001b[39m=\u001b[39mresponse)\n\u001b[1;32m 372\u001b[0m \u001b[39melif\u001b[39;00m \u001b[39m400\u001b[39m \u001b[39m<\u001b[39m\u001b[39m=\u001b[39m response\u001b[39m.\u001b[39mstatus_code \u001b[39m<\u001b[39m \u001b[39m500\u001b[39m:\n\u001b[1;32m 373\u001b[0m msg \u001b[39m=\u001b[39m \u001b[39m\"\u001b[39m\u001b[39m{}\u001b[39;00m\u001b[39m Client Error: \u001b[39m\u001b[39m{}\u001b[39;00m\u001b[39m [\u001b[39m\u001b[39m{}\u001b[39;00m\u001b[39m]\u001b[39m\u001b[39m\"\u001b[39m\u001b[39m.\u001b[39mformat(\n\u001b[1;32m 374\u001b[0m response\u001b[39m.\u001b[39mstatus_code,\n\u001b[1;32m 375\u001b[0m response\u001b[39m.\u001b[39mreason,\n\u001b[1;32m 376\u001b[0m error_str,\n\u001b[1;32m 377\u001b[0m )\n", - "\u001b[0;31mAccessUnauthorized\u001b[0m: Unauthorized: Authorization Error: [{'resource': 'Athlete', 'field': 'access_token', 'code': 'invalid'}]" + "athlete_id=98390356 token still valid until 2024-08-24 23:24:34+00:00\n", + "{\n", + " \"athlete_id\": 98390356,\n", + " \"training_week_planning\": \"To distribute the total weekly mileage of 44-46 miles, with a long run of 17-18 miles on Saturday, we can start by planning the rest of the week. If the long run is 18 miles, this leaves us with 26-28 miles for the other sessions. We want to have a speed workout on Wednesday and make Fridays a moderate run to prepare for the Saturday long run. Considering Monday and Thursday as easy runs for recovery, and Tuesday a rest day or a very light session if needed. Sunday is a rest day for full recovery.\\n\\n- Saturday: Long run = 18 miles\\n- Wednesday: Speed workout (such as 5 miles of intervals) = 7 miles total\\n- Monday: Easy run = 5 miles\\n- Thursday: Easy run = 5 miles\\n- Friday: Moderate run = 7 miles\\n\\nThis gives us: 18 (long) + 7 (speed) + 5 (easy) + 5 (easy) + 7 (moderate) = 42 miles, leaving us 2-4 miles to adjust over the week. Let's add another mile to Thursday for a total of 44 miles. \\n\\nTherefore, our total mileage distribution is 45 miles: \\n- Easy runs: Monday and Thursday totaling 11 miles \\n- Moderate run: Friday 7 miles \\n- Speed workout: Wednesday 7 miles \\n- Recovery efforts or rest days: Tuesday and Sunday\\n- Long run: Saturday 18 miles\",\n", + " \"training_week\": [\n", + " {\n", + " \"day\": \"mon\",\n", + " \"session_type\": \"easy run\",\n", + " \"distance\": 5.0,\n", + " \"notes\": \"Easy pace, focus on recovery from the weekend's long run\"\n", + " },\n", + " {\n", + " \"day\": \"tues\",\n", + " \"session_type\": \"rest day\",\n", + " \"distance\": 0.0,\n", + " \"notes\": \"Rest or light cross-training if necessary\"\n", + " },\n", + " {\n", + " \"day\": \"wed\",\n", + " \"session_type\": \"speed workout\",\n", + " \"distance\": 7.0,\n", + " \"notes\": \"2x2mi @ 10k pace with warm-up and cool-down\"\n", + " },\n", + " {\n", + " \"day\": \"thurs\",\n", + " \"session_type\": \"easy run\",\n", + " \"distance\": 6.0,\n", + " \"notes\": \"Easy pace, gentle effort throughout\"\n", + " },\n", + " {\n", + " \"day\": \"fri\",\n", + " \"session_type\": \"moderate run\",\n", + " \"distance\": 7.0,\n", + " \"notes\": \"Steady effort, faster than easy pace but comfortable\"\n", + " },\n", + " {\n", + " \"day\": \"sat\",\n", + " \"session_type\": \"long run\",\n", + " \"distance\": 18.0,\n", + " \"notes\": \"Long slow distance, maintain a steady pace throughout\"\n", + " },\n", + " {\n", + " \"day\": \"sun\",\n", + " \"session_type\": \"rest day\",\n", + " \"distance\": 0.0,\n", + " \"notes\": \"Full rest day to recover from the long run\"\n", + " }\n", + " ],\n", + " \"typical_week_training_review\": \"Your training schedule shows a strong routine with consistency on Wednesdays for more structured workouts and Saturdays for long runs, aligned with your preference for these days. Mondays and Fridays appear to serve as moderate recovery days, while Tuesdays and Thursdays seem lighter, potentially allowing for rest or additional cross-training, with Sundays typically being your rest day or an easy effort.\",\n", + " \"weekly_mileage_target\": \"Based on your recent training history, you've been steadily increasing your mileage and long run distances quite well. For Week 34, let's target a total weekly mileage of around 44-46 miles, maintaining a conservative increase to ensure continued progress while reducing injury risk. Aim for a long run of 17-18 miles on Saturday, as you're preparing your body to gradually extend endurance without overexerting yourself. Make sure to continue prioritizing recovery and listening to your body\\u2019s signals to adjust as needed.\"\n", + "}\n", + "training_week_with_coaching.total_weekly_mileage=43.0\n", + "Subject: Training Schedule Just Dropped 🏃\n", + "To: {'email': 'voynow99@gmail.com', 'name': 'Jamie Voynow'}\n", + "Sender: {'name': 'Jamie Voynow', 'email': 'voynowtestaddress@gmail.com'}\n", + "HTML Content: \n", + " \n", + " \n", + "