a
    ÑÑujñ.  ã                   @   s,  d Z ddlZddlZddlZddlmZ edƒZedƒZi ai a	i a
dddœZdd	„ Zh d
£Zdd„ Zdd„ Zdd„ Zdd„ Zdd„ Zdd„ Zdd„ Zd5dd„ZedƒZdZdadd„ Zd6d d!„Zd7d"d#„Zd8d%d&„Zd'd(„ Zd)d*„ Zd+d,„ Zd-d.„ Z d/d0„ Z!d1d2„ Z"eƒ  e #¡ j$ed3< e #¡ j$ed4< dS )9a/  
Shared loader for league_map.json and team_map.json.
Single source of truth for team/league name translations across:
  - API-Football (signals, live scores)
  - football-data.co.uk (bet resolution)
  - Kalshi (exchange prices)

Hot-reload: call reload() to pick up changes without restarting scripts.
é    N)ÚPathzE:\soccer\data\league_map.jsonzE:\soccer\data\team_map.jsong        )ÚleagueÚteamc                 C   s0   | sdS t  d| ¡}d dd„ |D ƒ¡ ¡  ¡ S )u7  Lowercase + strip accents/diacritics for accent-insensitive matching.

    API-Football sends accented names (e.g. 'CastellÃ³n', 'AlmerÃ­a') while
    team_map keys are plain ASCII ('Castellon', 'Almeria'); folding both sides
    makes the lookup accent-insensitive so the Kalshi leg doesn't silently miss.
    Ú ZNFKDc                 s   s   | ]}t  |¡s|V  qd S ©N)ÚunicodedataZ	combining)Ú.0Úc© r
   úE:\soccer\main\maps.pyÚ	<genexpr>#   ó    z_fold.<locals>.<genexpr>)r   Z	normalizeÚjoinÚlowerÚstrip)ÚsZnfkdr
   r
   r   Ú_fold   s    r   >	   ZdaZdoZtheZacÚscZafcZcfZfcZdec                 C   s   dd„ t  dt| ƒ¡D ƒS )z–Whole words, accent-folded, minus noise tokens. Substring matching on
    space-stripped names produced three separate wrong-club bugs -- see _lookup.c                 S   s$   h | ]}t |ƒd kr|tvr|’qS )é   )ÚlenÚ_STOP)r   Útr
   r
   r   Ú	<setcomp>,   s   ÿz_tokens.<locals>.<setcomp>z
[^a-z0-9]+)ÚreÚsplitr   )r   r
   r
   r   Ú_tokens)   s    r   c                  C   sÜ   t tdd��} t | ¡aW d   ƒ n1 s,0    Y  t tdd��} t | ¡}W d   ƒ n1 sb0    Y  dd„ | ¡ D ƒai at ¡ D ]L\}}|tt	|ƒ< | 
d¡r¸|tt	|d ƒ< | 
dg ¡D ]}|tt	|ƒ< qÄqŠd S )Núutf-8©Úencodingc                 S   s    i | ]\}}|  d ¡s||“qS )Ú_)Ú
startswith©r   ÚkÚvr
   r
   r   Ú
