
    حi4                        d Z ddlZddlZddlmZmZmZ dZi ddddd	d
dddddddddddddddddddddd d!d"d#d$d%d&i d'd(d)d*d+d,d-d.d/d0d1d2d3d4d5d6d7d8d9d:d;d<d=d>d?d@dAdBdCdDdEdFdGdHdIdJdKdLdMdNdOZg dPZg dQZ	dR Z
dS ZdT ZdedUZdfdVZdW ZdgdXZedYk(  rddlZdZej&                  v ZdZej&                  d[d D ]&  Zej/                  d\      rej1                  d]      Z n  ed^        ed_        ed` ej4                  ej6                        j9                                  eda ee              er edb ee       dc        ed^        eeed       yy)hze
The Odds API - Game Odds Scraper
Fetches spreads, totals (O/U), and moneylines for all major sports
    N)datetimetimezone	timedeltazhttps://api.the-odds-api.com/v4nbabasketball_nbanflamericanfootball_nflmlbbaseball_mlbnhlicehockey_nhlncaafamericanfootball_ncaafncaabbasketball_ncaabmlssoccer_usa_mlsmmamma_mixed_martial_artsboxingboxing_boxingepl
soccer_epllaligasoccer_spain_la_ligaserieasoccer_italy_serie_a
bundesligasoccer_germany_bundesligaligue1soccer_france_ligue_oneuclsoccer_uefa_champs_leagueuelsoccer_uefa_europa_leaguechampionshipsoccer_efl_champleague1soccer_england_league1league2soccer_england_league2facupsoccer_fa_cupeflcupsoccer_england_efl_cup
eredivisiesoccer_netherlands_eredivisieligamxsoccer_mexico_ligamxprimeirasoccer_portugal_primeira_ligaspl
soccer_spl	super_ligsoccer_turkey_super_leaguebrazil_asoccer_brazil_campeonato	argentina!soccer_argentina_primera_divisionlibertadores!soccer_conmebol_copa_libertadoresahlicehockey_ahlshlicehockey_sweden_hockey_league
euroleaguebasketball_euroleaguenblbasketball_nblatp_aus_opentennis_atp_aus_open_singlestennis_wta_aus_open_singlesrugbyleague_nrlrugbyunion_six_nationsaussierules_aflcricket_big_bashcricket_international_t20)wta_aus_opennrlsix_nationsaflbig_bashcricket_t20)r   r   r
   r   r   r   r   r   r   r   r    r"   r   )r   r   r&   r,   r$   r0   r2   r4   r@   rD   r:   r<   c                      t        dd      5 } | D ]S  }|j                  d      s|j                  dd      d   j                         j                  d      d   c cd d d        S  	 d d d        y# 1 sw Y   yxY w)	N/var/www/html/eventheodds/.envrzSPORTS_DATABASE_URL==   ?r    open
startswithsplitstripflines     4/var/www/html/eventheodds/scripts/scrape_odds_api.pyload_db_urlrf   _   s    	.	4 C 	CD56zz#q)!,224::3?BBC C	CC 	C s   A06A0%A00A9c                      t        dd      5 } | D ]A  }|j                  d      s|j                  dd      d   j                         c cd d d        S  	 d d d        y # 1 sw Y   y xY w)NrW   rX   zTHE_ODDS_API_KEY=rY   rZ   r]   rb   s     re   load_api_keyrh   f   sn    	.	4 5 	5D23zz#q)!,22445 5	55 	5 s   A$AAA'c                      t               } | syt         d}	 t        j                  |d| id      }|j                  j                  dd      }|j                  j                  dd      }||d	S #  Y yxY w)
zCheck remaining API quotaNz/sportsapiKey   paramstimeoutx-requests-remainingunknownzx-requests-used)	remainingused)rh   ODDS_API_BASErequestsgetheaders)api_keyurlresprq   rr   s        re   check_api_quotarz   m   sz    nGO7
#C||C7(;RHLL$$%;YG	|| 19=&55s   AA/ /A3c           
      &   ddl }t               }|st        d       g S t         d|  d}|||dd}t        d|  d	       d
}t	        d|dz         D ]  }	 t        j                  ||d      }	|	j                  dk(  rt        d       g c S |	j                  dk(  rt        d|  d       g c S |	j                  dk(  rt        d       g c S |	j                  dk7  rt        d|	j                          g c S |	j                         }
t        dt        |
       d       |	j                  j                  dd      }t        d|        |
c S  y# t
        j                  j                  t
        j                  j                  t
        j                  j                  f$ rO}||k  r+t        d| d| d|        |j                  d       Y d}~}t        d | d!|        g cY d}~c S d}~wt         $ r}t        d"|        g cY d}~c S d}~ww xY w)#z?Fetch odds from The Odds API. Retries once after 5s on timeout.r   NzNo API key foundz/sports//oddsamerican)rj   regionsmarkets
oddsFormatzFetching odds for ...   rZ   rk   rl   i  z  Invalid API keyi  z  Sport z not found or no eventsi  z  Rate limited - quota exceeded   
  Status: z  Found  eventsro   rp   z  API requests remaining: z-  WARNING: Timeout/connection error (attempt /z), retrying in 5s:    z  ERROR: Failed after z attempts: Error: )timerh   printrs   rangert   ru   status_codejsonlenrv   
exceptionsConnectTimeoutReadTimeoutConnectionErrorsleep	Exception)	sport_keyr   r~   _timerw   rx   rm   max_attemptsattemptry   datarq   es                re   
fetch_oddsr   |   s   nG !	O8I;e
4C 	F 
yk
-.LL1,- "!	<<FB?D3&)*	3&+BCD	3&78	3&
4#3#3"456	99;DHSYKw/0(()?KI.yk:;K-". ##22##//##335 	 %EgYaP\~]pqrpstuA*<.A3GHI 	GA3- I	sP   3EE-E(E6AEAH*G(G( H(H4HHHc                 |   t               }|sg S t         d|  d}|d|d|d}t        d|  d| d       	 t        j                  ||d	
      }|j
                  dk7  rt        d|j
                   d       g S |j                         j	                  dg       S # t        $ r}t        d|        g cY d}~S d}~ww xY w)z*Fetch historical odds (requires paid plan)z/historical/sports/r|   usr}   )rj   r~   r   r   datezFetching historical odds for z on r   rk   rl   r   r   z# (historical may require paid plan)r   r   N)rh   rs   r   rt   ru   r   r   r   )r   date_strr   rw   rx   rm   ry   r   s           re   fetch_historical_oddsr      s    nG	O.yk
?C F 
))D
#
FG||C;s"Jt//00STUIyy{vr** sm	s$   AB 8B 	B;!B60B;6B;c                 .    	 | y t        |       S #  Y y xY w)N)float)vals    re   
safe_floatr      s#    ;Szs    
 c                 	   t               }|st        d      t        j                  |      }|j	                         }d}|j                  d       |j                          d}| r| }n-|rt        }n$t        t        D cg c]  }|t        vs| c}z   }t               }	|	rt        d|	d    d|	d           d}
|D ]~  }t        j                  |      }|st        d	|        *t        |      }d}|D ]  }|j                  d
d      }|j                  dd      }|j                  dd      }|j                  dd      }	 t        j                  |j!                  dd            }|j                  dg       D ]~  }|j                  dd      }|j                  dg       D ]S  }|j                  dd      }|j                  dg       }d}d}d}d}d}|dk(  rg|D ]`  }|j                  d      |k(  rt)        |j                  d            }2|j                  d      |k(  sGt)        |j                  d            }b n	|dk(  r|D ]z  }|j                  d      |k(  r5t)        |j                  d            }t)        |j                  d            }L|j                  d      |k(  sat)        |j                  d            }| n|dk(  r|D ]z  }|j                  d      dk(  r5t)        |j                  d            }t)        |j                  d            }L|j                  d      dk(  sat)        |j                  d            }| 	 ddl} |j                  ||||||||||rt-        |      nd|rt-        |      nd|rt-        |      nd|rt-        |      nd| j/                  ||d      ||rt-        |      nd|rt-        |      nd|rt-        |      nd|rt-        |      ndf       |dz  }|dz  }V   |j                          |
t5        |      z  }
t        d |j7                          d!|         |j9                          |j9                          t                t        d"| d#|
 d$       d%||
