o
    ͿSi&                     @   s   d dl Z d dlZd dlmZmZ d dlmZmZmZ d dlZd dl	m
Z
 d dlmZ d dlmZ ddlmZ e  e Ze je jd	 e eZG d
d dZdee dee fddZdee fddZdee fddZdd Zedkr{e  dS dS )    N)datetime	timedelta)TupleListDict)BeautifulSoup)OpenAI)load_dotenv   )BearishDivergenceStrategy)levelc                   @   s   e Zd Zdd Zdeeeeef  eeeef  f fddZdedee fddZ	deee ee f fd	d
Z
dedee fddZededefddZdS )TopMoversFetcherc                 C   s
   || _ d S )N)db)selfr    r   ;/var/www/html/Trade-python/app/v1/services/zerolive/list.py__init__   s   
zTopMoversFetcher.__init__returnc              
      s  zh| j d ddi}|r#t |d  tddk r#|d |d fW S | d	}| d
}tddd| j d}|||   fdd|D } fdd|D }| j d jddid||t didd ||fW S  t	y } zt
jd| dd g g fW  Y d}~S d}~ww )z6Fetch top gainers and losers (with caching & mapping).	et_moverstype
top_moverslast_updated   )minutesgainersloserstop-gainersz
top-losersdummy)api_key
api_secretaccess_tokenr   c                    "   g | ]}|  |t d qS )raw_name
nse_symbol	mapped_atgetr   now.0raw
symbol_mapr   r   
<listcomp>-       z5TopMoversFetcher.fetch_top_movers.<locals>.<listcomp>c                    r!   r"   r&   r)   r,   r   r   r.   1   r/   z$set)r   r   r   T)upsertzError in fetch_top_movers: exc_infoN)r   find_oner   r(   r   _scrape_listr   map_et_to_zerodha
update_one	Exceptionloggererror)r   cachedraw_gainers
raw_losersstrategyr   r   er   r,   r   fetch_top_movers   s<   





z!TopMoversFetcher.fetch_top_moversslugc              
   C   s   d| }ddi}zLt j||dd}|  t|jd}|d}g }|D ]$}|d}	|	rI|	d	rIz|| |	d	  W q% t	yH   Y q%w q%|rO|W S t
d
 W n t	ym }
 zt
d|
 W Y d}
~
nd}
~
ww | |S )z
        Fetch the given ET page (gainers or losers) and extract the raw names.
        slug should be "top-gainers" or "top-losers".
        8https://economictimes.indiatimes.com/stocks/marketstats/z
User-AgentzuMozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36
   )headerstimeouthtml.parser#table tbody tra.MarketTable_ellipses__M8PxMhrefzaTopMoversFetcher._scrape_list: HTTP scrape returned no symbols; falling back to headless browser.zXTopMoversFetcher._scrape_list: HTTP fetch failed (%s); falling back to headless browser.N)requestsr'   raise_for_statusr   textselect
select_oneappend_extract_symbol_from_hrefr7   r8   warning_scrape_list_via_selenium)r   r@   urlrC   respsouprowssymbolsrowar>   r   r   r   r4   B   s@   
	


zTopMoversFetcher._scrape_listc                 C   s   |  d}g }||fS )z
        Legacy helper to load the ET gainers page and grab gainers.

        Currently this is implemented via the same HTTP + BeautifulSoup
        approach as `_scrape_list`, and returns (raw_gainers, losers=[]).
        r   )r4   )r   r;   r   r   r   r   _scrape_moversr   s   
zTopMoversFetcher._scrape_moversc                 C   s  zddl m} ddlm} W n ty   td g  Y S w d| }| }z|d W n ty<   |d Y nw |d |d	 |j	|d
}z~zQ|
| td t|jd}|d}g }	|D ]$}
|
d}|r|
drz|	| |d  W qh ty   Y qhw qh|	W W z|  W S  ty   Y S w  ty } z tjd|dd g W  Y d}~W z|  W S  ty   Y S w d}~ww z|  W w  ty   Y w w )aF  Best-effort fallback using a headless Chrome browser.

        This is only used if the primary HTTP+BeautifulSoup path fails
        or returns no symbols. If Selenium or a compatible driver is not
        installed on the server, this will log an error and return an
        empty list rather than crashing the app.
        r   )	webdriver)OptionszqTopMoversFetcher._scrape_list_via_selenium: Selenium not installed; cannot perform browser-based fallback scrape.rA   z--headless=newz
