ó
    å4 j-"  ã                   ó–  • S r SSKrSSKrSSKrSSKrSSKr\R                  R                  \R                  R                  \	5      5      r
\R                  R                  S5      rSS/SSS4S	S
S/SSS4SSS/SSS4S.rS rS rS#S jrS$S jrS rS$S jrS rS rS rS rS  rS$S! jr\S":X  a  \R6                  " \" 5       5        gg)%aÅ  
Puts BidBrain's timed jobs on this Mac's own scheduler (launchd), the same
way every existing com.bidbrain.* job was set up by hand, so a dealer can
add the daily reads without a session on the Mac writing plists for them
(Steven 2026-09-07: "set didn't win sync and under offer check to run
daily like purchases sync, i think compmatch should be the same too",
then "mark will want it to work the same way too").

  python3 setup_schedule.py                 show every BidBrain job this Mac has
  python3 setup_schedule.py --install       add the three daily reads at the default times
  python3 setup_schedule.py --install lost_bids=16:05 compmatch=16:45
                                            only those jobs, at those times
  python3 setup_schedule.py --remove lost_bids
                                            take one out again

What it can install (key, what runs, default time):

  lost_bids     lost_bids_run.py             16:05   Didn't win sync
  under_offer   daily_run.py --under-offer   16:30   Under offer check
  compmatch     share_comps.py --sync        16:45   CompMatch sync

The defaults sit clear of the jobs a Mac normally already runs (Purchases
sync 15:50, the DealerKit push 16:15, the 17:00 logins, the 17:10 run):
lost_bids and under_offer read Motorway through the one shared browser
profile, which only one process may hold at a time (bidbrain.browser
.open_context), so nothing that touches Motorway may fire in the same
minutes. compmatch uses no browser at all, only git.