<dictcomp>7   r   z_load.<locals>.<dictcomp>ÚfdcoÚaliases)ÚopenÚLEAGUE_MAP_FILEÚjsonÚloadÚ_league_mapÚTEAM_MAP_FILEÚitemsÚ	_team_mapÚ
_alias_idxr   Úget)ÚfÚrawÚkeyÚinfoÚar
   r
   r   Ú_load0   s    ((
r6   c                  C   sJ   t  ¡ j} t ¡ j}| td ks,|td krFtƒ  | td< |td< dS dS )z/Re-read JSON files. Call from hot-reload loops.r   r   TF)r(   ÚstatÚst_mtimer,   Ú_mtimesr6   ©ZlmZtmr
   r
   r   ÚreloadD   s    

r;   c                  C   sB   z.t  ¡ j} t ¡ j}| td kp,|td kW S    Y dS 0 dS )zBReturn True if either JSON file has been modified since last load.r   r   FN)r(   r7   r8   r,   r9   r:   r
   r
   r   ÚchangedP   s    

r<   c                 C   s   t  | i ¡ d¡S )z&API-Football league ID for a div code.Zapi_football_id©r+   r0   ©Údivr
   r
   r   Úleague_api_id\   s    r@   c                 C   s   t  | i ¡ d| ¡S )zDisplay name for a div code.Únamer=   r>   r
   r
   r   Úleague_name`   s    rB   c                 C   s   t  | i ¡ d¡S )z-Kalshi series ticker for a div code, or None.Zkalshi_seriesr=   r>   r
   r
   r   Úleague_kalshi_seriesd   s    rC   c                 C   s   t  | i ¡ d|¡S )zYAPI-Football season for a div code. Returns the per-league override if set, else default.Zseasonr=   )r?   Údefaultr
   r
   r   Úleague_seasonh   s    rE   z#E:\soccer\data\af_season_cache.jsoni€:	 c               	   C   sl   t d urt S z:ttdd��} t | ¡a W d   ƒ n1 s:0    Y  W n  ttfyf   di dœa Y n0 t S )Nr   r   r   ©ÚfetchedÚseasons)Ú_season_cacher'   ÚSEASON_CACHE_FILEr)   r*   ÚFileNotFoundErrorÚ
ValueError)r1   r
   r
   r   Ú_season_cache_load€   s    ,rM   Fc              	   C   s¤  ddl }tƒ }|  ¡ | dd¡ tk }|rN|sN| d¡rNdd„ |d  ¡ D ƒS ddl}i }tƒ D ]¤}t|ƒ}|srq`d}	z^|jd| d|id	d
�}
|
 ¡  d¡p i gd }| d¡p²g D ]}| d¡r´| d¡}	 qÒq´W n t	yê   d}	Y n0 |	durø|	nt
