o
    i*                     @   s   d Z ddlZddlZddlZddlmZmZ ddlmZ ddlm	Z	 e
dZdZdd	ed
edefddZdddZdedefddZdedefddZdS )u  
Health screenshot parser — uses Claude Vision to extract data from
iHealth app screenshots sent via WhatsApp.

Supported screenshot types:
  1. Scale / body composition (iHealth Nexus Pro)
     → weight, body fat, BMI, muscle mass, body water, etc.
  2. Blood pressure history (iHealth BP monitor)
     → systolic, diastolic, pulse, multiple readings

Bill screenshots his iHealth app and sends to Luke. This module
extracts the numbers and updates the dashboard automatically.
    N)datedatetime)	Anthropic)Configzhealth-visionu  Look at this image. Determine what type of health screenshot it is and extract the data.

TYPE 1 — SCALE / BODY COMPOSITION (shows weight in large text, may show body fat, BMI, etc.)
Return:
{
  "type": "scale",
  "weight_lb": <number>,
  "weight_change": <number or null>,
  "bmi": <number or null>,
  "body_fat_pct": <number or null>,
  "muscle_mass_lb": <number or null>,
  "body_water_pct": <number or null>,
  "lean_body_mass_lb": <number or null>,
  "bone_mass_lb": <number or null>,
  "protein_pct": <number or null>,
  "visceral_fat": <number or null>,
  "bmr_kcal": <number or null>,
  "metabolic_age": <number or null>,
  "date": "<YYYY-MM-DD if visible, else null>",
  "time": "<HH:MM if visible, else null>"
}

TYPE 2 — BLOOD PRESSURE (shows SYS/DIA/Pulse readings, may show history)
Return:
{
  "type": "blood_pressure",
  "readings": [
    {
      "systolic": <number>,
      "diastolic": <number>,
      "pulse": <number>,
      "date": "<YYYY-MM-DD if visible>",
      "time": "<HH:MM if visible>",
      "reading_number": <1st/2nd/3rd label if visible, else null>
    }
  ]
}
For BP, extract ALL visible readings in the image (there may be multiple).
Sort from most recent to oldest.

NOT A HEALTH SCREENSHOT:
Return: {"type": "unknown"}

Rules:
- All numbers must be numeric, not strings
- weight_change is the small delta (like ↑0.9 means +0.9)
- For BP dates shown as "Feb 23, 2026", convert to "2026-02-23"
- For BP times shown as "7:46 a.m.", convert to "07:46"
- Return ONLY the JSON object, no other text
image/jpeg
image_datacontent_typereturnc           
   
   C   s  t tjd}d|v rd}n	d|v rd}nd}t| d}zY|jjtjdd	d
d||dddt	dgdgd}|j
d j }td|dd   |drb|ddd ddd  }t|}|dd}td|  |W S  tjy }	 ztd|	  td|dd   ddiW  Y d}	~	S d}	~	w ty }	 ztjd|	 dd  d!t|	d"W  Y d}	~	S d}	~	ww )#z
    Send a health app screenshot to Claude Vision for extraction.
    Returns dict with 'type' field indicating what was found.
    )api_keypngz	image/pngwebpz
