|
|
|
import os |
|
from datetime import datetime, timedelta |
|
import logging |
|
|
|
import gradio as gr |
|
from whoop import WhoopClient |
|
|
|
|
|
logging.basicConfig(level=logging.INFO) |
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
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. |
|
|
|
Args: |
|
email (str): WHOOP account email. |
|
password (str): WHOOP account password. |
|
|
|
Returns: |
|
str: Success or error message indicating authentication status. |
|
""" |
|
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. |
|
|
|
Returns: |
|
str: Summary of latest recovery data or an error message. |
|
""" |
|
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) |
|
if not cycles: |
|
return "β οΈ No cycle data available." |
|
|
|
latest = cycles[0] |
|
score = latest.get("score", {}).get("recovery") |
|
recovery_score = f"{score}" if score is not None else "Not Available" |
|
return f"π Latest Cycle:\nRecovery Score: {recovery_score}\n\nFull Data:\n{latest}" |
|
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. |
|
|
|
Args: |
|
days (int): Number of days to include in the average. |
|
|
|
Returns: |
|
str: Average strain value or an error message. |
|
""" |
|
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 format_latest_cycle(raw_text): |
|
""" |
|
Formats the raw text returned by get_latest_cycle_gr for display. |
|
|
|
Args: |
|
raw_text (str): Raw output from get_latest_cycle_gr. |
|
|
|
Returns: |
|
Tuple[str, str]: A short summary and detailed cycle data. |
|
""" |
|
if raw_text.startswith("β") or raw_text.startswith("β οΈ"): |
|
return raw_text, "" |
|
try: |
|
lines = raw_text.splitlines() |
|
recovery_score_line = next((line for line in lines if "Recovery Score" in line), "Recovery Score: Not Available") |
|
full_data = "\n".join(lines[2:]) |
|
return recovery_score_line, full_data |
|
except Exception as e: |
|
return f"β Failed to parse cycle: {e}", "" |
|
|
|
|
|
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="[email protected]") |
|
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=False, lines=6) |
|
|
|
with gr.Group(): |
|
gr.Markdown("## π Strain Insights") |
|
with gr.Row(): |
|
days_input = gr.Number(value=7, label="Number of Days", precision=0) |
|
strain_button = gr.Button("Calculate Average Strain") |
|
average_strain = gr.Label(label="Average Strain") |
|
|
|
|
|
auth_button.click(fn=initialize_whoop_client_with_input, inputs=[email_input, password_input], outputs=auth_output) |
|
cycle_button.click(fn=lambda: format_latest_cycle(get_latest_cycle_gr()), outputs=[latest_recovery, cycle_details]) |
|
strain_button.click(fn=get_average_strain_gr, inputs=days_input, outputs=average_strain) |
|
|
|
|
|
if __name__ == "__main__": |
|
demo.launch(mcp_server=True) |