|ƒ||< q`tdd„ | ¡ D ƒƒ�r |  ¡ |dœ}zTtjjddd� ttddd�� }tj||dd� W d  ƒ n1 �st0    Y  W n t�y”   Y n0 |tƒ d< |S )ai  {div: season_year} for every div with an api_football_id.

    Cached to disk for SEASON_CACHE_TTL. `af_headers` is passed in by the caller
    so this module never holds the API key. On any network/parse failure the
    league falls back to its league_map `season` value, so a dead API degrades
    to today's behaviour rather than emptying the watchlist.
    r   NrG   rH   c                 S   s   i | ]\}}||“qS r
   r
   r!   r
   r
   r   r$   ˜   r   z#current_seasons.<locals>.<dictcomp>z)https://v3.football.api-sports.io/leaguesÚidé   )ZheadersÚparamsZtimeoutZresponseZcurrentÚyearc                 s   s   | ]}|d uV  qd S r   r
   ©r   r#   r
   r
   r   r   ­   r   z"current_seasons.<locals>.<genexpr>rF   T)ÚparentsÚexist_okÚwr   r   é   )ÚindentrI   )ÚtimerM   r0   ÚSEASON_CACHE_TTLr-   ÚrequestsÚall_divsr@   r)   Ú	ExceptionrE   ÚanyÚvaluesrJ   ÚparentÚmkdirr'   ÚdumpÚOSErrorÚglobals)Ú
af_headersZforcerX   ÚcacheZfreshrZ   rH   r?   ZlidrQ   ÚrÚentryr   r1   r
   r
   r   Úcurrent_seasonsŒ   sD    

ÿ



4
rh   c                 C   sL   t ƒ  di ¡}| | ¡}|du r6|dur6t|ƒ | ¡}|du rHt| |ƒ}|S )a  Season year API-Football currently uses for `div`.

    Prefers the cached `current` season; falls back to the league_map override
    and then `default`. With af_headers=None it is read-only (cache or config),
    so callers on a hot path never trigger a refresh by accident.
    rH   N)rM   r0   rh   rE   )r?   rd   rD   re   rQ   r
   r
   r   Úleague_current_season¹   s    

ri   r
   c                 C   sh   | durt | ƒ tƒ  di ¡}dd„ | ¡ D ƒ}|dd„ dd„ tƒ D ƒD ƒO }|dd„ |D ƒO }t|ƒS )	a  Sorted distinct season years covering every tracked league.

    daily_watchlist queries /fixtures by DATE, where `season` filters globally
    rather than per league -- so it must sweep each distinct year our leagues
    live in, or leagues on an off-calendar season are dropped.
    NrH   c                 S   s   h | ]}t |tƒr|’qS r
   ©Ú
isinstanceÚintrR   r
   r
   r   r   Ó   r   zseason_years.<locals>.<setcomp>c                 S   s   h | ]}t |tƒr|’qS r
   rj   rR   r
   r
   r   r   Ô   r   c                 s   s   | ]}t |ƒV  qd S r   )rE   )r   Údr
   r
   r   r   Ô   r   zseason_years.<locals>.<genexpr>c                 S   s   h | ]}t |tƒr|’qS r
   rj   )r   Úyr
   r
   r   r   Õ   r   )rh   rM   r0   r^   r[   Úsorted)rd   Zextrare   Zyearsr
   r
   r   Úseason_yearsÉ   s    rp   c                   C   s   t t ¡ ƒS )zAll div codes in order.)Úlistr+   Úkeysr
   r
   r
   r   r[   Ø   s    r[   c                   C   s   t tƒS )zFull league map dict.)Údictr+   r
   r
   r
   r   Úleague_map_fullÜ   s    rt   c           	         sÜ   t | ƒ}t |¡}|r"|t| fS t| ƒ}|rØg }t ¡ D ]:\}}t|ƒ}|r:||ks^||kr:| t||@ ƒ|f¡ q:|rØtdd„ |D ƒƒ‰ ‡ fdd„|D ƒ}t|ƒdkr¾| 	¡ }|t| fS t
d| ›dt|ƒ› d�ƒ d	S )
zBFind team entry by any known name (exact, then alias, then fuzzy).c                 s   s   | ]\}}|V  qd S r   r
   )r   r   r   r
   r
   r   r     r   z_lookup.<locals>.<genexpr>c                    s   h | ]\}}|ˆ kr|’qS r
   r
   )r   r   r"   ©Zbestr
   r   r     r   z_lookup.<locals>.<setcomp>é   z  [maps] ambiguous team name z -> z8; refusing to guess (add an alias to data/team_map.json))NN)r   r/   r0   r.   r   r-   Úappendr   ÚmaxÚpopÚprintro   )	rA   Únr3   ÚntZscoredÚaliasr"   ZatÚtopr
   ru   r   Ú_lookupã   s&    
r   c                 C   s&   t | ƒ\}}|r"| d¡r"|d S | S )z=Translate API-Football team name to football-data.co.uk name.r%   ©r   r0   )Zapi_namer   r4   r
   r
   r   Ú	fdco_name  s    r�   c                 C   s   t | ƒ\}}|r| d¡S dS )z:Get Kalshi abbreviation for a team name (from any system).ZkalshiNr€   ©rA   r   r4   r
   r
   r   Ú
kalshi_abr  s    
rƒ   c                 C   s   t | ƒ\}}|S )zFull team entry dict, or None.)r   r‚   r
   r
   r   Ú	team_info   s    r„   r   r   )N)F)NN)Nr
   )%Ú__doc__r)   r   r   Úpathlibr   r(   r,   r+   r.   r/   r9   r   r   r   r6   r;   r<   r@   rB   rC   rE   rJ   rY   rI   rM   rh   ri   rp   r[   rt   r   r�   rƒ   r„   r7   r8   r
   r
   r
   r   Ú<module>   sH   	


-

-