
    1 i'                     x    S SK r S SKJrJr  S SKJr  SSKJr  S SKJr  \ R                  " \
5      r " S S5      rg)	    N)datetime	timedelta)timezone   )WorkingTimePolicy)Stampc                   P    \ rS rSrSrSS jrS rSS jrS rS r	S	 r
S
 rS rSrg)ComplianceChecker	   zEReal-time compliance checking to prevent violations before they occurNc                 J    Xl         X l        S U l        U R                  5         g N)user_id
company_idpolicy_load_policy)selfr   r   s      +D:\TimeStamps\company\compliance_checker.py__init__ComplianceChecker.__init__   s    $    c                     U R                   (       a;  [        R                  R                  U R                   S9R	                  5       U l        gSSKJn   UR                  R                  U R                  S9n[        R                  R	                  5       U l        g!   [        R                  R	                  5       U l         g= f! [         a)  n[        R                  SU 35        SU l         SnAgSnAff = f)z/Load working time policy for the user's company)r   r   )UseridzError loading policy: N)r   r   objectsfilterfirstr   user.modelsr   getr   	Exceptionloggererror)r   r   useres       r   r   ComplianceChecker._load_policy   s    	/77>>$//>Z``b -D<<++t||+<D #4";";"A"A"CDKD"3";";"A"A"CDK 	LL1!56DKK	s7   AC C AB %CC C 
C9C44C9c                 @   U R                   (       d  / $ / nU=(       d    [        R                  " 5       nUR                  5       n[        R
                  R                  U R                  US9R                  S5      nUR                  5       (       d  / $ U R                  U5      nU R                  XQ5      nU(       a  UR                  U5        U R                  XA5      nU(       a  UR                  U5        U R                  U5      nU(       a  UR                  U5        U$ )z
Check current compliance status and return any approaching violations

Returns:
    list: Array of compliance alerts with type, message, time_remaining
r   datetime)r   r   nowr(   r   r   r   r   order_byexists_calculate_work_time_check_daily_limitappend_check_break_requirements_check_consecutive_work_days)	r   current_timealertstodaytoday_stamps	work_timedaily_alertbreak_alertconsecutive_alerts	            r   check_compliance"ComplianceChecker.check_compliance%   s     {{I#5x||~!!# }}++LL , 
 (6
 	
 ""$$I --l;	 --iFMM+& 44\PMM+& !==lKMM+,r   c                    SSK Jn  Sn/ n/ nU H  n[        US5      (       d  M  SSKJn   UR
                  R                  UR                  S9nUR                  n	U	S;   a  UR                  UR                  5        Mo  U	S;   a  UR                  UR                  5        M  M     [        [        U5      5       HA  u  pU
[        U5      :  d  M  [        U5      U
   nX:  d  M+  X-
  R                  5       nX=-  nMC     US-  $ !    M  = f)	z%Calculate total work time from stampsr   )r   stamp_functionFunctionr   )clock_in	lunch_out)	clock_outlunch_in  )r   r   hasattrfunctions.modelsr?   r   r   r=   question_typer/   r)   	enumeratesortedlentotal_seconds)r   stampsr   rK   	clock_ins
clock_outsstampr?   funcrG   iclock_in_timeclock_out_timedurations                 r   r-   &ComplianceChecker._calculate_work_timeP   s   &	
Eu.//5	#++//53G3G/HD$($6$6M$(AA!((4&*CC"))%**5 D   !*&*; <A3z?"!'
!3A!6!1 . >MMOH!-M != t##s   AD>!DDc                    U R                   (       d  g[        U R                   SS5      nU(       d  g[        U R                   SS5      nU(       d  g[        R                  " UR	                  5       U5      n[        R                  " UR	                  5       U5      nXe:  a  U[        SS9-  nXe-
  R                  5       S-  nXq-
  nUS::  a  SS	S
US S3SS.$ US::  a  SSSUS S3US.$ g)z+Check if approaching or at daily hour limitNmonday_friday_endmonday_friday_startr   daysrD   g      ?daily_limit_reachedcriticalz!You've reached your daily limit (.1fz hours). Please clock out.r   typeseveritymessagetime_remainingdaily_limit_warninghighzApproaching daily limit. z hours remaining.)r   getattrr   combiner(   r   rK   )	r   
work_hoursr2   daily_limit
start_timestart_datetimeend_datetime	max_hoursremaining_hourss	            r   r.   $ComplianceChecker._check_daily_limitq   s	   {{ dkk+>E T[[*?F
 ")),*;*;*=zJ''(9(9(;[I(I1--L!2AACdJ	#0c!-&>yoMgh"#	  !-"6s6KK\]"1	  r   c           
         U R                   (       d  g[        U R                   SS5      n[        U R                   SS5      nU(       a  U(       d  gSnSnU H  n[        US5      (       d  M  SSKJn   UR
                  R                  UR                  S9n	U	R                  n
U
S;   a  UR                  nM`  U
S	;   a1  U(       a(  UR                  U-
  R                  5       S
-  nXk-  nSnM  M  M     U(       aT  UR                  5       U-
  R                  5       S
-  nXl-  nXdS
-  S-
  :  a!  SSSUS
-  S S3[        SSXdS
-  -
  -
  5      S.$ g!    M  = f)zCheck if break is required soonN minimum_total_break_time_minutesr    short_break_required_after_hoursr=   r>   r   )r@   rC   )rB   rA   <      break_required_soonmediumz'Break required soon. You've worked for r]   z hours.r^   )r   re   rE   rF   r?   r   r   r=   rG   r)   rK   max)r   rL   r2   min_break_durationbreak_after_hourslast_clock_intotal_work_minutesrO   r?   rP   rG   rT   work_durations                r   r0   +ComplianceChecker._check_break_requirements   s~   {{ %T[[2TVWX#DKK1SUVW (: Eu.//5#++//53G3G/HD$($6$6M$(@@(-

&*DD((-

](B'Q'Q'SVX'XH.:.,0M ) E $ )..0=@OOQTVVM/ ""&<%BB1 (!HJ\]_J_adHeelm&)!R3E\^H^3^-_&`	  #s   3AE63EEc                    U R                   (       d  gSnUR                  5       n[        S5       H  nU[        US9-
  n[        R
                  R                  U R                  US9nUR                  5       (       d  MN  SSK	J
n  U H?  n UR
                  R                  UR                  S9n	U	R                  S:X  a  US	-  n  M  MA     M     US
:  a  SSSU S3SS.$ g!    M^  = f)zCheck consecutive work daysNr      rY   r'   r>   r   r@   r      consecutive_days_warninglowzYou've worked z. consecutive days. Consider taking a rest day.r^   )r   r(   ranger   r   r   r   r   r,   rF   r?   r   r=   rG   )
r   r2   days_worked
check_daterQ   date_to_checkrL   r?   rO   rP   s
             r   r1   .ComplianceChecker._check_consecutive_work_days   s    {{ !&&(
qA&)::M]]))" * F
 }}5#E'//33u7K7K3L--;'1,K! < $ ( !2!+K=8fg"&	  s   
8CC$c                 &   U R                   (       d  S/ / S.$ U R                  5       nU Vs/ s H  o3R                  S5      S:X  d  M  UPM     nn[        U5      S:H  UU Vs/ s H  o3R                  S5      S;   d  M  UPM     snS.$ s  snf s  snf )z
Check if creating a stamp would cause a violation

Returns:
    dict: Result with allowed (bool), alerts (list), warnings (list)
T)allowedr3   warningsr`   r\   r   )ru   r   )r   r:   r   rJ   )r   
stamp_typer3   acritical_alertss        r   check_before_stamp_creation-ComplianceChecker.check_before_stamp_creation   s     {{#rrBB &&( '-Pfj0AZ0O1fP ?+q0$*UFqeeJ.?CT.TFU
 	
 Q
 Vs   B	B	"B>B)r   r   r   r   )__name__
__module____qualname____firstlineno____doc__r   r   r:   r-   r.   r0   r1   r   __static_attributes__ r   r   r
   r
   	   s2    O&)V$B(T/b%N
r   r
   )loggingr   r   django.utilsr   modelsr   stamps.modelsr   	getLoggerr   r!   r
   r   r   r   <module>r      s0     ( ! % 			8	$~
 ~
r   