
    ֒Tij<                        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
mZmZ ddlmZ ddlmZ ddlmZ dd	lmZ dd
lmZ ddlmZ ddlmZmZ ddlmZ ddlmZm Z   ejB                  e"      Z# G d d      Z$ G d de      Z%y)z
Base class for cron tasks.
    N)ABCabstractmethod)date)OptionalDictAny)transaction)settings)timezone)CronExecution)ExecutionPattern)DatabaseLock)ValidationErrorConcurrentExecutionError)NotificationManager)CronjobHookContextget_global_cronjob_hook_managerc                   L    e Zd ZdZddedededededee   d	eeee	f      fd
Z
y)ExecutionResultzResult of task execution.Nsuccessmessage
error_codeskippedreason	exceptiondatac                 n    || _         || _        || _        || _        || _        || _        |xs i | _        y )N)r   r   r   r   r   r   r   )selfr   r   r   r   r   r   r   s           M/home/cursorai/projects/django-cronjob-utils/src/django_cronjob_utils/base.py__init__zExecutionResult.__init__   s7    $"JB	    )T r"   Fr"   NN)__name__
__module____qualname____doc__boolstrr   	Exceptionr   r   r     r!   r   r   r      s    # c C _c ux   KS  T]  K^   mu  vz  {~  @C  {C  vD  mE r!   r   c                   z   e Zd ZU dZdZeed<   dZeed<   ej                  Z
eed<   dZeed<   dZeed	<   d
Zeed<   dZee   ed<   defdZd ZdefdZd ZdefdZdefdZdefdZdefdZdedefdZdefdZd Z defdZ!d'dee   dee"ee#f      de$fd Z%d!e$fd"Z&d!e$fd#Z'defd$Z(e)d%ede*fd&       Z+y)(CronTaskz'Abstract base class for all cron tasks.r"   	task_name	task_codeexecution_patternFretry_on_failurer   max_retriesi,  retry_delayNtimeoutexecution_datec                     || _         || _        d| _        t               | _        t               | _        |j                  dd      | _        y)z
        Initialize cron task.
        
        Args:
            execution_date: Date for which the task should be executed
            **options: Additional options (e.g., force, rerun)
        Nenable_hooksT)	r4   options	executionr   _notification_managerr   _hook_managerget_enable_hooks)r   r4   r7   s      r   r    zCronTask.__init__0   sC     -26%8%:"<>$[[>r!   c                 z    t        | j                  t              s!t        dt	        | j                               y)zBValidate task input. Override in subclasses for custom validation.z*execution_date must be a date object, got N)
isinstancer4   r   r   typer   s    r   validatezCronTask.validate?   s7    $--t4!$NtTXTgTgOhNi"jkk 5r!   returnc                 D   | j                   j                  dd      }|ry| j                   j                  dd      }|ry| j                  t        j                  k(  ry| j                  t        j
                  k(  rEt        j                  j                  | j                  | j                  d      j                          S | j                  t        j                  k(  ryt        j                  j                  | j                  | j                  d      j                          S )z0Check if task should run (duplicate prevention).forceFTrerun)r.   r4   r   )r.   r4   	completed)r7   r;   r/   r   ALWAYSRERUN_ON_FAILUREr   objectsfilterr.   r4   existsRATE_LIMITED)r   rD   rE   s      r   