image/webpr   zutf-8i   userimagebase64)type
media_typedata)r   sourcetext)r   r   )rolecontent)model
max_tokensmessagesr   zHealth vision raw response: Ni,  z```
   r   unknownzHealth vision detected type: z%Health vision: failed to parse JSON: zRaw response: zHealth vision API call failed: T)exc_infoerror)r   r   )r   r   CLAUDE_API_KEYr   	b64encodedecoder   createCLAUDE_MODEL_EXTRACTION_PROMPTr   r   striploggerinfo
startswithsplitrsplitjsonloadsgetJSONDecodeErrorr   	Exceptionstr)
r   r   clientr   	b64_imageresponser   r   	data_typee r6   scale_vision.pyparse_health_screenshotK   sT   	
 
r8   c                 C   s
   t | |S )N)r8   )r   r   r6   r6   r7   parse_scale_screenshot   s   
r9   r   c                    sb   r ds	dS t  g   fdd}|| dddd  D  }t| |S )	zDApply extracted scale data to the dashboard. Returns summary string.	weight_lb c                    s  d }d| vri | d< | d }||d< |d< d|d< | dg }|| t|dkr3|d	d  }||d< | d
d}t|dkrx|d |d  }t|d|d< |dk rp|| }|dkrd|t| nd}tdt||d< n| dd|d< t| d|| d|d<  d| d  drd| vri | d< | d }| dd }	d |d< |d< td |	 d|d<  dd  d  dr߈d d| d<  d d   i }
d!D ]} |d ur| |
|< q|
r>|
d< |
| d"< g } d#r|d$d#  d  d%d ur#|d&d%    d'r3|d(d'   |r> d)| | d< d S )*Nr:   weightcurrentr   ziHealth Nexus Pror   	series30d   igoal      ir   pacer   c   etaWeeks?startlostzWeight: z lbbody_fat_pctbodyFatdeltaVsLastz
Body Fat: %bmi)valuer   zBMI: )muscle_mass_lbbody_water_pctlean_body_mass_lbbone_mass_lbprotein_pctvisceral_fatbmr_kcalmetabolic_agebodyCompositionrP   zMuscle: rU   zVisceral Fat: rW   zMetabolic Age: z | )r-   appendlenroundabsmaxjoin)	dashboardr<   wseriesr@   week_change	remainingweeksbfold_bf	body_compkeyextraschangesr   todayr6   r7   updater   sj   


z!apply_scale_data.<locals>.updaterzScale data captured:
r   c                 s       | ]}d | V  qdS u     • Nr6   .0cr6   r6   r7   	<genexpr>       z#apply_scale_data.<locals>.<genexpr>r-   r   rl   	isoformat_read_and_writer^   r&   r'   )r   dashboard_managerrm   summaryr6   rj   r7   apply_scale_data   s   
D
rz   c              
      s  |  dg s
dS d }| d| d| dr!s#dS t  	| d	| ddd	k r@d
k r@dndk rKd
k rKdndk sSdk rVdnddddddg   	f
dd}|| dddd  D  }t| |S )zMApply extracted blood pressure data to the dashboard. Returns summary string.readingsr;   r   systolic	diastolicpulser   timex   P   normal   elevated   Z   stage1stage2NormalElevatedzStage 1 HypertensionzStage 2 Hypertension)r   r   r   r   c                    s  |  di }|d< |d< |d< |d< |d< |d< d|d	<  d
 d d  d r@ d d | dg }d}|rV|d |krh|d| t|dkrh|d d }||d< dd  D ](}| d| d| d| d| ddd}||d vr|d | qrt|d dd ddd d |d< tdkrttdd D t }ttdd D t } dt d| d|  || d< 	| d< d S )NbloodPressurer|   r}   r~   r   r   statusziHealth BP Monitorr   zBP: /z mmHg ()zPulse: z bpmhistory)r|   r}   r~   r   r   r   r?   r   r;   c                 S   s   |  dd|  ddfS )Nr   r;   r   )r-   )xr6   r6   r7   <lambda>:  s    z0apply_bp_data.<locals>.updater.<locals>.<lambda>T)rh   reversec                 s       | ]}|d  V  qdS )r|   Nr6   rq   rr6   r6   r7   rs   @      z1apply_bp_data.<locals>.updater.<locals>.<genexpr>c                 s   r   )r}   Nr6   r   r6   r6   r7   rs   A  r   zAvg (z readings): )r-   rY   insertrZ   sortedr[   sum)r_   bpr   	new_entryr   entryavg_sysavg_dia
rk   dia_valr~   reading_datereading_timer{   r   status_labelssys_valrl   r6   r7   rm   	  s\   &

 zapply_bp_data.<locals>.updaterzBlood pressure captured:
r   c                 s   rn   ro   r6   rp   r6   r6   r7   rs   I  rt   z apply_bp_data.<locals>.<genexpr>ru   )r   rx   latestrm   ry   r6   r   r7   apply_bp_data   s<   



>
r   )r   )__doc__r   r+   loggingr   r   	anthropicr   configr   	getLoggerr&   r$   bytesr0   dictr8   r9   rz   r   r6   r6   r6   r7   <module>   s    
3
=U