
    $i                        S r SSKrSSKrSSKrSSKrSSKrSSKJr  SSKJ	r	J
r
  SSKrSSKJr  SSKJ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Jr  SSKJrJ r   \" 5         \" \!5      r"\" 5       r#S\	S\$S\	4S jr%\RL                  " 5       S 5       r'\'RQ                  S5      \RR                  " SSSSS9\RR                  " SSSSS9\RR                  " SSSSS 9S\*S\*S!\$SS4S" j5       5       5       5       r+\'RQ                  5       \RR                  " SSSSS9\RR                  " SSSSS9\RR                  " S#S$SS%S9\RR                  " S&S'S\RX                  " / S(QSS)9S*S+9S\*S\*S$\*S'\*4S, j5       5       5       5       5       r-\'RQ                  5       \RR                  " SSSSS9\RR                  " SSSSS9\RR                  " S-SS.S/9S\*S\*S0\.4S1 j5       5       5       5       r/\'RQ                  5       \RR                  " S2S3SS4S9\RR                  " S5SSS6S 9\RR                  " SSSS7S9\RR                  " SSSS8S9S3\*S9\.S\*S\*4S: j5       5       5       5       5       r0\'RQ                  5       \RR                  " SSSSS9\RR                  " SSSSS9\RR                  " S;S<\RX                  " / S=Q5      S>SS?S@9\RR                  " SASBSCSSDS 9\RR                  " S5SSS6S 9S\*S\*S<\*SB\*S9\.4
SE j5       5       5       5       5       5       r1\'RQ                  5       SF 5       r2\'RQ                  5       SG 5       r3\'RQ                  5       \RR                  " SSSSS9\RR                  " SSSSS9\RR                  " SHSISSJS9SK 5       5       5       5       r4\'RQ                  5       \RR                  " SLSM\Rj                  " SNSO5      SSPSQ9\RR                  " S&\RX                  " / S(QSS)9S*SR9\RR                  " SS\RX                  " STSU/SS)9STSSVS@9\RR                  " S-SS.S/9\RR                  " SWSSXS/9\RR                  " SYSSZS/9S[ 5       5       5       5       5       5       5       r6\!S\:X  a  \'" 5         gg)]z9Worker CLI for TipSharks ingestion and computation tasks.    N)
monthrange)date	timedelta)Console)Table)
get_loggersetup_logging)get_settings)
parse_date)get_session)IngestionService)MeetingRace)StarterRepositorynormalize_race_datadmonthsreturnc                     U R                   S-
  U-
  nU R                  US-  -   nUS-  S-   n[        X25      S   n[        U R                  U5      n[        X2U5      $ )zSubtract *months* from *d*, clamping day to month length.

Args:
    d: Starting date.
    months: Number of months to subtract (must be >= 0).

Returns:
    Date *months* months before *d*.
      )monthyearr   mindayr   )r   r   target_monthtarget_yearmax_day
target_days         </root/tipsharks/tipsharks-elo-api/apps/backend/worker/cli.py_subtract_monthsr!      sd     77Q;'L&&<2--K"$q(L3A6GQUUG$J:66    c                      g)zUTipSharks worker CLI.