--headlessz--disable-gpuz--no-sandbox)options   rE   rF   rG   rH   zFTopMoversFetcher._scrape_list_via_selenium: headless scrape failed: %sTr1   N)seleniumrZ   !selenium.webdriver.chrome.optionsr[   ImportErrorr8   r9   add_argumentr7   Chromer'   timesleepr   page_sourcerL   rM   rN   rO   quit)r   r@   rZ   r[   rR   optsdriverrT   rU   rV   rW   rX   r>   r   r   r   rQ   }   sv   






	z*TopMoversFetcher._scrape_list_via_seleniumrH   c                 C   s(   |  dd }| dd  ddS )uv   
        '/glenmark-pharmaceuticals-ltd/stocks/companyid-4255.cms' →
        'GLENMARK_PHARMACEUTICALS_LTD'
        z/stocksr   /-_)splitupperreplace)rH   partsr   r   r   rO      s   z*TopMoversFetcher._extract_symbol_from_hrefN)__name__
__module____qualname__r   r   r   r   strr?   r4   rY   rQ   staticmethodrO   r   r   r   r   r      s    .+0;r   	companiesr   c              
   C   s   d|  }z*t jjjdd|dgdd}|jd jj }|dr't	|W S t
d	| g W S  tyI } zt
d
| g W  Y d}~S d}~ww )z
    Use OpenAI to convert company names to NSE trading symbols
    (Zerodha KiteConnect style), returning a raw list of uppercase strings.
    zYou are a financial assistant. Convert the following Indian company names to their correct NSE trading symbols (used by Zerodha KiteConnect). Return ONLY a valid Python list of UPPERCASE strings.

Company Names: zgpt-4o-miniuser)rolecontentg?)modelmessagestemperaturer   [z!Unexpected format from OpenAI: %szOpenAI API call failed: %sN)clientchatcompletionscreatechoicesmessagery   strip
startswithevalr8   debugr7   r9   )rv   promptresponsery   r>   r   r   r   map_company_to_symbol   s(   


r   c                 C   sp   t d t| }| \}}g }|D ]}|dp |dp d  }|r,|| qt dt|| |S )uC   Helper to return just the valid NSE symbols from today’s gainers.u   Fetching top gainers…r$   r#    z5Found %d gainer symbols (including raw fallbacks): %s	r8   infor   r?   r'   r   rn   rN   len)r   fetcherr   rl   rV   gsymr   r   r   get_top_gainer_symbols   s   
 
r   c                 C   sp   t d t| }| \}}g }|D ]}|dp |dp d  }|r,|| qt dt|| |S )Nu   Fetching top losers…r$   r#   r   z4Found %d loser symbols (including raw fallbacks): %sr   )r   r   rl   r   rV   lr   r   r   r   get_top_loser_symbols   s   
 
r   c                  C   st   ddl m}  |  j}td t|}t }| \}}tdt | ddt| dt| d td	| d
S )u   
    Standalone debug entrypoint — requires a `db` argument
    or you can stub one in if you just want to test scraping.
    r   )MongoClientz=== ET Top Movers Fetcher ===zFetched in z.2fu   s → z
 gainers, z loserszGainers:N)pymongor   testprintr   rc   r?   r   )r   r   r   startr   r   r   r   r   main   s   .r   __main__)loggingrc   r   r   typingr   r   r   rI   bs4r   openair   dotenvr	   r=   r   r~   basicConfigINFO	getLoggerrq   r8   r   rt   r   r   r   r   r   r   r   r   <module>   s,    
 0
