
    iz[                        d Z ddlZddlZddlZddl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mZmZ ddlZ ee      j&                  j&                  dz  Zej+                         r ee      5 ZeD ]  Zej3                         sej5                  d      r&dev s+ej3                         j7                  dd	      \  ZZej<                  j?                  eej3                  d
      j3                  d              	 ddd       ej<                  jA                  dd      Z!ej<                  jA                  d      Z"ej<                  jA                  dd      Z#e"s e$d        ejJ                  d	       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/d0d1d2d3d4iZ&e&jO                         D  ci c]  \  } }|| 
 c}} Z(d e(d!<   d5e(d&<   d6e(d(<   d7e(d,<   d8e(d.<   g d9Z) G d: d;      Z*d<ee+   fd=Z,d<ee-   fd>Z.d?ed<efd@Z/d?ed<ee   fdAZ0dB Z1dCee   dDe2fdEZ3dFee   fdGZ4dLdCee   dDe2dHe2fdIZ5dJ Z6e7dKk(  r e6        yy# 1 sw Y   XxY wc c}} w )Ma[  
SportsGameOdds API - Comprehensive Odds Fetcher

Replaces BallDontLie for odds fetching with the more reliable SportsGameOdds API.
Supports 55+ leagues including NBA, NFL, MLB, NHL, NCAA, EPL, UFC, and more.

Usage:
    python scripts/fetch_sportsGameOdds.py [league] [--props] [--all]

    league: nba, nfl, mlb, nhl, ncaab, ncaaf, epl, laliga, mma, etc. (default: nba)
    --props: Include player props
    --all: Fetch all major leagues

Examples:
    python scripts/fetch_sportsGameOdds.py nba
    python scripts/fetch_sportsGameOdds.py nfl --props
    python scripts/fetch_sportsGameOdds.py --all
    N)datetimetimezone)Path)DictListAnyOptionalr	   z.env#=   "'SPORTSGAMEODDS_BASE_URLz!https://api.sportsgameodds.com/v2SPORTSGAMEODDS_API_KEYSPORTS_DATABASE_URL z4ERROR: SPORTSGAMEODDS_API_KEY not set in environmentnbaNBAnflNFLmlbMLBnhlNHLncaabNCAABncaafNCAAFwnbaWNBAmmaUFCufceplEPLlaligaLA_LIGAseriea
IT_SERIE_A
bundesliga
BUNDESLIGAligue1
FR_LIGUE_1uclUEFA_CHAMPIONS_LEAGUEmlsMLScflCFLahlAHLla_ligaserie_aligue_1champions_league)r   r   r   r   r   r   r$   r!   c                       e Zd ZdZefdedefdZddededefd	Z	 	 	 dd
ede	de	de
dedefdZd
edee   fdZdd
ede
defdZdd
edefdZdefdZy)SportsGameOddsClientz Client for SportsGameOdds API v2api_keybase_urlc                     || _         || _        t        j                         | _        | j                  j
                  j                  |dd       d| _        d| _        y )Nzapplication/json)z	x-api-keyzContent-Typer   )	r<   r=   requestsSessionsessionheadersupdaterequests_madeobjects_used)selfr<   r=   s      9/var/www/html/eventheodds/scripts/fetch_sportsGameOdds.py__init__zSportsGameOddsClient.__init__Z   sU     '')## .%
 	     Nendpointparamsreturnc                 .   | j                    d| }	 | j                  j                  ||d      }| xj                  dz  c_        |j                  dk(  rdddS |j                  d	k(  rdd
dS |j                  dk(  rdd| dS |j                          |j                         }d|v r4t        |d   t              r!| xj                  t        |d         z  c_	        |S # t        j                  j                  $ r}dt        |      dcY d}~S d}~ww xY w)z$Make API request with error handling/   )rK   timeoutr   i  FzInvalid API key)successerrori  zRate limited - quota exceededi  zEndpoint not found: dataN)r=   rA   getrD   status_coderaise_for_statusjson
isinstancelistrE   lenr?   
exceptionsRequestExceptionstr)rF   rJ   rK   urlresprS   es          rG   _requestzSportsGameOddsClient._requeste   s   q
+	7<<##C#CD!#3&#(3DEE3&#(3RSS3&#(5I(3TUU!!#99;D ~*T&\4"@!!Sf%66!K""33 	7$s1v66	7s1   AC C .C AC D<D	DD	league_idodds_availableinclude_alt_lineslimitcursorc                 |    |t        |      j                         |d}|rd|d<   |r||d<   | j                  d|      S )z#Fetch events with odds for a league)leagueIDoddsAvailablere   trueincludeAltLinesrf   events)r]   lowerra   )rF   rb   rc   rd   re   rf   rK   s          rG   
get_eventszSportsGameOddsClient.get_events~   sP    
 " 0668

 (.F$%%F8}}Xv..rI   c                 |   g }d}	  | j                   |fd|i|}|j                  dd      rd|v r!t        d|j                  dd              	 |S |j                  dg       }|s	 |S |j                  |       t        d	t	        |       d
       |j                  d      }|s	 |S t        j                  d       )z Fetch all events with paginationNTrf   rQ   rR   z	  Error: zUnknown errorrS   z
  Fetched z
 events...
