a
    8ñ»jÒl  ã                   @   sö  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 d dlmZ d dlmZ d dlmZmZmZmZ d dlZd dlZd dlZd dlZd dlm Z  d d	l!m"Z" d d
l#m$Z$m%Z% ddl&m'Z'm(Z(m)Z)m*Z*m+Z+ G dd„ deƒZ,G dd„ deƒZ-G dd„ de-ƒZ.G dd„ de-ƒZ/ej0e1dœdd„Z2ee1e3e1 f e4e1 dœdd„Z5ee1e3e1 f e4e1 dœdd„Z6d)ej7ee3e1  ej7dœdd„Z8e9e1e1f d œd!d"„Z:e9e;d#œd$d%„Z<d*e"e1ee1 ee1 d&œd'd(„Z=dS )+é    N)ÚABCÚabstractmethod)ÚIterable)ÚdatetimeÚ	timedeltaÚtimezone)ÚEnum)ÚPath)ÚIOÚCallableÚOptionalÚUnion)Úrelativedelta)Ú_Element)ÚJavascriptExceptionÚWebDriverExceptioné   )ÚDATA_DIRÚLEAGUE_DICTÚMAXAGEÚTEAMNAME_REPLACEMENTSÚloggerc                   @   s\   e Zd ZdZdZdZeed dœdd„ƒZee	e d dœdd	„ƒZ
eeef ed
œdd„ZdS )Ú
SeasonCodezÄHow to interpret season codes.

    Attributes
    ----------
    SINGLE_YEAR: The season code is a single year, e.g. '2021'.
    MULTI_YEAR: The season code is a range of years, e.g. '2122'.
    zsingle-yearz
multi-year)ÚleagueÚreturnc                 C   sx   | t vrtd| › d�ƒ‚t |  }d|v r4t|d ƒS t | dd¡d¡j}t | dd¡d¡j}|| d	k rrtjS tjS )
aS  Return the default season code for a league.

        Parameters
        ----------
        league : str
            The league to consider.

        Raises
        ------
        ValueError
            If the league is not recognized.

        Returns
        -------
        SeasonCode
            The season code format to use.
        úInvalid league 'ú'Zseason_codeZseason_startÚAugú%bÚ
season_endÚMayr   )	r   Ú
ValueErrorr   r   ÚstrptimeÚgetÚmonthÚ
MULTI_YEARÚSINGLE_YEAR)r   Zselect_league_dictZstart_monthZ	end_month© r'   ú8E:\soccer\ml_env\Lib\site-packages\soccerdata/_common.pyÚfrom_league&   s    
þ
þzSeasonCode.from_league)Úleaguesr   c                 C   s6   dd„ | D ƒ}t |ƒdkr"| ¡ S tjddd� tjS )as  Determine the season code to use for a set of leagues.

        If the given leagues have different default season codes,
        the multi-year format is usded.

        Parameters
        ----------
        leagues : list of str
            The leagues to consider.

        Returns
        -------
        SeasonCode
            The season code format to use.
        c                 S   s   h | ]}t  |¡’qS r'   )r   r)   )Ú.0r   r'   r'   r(   Ú	<setcomp>Y   ó    z*SeasonCode.from_leagues.<locals>.<setcomp>r   zOThe leagues have different default season codes. Using multi-year season codes.é   ©Ú
stacklevel)ÚlenÚpopÚwarningsÚwarnr   r%   )r*   Zseason_codesr'   r'   r(   Úfrom_leaguesH   s    þzSeasonCode.from_leagues©Úseasonr   c              
      sN  t |ƒ}t d¡‡fdd„ft d¡‡fdd„ft d¡‡fdd„ft d¡‡fd	d„ft d
