import os from datetime import datetime, timedelta import logging import gradio as gr from whoop import WhoopClient # Configure logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) # Global Whoop client whoop_client = None def initialize_whoop_client_with_input(email, password): """ Authenticates a user using the provided WHOOP email and password. Stores credentials in the environment variables and initializes the WhoopClient. """ global whoop_client if not email or not password: return "❌ Please enter both email and password." os.environ["WHOOP_EMAIL"] = email os.environ["WHOOP_PASSWORD"] = password try: whoop_client = WhoopClient(username=email, password=password) return "✅ Successfully authenticated with Whoop." except Exception as e: return f"❌ Authentication failed: {e}" def get_latest_cycle_gr(): """ Retrieves the most recent WHOOP cycle (recovery data) for the authenticated user. """ if not whoop_client: return "❌ Not authenticated." try: end_date = datetime.now().strftime("%Y-%m-%d") start_date = (datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d") cycles = whoop_client.get_cycle_collection(start_date, end_date) return cycles except Exception as e: return f"❌ Error: {e}" def get_average_strain_gr(days): """ Calculates the average strain over a given number of past days. """ if not whoop_client: return "❌ Not authenticated." try: end_date = datetime.now().strftime("%Y-%m-%d") start_date = (datetime.now() - timedelta(days=int(days))).strftime("%Y-%m-%d") cycles = whoop_client.get_cycle_collection(start_date, end_date) if not cycles: return "⚠️ No cycle data available." strains = [c['score']['strain'] for c in cycles if c.get('score') and c['score'].get('strain') is not None] if not strains: return "⚠️ No strain data available." avg = sum(strains) / len(strains) return f"📊 Average Strain over {days} days: {avg:.2f} (from {len(strains)} entries)" except Exception as e: return f"❌ Error: {e}" def get_workouts_gr(): """ Fetches recent workouts for the authenticated user. """ if not whoop_client: return "❌ Not authenticated." try: end_date = datetime.now().strftime("%Y-%m-%d") start_date = (datetime.now() - timedelta(days=10)).strftime("%Y-%m-%d") workouts = whoop_client.get_workout_collection(start_date, end_date) if not workouts: return "⚠️ No workout data available." summary = "\n\n".join([f"🏋️ Workout {i+1}:\nType: {w.get('sport_id')}, Avg HR: {w.get('avg_heart_rate')}, Calories: {w.get('kilojoule')} KJ" for i, w in enumerate(workouts)]) return summary except Exception as e: return f"❌ Error fetching workouts: {e}" def get_sleeps_gr(): """ Fetches recent sleep records for the authenticated user. """ if not whoop_client: return "❌ Not authenticated." # try: end_date = datetime.now().strftime("%Y-%m-%d") start_date = (datetime.now() - timedelta(days=10)).strftime("%Y-%m-%d") sleeps = whoop_client.get_sleep_collection(start_date, end_date) return sleeps # UI definition with gr.Blocks(title="Whoop API Explorer") as demo: gr.Markdown("# 🧠 WHOOP API Dashboard") gr.Markdown("Easily authenticate and explore your WHOOP recovery and strain data.") with gr.Group(): gr.Markdown("## 🔐 Authentication") with gr.Row(): email_input = gr.Textbox(label="WHOOP Email", placeholder="your@email.com") password_input = gr.Textbox(label="WHOOP Password", type="password", placeholder="••••••••") auth_button = gr.Button("Authenticate") auth_output = gr.Label(label="Auth Status") with gr.Group(): gr.Markdown("## 🔁 Latest Recovery Cycle") cycle_button = gr.Button("Fetch Latest Cycle") latest_recovery = gr.Label(label="Recovery Score") cycle_details = gr.Textbox(label="Full Cycle Data", visible=True, lines=6) with gr.Group(): gr.Markdown("## 🏋️ Workout Summary") workout_button = gr.Button("Fetch Recent Workouts") workout_output = gr.Textbox(label="Workout Summary", lines=10) with gr.Group(): gr.Markdown("## 😴 Sleep Summary") sleep_button = gr.Button("Fetch Recent Sleeps") sleep_output = gr.Textbox(label="Sleep Summary", lines=10) # Bind actions auth_button.click(fn=initialize_whoop_client_with_input, inputs=[email_input, password_input], outputs=auth_output) cycle_button.click(fn=get_latest_cycle_gr, outputs=cycle_details) workout_button.click(fn=get_workouts_gr, outputs=workout_output) sleep_button.click(fn=get_sleeps_gr, outputs=sleep_output) # Launch app if __name__ == "__main__": demo.launch(mcp_server=True)