
    2iC                     ~    d Z ddlmZmZ ddlmZmZ ddlmZ ddl	m
Z
 ddlmZ  ee          Z G d d          Zd	S )
zWeekly health report generator.

Generates a Slack-formatted weekly health summary covering sleep, steps,
recovery, body battery, and lifestyle data.
    )date	timedelta)AnyOptional)ManualLogStorage)HealthDataQuery)setup_loggerc                   V   e Zd ZdZddZdedefdZdededeee	f         fd	Z
dededeee	f         fd
Zdededeee	f         fdZdededeee	f         fdZdededeee	f         fdZdededeee	f         fdZdededeee	f         fdZdedefdZdeee	f         defdZdS )WeeklyReportGeneratorzGGenerates weekly health reports from stored Garmin and manual log data.returnNc                 R    t                      | _        t                      | _        d S )N)r   queryr   manual_storage)selfs    !health/analytics/weekly_report.py__init__zWeeklyReportGenerator.__init__   s#    $&&
.00    
week_startc                 |    |t          d          z   }|t          d          z
  }|t          d          z
  }t                              d| d|                                 ||          }                     ||          }                     ||          }                     ||          }                     ||          }	                     ||          }
                     ||          } 	                    ||          }|||||	||d} 
                    |          }g }|                    d| d| d	           |                    d
           |                    d           |                    dd          dk    r|                    d|d         dd           |                    d                     |d                               |                    d          r)|                    d|d          d|d         dd           |                    d          r)|                    d|d          d|d         dd           n|                    d           |                    d
           |                    dd          }|                    d|rdt          |          z   dz   nd
            |                    dd          dk    r*|                    d |d!          d"|d#         d$d%           n|                    d&           |                    d'          r:|                    d(|d)          d*|d+          d,|d-          d.|d/          d0	           |                    d1g d2          t          d3 D                       rqd4                     fd5t#          d6          D                       }|                    d
           |                    d7           |                    d8|            |                    d9g           }|r|                    d
           |                    d:           |D ]t}d
}|d;         s|d<         rd=|d;          d>|d<          d?}|d@         rdA|d@          dBnd
}|                    d8|dC          d4|dD          d4|dE          dF| | 	           u|                    d
           |                    dG           |	                    dd          dk    rdH|	dI         ddJ}|
                    dd          dk    r'|	dI         |
dI         z
  }|dk    rdKnd
}|dL| |ddMz  }|                    |           dN|	dO         dd?}|
                    dd          dk    r'|	dO         |