A job this Mac already has (by label) is LEFT ALONE unless its time is
given on the command line, so a Mac that set one of these up by hand
earlier (Mark's com.bidbrain.lostbids at 15:55) keeps its own time on a
plain --install. Every file follows the conventions of the jobs already
here: the absolute path of this very python (launchd does not use the
shell PATH), WorkingDirectory at the project root, one log per job under
data/logs, no KeepAlive or RunAtLoad (a timed job fires at its time and
exits). Loaded with launchctl straight away, so nothing waits for a
restart. Never touches any other com.bidbrain.* file.

Opt in only. Nothing in BidBrain calls this on its own, so a Mac that
never runs it is exactly as it was.
é    Nz~/Library/LaunchAgentszcom.bidbrain.lostbidszlost_bids_run.pyz16:05ÚlostbidszDidn't win synczcom.bidbrain.under-offerzdaily_run.pyz--under-offerz16:30zunder-offerzUnder offer checkzcom.bidbrain.compmatchzshare_comps.pyz--syncz16:45Ú	compmatchzCompMatch sync)Ú	lost_bidsÚunder_offerr   c                 ó�  • [         R                  " SU =(       d    SR                  5       5      nU(       aO  S[        UR	                  S5      5      s=::  a  S::  a*  O  O'S[        UR	                  S5      5      s=::  a  S::  d  O  [        SU  S	35      e[        UR	                  S5      5      [        UR	                  S5      5      4$ )
zB'16:05' -> (16, 5). Anything else is refused with a clear message.z(\d{1,2}):(\d{2})Ú r   é   é   é   é;   Ú'z' is not a time like 16:05)ÚreÚ	fullmatchÚstripÚintÚgroupÚ
ValueError)ÚtextÚms     Ú//Users/stevendouglas/BidBrain/setup_schedule.pyÚ
parse_timer   =   s‹   € ä
�ŠÐ)¨D¯J°B×+=Ñ+=Ó+?Ó@€AÞ�Qœ#˜aŸg™g a›j›/Õ/¨RÖ/°A¼¸Q¿W¹WÀQ»Z»Õ4NÈBÕ4NÜ˜1˜T˜FÐ"<Ð=Ó>Ð>Üˆq�w‰w�q‹z‹?œC §¡¨£
›OÐ+Ð+ó    c           	      ó  • U (       d  [          Vs0 s H  oS_M     sn$ 0 nU  H\  nUR                  S5      u  pEnU[         ;  a%  [        SU SSR                  [         5       S35      eU(       a  [	        U5      OSX$'   M^     U$ s  snf )zÀ['lost_bids=16:05', 'compmatch'] -> {'lost_bids': (16, 5), 'compmatch': None}.
No words at all means every job at its default. None means 'keep the
time the Mac already has, else the default'.NÚ=r   z!' is not a job this can install (ú, Ú))ÚJOBSÚ	partitionr   Újoinr   )ÚwordsÚkÚoutÚwÚkeyÚ_Úts          r   Úparse_requestsr'   E   s‡   € ö Ý!%Ó&¢˜A�4’¡Ñ&Ð&Ø
€CÛˆØ—K‘K Ó$‰	ˆ�Ø”d‹?Ü˜q  Ð%FÀtÇyÁyÔQUÃÐFWÐWXÐYÓZÐZÞ$%”:˜a”=¨4ˆ‹ñ	 ð
 €Jùò 's   �Bc                 ó`  • [         U    u  pVpxnU=(       d    [        nUU=(       d    [        R                  /[        R
                  R                  XFS   5      /-   USS -   UXS.[        R
                  R                  USSU S35      [        R
                  R                  USSU S35      S.$ )	z=The launchd file for one job, matching the hand written ones.r   r	   N)ÚHourÚMinuteÚdataÚlogsz.log)ÚLabelÚProgramArgumentsÚWorkingDirectoryÚStartCalendarIntervalÚStandardOutPathÚStandardErrorPath)r   ÚPROJECT_ROOTÚsysÚ
executableÚosÚpathr   )	r$   ÚhourÚminuteÚpythonÚrootÚlabelÚargvr%   Úlogs	            r   Ú	plist_forr?   T   s¢   € ä! #™YÑ€E�˜Ø×”<€DàØ#×5¤s§~¡~Ð6¼"¿'¹'¿,¹,ÀtÐRSÉWÓ:UÐ9VÑVÐY]Ð^_Ð^`ÐYaÑaØ Ø*.Ñ!AÜŸ7™7Ÿ<™<¨¨f°fÀÀÀT¸lÓKÜŸW™WŸ\™\¨$°¸À3À%ÀtÀÓMñð r   c                 óv   • [         R                  R                  U=(       d    [        [        U    S   S-   5      $ )Nr   z.plist)r6   r7   r   Ú
AGENTS_DIRr   )r$   Ú
agents_dirs     r   Ú
plist_pathrC   b   s)   € Ü�7‰7�<‰<˜
×0¤j´$°s±)¸A±,ÀÑ2IÓJÐJr   c                 ó8  •  [        U S5       n[        R                  " U5      R                  S5      =(       d    0 nSSS5        [	        WR                  SS5      5      [	        UR                  SS5      5      4$ ! , (       d  f       ND= f! [
         a     gf = f)z5(hour, minute) of a job already on this Mac, or None.Úrbr0   Nr)   r   r*   )ÚopenÚplistlibÚloadÚgetr   Ú	Exception)r7   ÚfhÚcals      r   Úexisting_timerM   f   s~   € ðÜ�$˜Ô Ü—-’- Ó#×'Ñ'Ð(?Ó@×FÀBˆC÷ ä�3—7‘7˜6 1Ó%Ó&¬¨C¯G©G°H¸aÓ,@Ó(AÐAÐA÷ Õûô ó Ùðús'   ‚B Ž/A;½=B Á;