d&S c c}w #  t        j"                  t$        j&                        }Y GxY w# t0        $ r}!|j3                          Y d}!~!/d}!~!ww xY w)'zMain ingest functionzSPORTS_DATABASE_URL not setr   a  
        CREATE TABLE IF NOT EXISTS "GameOdds" (
            id BIGSERIAL PRIMARY KEY,
            league TEXT NOT NULL,
            "gameId" TEXT NOT NULL,
            "gameDate" TIMESTAMPTZ,
            "homeTeam" TEXT,
            "awayTeam" TEXT,
            "bookmaker" TEXT,
            "market" TEXT NOT NULL,
            "lineValue" FLOAT,
            "homeOdds" INT,
            "awayOdds" INT,
            "overOdds" INT,
            "underOdds" INT,
            "fetchedAt" TIMESTAMPTZ DEFAULT NOW(),
            raw JSONB,
            source TEXT DEFAULT 'theoddsapi',
            UNIQUE(league, "gameId", "bookmaker", "market")
        )
    a  
        INSERT INTO "GameOdds"
        (league, "gameId", "gameDate", "homeTeam", "awayTeam", "bookmaker", "market", "lineValue", "homeOdds", "awayOdds", "overOdds", "underOdds", raw,
         "openingLineValue", "openingHomeOdds", "openingAwayOdds", "openingOverOdds", "openingUnderOdds")
        VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
        ON CONFLICT (league, "gameId", "bookmaker", "market")
        DO UPDATE SET
            "lineValue" = EXCLUDED."lineValue",
            "homeOdds" = EXCLUDED."homeOdds",
            "awayOdds" = EXCLUDED."awayOdds",
            "overOdds" = EXCLUDED."overOdds",
            "underOdds" = EXCLUDED."underOdds",
            "fetchedAt" = NOW(),
            raw = EXCLUDED.raw,
            "openingLineValue" = COALESCE("GameOdds"."openingLineValue", EXCLUDED."openingLineValue"),
            "openingHomeOdds" = COALESCE("GameOdds"."openingHomeOdds", EXCLUDED."openingHomeOdds"),
            "openingAwayOdds" = COALESCE("GameOdds"."openingAwayOdds", EXCLUDED."openingAwayOdds"),
            "openingOverOdds" = COALESCE("GameOdds"."openingOverOdds", EXCLUDED."openingOverOdds"),
            "openingUnderOdds" = COALESCE("GameOdds"."openingUnderOdds", EXCLUDED."openingUnderOdds")
    zAPI Quota - Remaining: rq   z, Used: rr   zUnknown league: idr\   commence_time	home_team	away_teamZz+00:00