dO         z
  }|dk    rdKnd
}|dL| |ddMz  }|                    |           n|                    dP           |                    d
           |                    dQ           |                    dd          dk    r?|                    dR|dS         d           |                    dT|dU         d           n|                    dV           |                    d
           |                    dW           |                    d'          r|                    dX|dY          dZ           |                    d[          rOd\                    d] |d[                                         D                       }|                    d^|            |                    d_|d`          dZ           n|                    da           |                    d
           |                    db|            dc                    |          S )dzGenerate a Slack-formatted weekly health report.

        Args:
            week_start: The Monday that starts the report week.

        Returns:
            Formatted report string ready for Slack posting.
           )days      zGenerating weekly report for z ~ )sleepstepsactivity	intensityrecoverybattery	lifestyleu   📊 *健康周报 * u   😴 *睡眠*countr   u     • 平均得分：	avg_score.0fu    分u     • 平均时长：avg_duration_sec	best_dateu     • 最佳：z (
best_scoreu    分)
worst_dateu     • 最差：worst_scoreu     • 本周无睡眠数据u   🏃 *运动*u   （共 u    次）u#     • 步数达标（≥10000）：	goal_daysu    天 | 平均 	avg_stepsz,.0fu    步u     • 本周无步数数据has_datau     • 强度分钟：中等 moderate_minu   min + 剧烈 vigorous_minzmin = 	total_minu#    min（WHO 目标 150 min，完成 goal_pctu   %）zone_secondsr   r   r   r   r   c              3   "   K   | ]
}|d k    V  dS )r   N .0ss     r   	<genexpr>z1WeeklyReportGenerator.generate.<locals>.<genexpr>l   s&      ++q1u++++++r   z  c              3   `   K   | ](}d |dz    d                     |                    V  )dS )Zr    N)_format_duration)r7   ir   r2   s     r   r9   z1WeeklyReportGenerator.generate.<locals>.<genexpr>m   s^       # # EAEDDD11,q/BBDD# # # # # #r      u"     *心率区间（全周累计）*u     • 
activitiesu     *运动明细*avg_hrmax_hru    | ♥ /z bpmcaloriesz | kcalr   nameduration_minminu   ❤️ *恢复*u     • 平均 HRV：avg_hrvz ms+   （u    vs 上周）u     • 平均静息心率：avg_rhru     • 本周无恢复数据u   ⚡ *Body Battery*u     • 平均充电值：avg_chargedu     • 平均消耗值：avg_drainedu#     • 本周无 Body Battery 数据u   🥗 *生活方式*u     • 饮酒天数：alcohol_daysu    天fasting_modesz, c              3   ,   K   | ]\  }}| d | dV  dS )rK   u   天）Nr5   )r7   mcs      r   r9   z1WeeklyReportGenerator.generate.<locals>.<genexpr>   sH       " "+/1aq&&Q&&&" " " " " "r   u     • 禁食模式：u     • 补剂记录天数：supplement_daysu     • 本周无手动记录u   💡 *本周亮点：* 
)r   loggerinfo_get_sleep_stats_get_steps_stats_get_activity_stats_get_intensity_stats_get_recovery_stats_get_battery_stats_get_lifestyle_stats_pick_highlightappendgetr=   stranyjoinrangeitems)r   r   week_endprev_week_startprev_week_endsleep_statssteps_statsactivity_statsintensity_statsrecovery_statsprev_recoverybattery_statslifestyle_stats	all_stats	highlightlines	act_count
zone_partsr@   acthr_partcal_parthrv_linediffsignrhr_linemodesr2   s   `                          @r   generatezWeeklyReportGenerator.generate   sj	    	q 1 1 11$ya'8'8'88"YA%6%6%66MJMM8MMNNN++JAA++JAA11*hGG33JII11*hGG00-PP//
HEE33JII ! &(&$(
 
	 ((33	 	E:EE(EEEFFFR 	_%%%??7A&&**LLS[1ISSSSTTTLL`(=(=kJ\>](^(^``   {++ fk+&>ff+lB[ffff   |,, hk,&?hh;}C]hhhh   LL6777R #&&w22	by%`YY%?)%K%K^`bbccc ??7A&&**LLAk+6N A A(5@A A A   
 LL6777 z** 	LLT~/N T T,^<T T%k2T T 3B*2MT T T   &)).///JJ++l+++++ 	0 # # # # #q# # #  J LLLL=>>>LL.*../// $''b99
 	LLLL+,,,!  x= LCM LKHKKHKKKG:=j/Q6Z6666rES[ E ECK E E^,E E19E;BE E    	R 	&'''gq))A--O^I-FOOOOH  !,,q00%i0=3KK"aiissR?$??????LL"""X^I5NXXXXH  !,,q00%i0=3KK"aiissR?$??????LL""""LL6777R 	)***Wa((1,,LLVM-4PVVVWWWLLVM-4PVVVWWWWLL>???R 	*+++z** 		8LLV1PVVVWWW""?33 >		 " "3B?3S3Y3Y3[3[" " "   <U<<===LL_GX7Y___````LL6777R 	:y::;;;yyr   startendc           
      *   | j                             d||          }|sddiS d |D             }d |D             }|sddiS t          |d           }t          |d           }t	          |          t          |          t	          |          z  |r,t          t          |          t	          |          z            nd|                    d	          |                    d
d          |                    d	          |                    d
d          dS )z2Compute sleep statistics for the given date range.r   r#   r   c                 H    g | ]}|                     d           |d           S )sleep_scorera   r7   rs     r   
<listcomp>z:WeeklyReportGenerator._get_sleep_stats.<locals>.<listcomp>   s.    LLLqquu]7K7KL!M"LLLr   c                 H    g | ]}|                     d           |d           S )total_sleep_secondsr   r   s     r   r   z:WeeklyReportGenerator._get_sleep_stats.<locals>.<listcomp>   s0    ___!!%%H]B^B^_Q,-___r   c                 .    |                      dd          S )Nr   r   r   r   s    r   <lambda>z8WeeklyReportGenerator._get_sleep_stats.<locals>.<lambda>   s    !%%q*A*A r   )keyc                 .    |                      dd          S )Nr   d   r   r   s    r   r   z8WeeklyReportGenerator._get_sleep_stats.<locals>.<lambda>   s    155+D+D r   r   r   )r#   r$   r&   r'   r(   r)   r*   )r   get_metric_rangemaxrH   lensumintra   )r   r   r   recordsscores	durationsbestworsts           r   rX   z&WeeklyReportGenerator._get_sleep_stats   s&   *--gucBB 	 Q<LLGLLL__w___	 	 Q<7 A ABBBG!D!DEEE [[Vs6{{2HQ XC	NNS^^$C D D DWX&))((=!44))F++ 99]A66
 
 	
r   c                     | j                             d||          }|sddiS d |D             }|sddiS t          |          t          |          t          |          z  t          d |D                       dS )z1Compute step statistics for the given date range.r   r#   r   c                 H    g | ]}|                     d           |d           S )total_stepsr   r   s     r   r   z:WeeklyReportGenerator._get_steps_stats.<locals>.<listcomp>   s.    ]]]AAEE-<P<P<\q'<\<\<\r   c              3   &   K   | ]}|d k    dV  dS )i'  r   Nr5   r6   s     r   r9   z9WeeklyReportGenerator._get_steps_stats.<locals>.<genexpr>   s&      BB1qEzzQzzzzBBr   )r#   r,   r+   r   r   r   r   )r   r   r   r   step_valuess        r   rY   z&WeeklyReportGenerator._get_steps_stats   s    *--gucBB 	 Q<]]]]] 	 Q< %%[))C,<,<<BBBBBBB
 
 	
r   c                    | j                             ||          }|sdg g ddS g }g d}|D ]}|                    d          pi }t          |t                    r-ddl}	 |                    |          }n# t          $ r i }Y nw xY wt          dd          D ]U}	|                    d|	           }
|
9	 ||	dz
  xx         t          |
          z  cc<   ># t          t          f$ r Y Qw xY wV|                    d	          pd}|                    |                    d
          r2|                    d
d          dd                             dd          nd|                    d          pd|rt          |dz            nd|                    d          pd|                    d          pd|                    d          pdd           t          |          ||dS )a	  Compute activity detail statistics from get_activities_range().

        Args:
            start: Start date (inclusive).
            end: End date (inclusive).

        Returns:
            Dict with count, activities list, and cumulative HR zone seconds.
        r   r3   )r#   r@   r2   raw_dataNr   r   hrTimeInZone_duration_secondsr   r"   -activity_nameu   未知<   rD   average_heart_ratemax_heart_rate)r   rF   rG   rD   rA   rB   )r   get_activities_rangera   
isinstancerb   jsonloads	Exceptionre   r   	TypeError
ValueErrorr`   replaceroundr   )r   r   r   r   r@   r2   r   rawr   r>   valduration_secs               r   rZ   z)WeeklyReportGenerator._get_activity_stats   s[    *11%== 	Sb///RRR+-
& 	 	A%%
##)rC#s## **S//CC    CCC 1a[[  gg1a1122?$QU+++s3xx7++++%z2    # 55!3449LDEEE&MMYfb))"##.66sC@@@WYo..:(<H OlR&7 8 8 8aEE*--2%% 455:%% 0116Q      __$(
 
 	
s$   "A88BB7 CC,+C,c                     | j                             d||          }|sddiS t          d |D                       }t          d |D                       }||dz  z   }t          |dz  dz            }d	||||d
S )a  Compute intensity minutes vs WHO 150-min target.

        Args:
            start: Start date (inclusive).
            end: End date (inclusive).

        Returns:
            Dict with moderate_min, vigorous_min, total_min (WHO-weighted), goal_pct.
        intensity_minutesr-   Fc              3   D   K   | ]}|                     d           pdV  dS )moderate_minutesr   Nr   r   s     r   r9   z=WeeklyReportGenerator._get_intensity_stats.<locals>.<genexpr>#  4      KKa155!3449KKKKKKr   c              3   D   K   | ]}|                     d           pdV  dS )vigorous_minutesr   Nr   r   s     r   r9   z=WeeklyReportGenerator._get_intensity_stats.<locals>.<genexpr>$  r   r         r   T)r-   r.   r/   r0   r1   )r   r   r   r   )r   r   r   r   r.   r/   r0   r1   s           r   r[   z*WeeklyReportGenerator._get_intensity_stats  s     *--.A5#NN 	'&&KK7KKKKKKK7KKKKK <!#33	S3.// ((" 
 
 	