¡‡fdd„ft d¡‡fdd„ft d¡‡fdd„fg}tjtjd�j‰ t t dœ‡ ‡fdd„‰t t dœ‡ ‡fdd„‰t t dœ‡fdd„‰t t dœ‡fdd„‰t t dœ‡ ‡fdd„‰|D ]"\}}| |¡�r||ƒ  S �qt	d|› d�ƒ‚dS )z4Convert a string or int to a standard season format.z
^[0-9]{4}$c                    s   ˆ | ƒS ©Nr'   ©Ús)Úprocess_four_digit_yearr'   r(   Ú<lambda>h   r-   z"SeasonCode.parse.<locals>.<lambda>z
^[0-9]{2}$c                    s   ˆ | ƒS r8   r'   r9   )Úprocess_two_digit_yearr'   r(   r<   l   r-   z^[0-9]{4}-[0-9]{4}$c                    s   ˆ | ƒS r8   r'   r9   ©Úprocess_full_year_ranger'   r(   r<   p   r-   z^[0-9]{4}/[0-9]{4}$c                    s   ˆ |   dd¡ƒS ©Nú/ú-©Úreplacer9   r>   r'   r(   r<   t   r-   z^[0-9]{4}-[0-9]{2}$c                    s   ˆ | ƒS r8   r'   r9   )Úprocess_partial_year_ranger'   r(   r<   x   r-   z^[0-9]{2}-[0-9]{2}$c                    s   ˆ | ƒS r8   r'   r9   ©Úprocess_short_year_ranger'   r(   r<   |   r-   z^[0-9]{2}/[0-9]{2}$c                    s   ˆ |   dd¡ƒS r@   rC   r9   rF   r'   r(   r<   €   r-   ©Útzr6   c                    s  ˆt jkr¨t| dd… ƒt| dd… ƒd krp| dkrld| › d| dd… › d| dd… › d	�}tj|dd
� | S | dd… dkr„dS | dd… t| dd… ƒd d› S | dkr´dS | dkrÀdS | dd… dksà| dd… dkrä| S t| ƒˆ k�rd| dd…  S d| dd…  S )z/Process a 4-digit string like '1994' or '9495'.r.   Nr   Z2021zSeason id "z!" is ambiguous: interpreting as "rB   éþÿÿÿú"r/   Ú99Ú9900Ú02dZ1920Z1919Z2020Ú19Ú20)r   r%   Úintr3   r4   )r7   Úmsg©Úcurrent_yearÚselfr'   r(   r;   †   s.    
$
ÿ
ÿÿ$ z1SeasonCode.parse.<locals>.process_four_digit_yearc                    sJ   ˆt jkr*| dkrdS | t| ƒd d› S td|  ƒˆ krBd|  S d|  S )z#Process a 2-digit string like '94'.rL   rM   r   rN   rP   rO   ©r   r%   rQ   ©r7   rS   r'   r(   r=   ž   s    
z0SeasonCode.parse.<locals>.process_two_digit_yearc                    s.   ˆ t jkr"| dd… | dd…  S | dd… S )z4Process a range of 4-digit strings like '1994-1995'.r.   é   rJ   N©r   r%   rW   ©rU   r'   r(   r?   ¨   s    
z1SeasonCode.parse.<locals>.process_full_year_rangec                    s.   ˆ t jkr"| dd… | dd…  S | dd… S )z=Process a range of 4-digit and 2-digit string like '1994-95'.r.   rX   rJ   NrY   rW   rZ   r'   r(   rE   ®   s    
z4SeasonCode.parse.<locals>.process_partial_year_rangec                    sZ   ˆt jkr"| dd… | dd…  S td| dd…  ƒˆ krJd| dd…  S d| dd…  S )z0Process a range of 2-digit strings like '94-95'.Nr.   rJ   rP   rO   rV   rW   rS   r'   r(   rG   ´   s
    
