+
    *Qj                    j    R t ^ RIHt ^ RIt]P                  ! R4      tR	tRtR R lt ! R R4      t	R# )
u  Routine scheduler — fires time-based routines server-side (APScheduler).

Phase-2 routines have two kinds of trigger:
  * TIME (or the time half of BOTH) — fires from this gateway on a cron, whether
    or not the phone is even awake. That's what this module owns.
  * PLACE (or the place half of BOTH) — an on-device geofence; the phone sends a
    ``new_location`` event naming the matched place, which ``main`` matches to
    routines directly. Nothing here.

``RoutineScheduler`` loads routines from the phone store on startup, schedules a
cron job per time/both routine, and calls the injected ``dispatch(routine)``
coroutine when one fires. ``reload()`` is re-run after any routine
create/cancel so the live schedule always matches storage.

APScheduler is imported LAZILY inside ``start`` so ``import scheduler`` (and the
test suite) works with no event loop and even if the package is missing until
runtime. Tests inject a fake scheduler backend via ``sched=`` and never import
APScheduler.
)annotationsNzjarvis.gateway.schedulerzAsia/Riyadhc                    V ^8  d   QhRRRR/# )   triggerdictreturnzdict | None )formats   "scheduler.py__annotate__r      s     A A A A    c                   \        V \        4      '       g   R# V P                  R4      R9  d   R# \        V P                  R4      ;'       g    R4      P	                  4       pRV9  d   R# VP                  R4      w  r#p \        V4      p\        V4      p^ Tu;8:  d   ^8:  d   M R# ^ Tu;8:  d
   ^;8:  g    R#  R# T P                  R4      ;'       g    . p. p\        T\        4      '       d^   T FW  p	\        T	4      P	                  4       P                  4       R,          p
T
\        9   g   K>  Y9  g   KF  TP                  T
4       KY  	  RTR	TR
T'       d   RP                  T4      /# R/#   \         d     R# i ; i)a   Turn a routine trigger into cron fields, or None if it isn't time-based.

Returns ``{"hour", "minute", "day_of_week"}`` where ``day_of_week`` is a
comma list ('mon,wed') or None for every day. Place-only triggers, or
malformed times, return None (the routine simply isn't time-scheduled).Ntypeat :days:N   Nhourminuteday_of_week,)timeboth)
isinstancer   getstrstrip	partitionint
ValueErrorlistlower	_WEEKDAYSappendjoin)r   r   hh_mmr   r   r   pickeddkeys   &          r
   