Commands for ingesting TAB racing data and computing ratings.
N r$   r"   r    clir%   /   s     	r"   znormalize-races--from	date_fromFzStart date (YYYY-MM-DD))requiredhelp--todate_tozEnd date (YYYY-MM-DD)z--commit-every   Tz$Commit interval for database updates)defaultshow_defaultr)   commit_everyc           
         U (       a  [        U 5      OSnU(       a  [        U5      OSn[        5        nUR                  [        5      R	                  [
        5      nU(       a"  UR                  [
        R                  U:  5      nU(       a"  UR                  [
        R                  U:*  5      nUR                  [        R                  5      R                  [
        R                  [        R                  5      R                  5        Vs/ s H  u  nUPM	     nn[        U5      n	[        R                  SU	 S35        Sn
Sn[!        US5       GHL  u  pUR                  [        5      R                  [        R                  U:H  5      R#                  5       n[%        UR&                  =(       d    0 5      nXR&                  :w  a  Xl        U
S-  n
UR)                  S5      =(       d    0 R)                  S5      n[+        U[,        5      (       aV  U HP  n[+        U[.        5      (       d  M  UR)                  S5      n[0        R2                  " X]R                  UUS	9  US-  nMR     X-  S:X  d  GM   UR5                  5         [        R                  S
U SU	 S35        GMO     UR5                  5         SSS5        [        R                  SW
 SW S35        gs  snf ! , (       d  f       N0= f)zJNormalize race/starter JSON structure and backfill starters from raw JSON.Nz[bold]Normalizing z races[/bold]r   r   raw_jsonstartersplacing)r3   z[dim]Processed /z races[/dim]u   [green]✓ Updated races: z, upserted starters: [/green])r   r   queryr   joinr   filtermeeting_datewith_entitiesidorder_byrace_numberalllenconsoleprint	enumerateoner   r1   get
isinstancelistdictr   upsertcommit)r'   r+   r/   
start_dateend_datesessionr6   race_idrace_idstotalupdated_racesupserted_startersidxrace
normalizedr2   starterr3   s                     r    normalize_racesrV   8   sU   * +4I&J&-z'"4H	'd#((1LL!5!5!CDELL!5!5!ABE $11$'':Xg**D,<,<=SU

  	 
 H*5'?@%h2LC==&--dgg.@AEEGD,T]]-@bAJ]]* *""z28b==jIH(D))'G%gt44 %kk)4G%,,WggwPWX%*%  ( !Q& uAeWLIJ% 3( 	O 
R MM
$]O3HIZH[[cdE
 
s&   CK	KEK-AKK
K&z--datesingle_datez"Single date to ingest (YYYY-MM-DD)z
--categorycategory)THG)case_sensitivez=Racing category: T (Thoroughbred), H (Harness), G (Greyhound))r(   typer)   c           	         U(       aG  U (       d  U(       a+  [         R                  S5        [        R                  " S5        [	        U5      nUnOPU (       a  U(       a  [	        U 5      n[	        U5      nO+[         R                  S5        [        R                  " S5        [        5       nU(       a  UR                  5       OUR                  R                  n[         R                  SU SW SW S35         [        5        n[        U5      n	[        R                  " U	R                  XEUS95      u  pnS	S	S	5        [        S
S9nUR                  SSS9  UR                  SSSS9  UR!                  S[#        W
5      5        UR!                  S[#        W5      5        UR!                  S[#        W5      5        UR!                  S[#        W	R$                  S   5      U	R$                  S   S:  a  SOSS9  [         R                  U5        U	R$                  S   S:  a'  [         R                  SU	R$                  S    S35        g	[         R                  S5        g	! , (       d  f       GN+= f! [&         aP  n[(        R+                  SU 3SS9  [         R                  S U S!35        [        R                  " S5         S	nAg	S	nAff = f)"zIngest meetings and races from TAB API.

Examples:
    ingest --from 2024-01-01 --to 2024-01-31
    ingest --date 2024-01-15
    ingest --date 2024-01-15 --category H
z4[red]Error: Cannot use --date with --from/--to[/red]r   z;[red]Error: Must specify either --date or --from/--to[/red]z
[bold]Ingesting z meetings from  to [/bold]
rX   NzIngestion ResultstitleEntitycyanstyleCountgreenrightrg   justifyMeetingsRacesStartersErrorserrorsr   red   
[yellow]⚠ z* errors occurred during ingestion[/yellow]u4   
[green]✓ Ingestion completed successfully[/green]zIngestion failed: Texc_infou   
[red]✗ Ingestion failed: [/red])r@   rA   sysexitr   r
   uppertabdefault_categoryr   r   asyncioruningest_date_ranger   
add_columnadd_rowstrstats	Exceptionloggererror)r'   r+   rW   rX   rJ   rK   settingseffective_categoryrL   servicemeetingsracesr2   tablees                  r    ingestr   ~   s@   F MMPQHHQK,
	w	*
g&ST~H-5)8<<;X;XMM
/0
|4PXzYbc#]g&w/G(/))3E * )%HX  /00Aj#h-0gs5z*j#h-0h'("==2Q6%G 	 	
 	e=="Q&MM x!8 99cd MMQR; ]>  )!-=5aS?@s8   6
I)  3I3DI) I) 
I&!I) )
K3AJ>>Kz--clearz'Clear existing ratings before recompute)is_flagr)   clearc                    [        U 5      n[        U5      n[        R                  SU SU S35        U(       a  [        R                  S5         SSKJn  [        5        nU" XcXBS9nSSS5        [        R                  S	W S
35        g! , (       d  f       N(= f! [         aP  n[        R                  SU 3SS9  [        R                  SU S35        [        R                  " S5         SnAgSnAff = f)zRecompute ratings from stored race results.

This will deterministically recompute all ratings in the date range.

Examples:
    recompute --from 2024-01-01 --to 2024-12-31
    recompute --from 2024-01-01 --to 2024-12-31 --clear
 
[bold]Recomputing ratings from r_   r`   z-[yellow]Clearing existing ratings...[/yellow]r   recompute_ratings)clear_existingNu    
[green]✓ Recompute complete: ! rating snapshots created[/green]Recompute failed: Trt      
[red]✗ Recompute failed: rv   r   )r   r@   rA   packages.core.ratings.recomputer   r   r   r   r   rw   rx   )	r'   r+   r   rJ   rK   r   rL   snapshot_countr   s	            r    	recomputer      s    6 I&J'"HMM
+J<tH:YO EFE]g.XN 
 	//??`a	
 ]  )!-=5aS?@s1   B  B
(!B 

BB 
C5%AC00C5z--urls	urls_filez/File containing HRNZ result URLs (one per line)z--overwrite/--no-overwritezHOverwrite existing meetings/races/starters for matching HRNZ meeting IDszFilter: Start date (YYYY-MM-DD)zFilter: End date (YYYY-MM-DD)	overwritec                   ^^	^
^^^^^^^^^^^ SSK Jm  SSKJm	JmJmJmJmJm  SSK	J
m  SSKJm
  U(       a  [        U5      O	T" SSS5      mU(       a  [        U5      O	T" SS	S
5      m [        U 5       nU Vs/ s H)  oUR                  5       (       d  M  UR                  5       PM+     snmSSS5        ["        R%                  S[+        T5       S35        ["        R%                  ST ST S35        ["        R%                  S5        SSSSSSSS.m U	U
UUUUUUUUUUUU4S jn[,        R.                  " U" 5       5        [1        SS9nUR3                  SSS9  UR3                  SSSS9  UR5                  S[7        TS    5      5        UR5                  S![7        TS"   5      5        UR5                  S#[7        TS$   5      5        UR5                  S%[7        TS&   5      5        UR5                  S'[7        TS(   5      5        UR5                  S)[7        TS*   5      5        UR5                  S+[7        TS,   5      TS,   S:  a  S-OSS9  ["        R%                  S5        ["        R%                  U5        TS,   S:  a2  ["        R%                  S.TS,    S/35        ["        R%                  S05        g["        R%                  S15        ["        R%                  S25        gs  snf ! , (       d  f       GN6= f! [          a3    ["        R%                  SU  S35        [&        R(                  " S5         GNuf = f! [8         aP  n[:        R=                  S3U 3S4S59  ["        R%                  S6U S35        [&        R(                  " S5         SnAgSnAff = f)7a  Import historical data from HRNZ results archive.

Scrapes HRNZ InfoHorse results pages and imports into database.

WARNING: Web scraping should only be used if official API access is unavailable.
Always check HRNZ's Terms of Service before scraping.

Examples:
    scrape-hrnz --urls hrnz_urls.txt
    scrape-hrnz --urls hrnz_urls.txt --from 2024-01-01 --to 2024-12-31

URL File Format:
    102402rs.htm
    102502rs.htm
    102602rs.htm
r   r   DriverRepositoryHorseRepositoryMeetingRepositoryRaceRepositoryr   TrainerRepository)HRNZScraperHRNZDataMapperi  r   i  r      Nz[red]Error: File not found: rv   z
[bold]Scraping z HRNZ meetings[/bold]zDate filter: r_   
P   [yellow]⚠ Using web scraper - please ensure compliance with HRNZ ToS[/yellow]
r   r   r2   horsesdriverstrainersrq   c                  R  >#    T" 5        IS h  vN n [        T!S5       GH  u  p [        R                  SU S[        T!5       SU S35        U R	                  U5      I S h  vN nUR                  S5      nU(       a7  TR                  U5      nTUs=::  a  T::  d  O  [        R                  S5        M  T" 5       nUR                  U5      nUR                  U5      n[        5        n	T(       aU  U	R                  [        5      R                  [        R                  US   :H  5      R                  S	S
9  U	R                  5         TR!                  X5        T S==   S-  ss'   US    H9  n
TR!                  U	U
S   U
S   U
R                  S5      5        T S==   S-  ss'   M;     US    H2  nTR!                  XS   UR                  S5      S9  T S==   S-  ss'   M4     US    H3  nTR!                  U	US   UR                  S5      S9  T S==   S-  ss'   M5     UR#                  X7S   5      n0 nU H6  nTR!                  XS   U5      nUR                  XS   '   T S==   S-  ss'   M8     UR%                  X>5      nU H7  nTR                   " U	US   UUR                  S5      5        T S==   S-  ss'   M9     U	R                  5         S S S 5        [        R                  S[        W5       S[        W5       S35        GM     S S S 5      IS h  vN   g  GN GN! , (       d  f       NX= f! [&         aM  nT S==   S-  ss'   [(        R+                  SU SU 35        [        R                  SU S 35         S nAGMc  S nAff = f Nv! , IS h  vN  (       d  f       g = f7f)!Nr   [r4   ] Scraping ...r   )  [dim]Skipped (outside date range)[/dim]meetingFsynchronize_sessionr   r   r;   namer1   r   	driver_idr   
trainer_idr=   r   rM   r3   r2        [green]✓ Imported:  races,  starters[/green]rq   Failed to scrape :      [red]✗ Error: rv   )rB   r@   rA   r?   get_meeting_resultsrD   fromisoformatmap_meetingmap_entitiesr   r6   r   r8   r;   deleterI   rH   	map_racesmap_startersr   r   r   )"scraperrR   urlscrapedmeeting_date_strr9   mapperr   entitiesrL   horsedrivertrainerr   race_id_maprS   race_objr2   rU   r   r   r   r   r   r   r   r   r   	date_typerK   r   rJ   r   urlss"                       r    scrape_and_import&scrape_hrnz.<locals>.scrape_and_importh  s    "}} )$ 2HCY!#aD	{+cU#&NO )0(C(CC(H"H ,3;;v+>(++4+B+BCS+TL$.,$J($J '$O!" !) "0!1"("4"4W"=#)#6#6w#? )]g( 'g 6 = =$+JJ')2D$D!""(&U&"C ' 0 .44WF!*-2- *2(); / 6 6$+$)$K$)&M$)IIj$9	!" !&h1 4 *< +39*= 0 7 7$+F^vzzRVGW !8 !" !&i 0A 5 0	 +> ,4J+? 1 8 8$+$+FO/6{{4/@ !9 !"
 !&j 1Q 6 1 ,@ %+$4$4Wi>P$QE*,K(-+9+@+@$+Y-?," DL;;,? @ %g! 3 ). (.':':7'PH+3 1 8 8$+$+I$6$+$+KK	$:	!" !&j 1Q 6 1 ,4 $NN,q +t  5c%j\"8}o->@c !3 %}} #I$ +]~ % !h1,'8Rs%CD(:1#V&DE 	!o %}}}s   N'LN'N<L1#L$AL15N73L1*G$L 6L1N	N'NN'L1 
L.*L11
N;AN<NNNN'N$NN$ N'zHRNZ Scraping Resultsrb   rd   re   rf   rh   ri   rj   rk   rm   r   rn   r   ro   r2   Horsesr   Driversr   Trainersr   rp   rq   rr   rs   ) errors occurred during scraping[/yellow]J[yellow]Tip: Run 'recompute' to compute ratings for imported data[/yellow]3   
[green]✓ Scraping completed successfully[/green]H[green]Tip: Run 'recompute' to compute ratings for imported data[/green]Scraping failed: Trt      
[red]✗ Scraping failed: )datetimer   "packages.core.storage.repositoriesr   r   r   r   r   r   packages.hrnz_scraperr   packages.hrnz_scraper.mapperr   r   openstripFileNotFoundErrorr@   rA   rw   rx   r?   r|   r}   r   r   r   r   r   r   r   )r   r   r'   r+   fliner   r   r   r   r   r   r   r   r   r   r   r   rK   rJ   r   r   s    `       @@@@@@@@@@@@@r    scrape_hrnzr     s   T +  2; +4I&4A9NJ&-z'"9T2r3JH)_-.?QT**,LDJJLQ?D  MM%c$i[0EFGMMM*T(2>?MM[
 EF\	! \	! \	!| 	%'( 340Aj#eJ&7"89gs5>23j#eJ&7"89hE(O 45iU9%5!67j#eJ&7"89h  ?Q.%G 	 	
 	de?QMM x 11Z[ MM\ MMPQMMZi @ _ 4YKvFGn  (,t<4QCv>?sh   *K. 5K:KK*K-K. FL. ,*L. K
K+&K. +K. .9L+*L+.
N8ANNz--race-typerace_day_type)OfficialRaces
TrialRacesWorkoutRacesr   z-Race day type filter from the results enquiry)r]   r-   r.   r)   z--clubclub_no z7Optional club number filter (leave blank for all clubs)c                 L  ^^^^^	^
^^^^^^^^^ SSK Jm  SSKJmJmJmJmJmJm  SSK	J
m
  SSKJm	  [        U 5      m[        U5      m[        R                  ST ST S35        [        R                  S	5        SSSSSSSS
.m UU	U
UUUUUUUUUUUU4S jn[         R"                  " U" 5       5        [%        SS9nUR'                  SSS9  UR'                  SSSS9  UR)                  S[+        TS   5      5        UR)                  S[+        TS   5      5        UR)                  S[+        TS   5      5        UR)                  S[+        TS   5      5        UR)                  S[+        TS   5      5        UR)                  S[+        TS    5      5        UR)                  S![+        TS"   5      TS"   S:  a  S#OSS9  [        R                  S$5        [        R                  U5        TS"   S:  a2  [        R                  S%TS"    S&35        [        R                  S'5        g0[        R                  S(5        [        R                  S)5        g0! [,         aP  n[.        R1                  S*U 3S+S,9  [        R                  S-U S.35        [2        R4                  " S/5         S0nAg0S0nAff = f)1z<Scrape HRNZ historical results via the Results Enquiry page.r   r   r   )HRNZHistoricalResultsScraperr   z"
[bold]Scraping HRNZ results from r_   r`   r   r   c                    >#    T" 5        IS h  vN n U R                  T#T T"TS9 Vs/ s S h  vN nUPM   N' N

 Os  snf nn[        US5       GH  u  p4 US   n[        R                  SU S[	        U5       SU S35        U R                  XT5      I S h  vN  nUR                  S5      (       d,  UR                  S	5      (       a  US	   R                  5       US'   UR                  S
5      (       d  [        R                  S5        M  UR                  S5      nU(       a8  TR                  U5      nT#Us=::  a  T ::  d  O  [        R                  S5        GM  T" 5       n	U	R                  U5      nU	R                  U5      n
[        5        nT!(       aU  UR                  [        5      R                  [        R                  US   :H  5      R!                  SS9  UR#                  5         TR%                  X5        T$S==   S-  ss'   U
S    H9  nTR%                  UUS   US   UR                  S5      5        T$S==   S-  ss'   M;     U
S    H2  nTR%                  XS   UR                  S5      S9  T$S==   S-  ss'   M4     U
S    H3  nTR%                  UUS   UR                  S5      S9  T$S==   S-  ss'   M5     U	R'                  XaS   5      n0 nU H7  nTR%                  XS   U5      nUR                  UUS   '   T$S
==   S-  ss'   M9     U	R)                  UU5      nU H7  nTR$                  " UUS   UUR                  S5      5        T$S==   S-  ss'   M9     UR#                  5         S S S 5        O! , (       d  f       O= f[        R                  S[	        W5       S[	        W5       S35        GMq  ! [*         a\  nT$S ==   S-  ss'   [,        R/                  S!UR                  S5       S"U 35        [        R                  S#U S$35         S nAGM  S nAff = f   S S S 5      IS h  vN    g ! , IS h  vN  (       d  f       g = f7f)%N)r   r   r   results_urlr   r4   r   r   r   r9   r   z%  [dim]Skipped (no races found)[/dim]r   r   Fr   r   r   r;   r   r1   r   r   r   r   r=   rM   r3   r2   r   r   r   rq   r   r   r   rv   )iter_meetingsrB   r@   rA   r?   r   rD   	isoformatr   r   r   r   r6   r   r8   r;   r   rI   rH   r   r   r   r   r   )%r   r   r   rR   meeting_metar   r   r   r9   r   r   rL   r   r   r   r   r   rS   r   r2   rU   r   r   r   r   r   r   r   r   r   r   r   rK   r   r   rJ   r   s%                         r    r   .scrape_hrnz_enquiry.<locals>.scrape_and_import8  sA    355 *1)>)>" &3 '	 *? * %  6   *38Q)?%CZ!*=9#aHk#c&RS(/(C(CC(V"V"V&{{622|7G7G7W7W.:>.J.T.T.VGFO&{{733#MM*QR$+2;;v+>(++4+B+BCS+TL$.,$J($J '$O!" !)!/!1"("4"4W"=#)#6#6w#?(]g( 'g 6 = =$+JJ')2D$D!""(&U&"C ' 0-44WF!*-2-)1(); / 6 6$+$)$K$)&M$)IIj$9	!" !&h1 4 *< +39*= 0 7 7$+F^vzzRVGW !8 !" !&i 0A 5 0	 +> ,4J+? 1 8 8$+$+FO/6{{4/@ !9 !"
 !&j 1Q 6 1 ,@ %+$4$4Wi>P$QE*,K(-+9+@+@$+Y-?," DL;;D,? @ %g! 3 ). (.':':7K'PH+3 1 8 8$+$+I$6$+$+KK	$:	!" !&j 1Q 6 1 ,4 $NN,i +]]l  5c%j\"8}o->@ % !h1,/0@0@0O/PPRSTRUV  (:1#V&DE !k *@ 655555s   Q7QP>=;9;=Q;=P>AOBA2OP>AOP>3OG&M?6	O?
N	5O>P>
P'AP"P>"P''P>,Q7P:8Q>QQQQz%HRNZ Results Enquiry Scraping Resultsrb   rd   re   rf   rh   ri   rj   rk   rm   r   rn   r   ro   r2   r   r   r   r   r   r   rp   rq   rr   r   rs   r   r   r   r   r   Trt   r   rv   r   N)r   r   r   r   r   r   r   r   r   r   r   r   r   r   r@   rA   r|   r}   r   r   r   r   r   r   r   rw   rx   )r'   r+   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   rK   rJ   r   s     ```   @@@@@@@@@@@@r    scrape_hrnz_enquiryr     sZ   L +  C;I&J'"HMM
-j\hZyQ MM[
 EOf	! f	! f	!P 	%'(CD0Aj#eJ&7"89gs5>23j#eJ&7"89hE(O 45iU9%5!67j#eJ&7"89h  ?Q.%G 	 	
 	de?QMM x 11Z[ MM\ MMPQMMZ  (,t<4QCv>?s    FI	 *I	 	
J#AJJ#c            	      $   SSK Jn Jn  [        R	                  S5         U" 5       nU " U5      n[        R	                  U5        UR                  S/ 5      nUR                  S/ 5      nUR                  S5      (       a  [        R	                  SUS    S35        U(       a.  [        R	                  S	5        [        R	                  S
U< 35        U(       a  U(       d  [        R	                  S5        gU(       d;  U(       d3  [        R	                  S[        UR                  S/ 5      5       S35        ggg! [         aP  n[        R                  SU 3SS9  [        R	                  SU S35        [        R                  " S5         SnAgSnAff = f)zRefresh HRNZ club codes by scraping the HRNZ results pages.

Fetches the latest list of club codes from HRNZ, compares them with
the hardcoded ``HRNZ_ALL_CLUB_CODES`` list, and reports any
differences (new, missing, or unmatched codes).
r   )generate_diff_reportrefresh_club_codesz6
[bold cyan]Refreshing HRNZ club codes...[/bold cyan]
newmissingr   rs   	[/yellow]u   
[yellow]⚠ New club codes detected. Update HRNZ_ALL_CLUB_CODES in packages/core/common/settings.py to include these codes.[/yellow]z
Add to settings.py:
  u?   
[green]✓ All hardcoded club codes were found online.[/green]u5   
[green]✓ Hardcoded club codes are up to date (all fetchedz codes confirmed).[/green]zClub code refresh failed: Trt   u$   
[red]✗ Club code refresh failed: rv   r   N)"packages.hrnz_scraper.club_refreshr   r   r@   rA   rD   r?   r   r   r   rw   rx   )r   r   resultreport	new_codesmissing_codesr   s          r    r   r     sK   
 MMLM!#%%f-fJJub)	

9b1::gMMN6'?*;9EFMM3
 MM5i]CDMMR =MMFJJy"5677QS $1  1!5E=aSGHs    CD5 2A D5 5
F?AF

Fc                     [        5       n [        SS9nUR                  SSS9  UR                  SSS9  UR                  SU R                  R
                  5        UR                  S	U R                  R                  5        UR                  S
U R                  R                  5        UR                  SU R                  R                  (       a  SOS5        UR                  SSU R                  R                  ;   a(  U R                  R                  R                  S5      S   OU R                  R                  5        UR                  SU R                  R                  5        UR                  S[        U R                  R                   5      5        UR                  S[        U R                  R"                  5      5        UR                  S[        U R                  R$                  5      5        UR                  S[        U R                  R&                  5      5        UR                  SU R                  R(                  (       a  SOS5        UR                  SU R                  R*                  (       a  SOS5        UR                  SU R                  R,                  (       a  SOS5        [.        R1                  U5        g)z"Display configuration information.zTipSharks Configurationrb   Settingre   rf   ValueyellowzTAB Base URLzTAB Default CategoryzTAB Default CountryzTAB Mock Modeu   ✓u   ✗zDatabase URL@z	Log LevelzElo Scale (C)zElo Ku   Driver Weight (α)u   Trainer Weight (β)zEnable DriverzEnable TrainerzEnable AdjustmentsN)r
   r   r   r   rz   base_urlr{   default_country	mock_modedatabaser   splitlogginglevelr   ratingelo_scale_c
elo_k_basedriver_weight_alphatrainer_weight_betaenable_driverenable_trainerenable_adjustmentsr@   rA   )r   r   s     r    infor    s    ~H12E	Yf-	WH- 
MM.(,,"7"78	MM((,,*G*GH	MM')E)EF	MM/HLL,B,B5N 
MM h''+++ !!'',R0""&& 
MM+x//556 
MM/3x'B'B#CD	MM'3x99:;	MM&HOO,O,O(PQ	MM'X__-P-P)QR	MM/HOO,I,I5uU	MM"X__-K-KEQVW	MMx'I'Ieu MM%r"   z--outoutput_filez Output JSON file path (optional)c                 Z   SSK nSSKJnJn  [        R                  S5        [        U 5      n[        U5      n[        R                  SU SU 35        [        5        nU" U5      n	U	R                  Xg5      n
U" U5      nU(       a  U
R                  R                  U5        SSS5        [        SS9nUR                  S	S
S9  UR                  SSS9  UR                  S[        W
R                  5      5        UR                  S[        U
R                   5      5        UR                  S[        U
R"                  5      5        UR                  SSU
R$                   S35        UR                  SSU
R&                   S35        UR                  S[        [)        S U
R                   5       5      5      5        [        R                  U5        U
R*                  (       a  [        SS9nUR                  S	S
S9  UR                  SSS9  U
R*                  R-                  5        H@  u  pUR/                  SS5      R1                  5       nUR                  U[        U5      5        MB     [        R                  U5        U
R                  (       GaI  [        R                  S5        S GH-  nU
R                   Vs/ s H  nUR2                  U:X  d  M  UPM     nnU(       d  M;  [        UR5                  5        S3S9nUR                  SS
S9  UR                  S SS9  UR                  S!S"S9  USS#  HP  nUR                  UR6                  UR8                  UR:                  (       a  [        UR:                  5      OS$5        MR     [        R                  U5        [=        U5      S#:  d  GM  [        R                  S%[=        U5      S#-
   S&U S'35        GM0     U(       Ga<  U
R>                  RA                  5       U
RB                  RA                  5       U
RD                  RA                  5       U
R                  U
R                   U
R"                  U
R$                  U
R&                  S(.U
R*                  U
R                   Vs/ s HS  nUR2                  UR6                  UR8                  UR:                  URF                  URH                  URJ                  S).PMU     snS*.n[M        US+5       nURN                  " UUS,S-9  SSS5        [        R                  S.U S/35        U
RP                  (       a,  [        R                  S05        [R        RT                  " S15        g[        R                  S25        [R        RT                  " S5        g! , (       d  f       GN= fs  snf s  snf ! , (       d  f       N= f)3zlGenerate data quality report for date range.

Validates data completeness, accuracy, and identifies issues.
r   N)DataQualityValidatorcheck_data_freshnessz8[bold cyan]Generating Data Quality Report...[/bold cyan]zDate range: r_   zData Quality Summaryrb   Metricre   rf   r  r	  zTotal MeetingszTotal RaceszTotal Startersrp   z[red]rv   Warningsz[yellow]r   Infoc              3   H   #    U  H  oR                   S :X  d  M  Sv   M     g7f)r  r   N)severity).0is     r    	<genexpr>data_quality.<locals>.<genexpr>`  s     I=aJJ&4H=s   "	"zData Metrics_ z!
[bold]Issues by Category:[/bold])r   warningr  z IssuesCategoryMessagezRace IDmagenta   -z[dim]... and z more z issues[/dim])total_meetingstotal_racestotal_starterserror_countwarning_count)r$  rX   messagerM   
meeting_id
starter_iddetails)rJ   rK   generated_atsummarymetricsissuesw   )indentz
[green]Report saved to r5   u3   
[bold red]⚠ Data quality check FAILED[/bold red]r   u7   
[bold green]✓ Data quality check PASSED[/bold green])+json!packages.core.common.data_qualityr  r  r@   rA   r   r   generate_reportr=  appendr   r   r   r   r1  r2  r3  r4  r5  sumr<  itemsreplacerc   r$  ry   rX   r6  rM   r?   rJ   r   rK   r:  r7  r8  r9  r   dump
has_errorsrw   rx   )r'   r+   r  rA  r  r  rJ   rK   rL   	validatorr  freshness_issuesummary_tablemetrics_tablekeyvalueformatted_keyrX   r&  category_issuesissues_tableissuereport_datar   s                           r    data_qualityrU  #  s   0 
 MMLM I&J'"HMMLD
;<	'(1	 **:@ /w7MM  1 
  67MXV4WH5*C0E0E,FG-V-?-?)@A*C0E0E,FG(eF,>,>+?v$FG*1E1E0Fi&PQCI6==IIJ MM-  ~~N3   8   9 ....0JCKKS1779M!!-U< 1 	m$ }}}:;4H*0--R-Q1::;Qq-OR$hnn.>-?w+GH''
&'A''	'B''	'C,Sb1E ((.3mmEMM* 2 l+'",MM'O(<r(A'B&
R_`% 5.  ++557113"//99;"("7"7%11"("7"7%11!'!5!5 ~~ $]] +E !& %$}}$}}"'"2"2"'"2"2$}} +
4 +s#qIIk1Q/ $ 	1+hGH LMPQQ 
X SF $#s,   AV 3VV8AV#V 
V
V*z--monthsr   r      z%Number of months to backfill (max 24))r-   r]   r.   r)   )r]   r)   z--sourcerz   r   z=Data source: tab (TAB API) or ingest (tab-api-ingest service)z--learn-adjustmentsz3Learn barrier/handicap adjustments during recomputez--skip-evalz(Skip accuracy evaluation after recomputec                 Z   [         R                  " 5       n[        X`5      n[        5       nU(       a  UR	                  5       OUR
                  R                  n	[        R                  SU	 SU SU S35        [        R                  SU SU  SU SU S	3	5        S
S
S
S
S.n
/ nUnS
nX::  a  [        U[        SS9-   U5      nUS-  n[        R                  SU SU SU S35         [        5        n[        XS9n[        R                  " UR                  XU	S95      u  nnnU
S==   U-  ss'   U
S==   U-  ss'   U
S==   U-  ss'   U
S==   UR                   S   -  ss'   SSS5        [        R                  SW SW SW S35        U[        SS9-   nX::  a  M  [        R                  S#5        [        R                  S$U 35        [        R                  S%U
S    35        [        R                  S&U
S    35        [        R                  S'U
S    35        U(       aN  [        R                  S([-        U5       S)35        U H&  u  nnn[        R                  S*U SU SU S+35        M(     S
nU
S   S
:X  a  [        R                  S,5        OY[        R                  S-U SU S.35         S
S/KJn  [        5        nU" UUUUUS09nSSS5        [        R                  S1U S235        SnSnU(       GdV  U
S   S
:  GaL  [        R                  S55         [6        R8                  R;                  [6        R8                  R=                  [6        R8                  R?                  [@        5      5      S6S6S6S7S85      n[B        R                  " [2        RD                  US9URG                  5       S:URG                  5       S;/SSSS<S=9n[H        RJ                  " URL                  5      nS>U;   aO  US>   (       aE  US>   S?   nUS>   S@   n[        R                  SAUSB SC35        [        R                  SDUSB SC35        O[        R                  SE5         [O        SHSI9nURQ                  SJSKSL9  URQ                  SMSNSOSP9  URS                  SQ[+        U5      5        URS                  SR[+        U
S   5      5        URS                  SS[+        U
S   5      5        URS                  ST[+        U
S   5      5        URS                  SU[+        U5      5        Ub  URS                  SVUSB 5        Ub  URS                  SWUSB 5        URS                  SX[+        U
S   5      U
S   S
:  a  SYOSNSL9  U(       a#  URS                  SZ[+        [-        U5      5      SYSL9  [        R                  S	5        [        R                  U5        U
S   S
:  a  [        R                  S[U
S    S\35        g[        R                  S]5        g! , (       d  f       GN= f! ["         aj  n[$        R'                  SU SU SU 3SS 9  [        R                  S!U S"35        UR)                  X[+        U5      45        U
S==   S-  ss'    SnAGNSnAff = f! , (       d  f       GN= f! ["         aQ  n[$        R'                  S3U 3SS 9  [        R                  S4U S"35        [2        R4                  " S5         SnAGNSnAff = f! ["         a;  n[$        R'                  SFU 3SS 9  [        R                  SGU S+35         SnAGNSnAff = f)^a`  Backfill historical data and recompute ratings.

Ingests data week by week from (today - months) to today,
then recomputes all ratings and evaluates prediction accuracy.

Examples:

    backfill --months 12 --category H --source tab

    backfill --months 6 --category T --clear --learn-adjustments

    backfill --months 3 --source ingest --skip-eval
z
[bold]Backfilling z data from r_   z[/bold]zSource: z
, Months: z	, Clear: z, Learn adjustments: r   r   )r   r   r2   rq      )daysr   z[bold cyan]Week r   z[/bold cyan])sourcera   r   r   r2   rq   Nu     [green]✓ z meetings, r   r   zWeek z	 failed: Trt   u     [red]✗ Week failed: rv   z!
[bold]Ingestion complete:[/bold]z  Weeks processed: z  Total meetings: z  Total races: z  Total starters: rs   z week(s) had errors:[/yellow]z
  [yellow]r   u:   
[yellow]No data ingested — skipping recompute.[/yellow]r   z
...[/bold]r   )r   learn_adjustmentsu   [green]✓ r   r   r   z/
[bold]Evaluating prediction accuracy...[/bold]z..scriptszevaluate_accuracy.pyr&   r*   z--jsoniX  )capture_outputtextchecktimeoutr>   brier
winner_accz  [green]Winner accuracy: z.4fr5   z  [green]Brier score: z1  [yellow]Evaluation returned no results[/yellow]zEvaluation failed: u/     [yellow]⚠ Evaluation skipped due to error: zBackfill Summaryrb   r   re   rf   r  ri   rj   rk   zWeeks processedzMeetings ingestedzRaces ingestedzStarters ingestedzRating snapshotszBrier scorezWinner accuracyrp   rr   zFailed weeksu   
[yellow]⚠ Completed with z error(s)[/yellow]u3   
[green]✓ Backfill completed successfully[/green])*r   todayr!   r
   ry   rz   r{   r@   rA   r   r   r   r   r|   r}   r~   r   r   r   r   rD  r   r?   r   r   rw   rx   ospathr7   dirnameabspath__file__
subprocess
executabler   rA  loadsstdoutr   r   r   ) r   rX   rZ  r   r[  	skip_evalrc  rJ   r   r   totalsfailed_weekscurrent
week_countweek_endrL   r   r   r   r2   r   fw_startfw_endfw_errr   r   ra  rb  script_patheval_resultdatar   s                                    r    backfillry    s   d JJLE!%0J~H-5)8<<;X;XMM
12 3|4wg	/ MM
6(*VH --.?-@	D 	F 24L GJ

w!22E:a
zl"WIT(<P	
	"'*7B,3KK--4F . -)%
 z"h."w5(z"h."x GMM($;;   MMz'(+<> YA..= 
B MM67MM'
|45MM&vj'9&:;<MMOF7O#456MM&vj'9&:;<s<'8&99VWX(4$HffMMJxjVHBvhiPQ )5 NjQST/J<tE7*U	
	I'!2#(&7"  MMK'77XYZ E#J
+a/HI"	Z'',, 9:&K %..NN((*OO%  $K ::k001D}eUG,!%[6
 ::c:J(ST 6uSkJKQR *+E	XV,	WGW=	MM#S_5	MM%s6*+='>?	MM"Cw$89	MM%s6*+='>?	MM$c.&9:mc{4'Js+;=	MMF8h'!+e  
 nc#l*;&<EJMM$MM%h!+F8,<+==OP	
 	LMI    	"LL5	hZyDtLTMM4QCv>?CF ;<8!		">   	LL-aS1DLAMM9!FCDHHQKK	T  	ZLL.qc2TLBMMKA3iXYY	Zs   &
W> 0A3W,#'W> .Z >Y5	!Z D[% 6[% ,
W;6W> >
Y2AY--Y25
Z?Z 
["A[["%
\*/0\%%\*__main__)7__doc__r|   rA  rd  ri  rw   calendarr   r   r   r   clickrich.consoler   
rich.tabler   packages.core.common.loggingr   r	   packages.core.common.settingsr
   packages.core.common.utilsr   packages.core.storage.databaser   packages.core.storage.ingestionr   packages.core.storage.modelsr   r   r   r   r   __name__r   r@   intr!   groupr%   commandoptionr   rV   Choicer   boolr   r   r   r   r  rU  IntRangery  r$   r"   r    <module>r     s   ?   	  
  $     B 6 1 6 < 6 U 	H	
)7 7c 7d 7$ 	 	 	"	 
	 	 	/	0s 0S 0 0 0  &0f 	"	 
	 	 	-	 	oe	<	H@c @C @c @S @ 4@F 	"	 
	 	 	2
# #s #4 # $#L 	:	  	S	 	*	 
	(	@3 @4 @C @# @ 2@F 	"	 
	 	 	E	F	8 	B  	S	tt t14t?BtOSt Dtn / /d $ $N 	"	 
	 	 	+	| &|~ 	2		0 	oe	<	H
 	uh'	>	H 	2
 	>
 	3
vN FvNr zE r"   