
    lhz4                     t    S r SSKJrJr  SSKJr  SSKJr  SSKJr  SSK	J
r
  SSKJr  SSKJr   " S	 S
5      rg)zO
Burnout Detection Service
Uses ML and pattern analysis to detect burnout risk
    )datetime	timedelta)timezone)Stamp)WorkBalanceFunction)WorkingTimePolicy)UserSettingsc                   d    \ rS rSrSrS rSS jrS rS rS r	S r
S	 rS
 rS rS rS rS rSrg)BurnoutDetector   z,Detects burnout patterns from user work datac                     Xl         g Nuser_id)selfr   s     ,D:\TimeStamps\ml_service\burnout_detector.py__init__BurnoutDetector.__init__   s        c                     [         R                  R                  U R                  S9nUR                  (       d  g [        R                  " 5       R                  5       nU[        US9-
  nU R                  XC5      nU R                  U5      nU R                  US   5      nUS   US   US   US.$ ! [         R
                   a     gf = f)z
Analyze user for burnout risk over last N days

Returns:
    dict: {
        'risk_score': float (0-100),
        'severity': str ('low', 'medium', 'high', 'critical'),
        'risk_factors': dict,
        'recommendations': list
    }
)user__idN)days
risk_scoreseverityrisk_factors)r   r   r   recommendations)r   objectsgetr   burnout_notifications_enabledDoesNotExistr   nowdater   _extract_features_calculate_risk_get_recommendations)r   r   settingsend_date
start_datefeaturesrisk_analysisr   s           r   analyze_userBurnoutDetector.analyze_user   s    	#++///FH99 : <<>&&(	t 44
 ))*? ,,X6 33M,4OP (5%j1).9.	
 	