should_runzCronTask.should_runD   s     %0  %0!!%5%<%<<!!%5%F%FF$,,33..#22 4  fh	  !!%5%B%BB !((//nn.. 0 
 &(	 	r!   c                 B    t        | j                  | j                        S )z6Acquire database lock to prevent concurrent execution.)r   r.   r4   r@   s    r   acquire_lockzCronTask.acquire_locke   s    DNND,?,?@@r!   c                     t         j                  j                  | j                  | j                  | j
                  t        j                         dd      | _        | j                  S )z$Create execution record in database.F)r.   r-   r4   pidrF   r   )	r   rI   creater.   r-   r4   osgetpidr8   r@   s    r   create_execution_recordz CronTask.create_execution_recordi   sQ    &..55nnnn..		 6 
 ~~r!   c                 Z    | j                   r| j                         S | j                         S )zExecute with optional timeout.)r3   _execute_with_signal_timeout_executer@   s    r   execute_with_timeoutzCronTask.execute_with_timeoutu   s$    <<4466}}r!   c                 .    t        t        d      s3t        j                  d j                   d        j                         S  fd}t        j                  t        j                  |      }t        j                   j                         	  j                         }t        j                  d       t        j                  t        j                  |       |S # t        j                  d       t        j                  t        j                  |       w xY w)z.Execute with signal-based timeout (Unix only).SIGALRMz-Timeout not supported on this platform. Task z will run without timeout.c                 6    t        dj                   d      )Nz#Task execution exceeded timeout of  seconds)TimeoutErrorr3   )signumframer   s     r   timeout_handlerz>CronTask._execute_with_signal_timeout.<locals>.timeout_handler   s    !DT\\NRZ[\\r!   r   )	hasattrsignalloggerwarningr-   rX   r[   alarmr3   )r   ra   old_handlerresults   `   r   rW   z%CronTask._execute_with_signal_timeout{   s     vy)NN''AC ==?"	] mmFNNODT\\"	7]]_F LLOMM&..+6 LLOMM&..+6s   C ;Dc                    	 | j                  | j                        }t        |t              r?t	        |j                  dd       |j                  dd      |j                  dd            S t        |t              r|S t	        d|rt        |      	      S d	      S # t        $ rX}| j                  |      }t        j                  d
| j                   d|        t	        dt        |      ||      cY d}~S d}~ww xY w)zInternal execution method.errorFr   r"   r   r   r   r   TzTask completed successfully)r   r   Task z execution failed: )r   r   r   r   N)executer4   r>   dictr   r;   r(   r)   get_error_coderd   r   r-   )r   rh   er   s       r   rX   zCronTask._execute   s    	\\$"5"56F &$'& &

7E ::"JJy"5%zz,;  &/2 #'-F 3P   	,,Q/JuT^^$44GsKL"A%	 	s1   A)B ,B >B B 	C>&AC93C>9C>r   c                 .    |j                   j                  S )z?Get error code from exception. Override for custom error codes.)	__class__r#   )r   r   s     r   ro   zCronTask.get_error_code   s    ""+++r!   c                     | j                   sy| j                  sy| j                  j                  | j                  k\  ryy)z Check if task should be retried.FT)r0   r8   retry_countr1   r@   s    r   should_retryzCronTask.should_retry   s6    $$~~>>%%)9)99r!   c                     | j                   r| j                   j                          t        j                  d| j                   j                  dz    d| j
                   d| j                   d| j                   d	       y)z:Schedule retry execution. Override for custom retry logic.zScheduling retry    /z
 for task z after r]   N)r8   increment_retryrd   infort   r1   r-   r2   r@   s    r   schedule_retryzCronTask.schedule_retry   sn    >>NN**, : :Q >?qAQAQ@R S'wt/?/?.@J	
r!   rh   c                     | j                   r7|j                  xs d}| j                  j                  | j                   |       yy)zNotify stakeholders of failure.zUnknown errorN)r8   r   r9   notify_failure)r   rh   error_messages      r   r}   zCronTask.notify_failure   s6    >>"NN=oM&&55dnnmT r!   execution_idmetadatac                     t        d| j                  | j                  | j                  | j                  || j
                  r| j
                  j                  nd| j                  |xs i 	      S )z*Create hook context for current execution.rm   r   )		operationr.   r-   r4   r/   r   rt   r7   r   )r   r.   r-   r4   r/   r8   rt   r7   )r   r   r   s      r   _create_hook_contextzCronTask._create_hook_context   sX    !nnnn.."44%6:nn22!LL^

 
	
r!   contextc                 r    | j                   sy	 | j                  j                  |       y# t        $ r  w xY w)z(Execute PRE hooks before task execution.N)r<   r:   execute_pre_hooksr)   r   r   s     r   _execute_pre_hookszCronTask._execute_pre_hooks   s9    !!	009 		s   + 6c                 T    | j                   sg S | j                  j                  |      S )z(Execute POST hooks after task execution.)r<   r:   execute_post_hooksr   s     r   _execute_post_hookszCronTask._execute_post_hooks   s'    !!I!!44W==r!   c                    | j                          | j                         s1t        d| j                  t        j
                  k7  rd      S d      S | j                  d      }	 | j                  |       d}	 | j!                         5  | j#                         | _        | j$                  j&                  }| j                  ||j(                        }	 | j+                         }| j$                  j-                  |j.                  |j0                  |j2                         | j5                  |      }|r=t        j                  d	| j                   d|D cg c]  }|j2                   c}        |j.                  s| j7                  |       |j.                  s | j9                         r| j;                          |j.                  s|j<                  r|j<                  |cddd       S # t        $ rz}t        |d|j                  j                        }t        |      }t        |di       }t        j                  d	| j                   d
