
    qi(6                         d Z ddlmZmZmZ ddlmZ ddlmZ ddlZddl	m
Z
mZ ddlmZmZmZmZmZmZmZmZmZ d	Z G d
 d          ZdS )z.WHOOP API v2 client for health data retrieval.    )datetime	timedeltatimezone)ZoneInfo)OptionalN   )	WhoopAuthget_auth_from_env)	RecoverySleepCycleWorkoutRecoverySummarySleepSummaryStrainSummaryWorkoutSummary	TodayDataz'https://api.prod.whoop.com/developer/v2c            
       l   e Zd ZdZddee         fdZedej	        fd            Z
defdZdded	ee         defd
Z	 	 	 d dee         dee         dedee         fdZ	 	 	 d dee         dee         dedee         fdZ	 	 	 d dee         dee         dedee         fdZ	 	 	 d dee         dee         dedee         fdZdefdZdefdZd!dedee         fdZd!dedee         fdZd!dedee         fdZd!dedee         fdZ e!dede"fd            Z#e!dee         defd            Z$dS )"WhoopClientzClient for WHOOP API v2.Nauthc                 >    |pt                      | _        d | _        d S N)r
   r   _client)selfr   s     l/Users/kimhansen/Desktop/03 Workspace/ceo-agents/chl-effectiveness/mcp-servers/whoop/src/whoop_mcp/client.py__init__zWhoopClient.__init__   s     /-//	/3    returnc                 ^    | j          t          j        t          d          | _         | j         S )z(Lazy-load HTTP client with auth headers.Ng      >@)base_urltimeout)r   httpxClientAPI_BASEr   s    r   clientzWhoopClient.client   s4     < <!  DL |r   c                 f    | j                                         }|st          d          d| ddS )z.Get request headers with current access token.z(Not authorized. Run setup_auth.py first.zBearer zapplication/json)AuthorizationzContent-Type)r   get_valid_access_token
ValueError)r   tokens     r   _get_headerszWhoopClient._get_headers$   sJ    	0022 	IGHHH.u...
 
 	
r   endpointparamsc                     | j                             ||                                 |          }|                                 |                                S )zMake authenticated GET request.)headersr.   )r&   getr,   raise_for_statusjson)r   r-   r.   responses       r   _getzWhoopClient._get.   sS    ;??%%'' # 
 

 	!!###}}r   
   startendlimitc                     d|i}|r|                     d          |d<   |r|                     d          |d<   |                     d|          }|pi                     d          pg S )z&Get recovery records for a date range.r9   %Y-%m-%dT%H:%M:%S.000Zr7   r8   z	/recoveryrecordsstrftimer5   r1   r   r7   r8   r9   r.   datas         r   get_recovery_collectionz#WhoopClient.get_recovery_collection<   s|     5! 	G#nn-EFFF7O 	CLL)ABBF5Myyf--
	**0b0r   c                     d|i}|r|                     d          |d<   |r|                     d          |d<   |                     d|          }|pi                     d          pg S )z#Get sleep records for a date range.r9   r;   r7   r8   z/activity/sleepr<   r=   r?   s         r   get_sleep_collectionz WhoopClient.get_sleep_collectionL   s}     5! 	G#nn-EFFF7O 	CLL)ABBF5Myy*F33
	**0b0r   c                     d|i}|r|                     d          |d<   |r|                     d          |d<   |                     d|          }|pi                     d          pg S )z*Get physiological cycles for a date range.r9   r;   r7   r8   z/cycler<   r=   r?   s         r   get_cycle_collectionz WhoopClient.get_cycle_collection\   s|     5! 	G#nn-EFFF7O 	CLL)ABBF5Myy6**
	**0b0r   c                     d|i}|r|                     d          |d<   |r|                     d          |d<   |                     d|          }|pi                     d          pg S )z%Get workout records for a date range.r9   r;   r7   r8   z/activity/workoutr<   r=   r?   s         r   get_workout_collectionz"WhoopClient.get_workout_collectionl   s}     5! 	G#nn-EFFF7O 	CLL)ABBF5Myy,f55
	**0b0r   c                 ,    |                      d          S )zGet user profile information.z/user/profile/basic)r5   r%   s    r   get_user_profilezWhoopClient.get_user_profile|   s    yy.///r   c                 	   t          j        t          j                                                  }|t          d          z
  }t          j        |t           j                                        t          j                  }t          j        t          j                  }d}d}d}	 | 	                    ||d          }|r|d         }	|	
                    d          pi }
|

                    d          |

                    d	          r$t          |

                    d	d          d          nd|

                    d
          r#t          |

                    d
d                    nd|

                    d          |

                    d          r$t          |

                    dd          d          nd|                     |

                    d                    d}n)# t          $ r}t          d|            Y d}~nd}~ww xY w	 |                     ||d          }|r|d         }|
                    d          pi }