nextCursorg      ?)rn   rT   printextendrZ   timesleep)rF   rb   kwargs
all_eventsrf   resultrl   s          rG   get_all_eventsz#SportsGameOddsClient.get_all_events   s    
$T__YHvHHF::i.'V2C	&**Wo"F!GHI  ZZ+F  f%Js:/z:;ZZ-F  JJsO# rI   c                 <    d|i}|r||d<   | j                  d|      S )zFetch player informationre   rh   playersra   )rF   rb   re   rK   s       rG   get_playersz SportsGameOddsClient.get_players   s*    5!!*F:}}Y//rI   c                 8    i }|r||d<   | j                  d|      S )zFetch team informationrh   teamsr{   )rF   rb   rK   s      rG   	get_teamszSportsGameOddsClient.get_teams   s%    !*F:}}Wf--rI   c                 $    | j                  d      S )zCheck API usagezaccount/usager{   )rF   s    rG   	get_usagezSportsGameOddsClient.get_usage   s    }}_--rI   )N)TFd   N)Ni  )__name__
__module____qualname____doc__SGO_BASE_URLr]   rH   r   ra   boolintrn   r   rx   r|   r   r    rI   rG   r;   r;   W   s    *5A 	 	s 	7 7d 7d 72 AEAD!%/C / /&*/;>//*./  $t* 40S 0 0d 0.3 .$ ..4 .rI   r;   rL   c                     | yt        | t        t        f      rt        |       S t        | t              r	 t        | j	                  dd            S y# t
        $ r Y yw xY w)z2Parse odds string like '+100' or '-110' to integerN+r   )rX   r   floatr]   replace