B	ÂB Â
BÂBc                 ó@  • / nU R                  5        H‡  u  p4[        X15      n[        U5      nUc8  U(       a  UR                  X6S   US   S45        MA  [	        [
        U   S   5      nUR                  X4S   US   U(       a  Xd:w  a  SO
U(       a  SOS45        M‰     U$ )z¸What --install would do, as (key, hour, minute, action) rows, where
action is 'add', 'change' or 'keep'. Pure, so it is testable without a
Mac: the launchctl calls happen in install().r   r	   Úkeepr   ÚchangeÚadd)ÚitemsrC   rM   Úappendr   r   )ÚrequestsrB   Úrowsr$   Úwantedr7   Úhaves          r   ÚplanrX   p   s™   € ð €DØ—~‘~Ö'‰ˆÜ˜#Ó*ˆÜ˜TÓ"ˆØ‰>ÞØ—‘˜S q¡'¨4°©7°FÐ;Ô<ÙÜ¤ S¡	¨!¡Ó-ˆFØ�‰�S ™) V¨A¡Y¾DÀTÃ^±ÖcgÑY_ÐmrÐsÖtñ (ð €Kr   c                  ó¬   • [         R                  " S/U QSSS9nUR                  S:H  UR                  =(       d    UR                  R                  5       4$ )NÚ	launchctlT)Úcapture_outputr   r   )Ú
subprocessÚrunÚ
returncodeÚstderrÚstdoutr   )ÚargsÚrs     r   Ú
_launchctlrc   �   sF   € Ü�Š˜Ð+ dÐ+¸DÀtÑL€AØ�<‰<˜1Ñ˜qŸx™x×3¨1¯8©8×:Ñ:Ó<Ð<Ð<r   c                 ó¾  • [         R                  " [        SS9  [         R                  " [         R                  R	                  [
        SS5      SS9  [        U 5       Hì  u  pp4[        U5      n[        U   S   nUS:X  a  [        U SUS S	US S
35        M;  US:X  a  [        SU5        [        US5       n[        R                  " [        XU5      U5        S S S 5        [        SSU5      u  p‰U(       aC  [        U SUS:X  a  SOS SUS S	US S[         R                  R                  U5       S3
5        MÜ  [        U SU	 35          g   g! , (       d  f       N|= f)NT)Úexist_okr+   r,   é   rO   z: already runs at Ú02dÚ:z, left as it isrP   ÚunloadÚwbrH   ú-wz: rQ   Únowzmoved toÚ z every day (r   z0: file written but launchctl could not load it: r	   r   )r6   ÚmakedirsrA   r7   r   r3   rX   rC   r   Úprintrc   rF   rG   Údumpr?   Úbasename)
rT   r$   r8   r9   Úactionr7   r    rK   ÚokÚmsgs
             r   Úinstallru   †   sW  € Ü‡K‚K”
 TÒ*Ü‡K‚K”—‘—‘œ\¨6°6Ó:ÀTÒJÜ%)¨(¦^Ñ!ˆ�6Ü˜#‹ˆÜ�S‘	˜!‘ˆØ�VÓÜ�U�GÐ-¨d°3¨Z°q¸À¸ÀOÐTÔUÙØ�XÓÜ�x Ô&Ü�$˜Ô Ü�MŠMœ) C¨vÓ6¸Ô;÷ ä˜V T¨4Ó0‰ˆÞÜ�U�G˜2 v°£™e¸JÐGÀqÈÈcÈ
ÐRSÐTZÐ[^ÐS_Ð_kÔln×lsÑls×l|Ñl|ð  ~Bó  mCð  lDð  DEð  Fö  Gä�U�GÐKÈCÈ5ÐQÔRÙñ &4ð  ÷ Õús   Â6"EÅ
E	c                 ó  • U  H  n[        U5      n[        U   S   n[        R                  R	                  U5      (       d  [        U S35        MN  [        SSU5        [        R                  " U5        [        U S35        M�     g)Nrf   z.: not scheduled on this Mac, nothing to removeri   rk   z	: removedr   )rC   r   r6   r7   Úexistsro   rc   Úremove)Úkeysr$   r7   r    s       r   rx   rx   œ   ss   € ÛˆÜ˜#‹ˆÜ�S‘	˜!‘ˆÜ�w‰w�~‰~˜d×#Ñ#Ü�U�GÐIÐJÔKÙÜ�8˜T 4Ô(Ü
�	Š	�$ŒÜ���yÐ!Ö"ñ ð r   c                  ób  • SS K n [        U R                  [        R                  R	                  [
        S5      5      5      nU(       d  [        S5        gU HV  n[        U5      nU(       a  US   S SUS   S 3OSn[        U S[        R                  R                  U5      S S	  35        MX     g)
Nr   zcom.bidbrain.*.plistz+No BidBrain jobs are scheduled on this Mac.rg   rh   r	   z	always onz  iúÿÿÿ)	ÚglobÚsortedr6   r7   r   rA   ro   rM   rq   )r{   ÚfilesÚfr&   Úwhens        r   Úshowr€   ©   sš   € ÛÜ�4—9‘9œRŸW™WŸ\™\¬*Ð6LÓMÓNÓO€EÞÜÐ;Ô<ØÛˆÜ˜!ÓˆÞ+,�!�A‘$�s�˜1˜Q˜q™T #˜JÑ'°+ˆÜ���bœŸ™×)Ñ)¨!Ó,¨S¨bÐ1Ð2Ð3Ö4ñ ð r   c                 óÀ   •  [         R                  R                  S[        5        SSKJn  UR                  S5        UR                  SX5        g! [         a     gf = f)z˜The outcome for the Run menu's board and Dealer OS, when run as the
install_schedule pass. Best effort: a missing record never fails the
install itself.r   )ÚrunsÚinstall_scheduleN)	r4   r7   Úinsertr3   Úbidbrainr‚   ÚbeginÚfinishrJ   )rs   Úsummaryr‚   s      r   Ú_recordr‰   ¶   sJ   € ðÜ�‰�‰˜œ<Ô(Ý!Ø�
‰
Ð%Ô&Ø�‰Ð&¨Õ4øÜó Ùðús   ‚AA Á
AÁAc                 óÎ  • [        U c  [        R                  SS  OU 5      n [        R                  S:w  a  U (       a  [	        S5        g U (       d
  [        5       $ U S   S:X  aW  [        U SS  5      n[        U5      nSR                  S [        U5       5       5      n[        US:H  US:X  a  SU S	3OS
5        U$ U S   S:X  a]  U SS  nU Vs/ s H  oU[        ;  d  M  UPM     nnU(       a  U(       a"  [        SSR                  [        5       S35      e[        U5      $  [	        [        R                  5       5        gs  snf ! [         a  n[	        U5         S nAgS nAff = f)Nr	   Údarwinz5This sets up launchd jobs, which only exist on a Mac.r   z	--installr   c              3   óT   #   • U  H  u  pp4[         U   S     SUS SUS 3v •  M      g7f)rf   rm   rg   rh   N)r   )Ú.0r!   Úhr   Ú_as        r   Ú	<genexpr>Úmain.<locals>.<genexpr>Î   s3   é € Ð`ÒQ_Á+À!È¤ a¡¨¡ ¨A¨a°¨W°A°a¸°WÕ=ÒQ_ùs   ‚&(zTimed jobs set up: Ú.z.launchctl could not load a job, see data/logs.z--removezsay which job to remove (r   )Úlistr4   r=   Úplatformro   r€   r'   ru   r   rX   r‰   r   r   rx   Ú__doc__r   )r=   rT   Úrcr    ry   r!   ÚbadÚes           r   Úmainr™   Ã   sA  € Ü ¡”—‘˜˜‘°$Ó7€DÜ
‡|�|�xÓ¦DÜÐEÔFØðÞÜ“6ˆMØ�‰7�kÓ!Ü% d¨1¨2 hÓ/ˆHÜ˜Ó"ˆBØ—I‘IÑ`ÔQUÐV^ÔQ_Ó`Ó`ˆEÜ�B˜!‘G¸rÀQ»wÐ2°5°'¸Ñ;ÐL|Ô}ØˆIØ�‰7�jÓ Ø˜˜�8ˆDÙ"Ó4šd˜¬t¡m—1™dˆCÐ4Þž3Ü Ð#<¸T¿Y¹YÄt»_Ð<MÈQÐ!OÓPÐPÜ˜$“<Ðð !ô 
Œ'�-‰-‹/ÔØùò 5øô ó ÜˆaŒÜûðús=   Á
E ÁAE Â;E ÃE ÃE Ã$<E Å E Å
E$ÅEÅE$Ú__main__)NN)N)r•   r6   rG   r   r\   r4   r7   ÚdirnameÚabspathÚ__file__r3   Ú
expanduserrA   r   r   r'   r?   rC   rM   rX   rc   ru   rx   r€   r‰   r™   Ú__name__Úexit© r   r   Ú<module>r¢      sò   ðñ(óT 
Û Û 	Û Û 
à�w‰w�‰˜rŸw™wŸ™¨xÓ8Ó9€Ø�W‰W×ÑÐ 8Ó9€
ð *Ð,>Ð+?ÀÈ*ÐVgÐhØ.°ÀÐ0QÐSZÐ\iÐk~ÐØ*Ð-=¸xÐ,HÈ'ÐS^Ð`pÐqñ€ò,òôôKòôò"=ò
ò,
ò
ò
ôð6 ˆzÓØ‡H‚H‰T‹VÕð r   