z2SeasonCode.parse.<locals>.process_short_year_rangezUnrecognized season code: 'r   N)
ÚstrÚreÚcompiler   Únowr   ÚutcÚyearÚmatchr!   )rU   r7   ÚpatternsÚpatternÚactionr'   )rT   r;   r?   rE   rG   r=   rU   r(   Úparseb   sB    
þ
þ
þ
þ
þ
þ
þç
zSeasonCode.parseN)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r&   r%   Ústaticmethodr[   r)   Úlistr5   r   rQ   re   r'   r'   r'   r(   r      s   !r   c                
   @   s  e Zd ZdZddddefeeeee f  eeeee e	g ef f  e
e
edœdd„Zdeddfeee eeeef  e
eeeee f  ee dœdd	„Zd(ee eeeef  e
d
œdd„Zed)eee eeeee f  ee dœdd„ƒZeee dœdd„ƒZeeeef dœdd„ƒZed*ejeejdœdd„ƒZeeeef dœdd„ƒZej d+eeeee f  ddœdd„ƒZee!dœdd„ƒZ"eee
dœd d!„Z#eee dœd"d#„ƒZ$eee dœd$d%„ƒZ%e%j eeeeeeeef  f  dd&œd'd%„ƒZ%dS ),Ú
BaseReadera   Base class for data readers.

    Parameters
    ----------
    leagues : str or list of str, optional
        The leagues to read. If None, all available leagues are read.
    proxy : 'tor' or or dict or list(dict) or callable, optional
        Use a proxy to hide your IP address. Valid options are:
            - "tor": Uses the Tor network. Tor should be running in
              the background on port 9050.
            - str: The address of the proxy server to use.
            - list(str): A list of proxies to choose from. A different proxy will
              be selected from this list after failed requests, allowing rotating
              proxies.
            - callable: A function that returns a valid proxy. This function will
              be called after failed requests, allowing rotating proxies.
    no_cache : bool
        If True, will not use cached data.
    no_store : bool
        If True, will not store downloaded data.
    data_dir : Path
        Path to directory where data will be cached.
    NF)r*   ÚproxyÚno_cacheÚno_storeÚdata_dirc                    sÈ   t ˆ tƒr"ˆ  ¡ dkr"dd„ | _nNt ˆ tƒr<‡ fdd„| _n4t ˆ tƒrV‡ fdd„| _ntˆ ƒrfˆ | _n
dd„ | _|| _|| _|| _|| _	d| _
d| _| jr¦t d¡ nt d	| j	¡ | j	jd
d
d� dS )zCreate a new data reader.Ztorc                   S   s   dS )Nzsocks5://127.0.0.1:9050r'   r'   r'   r'   r(   r<   æ   r-   z%BaseReader.__init__.<locals>.<lambda>c                      s   ˆ S r8   r'   r'   ©rm   r'   r(   r<   è   r-   c                      s
   t  ˆ ¡S r8   )ÚrandomÚchoicer'   rq   r'   r(   r<   ê   r-   c                   S   s   d S r8   r'   r'   r'   r'   r(   r<   î   r-   r   zCaching is disabledzSaving cached data to %sT©ÚparentsÚexist_okN)Ú
isinstancer[   Úlowerrm   rk   ÚcallableÚ_selected_leaguesrn   ro   rp   Ú
rate_limitÚ	max_delayr   ÚinfoÚmkdir)rU   r*   rm   rn   ro   rp   r'   rq   r(   Ú__init__Ü   s&    	


zBaseReader.__init__)ÚurlÚfilepathÚmax_agern   Úvarr   c                 C   s\   |   ||¡}|s| js|s4t d|¡ |  |||¡S t d|¡ |du rPtdƒ‚|jdd�S )aÇ  Load data from `url`.

        By default, the source of `url` is downloaded and saved to `filepath`.
        If `filepath` exists, the `url` is not visited and the cached data is
        returned.

        Parameters
        ----------
        url : str
            URL to download.
        filepath : Path, optional
            Path to save downloaded file. If None, downloaded data is not cached.
        max_age : int for age in days, or timedelta object
            The max. age of locally cached file before re-download.
        no_cache : bool
            If True, will not use cached data. Overrides the class property.
        var : str or list of str, optional
            Return a JavaScript variable instead of the page source.

        Raises
        ------
        TypeError
            If max_age is not an integer or timedelta object.

        Returns
        -------
        io.BufferedIOBase
            File-like object of downloaded data.
        zScraping %szRetrieving %s from cacheNz%No filepath provided for cached data.Úrb©Úmode)Ú
