diff --git a/examples/challenge_tracker_examples.py b/examples/challenge_tracker_examples.py index 763be5a..80330e8 100644 --- a/examples/challenge_tracker_examples.py +++ b/examples/challenge_tracker_examples.py @@ -10,17 +10,35 @@ async def main(): - otf = Otf(USERNAME, PASSWORD) + async with Otf(USERNAME, PASSWORD) as otf: - # challenge tracker content is an overview of the challenges OTF runs - # and your participation in them - challenge_tracker_content = await otf.get_challenge_tracker_content() - print(challenge_tracker_content.benchmarks[0].model_dump_json(indent=4)) + # challenge tracker content is an overview of the challenges OTF runs + # and your participation in them + challenge_tracker_content = await otf.get_challenge_tracker_content() + print(challenge_tracker_content.benchmarks[0].model_dump_json(indent=4)) - """ - { - "equipment_id": 2, - "equipment_name": "Treadmill", + """ + { + "equipment_id": 2, + "equipment_name": "Treadmill", + "years": [ + { + "year": "2024", + "is_participated": false, + "in_progress": false + }, + ... + ], + "logo_url": "https://otf-icons.s3.amazonaws.com/benchmarks/Treadmill.png" + } + """ + + print(challenge_tracker_content.challenges[0].model_dump_json(indent=4)) + """ + { + "challenge_category_id": 10, + "challenge_sub_category_id": 8, + "challenge_name": "Catch Me If You Can 3G", "years": [ { "year": "2024", @@ -29,85 +47,67 @@ async def main(): }, ... ], - "logo_url": "https://otf-icons.s3.amazonaws.com/benchmarks/Treadmill.png" - } - """ + "logo_url": "https://otf-icons.s3.amazonaws.com/challenges/CatchMeIfYouCan.png" + } + """ - print(challenge_tracker_content.challenges[0].model_dump_json(indent=4)) - """ - { - "challenge_category_id": 10, - "challenge_sub_category_id": 8, - "challenge_name": "Catch Me If You Can 3G", - "years": [ - { - "year": "2024", - "is_participated": false, - "in_progress": false - }, - ... - ], - "logo_url": "https://otf-icons.s3.amazonaws.com/challenges/CatchMeIfYouCan.png" - } - """ + # challenge tracker details are detailed information about specific challenges + # this endpoint takes an equipment type and a challenge type as arguments + tread_challenge_details = await otf.get_challenge_tracker_detail(EquipmentType.Treadmill, ChallengeType.Other) + print(tread_challenge_details.details[0].model_dump_json(indent=4)) - # challenge tracker details are detailed information about specific challenges - # this endpoint takes an equipment type and a challenge type as arguments - tread_challenge_details = await otf.get_challenge_tracker_detail(EquipmentType.Treadmill, ChallengeType.Other) - print(tread_challenge_details.details[0].model_dump_json(indent=4)) - - """ - { - "challenge_category_id": 10, - "challenge_sub_category_id": null, - "equipment_id": 2, - "equipment_name": "Treadmill", - "metric_entry": { - "title": "22 MIN", + """ + { + "challenge_category_id": 10, + "challenge_sub_category_id": null, "equipment_id": 2, - "entry_type": "Distance", - "metric_key": "22MIN", - "min_value": "0.16093440000000003", - "max_value": "8.04672" - }, - "challenge_name": "Catch me If You Can", - "logo_url": "https://otf-icons.s3.amazonaws.com/challenges/CatchMeIfYouCan.png", - "best_record": 1.40012928, - "last_record": 1.40012928, - "previous_record": 1.40012928, - "unit": "km", - "goals": null, - "challenge_histories": [ - { - "challenge_objective": "None", - "challenge_id": 449906, - "studio_id": 1267, - "studio_name": "AnyTown OH - East", - "start_date": "2024-02-06 00:00:00", - "end_date": "2024-02-06 23:59:00", - "total_result": 1.40012928, - "is_finished": true, - "benchmark_histories": [ - { - "studio_name": "AnyTown OH - East", - "equipment_id": 2, - "result": 1.40012928, - "date_created": "2024-02-06 16:01:26", - "date_updated": "2024-02-06 16:01:26", - "class_time": "2024-02-06 09:45:00", - "challenge_sub_category_id": null, - "class_id": 86842386, - "substitute_id": 1, - "weight_lbs": 0, - "workout_type_id": null, - "workout_id": null, - "linked_challenges": [] - } - ] - } - ] - } - """ + "equipment_name": "Treadmill", + "metric_entry": { + "title": "22 MIN", + "equipment_id": 2, + "entry_type": "Distance", + "metric_key": "22MIN", + "min_value": "0.16093440000000003", + "max_value": "8.04672" + }, + "challenge_name": "Catch me If You Can", + "logo_url": "https://otf-icons.s3.amazonaws.com/challenges/CatchMeIfYouCan.png", + "best_record": 1.40012928, + "last_record": 1.40012928, + "previous_record": 1.40012928, + "unit": "km", + "goals": null, + "challenge_histories": [ + { + "challenge_objective": "None", + "challenge_id": 449906, + "studio_id": 1267, + "studio_name": "AnyTown OH - East", + "start_date": "2024-02-06 00:00:00", + "end_date": "2024-02-06 23:59:00", + "total_result": 1.40012928, + "is_finished": true, + "benchmark_histories": [ + { + "studio_name": "AnyTown OH - East", + "equipment_id": 2, + "result": 1.40012928, + "date_created": "2024-02-06 16:01:26", + "date_updated": "2024-02-06 16:01:26", + "class_time": "2024-02-06 09:45:00", + "challenge_sub_category_id": null, + "class_id": 86842386, + "substitute_id": 1, + "weight_lbs": 0, + "workout_type_id": null, + "workout_id": null, + "linked_challenges": [] + } + ] + } + ] + } + """ if __name__ == "__main__": diff --git a/examples/class_bookings_examples.py b/examples/class_bookings_examples.py index ee32892..c176ba5 100644 --- a/examples/class_bookings_examples.py +++ b/examples/class_bookings_examples.py @@ -11,115 +11,114 @@ async def main(): - otf = Otf(USERNAME, PASSWORD) - - resp = await otf.get_bookings(start_date=datetime.today().date()) - print(resp.model_dump_json(indent=4)) - - studios = await otf.search_studios_by_geo(40.7831, 73.9712, distance=100) - - studio_uuids = [studio.studio_uuid for studio in studios.studios] - - # To get upcoming classes you can call the `get_classes` method - # You can pass a list of studio_uuids or, if you want to get classes from your home studio, leave it empty - # this also takes a start date, end date, and limit - these are not sent to the API, they are used in the - # client to filter the results - classes = await otf.get_classes(studio_uuids, day_of_week=[DoW.TUESDAY, DoW.THURSDAY, DoW.SATURDAY]) - - print(classes.classes[0].model_dump_json(indent=4)) - - """ - { - "id": "0e39ef70-7403-49c1-8605-4a72643bd201", - "ot_base_class_uuid": "08cebfdb-e127-48d4-8a7f-e6ea4dd85c18", - "starts_at": "2024-06-13 10:00:00+00:00", - "starts_at_local": "2024-06-13 05:00:00", - "ends_at": "2024-06-13 11:00:00+00:00", - "ends_at_local": "2024-06-13 06:00:00", - "name": "Orange 3G", - "type": "ORANGE_60", - "studio": ..., - "coach": ..., - "max_capacity": 36, - "booking_capacity": 36, - "waitlist_size": 0, - "full": false, - "waitlist_available": false, - "canceled": false, - "mbo_class_id": "30809", - "mbo_class_schedule_id": "2655", - "mbo_class_description_id": "102", - "created_at": "2024-05-14 10:33:32.406000+00:00", - "updated_at": "2024-06-13 01:58:55.233000+00:00" - } - """ - - # You can also get the classes that you have booked - # You can pass a start_date, end_date, status, and limit as arguments - - bookings = await otf.get_bookings() - - print("Latest Upcoming Class:") - print(bookings.bookings[-1].model_dump_json(indent=4)) - - """ - { - "class_booking_id": 870700285, - "class_booking_uuid": "a36d76b1-0a55-4143-b96b-646e7520ca39", - "studio_id": 1234, - "class_id": 376344282, - "is_intro": false, - "member_id": 234488148, - "status": "Booked", - "booked_date": "2024-09-10T04:26:11Z", - "checked_in_date": null, - "cancelled_date": null, - "created_date": "2024-09-10T04:26:11Z", - "updated_date": "2024-09-10T04:26:13Z", - "is_deleted": false, - "waitlist_position": null, - "otf_class": { - "starts_at_local": "2024-09-28T10:30:00", - "ends_at_local": "2024-09-28T11:20:00", - "name": "Tread 50", - "class_uuid": "82ec9b55-950a-484f-818f-cd2344ce83fd", - "is_available": true, - "is_cancelled": false, - "program_name": "Group Fitness", - "coach_id": 1204786, - "studio": { - "studio_uuid": "49e360d1-f8ef-4091-a23f-61b321cb283c", - "studio_name": "AnyTown OH - East", - "description": "", - "status": "Active", - "time_zone": "America/Chicago", - "studio_id": 1267, - "allows_cr_waitlist": true - }, - "coach": { - "coach_uuid": "973516a8-0c6b-41ec-916c-1da9913b9a16", - "name": "Friendly", - "first_name": "Friendly", - "last_name": "Coach" - }, - "location": { - "address_one": "123 S Main St", - "address_two": null, - "city": "AnyTown", - "country": null, - "distance": null, - "latitude": 91.73407745, - "location_name": null, - "longitude": -80.92264626, - "phone_number": "2042348963", - "postal_code": "11111", - "state": "Ohio" + async with Otf(USERNAME, PASSWORD) as otf: + resp = await otf.get_bookings(start_date=datetime.today().date()) + print(resp.model_dump_json(indent=4)) + + studios = await otf.search_studios_by_geo(40.7831, 73.9712, distance=100) + + studio_uuids = [studio.studio_uuid for studio in studios.studios] + + # To get upcoming classes you can call the `get_classes` method + # You can pass a list of studio_uuids or, if you want to get classes from your home studio, leave it empty + # this also takes a start date, end date, and limit - these are not sent to the API, they are used in the + # client to filter the results + classes = await otf.get_classes(studio_uuids, day_of_week=[DoW.TUESDAY, DoW.THURSDAY, DoW.SATURDAY]) + + print(classes.classes[0].model_dump_json(indent=4)) + + """ + { + "id": "0e39ef70-7403-49c1-8605-4a72643bd201", + "ot_base_class_uuid": "08cebfdb-e127-48d4-8a7f-e6ea4dd85c18", + "starts_at": "2024-06-13 10:00:00+00:00", + "starts_at_local": "2024-06-13 05:00:00", + "ends_at": "2024-06-13 11:00:00+00:00", + "ends_at_local": "2024-06-13 06:00:00", + "name": "Orange 3G", + "type": "ORANGE_60", + "studio": ..., + "coach": ..., + "max_capacity": 36, + "booking_capacity": 36, + "waitlist_size": 0, + "full": false, + "waitlist_available": false, + "canceled": false, + "mbo_class_id": "30809", + "mbo_class_schedule_id": "2655", + "mbo_class_description_id": "102", + "created_at": "2024-05-14 10:33:32.406000+00:00", + "updated_at": "2024-06-13 01:58:55.233000+00:00" + } + """ + + # You can also get the classes that you have booked + # You can pass a start_date, end_date, status, and limit as arguments + + bookings = await otf.get_bookings() + + print("Latest Upcoming Class:") + print(bookings.bookings[-1].model_dump_json(indent=4)) + + """ + { + "class_booking_id": 870700285, + "class_booking_uuid": "a36d76b1-0a55-4143-b96b-646e7520ca39", + "studio_id": 1234, + "class_id": 376344282, + "is_intro": false, + "member_id": 234488148, + "status": "Booked", + "booked_date": "2024-09-10T04:26:11Z", + "checked_in_date": null, + "cancelled_date": null, + "created_date": "2024-09-10T04:26:11Z", + "updated_date": "2024-09-10T04:26:13Z", + "is_deleted": false, + "waitlist_position": null, + "otf_class": { + "starts_at_local": "2024-09-28T10:30:00", + "ends_at_local": "2024-09-28T11:20:00", + "name": "Tread 50", + "class_uuid": "82ec9b55-950a-484f-818f-cd2344ce83fd", + "is_available": true, + "is_cancelled": false, + "program_name": "Group Fitness", + "coach_id": 1204786, + "studio": { + "studio_uuid": "49e360d1-f8ef-4091-a23f-61b321cb283c", + "studio_name": "AnyTown OH - East", + "description": "", + "status": "Active", + "time_zone": "America/Chicago", + "studio_id": 1267, + "allows_cr_waitlist": true + }, + "coach": { + "coach_uuid": "973516a8-0c6b-41ec-916c-1da9913b9a16", + "name": "Friendly", + "first_name": "Friendly", + "last_name": "Coach" + }, + "location": { + "address_one": "123 S Main St", + "address_two": null, + "city": "AnyTown", + "country": null, + "distance": null, + "latitude": 91.73407745, + "location_name": null, + "longitude": -80.92264626, + "phone_number": "2042348963", + "postal_code": "11111", + "state": "Ohio" + }, + "virtual_class": null }, - "virtual_class": null - }, - "is_home_studio": true - } - """ + "is_home_studio": true + } + """ if __name__ == "__main__": diff --git a/examples/studio_examples.py b/examples/studio_examples.py index af973d1..407b7ad 100644 --- a/examples/studio_examples.py +++ b/examples/studio_examples.py @@ -9,66 +9,65 @@ async def main(): - otf = Otf(USERNAME, PASSWORD) + async with Otf(USERNAME, PASSWORD) as otf: + # if you need to figure out what studios are in an area, you can call `search_studios_by_geo` + # which takes latitude, longitude, distance, page_index, and page_size as arguments + # but you'll generally just need the first 3 + # same as with classes, you can leave it blank and get the studios within 50 miles of your home studio + studios_by_geo = await otf.search_studios_by_geo() + print(studios_by_geo.studios[0].model_dump_json(indent=4)) - # if you need to figure out what studios are in an area, you can call `search_studios_by_geo` - # which takes latitude, longitude, distance, page_index, and page_size as arguments - # but you'll generally just need the first 3 - # same as with classes, you can leave it blank and get the studios within 50 miles of your home studio - studios_by_geo = await otf.search_studios_by_geo() - print(studios_by_geo.studios[0].model_dump_json(indent=4)) + """ + { + "studio_id": 1297, + "studio_uuid": "8645fb2b-ef66-4d9d-bda1-f508091ec891", + "mbo_studio_id": 8612481, + "studio_number": "05414", + "studio_name": "AnyTown OH - East", + "studio_physical_location_id": 494, + "time_zone": "America/Chicago", + "contact_email": "studiomanager05414@orangetheoryfitness.com", + "studio_token": "ec2459b2-32b5-4b7e-9759-55270626925a", + "environment": "PROD", + "pricing_level": "", + "tax_rate": "0.000000", + "accepts_visa_master_card": true, + "accepts_american_express": true, + "accepts_discover": true, + "accepts_ach": false, + "is_integrated": true, + "description": "", + "studio_version": "", + "studio_status": "Active", + "open_date": "2017-01-13 00:00:00", + "re_open_date": "2020-05-26 00:00:00", + "studio_type_id": 2, + "sms_package_enabled": false, + "allows_dashboard_access": false, + "allows_cr_waitlist": true, + "cr_waitlist_flag_last_updated": "2020-07-09 02:43:55+00:00", + "royalty_rate": 0, + "marketing_fund_rate": 0, + "commission_percent": 0, + "is_mobile": null, + "is_otbeat": null, + "distance": 0.0, + "studio_location": ..., + "studio_location_localized": ..., + "studio_profiles": { + "is_web": true, + "intro_capacity": 1, + "is_crm": true + }, + "social_media_links": ... + } + """ - """ - { - "studio_id": 1297, - "studio_uuid": "8645fb2b-ef66-4d9d-bda1-f508091ec891", - "mbo_studio_id": 8612481, - "studio_number": "05414", - "studio_name": "AnyTown OH - East", - "studio_physical_location_id": 494, - "time_zone": "America/Chicago", - "contact_email": "studiomanager05414@orangetheoryfitness.com", - "studio_token": "ec2459b2-32b5-4b7e-9759-55270626925a", - "environment": "PROD", - "pricing_level": "", - "tax_rate": "0.000000", - "accepts_visa_master_card": true, - "accepts_american_express": true, - "accepts_discover": true, - "accepts_ach": false, - "is_integrated": true, - "description": "", - "studio_version": "", - "studio_status": "Active", - "open_date": "2017-01-13 00:00:00", - "re_open_date": "2020-05-26 00:00:00", - "studio_type_id": 2, - "sms_package_enabled": false, - "allows_dashboard_access": false, - "allows_cr_waitlist": true, - "cr_waitlist_flag_last_updated": "2020-07-09 02:43:55+00:00", - "royalty_rate": 0, - "marketing_fund_rate": 0, - "commission_percent": 0, - "is_mobile": null, - "is_otbeat": null, - "distance": 0.0, - "studio_location": ..., - "studio_location_localized": ..., - "studio_profiles": { - "is_web": true, - "intro_capacity": 1, - "is_crm": true - }, - "social_media_links": ... - } - """ - - # if you need to get detailed information about a studio, you can call `get_studio_detail` - # which takes a studio_uuid as an argument, but you can leave it blank to get details about your home studio - # this one has a result structure very much like the previous one - studio_detail = await otf.get_studio_detail() - print(studio_detail.model_dump_json(indent=4)) + # if you need to get detailed information about a studio, you can call `get_studio_detail` + # which takes a studio_uuid as an argument, but you can leave it blank to get details about your home studio + # this one has a result structure very much like the previous one + studio_detail = await otf.get_studio_detail() + print(studio_detail.model_dump_json(indent=4)) if __name__ == "__main__": diff --git a/examples/workout_examples.py b/examples/workout_examples.py index 3d911b3..dda29c9 100644 --- a/examples/workout_examples.py +++ b/examples/workout_examples.py @@ -9,184 +9,183 @@ async def main(): - otf = Otf(USERNAME, PASSWORD) + async with Otf(USERNAME, PASSWORD) as otf: + resp = await otf.get_member_lifetime_stats() + print(resp.model_dump_json(indent=4)) - resp = await otf.get_member_lifetime_stats() - print(resp.model_dump_json(indent=4)) + resp = await otf.get_body_composition_list() + print(resp.data[0].model_dump_json(indent=4)) - resp = await otf.get_body_composition_list() - print(resp.data[0].model_dump_json(indent=4)) - - # performance summaries are historical records of your performance in workouts - # `get_performance_summaries` takes a limit (default of 30) and returns a list of summaries - data_list = await otf.get_performance_summaries() - print(data_list.summaries[0].model_dump_json(indent=4)) - """ - { - "performance_summary_id": "29dd97f4-3418-4247-b35c-37eabc5e17f3", - "details": { - "calories_burned": 506, - "splat_points": 18, - "step_count": 0, - "active_time_seconds": 3413, - "zone_time_minutes": { - "gray": 2, - "blue": 13, - "green": 24, - "orange": 16, - "red": 2 - } - }, - "ratable": true, - "otf_class": { - "ot_base_class_uuid": "b6549fc2-a479-4b03-9303-e0e45dbcd8c9", - "starts_at_local": "2024-06-11T09:45:00", - "name": "Orange 60 Min 2G", - "coach": ..., - "studio": ..., - }, - "ratings": null - } - """ - - # you can get detailed information about a specific performance summary by calling `get_performance_summary` - # which takes a performance_summary_id as an argument - data = await otf.get_performance_summary(data_list.summaries[0].id) - print(data.model_dump_json(indent=4)) - - """ - { - "class_history_uuid": "29dd97f4-3418-4247-b35c-37eabc5e17f3", - "details": { - "calories_burned": 506, - "splat_points": 18, - "step_count": 3314, - "active_time_seconds": 0, - "zone_time_minutes": { - "gray": 2, - "blue": 13, - "green": 24, - "orange": 16, - "red": 2 + # performance summaries are historical records of your performance in workouts + # `get_performance_summaries` takes a limit (default of 30) and returns a list of summaries + data_list = await otf.get_performance_summaries() + print(data_list.summaries[0].model_dump_json(indent=4)) + """ + { + "performance_summary_id": "29dd97f4-3418-4247-b35c-37eabc5e17f3", + "details": { + "calories_burned": 506, + "splat_points": 18, + "step_count": 0, + "active_time_seconds": 3413, + "zone_time_minutes": { + "gray": 2, + "blue": 13, + "green": 24, + "orange": 16, + "red": 2 + } }, - "heart_rate": { - "max_hr": 0, - "peak_hr": 180, - "peak_hr_percent": 94, - "avg_hr": 149, - "avg_hr_percent": 78 + "ratable": true, + "otf_class": { + "ot_base_class_uuid": "b6549fc2-a479-4b03-9303-e0e45dbcd8c9", + "starts_at_local": "2024-06-11T09:45:00", + "name": "Orange 60 Min 2G", + "coach": ..., + "studio": ..., }, - "equipment_data": { - "treadmill": { - "avg_pace": { - "display_value": "15:23", - "display_unit": "min/mile", - "metric_value": "923" - }, - "avg_speed": { - "display_value": 3.9, - "display_unit": "mph", - "metric_value": 3.9 - }, - "max_pace": ..., - "max_speed": ..., - "moving_time": ..., - "total_distance": ..., - "avg_incline": ..., - "elevation_gained": ..., - "max_incline": ... + "ratings": null + } + """ + + # you can get detailed information about a specific performance summary by calling `get_performance_summary` + # which takes a performance_summary_id as an argument + data = await otf.get_performance_summary(data_list.summaries[0].id) + print(data.model_dump_json(indent=4)) + + """ + { + "class_history_uuid": "29dd97f4-3418-4247-b35c-37eabc5e17f3", + "details": { + "calories_burned": 506, + "splat_points": 18, + "step_count": 3314, + "active_time_seconds": 0, + "zone_time_minutes": { + "gray": 2, + "blue": 13, + "green": 24, + "orange": 16, + "red": 2 + }, + "heart_rate": { + "max_hr": 0, + "peak_hr": 180, + "peak_hr_percent": 94, + "avg_hr": 149, + "avg_hr_percent": 78 }, - "rower": ... + "equipment_data": { + "treadmill": { + "avg_pace": { + "display_value": "15:23", + "display_unit": "min/mile", + "metric_value": "923" + }, + "avg_speed": { + "display_value": 3.9, + "display_unit": "mph", + "metric_value": 3.9 + }, + "max_pace": ..., + "max_speed": ..., + "moving_time": ..., + "total_distance": ..., + "avg_incline": ..., + "elevation_gained": ..., + "max_incline": ... + }, + "rower": ... + } + }, + "ratable": false, + "otf_class": { + "starts_at_local": "2024-06-11T09:45:00", + "name": "Orange 60 Min 2G" } - }, - "ratable": false, - "otf_class": { - "starts_at_local": "2024-06-11T09:45:00", - "name": "Orange 60 Min 2G" } - } - """ + """ - # telemetry is a detailed record of a specific workout - minute by minute, or more granular if desired - # this endpoint takes a class_history_uuid, as well as a number of max data points - if you do not pass - # this value it will attempt to return enough data points for 30 second intervals + # telemetry is a detailed record of a specific workout - minute by minute, or more granular if desired + # this endpoint takes a class_history_uuid, as well as a number of max data points - if you do not pass + # this value it will attempt to return enough data points for 30 second intervals - telemetry = await otf.get_telemetry(performance_summary_id=data_list.summaries[0].id) - telemetry.telemetry = telemetry.telemetry[:2] - print(telemetry.model_dump_json(indent=4)) + telemetry = await otf.get_telemetry(performance_summary_id=data_list.summaries[0].id) + telemetry.telemetry = telemetry.telemetry[:2] + print(telemetry.model_dump_json(indent=4)) - """ - { - "member_uuid": "fa323d40-bfae-4e72-872c-e11188d182a7", - "class_history_uuid": "5945a723-930b-449a-bd8f-8267a4ff392f", - "class_start_time": "2024-06-11 14:46:07+00:00", - "max_hr": 191, - "zones": { - "gray": { - "start_bpm": 96, - "end_bpm": 116 - }, - "blue": { - "start_bpm": 117, - "end_bpm": 135 - }, - "green": { - "start_bpm": 136, - "end_bpm": 159 - }, - "orange": { - "start_bpm": 160, - "end_bpm": 175 - }, - "red": { - "start_bpm": 176, - "end_bpm": 191 - } - }, - "window_size": 30, - "telemetry": [ - { - "relative_timestamp": 0, - "hr": 105, - "agg_splats": 0, - "agg_calories": 2, - "timestamp": "2024-06-11 14:46:07+00:00", - "tread_data": { - "tread_speed": 1.34, - "tread_incline": 1.0, - "agg_tread_distance": 9 + """ + { + "member_uuid": "fa323d40-bfae-4e72-872c-e11188d182a7", + "class_history_uuid": "5945a723-930b-449a-bd8f-8267a4ff392f", + "class_start_time": "2024-06-11 14:46:07+00:00", + "max_hr": 191, + "zones": { + "gray": { + "start_bpm": 96, + "end_bpm": 116 }, - "row_data": { - "row_speed": 1.0, - "row_pps": 0.0, - "row_Spm": 0.0, - "agg_row_distance": 0, - "row_pace": 0 - } - }, - { - "relative_timestamp": 30, - "hr": 132, - "agg_splats": 0, - "agg_calories": 4, - "timestamp": "2024-06-11 14:46:37+00:00", - "tread_data": { - "tread_speed": 2.46, - "tread_incline": 1.0, - "agg_tread_distance": 62 + "blue": { + "start_bpm": 117, + "end_bpm": 135 }, - "row_data": { - "row_speed": 1.0, - "row_pps": 0.0, - "row_Spm": 0.0, - "agg_row_distance": 0, - "row_pace": 0 + "green": { + "start_bpm": 136, + "end_bpm": 159 + }, + "orange": { + "start_bpm": 160, + "end_bpm": 175 + }, + "red": { + "start_bpm": 176, + "end_bpm": 191 } }, - ... - ] - } - """ + "window_size": 30, + "telemetry": [ + { + "relative_timestamp": 0, + "hr": 105, + "agg_splats": 0, + "agg_calories": 2, + "timestamp": "2024-06-11 14:46:07+00:00", + "tread_data": { + "tread_speed": 1.34, + "tread_incline": 1.0, + "agg_tread_distance": 9 + }, + "row_data": { + "row_speed": 1.0, + "row_pps": 0.0, + "row_Spm": 0.0, + "agg_row_distance": 0, + "row_pace": 0 + } + }, + { + "relative_timestamp": 30, + "hr": 132, + "agg_splats": 0, + "agg_calories": 4, + "timestamp": "2024-06-11 14:46:37+00:00", + "tread_data": { + "tread_speed": 2.46, + "tread_incline": 1.0, + "agg_tread_distance": 62 + }, + "row_data": { + "row_speed": 1.0, + "row_pps": 0.0, + "row_Spm": 0.0, + "agg_row_distance": 0, + "row_pace": 0 + } + }, + ... + ] + } + """ if __name__ == "__main__": diff --git a/src/otf_api/api.py b/src/otf_api/api.py index 0684029..1003dce 100644 --- a/src/otf_api/api.py +++ b/src/otf_api/api.py @@ -111,6 +111,16 @@ def headers(self): "Accept": "application/json", } + async def __aenter__(self): + # Create the session only once when entering the context + self._session = aiohttp.ClientSession() + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + # Close the session when exiting the context + if self._session is not None: + await self._session.close() + @property def session(self): """Get the aiohttp session."""