parse_cronr,      s\    gt$${{6"22	W[[$$"	%	+	+	-B
"}S!IB22wR OO !"V 1r 1 !2;;v$$"DF$Aa&,,.&&(,CiC$5c"  D(Fv388F+A A;?A A  s   E1 1F ?F c                  j    ] tR t^?tRt]R3R R lltR R ltR R ltR	 R
 lt	R R lt
R R ltRtR# )RoutineScheduleru8  Owns the APScheduler AsyncIOScheduler and the routine→cron-job mapping.

``dispatch``       async callable invoked with the routine dict when it fires.
``list_routines``  async callable returning the current list of routine dicts.
``sched``          optional pre-built scheduler backend (tests inject a fake).
Nc                    V ^8  d   QhRRRR/# )r   tzr   r   Noner   )r	   s   "r
   r   RoutineScheduler.__annotate__G   s      C  $r   c                	6    Wn         W n        W0n        W@n        R # )N)dispatchlist_routinesr0   _sched)selfr4   r5   r0   scheds   &&&&&r
   __init__RoutineScheduler.__init__G   s     *r   c                   V ^8  d   QhRR/# r   r   r   r   )r	   s   "r
   r   r2   N   s     # #S #r   c                  "   V P                   f   ^ RIHp V! V P                  R7      V n         \	        V P                   RR4      '       g   V P                   P                  4        V P                  4       G Rj  xL
 #  L5i)z>Build+start the scheduler (if not injected) and load routines.N)AsyncIOScheduler)timezonerunningF)r6   apscheduler.schedulers.asyncior>   r0   getattrstartreload)r7   r>   s   & r
   rC   RoutineScheduler.startN   sW     ;;G*DGG<DKt{{Iu55KK[[]"""s   A7B 9A>:B c                   V ^8  d   QhRR/# r<   r   )r	   s   "r
   r   r2   W   s      c r   c                <  "   V P                   f   ^ # \        V P                   P                  4       4       F  pVP                  4        K  	   V P	                  4       G Rj  xL
 p^ pT;'       g    .  FK  p\        TP                  R4      ;'       g    / 4      pT'       g   K1  T P                  YV4       T^,          pKM  	  \        P                  RT4       T#  Ly  \
         d2   p\        P                  R\        T4      R,          4        Rp?^ # Rp?ii ; i5i)u   Rebuild all jobs from storage. Returns the number of time-scheduled
routines. Never raises — a storage hiccup just leaves the schedule empty.Nzroutine reload failed: %s:Nx   Nr   z1routine scheduler loaded %d time-based routine(s))r6   r!   get_jobsremover5   	Exceptionloggerwarningr   r,   r   _add_jobinfo)r7   jobroutinesenrcrons   &      r
   rD   RoutineScheduler.reloadW   s      ;;,,./CJJL 0	!//11H RAaeeI.44"5DMM!"FA   	GK 2 	NN6AtE	sT   ADC !C"C &
D1"DD#8DC D(&DDDDc               $    V ^8  d   QhRRRRRR/# )r   routiner   rU   r   r1   r   )r	   s   "r
   r   r2   m   s&     J J JD JT Jr   c                	    ^ RI Hp V! VR,          VR,          VR,          V P                  R7      pV P                  P	                  V P
                  WA.R\        VP                  RR4      4      ,           R	R
R	R7       R# )    )CronTriggerr   r   r   )r   r   r   r?   zroutine:namer   Ti,  )r   argsidreplace_existingmisfire_grace_timecoalesceN)apscheduler.triggers.cronr[   r0   r6   add_job_runr   r   )r7   rX   rU   r[   trigs   &&&  r
   rN   RoutineScheduler._add_jobm   sl    9VT(^'+M':TWWNIIt)CFB 788!cD 	 	Jr   c                    V ^8  d   QhRRRR/# )r   rX   r   r   r1   r   )r	   s   "r
   r   r2   v   s     > >$ >4 >r   c                	   "    V P                  V4      G R j  xL
  R #  L  \         dB   p\        P                  RTP	                  R4      \        T4      R,          4        R p?R # R p?ii ; i5i)Nz routine '%s' dispatch failed: %sr\   :N   N)r4   rK   rL   rM   r   r   )r7   rX   rR   s   && r
   rd   RoutineScheduler._runv   sU     	>--((( 	>NN=";;v.At> >	>s6   A0! ! A0! A-6A("A0(A--A0c                   V ^8  d   QhRR/# )r   r   r1   r   )r	   s   "r
   r   r2   }   s       r   c                	   "   V P                   e?   \        V P                   RR4      '       d     V P                   P                  RR7       R # R # R #   \         d     R # i ; i5i)Nr@   F)wait)r6   rB   shutdownrK   )r7   s   &r
   rn   RoutineScheduler.shutdown}   sW     ;;"wt{{Iu'M'M$$%$0 (N"  s(   ,A#A A#A A#A  A#)r6   r4   r5   r0   )__name__
__module____qualname____firstlineno____doc__
DEFAULT_TZr9   rC   rD   rN   rd   rn   __static_attributes__r   r   r
   r.   r.   ?   s4     ;E#,J> r   r.   )montuewedthufrisatsun)
rt   
__future__r   logging	getLoggerrL   r#   ru   r,   r.   r   r   r
   <module>r      s@   ( # 			5	6=	
A@C Cr   