ValueErrorvalues    rG   parse_odds_valuer      s`    }%#u&5z%	u}}S"-..   		s   A 	AAc                     | yt        | t        t        f      rt        |       S t        | t              r	 t        |       S y# t        $ r Y yw xY w)zParse line value to floatN)rX   r   r   r]   r   r   s    rG   parse_line_valuer      sT    }%#u&U|%	<   		s   
A 	AAeventc           	      6   | j                  di       }d}d}|j                  di       }|j                  di       }|r-t        |j                  d      xs |j                  d            }|r-t        |j                  d      xs |j                  d            }d}d}|j                  di       }|j                  di       }	|r-t        |j                  d	      xs |j                  d
            }|	r-t        |	j                  d	      xs |	j                  d
            }d}
|j                  di       }|r-t        |j                  d      xs |j                  d            }
|||||
dt        j                  t
        j                        j                         dS )z%Parse event and extract key odds dataoddsNzpoints-home-game-ml-homezpoints-away-game-ml-awaybookOddsfairOddszpoints-home-game-sp-homezpoints-away-game-sp-away
bookSpread
fairSpreadzpoints-all-game-ou-overbookOverUnderfairOverUndersportsgameodds)moneylineHomemoneylineAway
spreadHome
spreadAwaytotal
oddsSourceoddsUpdatedAt)rT   r   r   r   nowr   utc	isoformat)r   	odds_datahome_mlaway_mlml_homeml_awayspread_homespread_awaysp_homesp_awayr   ou_overs               rG   parse_event_oddsr      ss   		&"%I GGmm6;Gmm6;G"7;;z#:#Ugkk*>UV"7;;z#:#Ugkk*>UV KKmm6;Gmm6;G&w{{<'@']GKKP\D]^&w{{<'@']GKKP\D]^ Emm5r:G _!=!]_A]^ ! !!&!hll3==? rI   c                 R   g }| j                  di       }| j                  d      }| j                  d      }t        j                  ||r|j                         nd      }|j                         D ]  \  }}|j	                  d      }t        |      dk\  s&|d   dk(  s/|d	   }	|d
   }
|d   }|d   }|
dv rH|j                  d      }|j                  d      xs |j                  d      }||j                  |||
|	| d| t        |      |rt        |      ndd|d	        |S )z$Extract player prop lines from eventr   eventIDrh   unknown-      our   r         )homeawayallliner   r   Nr   )	leaguegameIdplayerExternalIdpropTypemarket	lineValueoddsAmericanvendorraw)	rT   SGO_TO_DB_LEAGUErm   itemssplitrZ   appendr   r   )r   propsr   event_id
sgo_leaguer   odd_idodd_datapartsstat	player_idperiodsider   r   s                  rG   extract_player_propsr     s>   E		&"%Iyy#H:&J!!*Jj.>.>.@T]^F%OO- S!u:?uQx4/8DaI1XF8D 33<<'D<<
+Gx||J/GD$&(1 $!'$0!&t15CI4.#
 
#: LrI   c                     t         sy	 t         j                  dd      } d| v r| j                  d      d   } | j                  d      \  }}|j                  d      \  }}|j                  d      \  }}d|v r|j                  d      \  }}n|d	}}t        j                  |t        |      |||
      }	|	S # t        $ r}
t        d|
        Y d}
~
yd}
~
ww xY w)zGet PostgreSQL connectionNzpostgresql://r   ?r   @:rN   5432)hostportdatabaseuserpasswordzDatabase connection error: )SPORTS_DB_URLr   r   psycopg2connectr   	Exceptionrq   )r^   	user_passhost_dbr   r   	host_portr   r   r   connr`   s              rG   get_db_connectionr   *  s    ##OR8#:))C.#C YYs^	7"-h%mmC0	8)"-JD$"F$DT
  +A3/0s   B+B5 5	C>CCrl   r   c                 ,   | r|sy| j                         }d}d}|D ]i  }	 |j                  d      }|j                  di       }|j                  di       }	|j                  di       }
|	j                  d      rG|	j                  di       j                  d	      xs& |	j                  dd
      j                  d      d   nd}|