| d|        t        d|||      cY d}~S d}~ww xY wc c}w # t>        t        f$ r}tA        |t>              s| jC                  |      nd}t        |      }| j$                  r| j$                  jE                  ||       | j7                  t        d||             | j9                         r| j;                           d}~ww xY w# 1 sw Y   yxY w# tF        $ r  t>        t        f$ r}|	 tH        jJ                  jM                  |        # tH        jN                  $ r tA        |t>              s| jC                  |      nd}t        |      }tQ        jR                         5  tH        jJ                  jU                  | jV                  | j                  | jX                  t[        j\                         dd||t_        j`                         	       ddd       Y  # 1 sw Y   Y  xY ww xY w d}~ww xY w)z5Main execution method with full lifecycle management.TzAlready executedSkipped)r   r   N)r   r   detailsrl   z rejected by PRE hook: z - F)r   r   r   r   )r   rk   z& completed but POST hooks had errors: TIMEOUT)r   r   )pk)	r.   r-   r4   rQ   rF   r   r   r   ended)1rA   rM   r   r/   r   rG   r   r   r)   getattrrr   r#   r(   rd   re   r-   rO   rU   r8   idr   rY   mark_completedr   r   r   r   r}   ru   r{   r   r^   r>   ro   mark_failedr   r   rI   r;   DoesNotExistr	   atomicrR   r.   r4   rS   rT   r   now)	r   r   rp   r   r~   error_detailsr   rh   post_hook_errorss	            r   runzCronTask.run   s    	  "-1-C-CGWG^G^-^) dm  +++>	##G,& Z	""$!%!=!=!?#~~00 33L7K[K[3\6!668F NN11 & &#)#4#4 2  (,'?'?'H$'#DNN#33Y6FG6F6FGHJ ">>++F3 ">>d.?.?.A++- ">>f.>.>$...!O %$)  	 L!++2F2FGJFM#Ay"5MNN''>zl#m_] #%%"	 	X  H" %i0 ?I!\?Z!4!4Q!7`iJ$'FM ~~22$1'1 3 
 '' % -#-)  ((*++-+S %$~ ( 	i( 	'!))--->$ # %11 ?I!\?Z!4!4Q!7`iJ$'FM$++-%--44&*nn&*nn+/+>+> "		&*$)$1'1"*,,. 5 
 .-  .- #" /	s   %G' 9L* 	A	LB I2I-&A7I2	L* '	I*0A/I%I*%I*-I22LBLLLL'#L* 'L* *QQ M'&Q'AP?<A+P1'P?/Q1P;6P?9Q;P??QQr   c                      y)aV  
        Business logic - must be implemented by subclasses.
        
        Args:
            date: Execution date
            
        Returns:
            dict with keys:
                - error: bool (True if error occurred)
                - message: str (status message)
                - error_code: str (optional error code)
        Nr*   )r   r   s     r   rm   zCronTask.executez  s     	r!   )NN),r#   r$   r%   r&   r-   r(   __annotations__r.   r   STANDARDr/   r0   r'   r1   intr2   r3   r   r   r    rA   rM   rO   r   rU   r   rY   rW   rX   r)   ro   ru   r{   r}   r   r   r   r   r   r   r   r   rn   rm   r*   r!   r   r,   r,   %   s`   1IsIs-66s6"d"KK!GXc]!?t ?l
D BA
 
o o 6/ @,	 ,c ,d 
U_ U
# 
QYZ^_bdg_gZhQi 
  vH 
	*< 	>+= >}_ }~ D T  r!   r,   )&r&   loggingrS   rc   abcr   r   datetimer   typingr   r   r   	django.dbr	   django.confr
   django.utilsr   django_cronjob_utils.modelsr   django_cronjob_utils.registryr   django_cronjob_utils.locksr   django_cronjob_utils.exceptionsr   r   "django_cronjob_utils.notificationsr   django_cronjob_utils.hooksr   r   	getLoggerr#   rd   r   r,   r*   r!   r   <module>r      sf     	  #  & & !   ! 5 : 3 U B Z			8	$
 
cs cr!   