|

                    d          pi }t          |                     |
                    dd          |
                    dd          z
            d          |

                    d          r$t          |

                    dd          d          nd|

                    d          r$t          |

                    dd          d          ndt          |                     |
                    dd                    d          t          |                     |
                    dd                    d          t          |                     |
                    dd                    d          |
                    dd          d}n)# t          $ r}t          d|            Y d}~nd}~ww xY w	 |                     ||d          }|r|d         }|
                    d          pi }
t          |

                    dd          d          t          |

                    dd                    |

                    dd          |

                    dd          d}n)# t          $ r}t          d|            Y d}~nd}~ww xY wt%          |                                |||           S )!z4Get today's combined health data for daily check-in.r   days)tzinfoNr7   r8   r9   r   scorerecovery_scorehrv_rmssd_milliresting_heart_ratespo2_percentageskin_temp_celsius)rO   hrvrhrspo2	skin_tempzonezError fetching recovery: stage_summarytotal_in_bed_time_millitotal_awake_time_millisleep_efficiency_percentagesleep_performance_percentagetotal_light_sleep_time_milli total_slow_wave_sleep_time_millitotal_rem_sleep_time_millidisturbance_count)total_hours
efficiencyperformancelight_hours
deep_hours	rem_hoursdisturbanceszError fetching sleep: strain	kilojouleaverage_heart_ratemax_heart_rate)
day_straincaloriesavg_hrmax_hrzError fetching strain: )daterecoverysleeprj   )r   nowr   utcrr   r   combinemintimerA   r1   round_get_recovery_zone	ExceptionprintrC   _ms_to_hoursrE   r   	isoformat)r   today	yesterdayr7   r8   recovery_data
sleep_datastrain_data
recoveriesrrO   esleepssstagescyclescs                    r   get_today_datazWhoopClient.get_today_data   sP   X\**//11I1----	  HL,=,=,?,?UUUl8<((
	355EsRS5TTJ 
qMg,""YY'788HM		RcHdHdn5+<a!@!@!DDDjnHM		RfHgHgq5+?!C!CDDDmq!II&788PUPYPYZmPnPn!xuyy1Da'H'H!!L!L!Ltx 33EII>N4O4OPP! !  	3 	3 	31a1122222222	3	0..U1.MMF 1Ig,"?339r#():):6::F_ab;c;cflfpfp  rJ  LM  gN  gN  <N  *O  *O  QR  $S  $S[`[d[d  fC  \D  \D  #N%		2OQR(S(SUV"W"W"W  JN]b]f]f  hF  ^G  ^G  $Q53QST)U)UWX#Y#Y#Y  MQ#():):6::Fdfg;h;h)i)ikl#m#m"'(9(9&**Egij:k:k(l(lno"p"p!&t'8'8D`bc9d9d'e'egh!i!i$*JJ/BA$F$F 
  	0 	0 	0.1..////////	0	1..U1.MMF 1Ig,""'		(A(>(>"B"B %eiiQ&?&? @ @#ii(<a@@#ii(8!<<	   	1 	1 	1/A//00000000	1 """	
 
 
 	
sK   &D9G   
H*HH
GO 
O=!O88O=B+R- -
S7SS   rL   c                 ^   t          j        t          j                  }|t	          |          z
  }|                     |||          }g }|D ]]}|                    d          pi }|                    dd          }|r
|dd         nd}	|                    t          |	|                    d	          |                    d
          r$t          |                    d
d          d          nd|                    d          r#t          |                    dd                    nd|                    d          |                    d          r$t          |                    dd          d          nd| 
                    |                    d	                                         _|S )z(Get recovery scores for the past N days.rK   rN   rO   
created_at Nr6   unknownrP   rQ   r   r   rR   rS   rT   )rr   rO   rU   rV   rW   rX   rY   )r   ru   r   rv   r   rA   r1   appendr   rz   r{   )
r   rL   r8   r7   r   resultsr   rO   createddate_strs
             r   get_recovery_historyz WhoopClient.get_recovery_history   s   l8<((iT****113d1SS
 	 	AEE'NN(bEeeL"--G'.=wss||IHNN?ii 011AFK\A]A]gE%))$5q991===cgAFK_A`A`jE%))$8!<<===fjYY011INSfIgIgq%		*=q A A1EEEmq,,UYY7G-H-HII       r   c                    t          j        t          j                  }|t	          |          z
  }|                     |||          }g }|D ]}|                    d          pi }|                    d          pi }|                    dd          }	|	r
|	dd         nd	}
|                    d
d          |                    dd          z
  }|                    t          |
t          | 
                    |          d          |                    d          r$t          |                    dd          d          nd|                    d          r$t          |                    dd          d          ndt          | 
                    |                    dd                    d          t          | 
                    |                    dd                    d          t          | 
                    |                    dd                    d          |                    dd                               |S )z#Get sleep data for the past N days.rK   rN   rO   rZ   r   r   Nr6   r   r[   r   r\   r   r]   r^   r_   r`   ra   rb   )rr   rc   rd   re   rf   rg   rh   ri   )r   ru   r   rv   r   rC   r1   r   r   rz   r~   )r   rL   r8   r7   r   r   r   rO   r   r   r   total_sleep_mss               r   get_sleep_historyzWhoopClient.get_sleep_history   s4   l8<((iT******Ct*LL 	 	AEE'NN(bEYY//52FeeL"--G'.=wss||IH#ZZ(A1EE

SkmnHoHooNNN<!$"3"3N"C"CQGGTYT]T]^{T|T|  G5+H!!L!LaPPP  CGV[V_V_`~VV  JE%)),JA"N"NPQRRR  FJ!$"3"3FJJ?]_`4a4a"b"bdeff !2!26::>`bc3d3d!e!eghii 1 1&**=Y[\2]2] ^ ^`abb#ZZ(;Q??	 	 	 	 	 	 	 r   c                 @   t          j        t          j                  }|t	          |          z
  }|                     |||          }g }|D ]}|                    d          pi }|                    dd          }|r