j                  d      rG|
j                  di       j                  d	      xs& |
j                  dd
      j                  d      d   nd}	 ddlm}  |||      } |||      }|j                  di       }|j                  d      }|j                  d      rd}n*|j                  d      rd}n|j                  d      rd}nd}|j                  di       }t        |j                  d      t              r!|j                  di       j                  d      n|j                  d      }t        |j                  d      t              r!|j                  di       j                  d      n|j                  d      }t        |      }|rPt        j                  |j                  dd            }|j                  dk\  r|j                  n|j                  dz
  }n/t        j                  t        j                         }|j                  }|j#                  d       |j#                  d|f       |j%                         }|r4|j#                  d||||d   |d   |d   |d    |d   d!|d   f
       |dz  }n5|j#                  d"||||||||||d   |d   |d   |d    |d   d!f       |dz  }|j#                  d#       l | j+                          |j-                          ||fS # t
        $ r Y w xY w# t&        $ rM}t)        d$|j                  d       d%|        	 |j#                  d&       n# t&        $ r Y nw xY wY d'}~d'}~ww xY w)(z Upsert games to SportsGame table)r   r   r   r   r~   r   r   teamIDnamesshortr   _UNK)normalize_to_fullstatusstartsAt	completedlive	cancelled	scheduledscorer   Zz+00:00   r   zSAVEPOINT event_spze
                SELECT id FROM "SportsGame"
                WHERE "externalGameId" = %s
            a  
                    UPDATE "SportsGame" SET
                        "homeScore" = COALESCE(%s, "homeScore"),
                        "awayScore" = COALESCE(%s, "awayScore"),
                        status = %s,
                        "moneylineHome" = COALESCE(%s, "moneylineHome"),
                        "moneylineAway" = COALESCE(%s, "moneylineAway"),
                        "spreadHome" = COALESCE(%s, "spreadHome"),
                        "spreadAway" = COALESCE(%s, "spreadAway"),
                        total = COALESCE(%s, total),
                        "oddsSource" = %s,
                        "oddsUpdatedAt" = NOW(),
                        "updatedAt" = NOW()
                    WHERE id = %s
                r   r   r   r   r   a{  
                    INSERT INTO "SportsGame" (
                        league, season, "gameDate", "homeTeam", "awayTeam",
                        "externalGameId", "homeScore", "awayScore", status,
                        "moneylineHome", "moneylineAway", "spreadHome", "spreadAway",
                        total, "oddsSource", "oddsUpdatedAt", "createdAt", "updatedAt"
                    ) VALUES (
                        %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, NOW(), NOW(), NOW()
                    )
                    ON CONFLICT (league, season, "gameDate", "homeTeam", "awayTeam") DO UPDATE SET
                        "externalGameId" = COALESCE("SportsGame"."externalGameId", EXCLUDED."externalGameId"),
                        "moneylineHome" = COALESCE(EXCLUDED."moneylineHome", "SportsGame"."moneylineHome"),
                        "moneylineAway" = COALESCE(EXCLUDED."moneylineAway", "SportsGame"."moneylineAway"),
                        "spreadHome" = COALESCE(EXCLUDED."spreadHome", "SportsGame"."spreadHome"),
                        "spreadAway" = COALESCE(EXCLUDED."spreadAway", "SportsGame"."spreadAway"),
                        total = COALESCE(EXCLUDED.total, "SportsGame".total),
                        "oddsSource" = EXCLUDED."oddsSource",
                        "oddsUpdatedAt" = NOW(),
                        "updatedAt" = NOW()
                zRELEASE SAVEPOINT event_spz  Error upserting event z: zROLLBACK TO SAVEPOINT event_spN)rf   rT   r   