bookmakerskeyr   outcomesNh2hnamepricespreadspointtotalsOverUnder)eventmarketrZ   z  z odds added: u   ✅ Game Odds ingested: z total records from r   T)successstats_addedevents)rf   RuntimeErrorpsycopg2connectcursorexecutecommitPRIORITY_SPORTSSECONDARY_SPORTSrz   r   SPORTSru   r   r   fromisoformatreplacenowr   utcr   r   intdumpsr   rollbackr   upperclose)"leaguespriority_onlydb_urlconncurr   sqltarget_leaguessquotatotal_eventsleaguer   r   league_countr   game_idr   r   r   	game_date	bookmakerbook_keyr   
market_keyr   
line_value	home_odds	away_odds	over_odds
under_oddsoutcomer   r   s"                                     re   ingest_oddsr      s   ]F899F#D
++-CKKK  	* 	KKMC*  	((7G+d!1TcKcA+ddE'k(:';8E&M?STL  O@JJv&	$VH-.I& B	(Eiib)G!IIor:M		+r2I		+r2I7$22=3H3Hh3WX	 #YY|R8 7(	$==3'mmIr: 4(F!'E2!6J%zz*b9H!%J $I $I $I!%J!U*'/ MG&{{62i?,6w{{77K,L	!(V!4	!A,6w{{77K,L		M $y0'/ MG&{{62i?-7G8L-M
,6w{{77K,L	!(V!4	!A,6w{{77K,L	M $x/'/ NG&{{62f<-7G8L-M
,6w{{77K,L	!(V!4!?-7G8L-M
N(#C"GY	9$j*.7C	NT.7C	NT.7C	NT/9C
Ot JJ&'IJ&.7C	NT.7C	NT.7C	NT/9C
Ot*  $q($)e4(7(B	(H 	F#6<<>"-~>?_O@b IIKJJL	#J	$[M1El^SZ
[\K<PP} ,e27$LL6	p % ((s1   =R R %RB,R0%R-0	S9SS__main__z
--priorityrZ   z--,z<============================================================z&THE ODDS API - EXPANDED SPORTS SCRAPERzTime: zConfigured sports: zMode: Priority sports only (z	 leagues))r   )h2h,spreads,totalsr   )r   )NF)__doc__rt   r   r   r   r   rs   r   r   r   rf   rh   rz   r   r   r   r   __name__sysargvr   r   argr_   r`   r   r   r   	isoformatr        re   <module>r      s?     2 21B
	B
 
!B
 
>	B

 
?B
 %B
 B
 
B
 
#B
 oB
 
<B
  $!B
" $#B
$ -%B
& ''B
, 
&-B
. 
&/B
4 &5B
6 '7B
8 '9B
: _;B
< &=B
B 1CB
D $EB
F /GB
H 
<IB
J -KB
P *QB
R 4SB
T 7UB
Z 
?[B
\ 
+]B
b )cB
d 
eB
j 1kB
l 2 +  #.CB
J 4l4YQv z CHH,MGxx| ~~d#iinG
 
(O	
23	F<8<<-779:
;<	F}
-.,S-A,B)LM	(O}5' r   