_is_cachedrn   r   ÚdebugÚ_download_and_saver!   Úopen)rU   r€   r�   r‚   rn   rƒ   Z	is_cachedr'   r'   r(   r#   ü   s    %zBaseReader.get)r�   r‚   r   c                 C   s    |dur8t |tƒrt|d�}q<t |tƒr.|}q<tdƒ‚nd}d}|durŠ|durŠ| ¡ rŠtj| ¡ jt	j
d�}t t	j
¡}|| |krŠd}| ož|duož| ¡ S )a	  Check if `filepath` contains valid cached data.

        Parameters
        ----------
        filepath : Path, optional
            Path where file should be cached. If None, return False.
        max_age : int for age in days, or timedelta object
            The max. age of locally cached file.

        Raises
        ------
        TypeError
            If max_age is not an integer or timedelta object.

        Returns
        -------
        bool
            True in case of a cache hit, otherwise False.
        N)Údaysz3'max_age' must be of type int or datetime.timedeltaFrH   T)rw   rQ   r   Ú	TypeErrorÚexistsr   ÚfromtimestampÚstatÚst_mtimer   r_   r^   )rU   r�   r‚   Z_max_ageZcache_invalidZlast_modifiedr^   r'   r'   r(   r‡   *  s    


zBaseReader._is_cached©r€   r�   rƒ   r   c                 C   s   dS )aÔ  Download data at `url` to `filepath`.

        Parameters
        ----------
        url : str
            URL to download.
        filepath : Path, optional
            Path to save downloaded file. If None, downloaded data is not cached.
        var : str or list of str, optional
            Return a JavaScript variable instead of the page source.

        Returns
        -------
        io.BufferedIOBase
            File-like object of downloaded data.
        Nr'   )rU   r€   r�   rƒ   r'   r'   r(   r‰   W  s    zBaseReader._download_and_save©r   c                 C   s   t |  ¡  ¡ ƒS )z6Return a list of league IDs available for this source.)ÚsortedÚ_all_leaguesÚkeys©Úclsr'   r'   r(   Úavailable_leagueso  s    zBaseReader.available_leaguesc                    s(   t ˆ dƒs"‡ fdd„t ¡ D ƒˆ _ˆ jS )zDReturn a dict mapping all canonical league IDs to source league IDs.Ú_all_leagues_dictc                    s&   i | ]\}}ˆ j |v r||ˆ j  “qS r'   )rf   ©r+   ÚkÚvr–   r'   r(   Ú
<dictcomp>x  s   z+BaseReader._all_leagues.<locals>.<dictcomp>)Úhasattrr   Úitemsr™   r–   r'   r–   r(   r”   t  s
    

