
    i%                         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mZmZmZ dZ	de
defdZde
fdZd	e
fd
ZdedefdZdeddfdZd Zedk(  r e        yy)a  
Backfill NCAAB SportsGame rows from orphaned OddsSnapshot records.

Steps:
  1. Find all OddsSnapshot rows where league='ncaab' and gameId IS NULL.
  2. Get distinct (homeTeam, awayTeam, gameDate::date) combinations.
  3. For each combination not already covered by a SportsGame row, insert one.
  4. Link OddsSnapshot rows to their newly created (or pre-existing) SportsGame rows.

Usage:
    python3 ncaab_backfill_games_from_odds.py [--dry-run]
    N)datetimedatetimezonez/var/www/html/eventheodds/.envpathreturnc                 l   i }t        |       5 }|D ]  }|j                         }|r|j                  d      r'd|vr,|j                  d      \  }}}|j                         }|j                         }t	        |      dk\  r|d   dv r|d   |d   k(  r|dd }|||<    	 ddd       |S # 1 sw Y   |S xY w)	zFParse a simple KEY=VALUE .env file, ignoring comments and blank lines.#=   r   )"'   N)openstrip
startswith	partitionlen)r   envfhlinekey_values          C/var/www/html/eventheodds/scripts/ncaab_backfill_games_from_odds.pyload_envr      s    
C	d r 	D::<D4??3/$ NN3/MCE))+CKKME5zQ58z#9eBi5QR8>SaCH	 J Js   BB))B3c                  p    t        t              } | j                  dd      }|st        dt        z         |S )NSPORTS_DATABASE_URL z!SPORTS_DATABASE_URL not found in )r   ENV_FILEgetRuntimeError)r   urls     r   
get_db_urlr$   2   s6    
8
C
'''
,C>IJJJ    r#   c                 T    | j                  d      d   }t        j                  |      S )z?Return a psycopg2 connection, stripping any ?schema=... suffix.?r   )splitpsycopg2connect)r#   	clean_urls     r   r*   r*   :   s%    		#q!II&&r%   	game_datec                 V    | j                   dk\  r| j                  S | j                  dz
  S )uw  
    Return the academic-year season label for a given date.

    Convention observed in the DB:
      - Aug–Dec of year Y  -> season Y      (fall semester of Y/Y+1)
      - Jan–Jul of year Y  -> season Y-1    (spring semester of Y-1/Y)

    Examples:
      Nov 2025 -> 2025
      Jan 2026 -> 2025
      Apr 2026 -> 2025
      Nov 2024 -> 2024
      Jan 2025 -> 2024
       r   )monthyear)r,   s    r   compute_seasonr1   D   s)     !~~~~!!r%   dry_runc                 d   t        j                         }t        dt        j                         dd       t        d|  d|        t               }t        |      }d|_        |j                  t        j                  j                        }|j                  d       |j                         d	   }t        d
|        |dk(  rt        d       |j                          y |j                  d       |j                         }t        dt!        |              g }|D ]  }|d   }	|d   }
|d   }|d   }|j                  d|	|
|f       |j                         }|rt        d| d|
 d|	 d|d    d| d       ]t#        |      }||k  rdnd}|j%                  |	|
||||d       t        d| d|
 d|	 d| d | d!| d        t        d"t!        |              |st        d#       |j                          y d}d}t        j                  t&        j(                        }|D ]  }t        |d   j*                  |d   j,                  |d   j.                  t&        j(                  $      }| r/t        d%|d    d|d&    d|d'    d|d(    d |d)    
       |d*z  }y|j                  d+|d(   ||d'   |d&   |d)   |f       |j                         }|r5|d*z  }t        d,|d    d|d    d|d&    d|d'    d|d(    d |d)           |d*z  }t        d-|d    d|d&    d|d'            t        d.| d/|        | r7t        d0       t        d1       |j1                          |j                          y t        d2       |j                  d3       |j2                  }t        d4|        |j                  d       |j                         d	   }t        d5|        |j5                          t        d6t        j                         dd7       |j                          |j                          y )8N[z%Y-%m-%d %H:%M:%Sz+] Starting NCAAB backfill from OddsSnapshotz
  dry_run=z  today=F)cursor_factoryz~
        SELECT COUNT(*) AS cnt
        FROM "OddsSnapshot"
        WHERE league = 'ncaab'
          AND "gameId" IS NULL
    cntz5
Orphaned OddsSnapshot rows (ncaab, gameId IS NULL): r   zNothing to do.aX  
        SELECT
            "homeTeam",
            "awayTeam",
            "gameDate"::date AS game_date,
            COUNT(*) AS snapshot_count
        FROM "OddsSnapshot"
        WHERE league = 'ncaab'
          AND "gameId" IS NULL
        GROUP BY "homeTeam", "awayTeam", "gameDate"::date
        ORDER BY "gameDate"::date, "homeTeam"
    z,Distinct (homeTeam, awayTeam, date) combos: homeTeamawayTeamr,   snapshot_countz
            SELECT id
            FROM "SportsGame"
            WHERE league = 'ncaab'
              AND "homeTeam" = %s
              AND "awayTeam" = %s
              AND "gameDate"::date = %s
            LIMIT 1
        z  SKIP  z  z @ z  -> already exists (id=idz, z snapshots)final	scheduled)homeawayr,   seasonstatusn_snapsz  QUEUE z	  season=z	  status=z  (z
Games to insert: zNo new SportsGame rows needed.)tzinfoz  DRY-RUN INSERT: r>   r=   r?   r@   r   aJ  
            INSERT INTO "SportsGame"
                (league, season, "gameDate", "homeTeam", "awayTeam", status, "updatedAt")
            VALUES
                ('ncaab', %s, %s, %s, %s, %s, %s)
            ON CONFLICT (league, season, "gameDate", "homeTeam", "awayTeam")
            DO NOTHING
            RETURNING id
        z  INSERTED id=z  CONFLICT (no-op)  z
Inserted: z  Conflicts/skipped: zL
DRY-RUN: would now run UPDATE to link OddsSnapshot rows to SportsGame rows.zNo changes committed.z9
Linking orphaned OddsSnapshot rows to SportsGame rows...aP  
        UPDATE "OddsSnapshot" os
        SET "gameId" = g.id
        FROM "SportsGame" g
        WHERE os.league = 'ncaab'
          AND os."gameId" IS NULL
          AND g.league = 'ncaab'
          AND g."homeTeam" = os."homeTeam"
          AND g."awayTeam" = os."awayTeam"
          AND g."gameDate"::date = os."gameDate"::date
    zOddsSnapshot rows linked: z&Remaining orphaned OddsSnapshot rows: z
[z] Done. Committed.)r   todayprintr   nowr$   r*   
autocommitcursorr)   extrasRealDictCursorexecutefetchoneclosefetchallr   r1   appendr   utcr0   r/   dayrollbackrowcountcommit)r2   rC   r#   conncurorphan_countcombos	to_insertrowr=   r>   gdaterA   existingr?   r@   insertedskipped_conflictnow_tsggame_dtresultlinked	remainings                           r   runrd   ]   s   JJLE	Ahlln.//Z
[\	Jwixw
/0
,C3<DDO
++X__%C%C+
DC
 KK  	 <<>%(L	B<.
QRq


 KK  	 \\^F	8V
FG
 I p::K &'  D% 	" <<>HUG2dV3tf4LXVZ^L\\^_f^ggrst#E*F %W;F"  "  HUG2dV3tfIfXYvhVYZaYbbmno=p@ 
I/
01./


 H\\(,,'F "V1[>..+0D0DanFXFX"*,,0 &q~&6b63qykak])AhK=B CMH  hKfIfIhK
	  MHN6$<.1[>2B"QvYKsSTU[S\R]ak])AhK=B C !(;(81V9+S6TUE"VH 
L
"78H7I
JK
 ]^%&

	
FGKK 
 
	 \\F	&vh
/0 KK  	 u%I	29+
>?KKM	C011C
DEIIKJJLr%   c                     t        j                  d      } | j                  ddd       | j                         }	 t	        |j
                         y # t        $ r# t        d       t        j                  d	       Y y t        $ rQ}t        d
| t        j                         dd l}|j                          t        j                  d	       Y d }~y d }~ww xY w)NzBBackfill NCAAB SportsGame rows from orphaned OddsSnapshot records.)descriptionz	--dry-run
store_truez7Print what would be done without making any DB changes.)actionhelp)r2   z
Interrupted.r   z
ERROR: )filer   )argparseArgumentParseradd_argument
parse_argsrd   r2   KeyboardInterruptrD   sysexit	Exceptionstderr	traceback	print_exc)parserargsexcrt   s       r   mainry     s    $$XF F  
 D	DLL!  	#cjj1	s   A )C>CACC__main__)__doc__osrp   rk   r)   psycopg2.extrasr   r   r   r    strdictr   r$   r*   intr1   boolrd   ry   __name__ r%   r   <module>r      s    
 
    - - ,3 4 (C ' '"d "s "2m m$ mh. zF r%   