! (( 		s   8B1 1CCc           	         SSK Jn   UR                  R                  U R                  S9n[        R                  R                  UUUS9R                  SS5      n[        R                  R                  U R                  UUS9n/ nU H<  nUR                  UR                  S	9n	UR                  U R                  X5      5        M>     U(       a  [        S
 U 5       5      [        U5      -  OSU(       a  [        S U 5       5      [        U5      -  OSU R                  U5      U R!                  U5      U R#                  U5      U R%                  U5      U R'                  U5      S.n
U
$ ! UR
                   a     gf = f)z)Extract features from stamps and balancesr   )UseridN)user	date__gte	date__lter$   time)r   r4   r5   r$   c              3   *   #    U  H	  oS    v   M     g7f)total_hoursN .0ds     r   	<genexpr>4BurnoutDetector._extract_features.<locals>.<genexpr>\   s     "L^]#3^   c              3   D   #    U  H  oR                  S S5      v   M     g7f)overtime_hoursr   N)r    r;   s     r   r>   r?   ]   s     %Y.Qee,<a&@&@.s    )avg_daily_hoursavg_overtime_hoursavg_break_durationbreak_complianceconsecutive_daysweekend_work_frequencytotal_break_time)user.modelsr0   r   r    r   r"   r   filterorder_byr   r$   append_analyze_daysumlen_calculate_avg_break_duration_calculate_break_compliance_calculate_consecutive_days!_calculate_weekend_work_frequency_calculate_total_break_time)r   r*   r)   r0   r3   stampsbalancesdaily_featuresbalance
day_stampsr+   s              r   r%   !BurnoutDetector._extract_features>   s|    	%	<<##t||#4D %%  & 
 (66
"	 	 &&--LL  . 
 GGLL9J!!$"3"3G"HI   gus"L^"LLsSaObbz{ tB#%Y.%Y"Y\_`n\o"o  HI"&"D"DV"L $ @ @ H $ @ @ J&*&L&LV&T $ @ @ H
 A    		s   #E( (E;:E;c                 .   UR                   (       a  UR                   S-  OSnUR                  (       a  UR                  S-  OSnU R                  U5      nUUUR                  (       a  UR                  S-  OS[	        S U 5       5      [        U5      S.$ )zAnalyze a single day  r   c              3   *   #    U  H	  oS    v   M     g7fdurationNr:   r<   bs     r   r>   /BurnoutDetector._analyze_day.<locals>.<genexpr>s   s     !@AJ-r@   )r9   rB   
flex_hoursbreak_duration
num_breaks)total_work_secondsovertime_seconds_get_break_durations_for_dayflex_secondsrO   rP   )r   rY   rV   r9   rB   breakss         r   rN   BurnoutDetector._analyze_dayg   s    ;B;U;Ug0047[\<C<T<T11D8Z[ 226: ',9@9M9M'..5ST!!@!@@f+
 	
r   c                   ^ / nSSK Jn  0 nU H?  n UR                  R                  UR                  S9nUR
                  XER                  '   MA     U Vs/ s H%  otR                  UR                  5      S:X  d  M#  UPM'     nnU Vs/ s H%  otR                  UR                  5      S:X  d  M#  UPM'     n	nU Vs/ s H%  otR                  UR                  5      S:X  d  M#  UPM'     n
nU Vs/ s H%  otR                  UR                  5      S:X  d  M#  UPM'     nn[        US	 S
9 Hv  m[        U4S jU	 5       S5      nU(       d  M"  UR                  TR                  -
  R                  5       S-  nUR                  SUTR                  UR                  S.5        Mx     [        U
S S
9 Hv  m[        U4S jU 5       S5      nU(       d  M"  UR                  TR                  -
  R                  5       S-  nUR                  SUTR                  UR                  S.5        Mx     U$ !   SXER                  '    GM+  = fs  snf s  snf s  snf s  snf )zGet break durations for a dayr   r   r1   N	break_outbreak_in	lunch_outlunch_inc                     U R                   $ r   r6   xs    r   <lambda>>BurnoutDetector._get_break_durations_for_day.<locals>.<lambda>       !&&r   )keyc              3   ^   >#    U  H"  oR                   TR                   :  d  M  Uv   M$     g 7fr   rs   r<   in_stamp	out_stamps     r   r>   ?BurnoutDetector._get_break_durations_for_day.<locals>.<genexpr>         V)h}}y~~7U)   -	-<   short_break)typer`   out_timein_timec                     U R                   $ r   rs   rt   s    r   rv   rw      rx   r   c              3   ^   >#    U  H"  oR                   TR                   :  d  M  Uv   M$     g 7fr   rs   r{   s     r   r>   r~      r   r   lunch_break)functions.modelsr	   r   r    stamp_functionquestion_typer2   sortednextr6   total_secondsrM   )r   rV   rk   StampFunctionstamp_functionsstampfuncs
break_outs	break_ins
lunch_outs	lunch_insmatching_inr`   r}   s                 @r   ri   ,BurnoutDetector._get_break_durations_for_dayw   s    	?E1$,,00E4H4H0I,0,>,>)  "(TA+>+>qtt+D+Sa
T &R1*=*=add*Cz*QQ	R!'TA+>+>qtt+D+Sa
T &R1*=*=add*Cz*QQ	R  
0@AIV)VK {',,y~~=LLNQSS) ( )*//	  B  
0@AIV)VK {',,y~~=LLNQSS) ( )*//	  B O1,0) URTRs;   ;H%"H<;H<"I-I9"II+"II%H9c                     SnSnUR                  SSS9R                  5        H8  nUR                  US9nU R                  U5      nU H  nX'S   -  nUS-  nM     M:     US:  a  X#-  $ S$ )z Calculate average break durationr   r$   Tflatr7   r`      )values_listdistinctrK   ri   )r   rV   total_durationcountr$   rZ   rk   
break_infos           r   rQ   -BurnoutDetector._calculate_avg_break_duration   s    &&vD&9BBDDD1J66zBF$
Z"88
 % E */~%99r   c                 "    [         R                  R                  U R                  S9R	                  S5      R                  5       nU(       d  gUR                  SSS9R                  5       R                  5       nSnUR                  SSS9R                  5        H^  nUR                  US	9nU R                  U5      n[        S
 U 5       5      n[        S U 5       5      n	U(       d  MP  U	(       d  MY  US-  nM`     US:  a  XC-  $ S$ !   Sn N= f)zCalculate break compliance rate)company__user__idz-created_atNg      ?r$   Tr   r   r7   c              3   0   #    U  H  oS    S:H  v   M     g7f)r   r   Nr:   ra   s     r   r>   >BurnoutDetector._calculate_break_compliance.<locals>.<genexpr>   s     G1fI6   c              3   0   #    U  H  oS    S:H  v   M     g7f)r   r   Nr:   ra   s     r   r>   r      s     "Nv!V9#=vr   r   )r
   r   rK   r   rL   firstr   r   r   ri   any)
r   rV   policy
total_dayscompliant_daysr$   rZ   rk   	has_lunchhas_short_breakss
             r   rR   +BurnoutDetector._calculate_break_compliance   s   	&..55"&,, 6 h}%eeg  ''T':CCEKKM
&&vD&9BBDDD1J66zBF GGGI  #"Nv"NN y--!# E /91n~*E#E/	Fs   AD Dc                     SnUR                  SSS9R                  5        H8  nUR                  US9nU R                  U5      nU[	        S U 5       5      -  nM:     U$ )zCalculate total break timer   r$   Tr   r7   c              3   *   #    U  H	  oS    v   M     g7fr_   r:   ra   s     r   r>   >BurnoutDetector._calculate_total_break_time.<locals>.<genexpr>   s     71:r@   )r   r   rK   ri   rO   )r   rV   totalr$   rZ   rk   s         r   rU   +BurnoutDetector._calculate_total_break_time   sg    &&vD&9BBDDD1J66zBFS7777E E
 r   c                 4   UR                  5       (       d  g[        U Vs/ s H  o"R                  PM     sn5      nSnSn[        S[	        U5      5       H1  nX6   X6S-
     -
  R
                  S:X  a  US-  nM$  [        XE5      nSnM3     [        XE5      $ s  snf )z"Calculate consecutive working daysr   r   )existsr   r$   rangerP   r   max)r   rW   rb   datesmax_consecutivecurrent_streakis          r   rS   +BurnoutDetector._calculate_consecutive_days   s      1112q#e*%A51:%++q0!#"%o"F!" & ?33 2s   Bc                 |    UR                  SS/S9nUR                  SSS9R                  5       R                  5       $ )z#Calculate frequency of weekend workr      )date__week_day__inr$   Tr   )rK   r   r   r   )r   rV   weekend_stampss      r   rT   1BurnoutDetector._calculate_weekend_work_frequency   sF     !1v ' 
 ))&t)<EEGMMOOr   c                    Sn0 nUS   S:  a  [        SUS   S-
  S-  5      nXCS'   X$-  nUS   S	:  a  [        S
US   S
-  5      nXCS'   X$-  nUS   S:  a  S	US   -
  S
-  nXCS'   X$-  nUS   S:  a  [        SUS   S-
  S-  5      nXCS'   X$-  nUS   S:  a  [        SUS   S-  5      nXCS'   X$-  nUS   S:  a  SUS   -
  S-  nXCS'   X$-  nUS:  a  SnOUS:  a  SnOUS:  a  SnOSn[        S U5      UUS!.$ )"zCalculate burnout risk scorer   rC   	         
   excessive_hoursrD   r      overtime_frequencyrF   g      ?poor_break_compliancerG   r      rH         weekend_workrE   -   insufficient_breaks(   lowF   mediumU   highcriticald   )r   r   r   )min)r   r+   r   r   pointsr   s         r   r&   BurnoutDetector._calculate_risk   s   
 %&*h'89A=CDF.4*+ J ()A-X&:;b@AF17-. J &'#-(#566"<F4:01 J &'",h'9:R?1DEF/5+, J ,-1X&>?!CDF+1( J ()B.8$899R?F28./ J ?H"_H"_H!H c:. (
 	
r   c                    / n[         R                  R                  U R                  S9R	                  S5      R                  5       nU(       a  UR                  S:  a1  UR                  SSUR                  S-  S S3US	:  a  S
OSSSS.5        UR                  S:  a1  UR                  SSUR                  S-  S S3US:  a  S
OSSSS.5        US:  a  UR                  SSSSSS.5        U$ )z)Get balance recommendations based on riskr   z-dater   	flex_timezUse r]   z.1fzh flex time to reduce workloadr   r   r   z(Available balance to offset excess hoursz+Use flex time to finish early or start late)balance_typemessagepriorityreasonaction	comp_timezTake zh comp time to recharger   z!Available comp time from overtimez"Schedule a day off using comp timeP   vacationz%Take vacation days to prevent burnoutr   z$High burnout risk + no recovery dayszPlan vacation to recover)	r   r   rK   r   rL   r   rj   rM   rh   )r   r   r   latest_balances       r   r'   $BurnoutDetector._get_recommendations7  s    %,,33LL 4 

(7
EEG 	 **Q.&&$/!%n&A&AD&H%MMkl*4r/xHK(  ..2&&$/!&~'F'F'Mc&RRij*4r/xAB(  B&&$.F *D8(  r   r   N)r   )__name__
__module____qualname____firstlineno____doc__r   r-   r%   rN   ri   rQ   rR   rU   rS   rT   r&   r'   __static_attributes__r:   r   r   r   r      sH    6'
R'R
 2h:F@	4&P7
r(r   r   N)r   r   r   django.utilsr   stamps.modelsr   worktimeservice.modelsr   r   r	   company.modelsr
   userSettings.modelsr   r   r:   r   r   <module>r      s-   
 ) !  . % , ,P Pr   