ÿzBaseReader._all_leaguesr   )ÚdfÚcolr   c                 C   sL   dd„ |   ¡  ¡ D ƒ}||  |¡ }tj|j||f< ||  |¡||< |S )z%Map source league ID to canonical ID.c                 S   s   i | ]\}}||“qS r'   r'   rš   r'   r'   r(   r�   €  r-   z0BaseReader._translate_league.<locals>.<dictcomp>)r”   rŸ   ÚisinÚnpÚnanÚlocrD   )r—   r    r¡   ÚflipÚmaskr'   r'   r(   Ú_translate_league}  s
    zBaseReader._translate_leaguec                 C   s   | j S )zIReturn a dict mapping selected canonical league IDs to source league IDs.)Ú_leagues_dictrZ   r'   r'   r(   rz   †  s    zBaseReader._selected_leagues)Úidsr   c              	   C   sŒ   |d u r|   ¡ | _ntt|ƒdkr(tdƒ‚t|tƒr8|g}i }|D ]@}||   ¡ vrptd|› dt |  ¡ ¡› d�ƒ‚|   ¡ | ||< q@|| _d S )Nr   z(Empty iterable not allowed for 'leagues'z)
                        Invalid league 'z.'. Valid leagues are:
                        z
                        )	r”   r©   r1   r!   rw   r[   ÚpprintÚpformatr˜   )rU   rª   Ztmp_league_dictÚir'   r'   r(   rz   ‹  s$    
ÿþÿc                 C   s   t  | j¡S r8   )r   r5   r*   rZ   r'   r'   r(   Ú_season_code   s    zBaseReader._season_code)r   r7   r   c                 C   sÊ   |t v rt | }n<dd„ |  ¡  ¡ D ƒ}||v r>t ||  }ntd|› d�ƒ‚d|vr|tt |dd… d¡jd	d
tjd�}n<tt |dd… d¡jt |d d¡j	d
tjd�t
d
d� }tjtjd�|kS )zCheck if a season is complete.c                 S   s   i | ]\}}||“qS r'   r'   rš   r'   r'   r(   r�   ©  r-   z+BaseReader._is_complete.<locals>.<dictcomp>r   r   r   rJ   Nz%yé   r   )Útzinfor   )ÚmonthsrH   )r   r”   rŸ   r!   r   r"   r`   r   r_   r$   r   r^   )rU   r   r7   Zleague_dictr¦   Zseason_endsr'   r'   r(   Ú_is_complete¤  s0    
üÿúùzBaseReader._is_completec                 C   s   t | j ¡ ƒS )z"Return a list of selected leagues.)rk   r©   r•   rZ   r'   r'   r(   r*   À  s    zBaseReader.leaguesc                 C   s   | j S )z"Return a list of selected seasons.)Ú_season_idsrZ   r'   r'   r(   ÚseasonsÅ  s    zBaseReader.seasons)r´   r   c                    sh   |d u r<t  d¡ tjtjd�j}dd„ t||d dƒD ƒ}t|t	t
fƒrP|g}‡ fdd„|D ƒˆ _d S )Nz?No seasons provided. Will retrieve data for the last 5 seasons.rH   c                 S   s   g | ]}|d  › d|› �‘qS )r   rB   r'   )r+   Úyr'   r'   r(   Ú
<listcomp>Ï  r-   z&BaseReader.seasons.<locals>.<listcomp>é   éÿÿÿÿc                    s   g | ]}ˆ j  |¡‘qS r'   )r®   re   )r+   r:   rZ   r'   r(   r¶   Ò  r-   )r   r}   r   r^   r   r_   r`   Úrangerw   r[   rQ   r³   )rU   r´   r`   r'   rZ   r(   r´   Ê  s    
)NN)NN)r   )N)&rf   rg   rh   ri   r   r   r   r[   rk   r   Úboolr	   r   r   rQ   r   r   r
   Úbytesr#   r‡   r   r‰   Úclassmethodr˜   Údictr”   ÚpdÚ	DataFramer¨   Úpropertyrz   Úsetterr   r®   r²   r*   r´   r'   r'   r'   r(   rl   Ã   sx   úú#úù0  ýü-  üû$rl   c                
       sÂ   e Zd ZdZddddedfeeeee f  eeeee e	g ef f  e
e
eeeeef  dœ‡ fdd„Zdeeeef  ejdœdd	„Zdeee eeeee f  ee d
œdd„Z‡  ZS )ÚBaseRequestsReaderz;Base class for readers that use the Python requests module.NF)r*   rm   rn   ro   rp   Úheadersc                    s&   t ƒ j|||||d� |  |¡| _dS )úInitialize the reader.©rn   ro   r*   rm   rp   N)Úsuperr   Ú_init_sessionÚ_session)rU   r*   rm   rn   ro   rp   rÃ   ©Ú	__class__r'   r(   r   Ø  s    
ûzBaseRequestsReader.__init__)rÃ   r   c                 C   s   t j|  ¡ |d�S )N)rm   rÃ   )Útls_requestsÚClientrm   )rU   rÃ   r'   r'   r(   rÇ   ì  s    z BaseRequestsReader._init_sessionr‘   c              
   C   sF  t dƒD �]&}zì| j |¡}t | jt ¡ | j  ¡ | ¡  |dur¢t	|t