r   c                    | j                             d||          }| j                             d||          }d |D             }d |D             }|s|sddiS t          t          |          t          |                    |rt	          |          t          |          z  nd|rt	          |          t          |          z  nddS )	zGCompute HRV and resting heart rate statistics for the given date range.hrv
heart_ratec                 H    g | ]}|                     d           |d           S )	hrv_valuer   r   s     r   r   z=WeeklyReportGenerator._get_recovery_stats.<locals>.<listcomp>5  s8     
 
 
uu[!!- kN---r   c                 H    g | ]}|                     d           |d           S )resting_heart_rater   r   s     r   r   z=WeeklyReportGenerator._get_recovery_stats.<locals>.<listcomp>:  sA     
 
 
uu)**
"#
 
 
r   r#   r           )r#   rI   rL   )r   r   r   r   r   )r   r   r   hrv_records
hr_records
hrv_values
rhr_valuess          r   r\   z)WeeklyReportGenerator._get_recovery_stats0  s    j11%DDZ00ucJJ

 
 
 
 



 

 
 

  	 * 	 Q< Z#j//::<FOs:Z88C<FOs:Z88C
 
 	
r   c                 0   | j                             d||          }|sddiS d |D             }d |D             }|s|sddiS t          |          |rt          |          t          |          z  nd|rt          |          t          |          z  nddS )zFCompute Body Battery charge/drain statistics for the given date range.body_batteryr#   r   c                 H    g | ]}|                     d           |d           S )chargedr   r   s     r   r   z<WeeklyReportGenerator._get_battery_stats.<locals>.<listcomp>O  -    QQQAAEE)4D4D4P1Y<4P4P4Pr   c                 H    g | ]}|                     d           |d           S )drainedr   r   s     r   r   z<WeeklyReportGenerator._get_battery_stats.<locals>.<listcomp>P  r   r   r   )r#   rM   rN   r   )r   r   r   r   r   r   s         r   r]   z(WeeklyReportGenerator._get_battery_statsI  s    *--neSII 	 Q<QQQQQQQQQQ 	 w 	 Q< \\:AJ3w<<#g,,66s:AJ3w<<#g,,66s
 
 	
r   c                    | j                             ||          }|sddiS t          d |D                       }t          d |D                       }i }|D ]/}|j        r&|                    |j        d          dz   ||j        <   0d|||dS )	zACompute lifestyle log statistics (alcohol, fasting, supplements).r-   Fc              3   (   K   | ]}|j         	d V  dS r   N)alcohol_entriesr7   logs     r   r9   z=WeeklyReportGenerator._get_lifestyle_stats.<locals>.<genexpr>a  s*      DD0CD1DDDDDDr   c              3   (   K   | ]}|j         	d V  dS r   )supplement_entriesr   s     r   r9   z=WeeklyReportGenerator._get_lifestyle_stats.<locals>.<genexpr>b  s*      JJC33IJaJJJJJJr   r   r   T)r-   rO   rT   rP   )r   get_logs_in_ranger   fasting_modera   )r   r   r   logsrO   rT   rP   r   s           r   r^   z*WeeklyReportGenerator._get_lifestyle_stats[  s    "44UC@@ 	'&&DDDDDDDJJ4JJJJJ(* 	] 	]C ]2?2C2CCDTVW2X2X[\2\c./ (.*	
 
 	
r   secondsc                 <    |dk    rdS |dz  }|dz  dz  }| d| dS )zFormat a duration in seconds to 'Xh Ym' string.

        Args:
            seconds: Total duration in seconds.

        Returns:
            Human-readable duration string, e.g. '7h 23m'.
        r   z0h 0mi  r   zh rR   r5   )r   r   hoursminutess       r   r=   z&WeeklyReportGenerator._format_durationt  s@     a<<74T>b(%%7%%%%r   statsc                 (   g }|                     di           }|                     dd          dk    rP|d         }|dk    r|                    |d|ddf           n%|d	k     r|                    d