|dd         nd}	|                    t          |	t          |                    d	d
          d          t          |                    dd
                    |                    dd
          |                    dd
                               |S )z$Get strain data for the past N days.rK   rN   rO   r   r   Nr6   r   rj   r   r   rk   rl   rm   )rr   rn   ro   rp   rq   )
r   ru   r   rv   r   rE   r1   r   r   rz   )
r   rL   r8   r7   r   r   r   rO   r   r   s
             r   get_strain_historyzWhoopClient.get_strain_history  s!   l8<((iT******Ct*LL 	 	AEE'NN(bEeeL"--G'.=wss||IHNN= 8Q!7!7;;uyya8899yy!5q99yy!1155       r   c                    t          j        t          j                  }|t	          |          z
  }|                     ||d          }g }|D ]:}|                    d          pi }|                    dd          }|                    dd          }	|rut          j        |                    dd	                    }
|
	                    t          d
                    }|                    d          }|                    d          }nd}d}d}|r|	r	 t          j        |                    dd	                    }t          j        |	                    dd	                    }||z
                                  dz  }n# t          t          f$ r Y nw xY wt          |                    dd                    }|                    t#          ||||                    dd          |                    dd          t%          |d          t%          |                    dd          d          |                    dd          |                    dd          t%          |                    dd                    
  
                   <|S )z,Get workouts/activities for the past N days.rK      rN   rO   r7   r   r8   Zz+00:00zAustralia/Brisbanez%Y-%m-%dz%H:%Mr   z00:00g        <   id
sport_nameUnknownsport_idr   rj   r   rl   rm   rk   )
r   rr   ry   r   r   duration_minutesrj   rp   rq   ro   )r   ru   r   rv   r   rG   r1   fromisoformatreplace
astimezoner   r>   total_secondsr*   	TypeErrorstrr   r   rz   )r   rL   r8   r7   workoutsr   wrO   
start_timeend_timeutc_dtbrisbane_dtr   time_strr   start_dtend_dt
workout_ids                     r   get_workout_historyzWhoopClient.get_workout_history  s   l8<((iT****..U2.NN '	 '	AEE'NN(bEw++JuuUB''H  #!/
0B0B30Q0QRR$//9M0N0NOO&//
;;&//88$"  # h '5j6H6Hh6W6WXXH%3H4D4DS(4S4STTF(.(9'H'H'J'JR'O$$"I.   D QUU4__--JNN>55y99z2..!&'7!;!;UYYx33Q77yy!5q99yy!1155uyya8899       s   A*FFFmsc                     | dz  S )zConvert milliseconds to hours.i6  )r   s    r   r~   zWhoopClient._ms_to_hoursO  s     ^$$r   rO   c                 .    | dS | dk    rdS | dk    rdS dS )z#Get recovery zone color from score.Nr   C   green"   yellowredr   )rO   s    r   r{   zWhoopClient._get_recovery_zoneT  s/     =9B;;7B;;8ur   r   )NNr6   )r   )%__name__
__module____qualname____doc__r   r	   r   propertyr"   r#   r&   dictr,   r   r5   r   intlistrA   rC   rE   rG   rI   r   r   r   r   r   r   r   r   r   r   staticmethodfloatr~   r{   r   r   r   r   r      sb       ""4 4Xi0 4 4 4 4     X
d 
 
 
 
 S (4. D      %)"&	1 1!1 h1 	1
 
d1 1 1 1$ %)"&	1 1!1 h1 	1
 
d1 1 1 1$ %)"&	1 1!1 h1 	1
 
d1 1 1 1$ %)"&	1 1!1 h1 	1
 
d1 1 1 1 0$ 0 0 0 0E
	 E
 E
 E
 E
N  T/5J    2 c $|2D    : s 43F    .1 1 1D4H 1 1 1 1n % % % % % \% (3- C    \  r   r   )r   r   r   r   zoneinfor   typingr   r"   r   r	   r
   modelsr   r   r   r   r   r   r   r   r   r$   r   r   r   r   <module>r      s   4 4 2 2 2 2 2 2 2 2 2 2              . . . . . . . .                      5J J J J J J J J J Jr   