ƒrV|g}d |¡}d}|t|dd� }t ||j¡}	dd„ |	D ƒ}
t |
¡ d¡}n|j}| jsì|durì|jd	d
��}| |¡ W d  ƒ n1 sâ0    Y  t |¡W   S  t�y.   t d||d ¡ |  ¡ | _Y qY q0 qtd|› d�ƒ‚dS )ú@Download file at url to filepath. Overwrites if filepath exists.é   Nú|s)   (%b)+[\s\t]*=[\s\t]*JSON\.parse\('(.*)'\)úutf-8)Úencodingc                 S   s(   i | ] \}}|  d ¡t |  d ¡¡“qS )Úunicode_escape)ÚdecodeÚjsonÚloads)r+   ÚkeyÚvaluer'   r'   r(   r�     s   ÿz9BaseRequestsReader._download_and_save.<locals>.<dictcomp>Úwbr…   z7Error while scraping %s. Retrying... (attempt %d of 5).r   úCould not download Ú.)r¹   rÈ   r#   ÚtimeÚsleepr{   rr   r|   Úraise_for_statusrw   r[   Újoinr»   r\   ÚfindallÚcontentrÔ   ÚdumpsÚencodero   rŠ   ÚwriteÚioÚBytesIOÚ	Exceptionr   Ú	exceptionrÇ   ÚConnectionError)rU   r€   r�   rƒ   r­   ÚresponseZ	var_namesZtemplate_understatZpattern_understatÚresultsÚdataÚpayloadÚfhr'   r'   r(   r‰   ï  s<    

