U
    1i                     @  s   d Z ddlmZ ddlZddlZddlmZ ddlmZ ddl	m
Z
 edZed Zd	d
ddddZdddddZddddddZdd	d
ddddddZdS )zEKeyword position tracking with state persistence and alert detection.    )annotationsN)Path)PROJECT_ROOT)KeywordPositionseo_optimizerzkeyword_state.jsonz
list[dict]z	list[str]zlist[KeywordPosition])
query_rowstracked_keywordsreturnc                 C  s   i }| D ]$}| dg }|r|||d  < qg }|D ]j}| | }|r|t|t| ddd| dd| ddd q6|t|dd	 td
| q6|S )a  Extract positions for tracked keywords from query API rows.

    Args:
        query_rows: Raw API rows with 'query' dimension.
        tracked_keywords: List of keywords to track.

    Returns:
        List of KeywordPosition (without previous/change data).
    keysr   position   clicksimpressions)keywordcurrent_positionr   r           )r   r   z%Keyword '%s' not found in search data)getlowerappendr   roundloggerdebug)r   r   Zquery_lookuprowr
   	positionsr    r   8/opt/web_system/seo_auto/src/analysis/keyword_tracker.pyextract_keyword_positions   s2    

	r   strzdict[str, float])site_urlr	   c              
   C  s   t  si S z2tt ddd}t|}W 5 Q R X || i W S  tjtfk
rz } zt	d| i  W Y S d}~X Y nX dS )zqLoad previous keyword positions from state file.

    Returns:
        Dict of keyword -> previous position.
    rutf-8encodingz Failed to load keyword state: %sN)

STATE_FILEexistsopenjsonloadr   JSONDecodeErrorOSErrorr   warning)r   fstateer   r   r   load_previous_state=   s    r.   None)r   r   r	   c              	   C  s   i }t  rTz(tt ddd}t|}W 5 Q R X W n tjtfk
rR   i }Y nX dd |D || < tt ddd}tj||ddd	 W 5 Q R X t	d
|  dS )z-Save current keyword positions to state file.r   r    r!   c                 S  s    i | ]}|j d kr|j|j qS )r   )r   r   ).0kpr   r   r   
<dictcomp>Y   s   
 z&save_current_state.<locals>.<dictcomp>wFr   )ensure_asciiindentzSaved keyword state for %sN)
r#   r$   r%   r&   r'   r(   r)   dumpr   r   )r   r   r,   r+   r   r   r   save_current_stateO   s    

r7      int)r   r   r   alert_thresholdr	   c              	   C  s   t | |}t|}|D ]}||jd}||_|jdkrd|dkrdt|j| d|_t|j|k|_	n|dkr|jdkrd|_d|_	|j	rt
d||j||j|j qt|| |S )ax  Full keyword tracking: extract, compare with previous, detect alerts.

    Args:
        query_rows: Raw API rows with 'query' dimension.
        tracked_keywords: Keywords to track.
        site_url: Site URL for state file key.
        alert_threshold: Position change threshold for alerts.

    Returns:
        List of KeywordPosition with previous/change/alert data.
    r   r   r   Tu?   Keyword alert [%s]: '%s' position %.1f → %.1f (change: %+.1f))r   r.   r   r   previous_positionr   r   changeabsis_alertr   r*   r7   )r   r   r   r:   r   previousr1   Zprev_posr   r   r   track_keywordse   s,    
    
r@   )r8   )__doc__
__future__r   r&   loggingpathlibr   src.config.settingsr   src.models.data_modelsr   	getLoggerr   r#   r   r.   r7   r@   r   r   r   r   <module>   s   
, 