team_namesr   ImportErrorrX   dictr   r   fromisoformatr   monthyearr   r   r   executefetchoner   rq   commitclose)r   rl   r   rf   insertedupdatedr   r   r~   home_team_dataaway_team_data	home_team	away_teamr   status_data
start_timer   r   
home_score
away_scorer   	game_dateseasonexistingr`   s                            rG   upsert_gamesr  K  s.   v[[]FHG BA	yy+H IIgr*E"YYvr2N"YYvr2N
 CQBTBTU]B^ ""7B/33G< ?""8R066s;A>di  CQBTBTU]B^ ""7B/33G< ?""8R066s;A>di 8-i@	-i@	
  ))Hb1K$4J {+$(-$$ IIgr*E?I%))TZJ[]a?b62.227;hmhqhqrxhyJ?I%))TZJ[]a?b62.227;hmhqhqrxhyJ $E*D $22:3E3Ec83TU	+4??a+?Y^^VWEW$LL6	" NN/0 NN  
 (H   
F)4+@&\(:DM$hqk	( 1   ( FIy)j*f)4+@&\(:DM$'4 ANN78sBH 	KKM
LLNW]  F  	,UYYy-A,B"QCHI?@ 	sa   C;N=N-5IN=-	N:6N=9N::N==	P P'O98P9	PPPPPr   c                    | r|sy| j                         }d}|D ]  }	 |j                  d|d   |j                  d      |d   |d   |j                  d      |d   |j                  d	      |j                  d
d      t        j                  |j                  di             f	       |j
                  dkD  r|dz  } | j                          |j                          |S # t        $ r
}Y d}~d}~ww xY w)z+Upsert player props to PlayerPropLine tabler   a  
                INSERT INTO "PlayerPropLine" (
                    league, "gameId", "playerExternalId", "propType", market,
                    "lineValue", "oddsAmerican", vendor, raw, "createdAt"
                ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, NOW())
                ON CONFLICT (league, "gameId", "playerExternalId", vendor, "propType", "lineValue", "oddsAmerican")
                DO NOTHING
            r   r   r   r   r   r   r   r   r   r   r   N)	rf   r  rT   rW   dumpsrowcountr   r  r  )r   r   rf   r  propr`   s         rG   upsert_player_propsr    s    u[[]FH 	NN  X 2D9K4LZ $((8"4d;6G($((8=M*N

488E2./	 "A!( 	KKM
LLNO  		s   BC	C0+C0sourcec           	         | r|sy| j                         }d}|D ]z  }	 t        j                  |t              }t	        j
                  |j                               j                         }|j                  d|d|||f       |j                  dkD  r|dz  }| | j                          |j                          |S # t        $ r
}	Y d}	~	d}	~	ww xY w)z5Save raw events to ExternalFeedRecord for audit trailr   )defaulta  
                INSERT INTO "ExternalFeedRecord" (
                    source, kind, league, "contentHash", raw, "fetchedAt", "createdAt"
                ) VALUES (%s, %s, %s, %s, %s, NOW(), NOW())
                ON CONFLICT (source, "contentHash") DO NOTHING
            r   r   N)rf   rW   r  r]   hashlibmd5encode	hexdigestr  r  r   r  r  )
r   rl   r   r  rf   r  r   raw_jsoncontent_hashr`   s
             rG   save_to_external_feedr"    s    v[[]FH 	zz%5H";;x'89CCELNN 
 &&,AC "A" 	KKM
LLNO  		s   A7B99	CCc                  |
   t        j                  d      } | j                  dddd       | j                  dd	d
       | j                  dd	d       | j                  dd	d       | j                  dt        dd       | j                  dd	d       | j	                         }t        d       t        d       t        d       t        t              }|j                         }|j                  d      dk7  rt        d|j                  di               |j                  rt        }n|j                  j                         g}|j                  rd n	t               }|rt        d       n|j                  st        d       d}d}d}d}	|D ]  }
t         j                  |
|
      }t        dd         t        d!| d"       t        d         |j#                  ||j$                  |j&                  #      }|st        d$|        |t        d%t)        |              |t)        |      z  }t+        d& |D              }t        d'|        |rGt-        |||
      \  }}t        d(| d)| d*       ||z  }|	|z  }	t/        |||
      }t        d+|        |j0                  rhg }|D ]  }t3        |      }|j5                  |         |rAt        d,t)        |              |t)        |      z  }|rt7        ||      }t        d-|        |st        d.       |d d/ D ]J  }|j                  d0i       }|j                  d1i       j                  d2i       j                  d3      xs" |j                  d1i       j                  d4d      }|j                  d5i       j                  d2i       j                  d3      xs" |j                  d5i       j                  d4d      }|j                  d6i       j                  d7      r%|j                  d6i       j                  d7d      d d8 nd}t9        |      }t        d9| d:| d;| d<       t        d=|d>    d?|d@    dA|dB    dC|dD           M  t        dE       t        dF       t        d       t        dG|        t        dH|        t        dI|        t        dJ|	        t        dK|j:                          t        dL|j<                          |j                         }|j                  d      rt        dM|d           |r|j?                          y y )NNz"Fetch odds from SportsGameOdds API)descriptionr   r   r   zLeague to fetch (default: nba))nargsr  helpz--props
store_truezInclude player props)actionr&  z--allzFetch all major leaguesz--alt-lineszInclude alternate linesz--limitr   zMax events per request)typer  r&  z	--dry-runzDon't save to databasez<============================================================zSPORTSGAMEODDS API ODDS FETCHERrQ   FzAPI Usage: rS   z Connected to PostgreSQL databasez?WARNING: Could not connect to database, will only print resultsr   
z(========================================z	Fetching z...)rd   re   z  No events found for z  Total events: c              3   D   K   | ]  }|j                  d       sd  yw)r   r   N)rT   ).0r`   s     rG   	<genexpr>zmain.<locals>.<genexpr>X  s     BQAEE&MqBs     z  Events with odds: z  Database: z inserted, z updatedz  External feed records: z  Player props found: z  Props inserted: z
  Sample events:r   r~   r   r   mediumr   r   r   r      z    z @ z ()z
      ML: r   rN   r   z | Spread: r   z
 | Total: r   z=