þ(ý
z%BaseRequestsReader._download_and_save)N)NN)rf   rg   rh   ri   r   r   r   r[   rk   r   rº   r	   r½   r   rË   rÌ   rÇ   r   r
   r»   r‰   Ú__classcell__r'   r'   rÉ   r(   rÂ   Õ  s0   ùù   üûrÂ   c                	       s¬   e Zd ZdZddddeddfeeeee f  eeeee e	g ef f  e
e
eee e
dœ‡ fdd„Zdd	œd
d„Zdeee eeeee f  ee dœdd„Z‡  ZS )ÚBaseSeleniumReaderz)Base class for readers that use Selenium.NFT)r*   rm   rn   ro   rp   Úpath_to_browserÚheadlessc           	   
      sf   t ƒ j|||||d� || _|| _z|  ¡ | _W n0 ty` } zt d|¡ W Y d}~n
d}~0 0 dS )rÄ   rÅ   z­
                The ChromeDriver was unable to initiate/spawn a new
                WebBrowser. You will not be able to scrape new data.
                %s
                N)	rÆ   r   rð   rñ   Ú_init_webdriverÚ_driverr   r   Úerror)	rU   r*   rm   rn   ro   rp   rð   rñ   ÚerÉ   r'   r(   r     s     ûúzBaseSeleniumReader.__init__z	sb.Driverr’   c                 C   sD   t | dƒr| j ¡  |  ¡ }d}|dur,d}tjd| j| j||d�S )zStart the Selenium driver.ró   Nz#MAP * ~NOTFOUND , EXCLUDE 127.0.0.1T)Zucrñ   Zbinary_locationZhost_resolver_rulesrm   )rž   ró   Úquitrm   ÚsbZDriverrñ   rð   )rU   Z	proxy_strZresolver_rulesr'   r'   r(   rò   =  s    

ûz"BaseSeleniumReader._init_webdriverr‘   c                 C   s–  t dƒD �]v}�z&| j |¡ t | jt ¡ | j  ¡ d| jjv rLt	dƒ‚|du rx| j 
d¡ d¡}|dkrÎtdƒ‚nVt|tƒsŠtd	ƒ‚z t | j 
d
| ¡¡ d¡}W n" tyÌ   t d¡ d¡}Y n0 | j�s(|du�r(|jjddd� |jdd��}| |¡ W d  ƒ n1 �s0    Y  t |¡W   S  t�y~   t d||d |d ¡ t |d ¡ |  ¡ | _Y qY q0 qtd|› d�ƒ‚dS )rÍ   rÎ   zIncapsula incident IDz<Your IP is blocked. Use tor or a proxy to continue scraping.Nzreturn document.body.innerHTML;rÐ   r-   zEmpty response.z&Only implemented for single variables.zreturn Trt   rØ   r…   zEError while scraping %s. Retrying in %d seconds... (attempt %d of 5).é
   r   rÙ   rÚ   )r¹   ró   r#   rÛ   rÜ   r{   rr   r|   Zpage_sourcer   Zexecute_scriptrâ   ræ   rw   r[   ÚNotImplementedErrorrÔ   rá   r   ro   Úparentr~   rŠ   rã   rä   rå   r   rç   rò   rè   )rU   r€   r�   rƒ   r­   ré   rí   r'   r'   r(   r‰   O  sP    ÿÿþ

ÿ*ü
z%BaseSeleniumReader._download_and_save)NN)rf   rg   rh   ri   r   r   r   r[   rk   r   rº   r	   r   rò   r   r
   r»   r‰   rî   r'   r'   rÉ   r(   rï     s4   øø!  üûrï   )Úrowr   c                 C   sH   t  | d ¡r$d | d | d ¡}n d | d  d¡| d | d ¡}|S )z3Return a game id based on date, home and away team.Údatez{}-{}Z	home_teamZ	away_teamz{} {}-{}z%Y-%m-%d)r¾   ZisnullÚformatÚstrftime)rû   Zgame_idr'   r'   r(   Úmake_game_id€  s    þýrÿ   )Úteamr   c                 C   sV   t | tƒr| gn| }tƒ }|D ]2} t ¡ D ]\}}|| kr*| |¡ q*| | ¡ q|S )aÞ  Add a set of alternative team names for a standardized team name.

    If a standardized team name is given, add the set of alternative
    names used by the data sources. If a non-standardized name is given,
    a set only containing the given name is returned.

    Parameters
    ----------
    team : str or list of str
        The team name(s) to consider.

    Returns
    -------
    set of str
        A set contraining the given team name(s) and alternative names.
    ©rw   r[   Úsetr   rŸ   Úadd)r   ÚteamsZ	alt_teamsÚalt_nameÚ	norm_namer'   r'   r(   Úadd_alt_team_names�  s    r  c                 C   sV   t | tƒr| gn| }tƒ }|D ]2}t ¡ D ]\}}||kr*| |¡ q*| |¡ q|S )a¾  Add the standardized team name for a non-standardized team name.

    If a non-standardized team name is given, add the standardized
    name. If a standardized name is given, a set only containing the given
    name is returned.

    Parameters
    ----------
    team : str or list of str
        The team name(s) to consider.

    Returns
    -------
    set of str
        A set contraining the given team name(s) and standardized names.
    r  )r   r  Z	std_teamsZ_teamr  r  r'   r'   r(   Úadd_standardized_team_name¬  s    r  )r    Úcolsr   c                    sˆ   t t dœdd„‰ | jjdkr\|du r\|  ¡ }‡ fdd„| jjd D ƒ}|jj|dd	�|_|S |du rnt| jƒ}| j‡ fd
d„|D ƒd�S )z-Convert DataFrame column names to snake case.)Únamer   c                 S   sB   t  dd| ¡} t  dd| ¡} t  dd| ¡} |  ¡  dd¡ dd	¡S )
Nz(.)([A-Z][a-z]+)z\1_\2z	__([A-Z])z_\1z([a-z0-9])([A-Z])rB   Ú_ú Ú )r\   Úsubrx   rD   )r
  r'   r'   r(   Úto_snakeÊ  s    z&standardize_colnames.<locals>.to_snaker   Nc                    s   g | ]}ˆ |ƒ‘qS r'   r'   ©r+   Úc©r  r'   r(   r¶   Ó  r-   z(standardize_colnames.<locals>.<listcomp>r   )Úlevelc                    s   i | ]}|ˆ |ƒ“qS r'   r'   r  r  r'   r(   r�   Ú  r-   z(standardize_colnames.<locals>.<dictcomp>)Úcolumns)r[   r  ZnlevelsÚcopyZlevelsZ
set_levelsrk   Úrename)r    r	  Znew_dfZnew_colsr'   r  r(   Ústandardize_colnamesÇ  s    
r  r’   c            	      C   sæ   dg} g }| D ]P}t  t |¡j¡d }| |¡ |sHt d¡ i   S t dt|ƒ› d�¡ qg }|D ]T}|d d }|d }|d	 }|d
 | d | |d
 | d | dœ}| 	|¡ qh|D ]}t
|ƒrÂ|  S qÂt d¡ i S )zReturn a public proxy.zOhttps://proxylist.geonode.com/api/proxy-list?sort_by=lastChecked&sort_type=descrë   z4There are currently no proxies available. Exiting...zFound z proxy servers. Checking...Z	protocolsr   ÚipÚportz://ú:)ÚhttpsÚhttp)rÔ   rÕ   rË   r#   ÚtextÚextendr   r}   r1   ÚappendÚcheck_proxy)	Zlist_of_proxy_contentZfull_proxy_listÚ	proxy_urlZ
proxy_jsonZfinal_proxy_listrm   ÚprotocolZip_r  r'   r'   r(   Ú	get_proxyÝ  s0    ÿ

þ

r#  )rm   r   c              
   C   sZ   zt jd| dd�}|jdkW S  tyT } z t d|›d�¡ W Y d}~dS d}~0 0 dS )	zCheck if proxy is working.zhttps://ipinfo.io/jsoné   )ÚproxiesÚtimeoutéÈ   zBAD PROXY: Reason: Ú
NF)rË   r#   Ústatus_coderæ   r   rô   )rm   Zr0rô   r'   r'   r(   r     s    r   )ÚnodeÚ
xpath_exprr4   r   c                 C   s4   |   |¡}|s(|d ur(tj|dd� d S |d  ¡ S )Nr.   r/   r   )Zxpathr3   r4   Ústrip)r*  r+  r4   Úresultr'   r'   r(   Úsafe_xpath_text  s
    
r.  )N)N)>rä   rÔ   r«   rr   r\   rÛ   r3   Úabcr   r   Úcollections.abcr   r   r   r   Úenumr   Úpathlibr	   Útypingr
   r   r   r   Únumpyr£   Zpandasr¾   Zseleniumbaser÷   rË   Zdateutil.relativedeltar   Z
lxml.etreer   Zselenium.common.exceptionsr   r   Ú_configr   r   r   r   r   r   rl   rÂ   rï   ZSeriesr[   rÿ   rk   r  r  r  r¿   r  r½   r#  rº   r   r.  r'   r'   r'   r(   Ú<module>   sD    *  Dg   *