|z
  d|ddf           |                     di           }|                     dd          dk    rH|d         }|dk    r|                    |dz  d| df           n|dk    r|                    d           |                     di           }|                     d          rS|d         }|d         }	|d
k    r|                    dd|	 df           n!|dk     r|                    dd|	 df           |                     di           }
|
                     dd          dk    rg|
                     d d          dk    rM|
d          }|d!k    r|                    |d"|dd#f           n"|d$k     r|                    d%d&|dd'f           |                     d(i           }|                     d          r/|                     d)d          dk    r|                    d*           |sd+S |                    d, d-.           |d         d/         S )0zSelect the most noteworthy insight from aggregated stats.

        Args:
            stats: Dictionary containing all section stats.

        Returns:
            A single highlight sentence in Chinese.
        r   r#   r   r$   U   u"   睡眠质量优秀，平均得分 r%   u	    分 🌟A   r   u"   睡眠质量偏低，平均得分 u    分，建议关注r   r+   r?   
   u   本周 u0    天达到万步目标，运动坚持度佳 💪)g      N@u<   本周没有达到万步目标，需要增加日常活动量r   r-   r1   r0   g     V@u   本周强度分钟达标（u    min），运动量充足 🎯2   g     Q@u   强度分钟仅 u<    min，不足 WHO 目标的一半，建议增加有氧运动r   rI   r   u   HRV 均值 u    ms，恢复状态良好 ✅#   g      T@u   HRV 偏低（u$    ms），注意休息与压力管理r    rO   )g      I@u)   本周零饮酒，生活方式健康 🌿u*   数据积累中，下周见更多洞察。c                     | d         S )Nr   r5   )xs    r   r   z7WeeklyReportGenerator._pick_highlight.<locals>.<lambda>  s
    ad r   T)r   reverser   )ra   r`   sort)r   r   
highlightsr   scorer   r+   r   pctr0   r   r   r    s                r   r_   z%WeeklyReportGenerator._pick_highlight  s#    /1
		'2&&99Wa  1$$+&E{{!!5*cu*c*c*c*c"deeee!!3;0sUZ0s0s0s0s"tuuu		'2&&99Wa  1$$k*IA~~!!9r>3xY3x3x3x"yzzzza!!"hiiiIIk2..	==$$ 	FJ'C!+.Iczz!!4)py)p)p)p"qrrrrr!!4  *DI  *D  *D  *D  #E  F  F  F99Z,,<<##a''HLLA,F,F,J,J9%Cbyy!!3([c([([([(["\]]]]r!!4)f)f)f)f)f"ghhhIIk2..	==$$ 	S~q)I)IQ)N)NQRRR 	@??NND999!}Qr   )r   N)__name__
__module____qualname____doc__r   r   rb   r   dictr   rX   rY   rZ   r[   r\   r]   r^   r   r=   r_   r5   r   r   r   r      s       QQ1 1 1 1[ 4 [ C [  [  [  [ B
d 
 
$sCx. 
 
 
 
2
d 
 
$sCx. 
 
 
 
 1
 1
D 1
T#s(^ 1
 1
 1
 1
f
$ 
T 
d38n 
 
 
 
6
 
D 
T#s(^ 
 
 
 
2
 
4 
DcN 
 
 
 
$
$ 
T 
d38n 
 
 
 
2& & & & & &4 T#s(^ 4  4  4  4  4  4  4 r   r   N)r   datetimer   r   typingr   r   "health.services.manual_log_storager   health.services.queryr   health.utils.logging_configr	   r   rV   r   r5   r   r   <module>r      s     % $ $ $ $ $ $ $                 ? ? ? ? ? ? 1 1 1 1 1 1 4 4 4 4 4 4	h		f  f  f  f  f  f  f  f  f  f r   