============================================================SUMMARYzTotal events fetched: zTotal player props: zDatabase inserts: zDatabase updates: zAPI requests made: zObjects used: zRemaining quota: ) argparseArgumentParseradd_argumentr   
parse_argsrq   r;   SGO_API_KEYr   rT   r   MAJOR_LEAGUESr   rm   dry_runr   
LEAGUE_MAPrx   	alt_linesre   rZ   sumr  r"  r   r   rr   r  r   rD   rE   r  )parserargsclientusageleaguesr   total_eventstotal_propstotal_insertedtotal_updatedr   rb   rl   events_with_oddsr  r  
feed_count	all_propsr   r   props_insertedr~   r   r   startr   final_usages                              rG   mainrK    sj   $$1UVF
UAab
	,=ST
;TU
lAZ[
	S?WX
L?WXD	(O	
+,	(O!+.F Eyyu$EIIfb1234 xx;;$$&' <<4%6%8D01\\OPLKNM <LNN662	6(m	)C() &&"nn** ' 
 *9+67 V./F# B&BB$%5$678  ,T66 BHgL
+gYhGHh&NW$M /tVVDJ-j\:; ::I (,U3  '( .s9~.>?@s9~-%8y%IN.~.>?@ &' L		'2.yy,00"=AA(KwuyyY_acOdOhOhiqsvOwyy,00"=AA(KwuyyY_acOdOhOhiqsvOwMRYYW_acMdMhMhisMt		(B/33JDSbIz}'.TF#dV2eWA67
4#8"94;P:QQ\]abn]o\ppz{  AH  |I  {J  K  LLk<L~ 
/	)	(O	"<.
12	 
./	~.
/0	}o
./	 4 45
67	N6../
01 ""$Kv!+f"5!678

 rI   __main__)r   )8r   ossysrW   rs   r  r2  r   r   r   pathlibr   typingr   r   r   r	   r?   __file__parentENV_FILEexistsopenfr   strip
startswithr   keyvalenviron
setdefaultrT   r   r6  r   rq   exitr9  r   r   r7  r;   r   r   r   r   r   r   r   r]   r  r  r"  rK  r   )kvs   00rG   <module>r`     sI  & 
 
      '  6 6  >  ''&0??	h F1 	FDzz|DOOC$8SD[::<--c15S

%%c399S>+?+?+DE	FF zz~~79\]jjnn56

4b9	
@ACHHQK	5	5 
5 
5	
 W W F 
5 
5 
5 i l , l 
"  
5!" 
5#$ 
5%
, &0%5%5%78TQAqD8   '  !*  !*  ,> ( ) Ma. a.Hx} x (D (T (V% %d %PBOtDz O3 OdT$Z BT
 C  <vr zF cF FL 9s%   6III$AII'I$