U
    :iI                     @   s  d dl mZmZmZm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 d dlmZ d dlmZ d dlZd dlZe Zeed	d
dZeedddZeedddZeedddZeeeedddZedededeefeee ee edddZedededeefeee ee edddZ edededeefee ee ed d!d"Z!eed#d$d%Z"ed&eefeed'd(d)Z#dS )*    )	APIRouterDependsHTTPExceptionQuery)Session)text)datetimetimezone)OptionalListDict)get_db)ActivityCategorizerN)secreturnc                 C   sZ   t | pd} t| d\}}t|d\}}|r<| d| dS |rP| d| dS | dS )Nr     <   zh mzm s)intdivmod)r   hrr   r    r    ./activity_categorization_api.py_format_seconds   s    r   )r   r   c                 C   s   | pd   d  S )N u   –—-)strip)r   r   r   r   _clean_token   s    r   )pathr   c                    s   | sdS |  dd} dd | dD }|s0dS d|d krH|dd }|sPdS d	d
dddddh  fdd|D }|s||}|d S )zT
    Guess project from file path by taking the last meaningful directory name.
    r   \/c                 S   s   g | ]}|r|qS r   r   .0pr   r   r   
<listcomp>$   s      z0infer_project_from_file_path.<locals>.<listcomp>.NsrcZappZappslibZdistZbuildZpublicc                    s   g | ]}|   kr|qS r   lowerr"   Zgenericr   r   r%   -   s      )replacesplit)r   partscandr   r,   r   infer_project_from_file_path   s    r1   )titler   c              	      s   | sdS |  dd dd}dd |dD }ddd	d
dddddh	  fdd|D }|s`dS |d }| dkrxdS td|rdS |S )u   
    Extract project/site/workspace name from window title patterns.
    Examples:
      'crud.py - mahindra-manulife-retail - Visual Studio Code'
      'Plugins « Naturals — WordPress – Google Chrome'
    r   u   —-u   –c                 S   s   g | ]}t |rt |qS r   )r   r"   r   r   r   r%   <   s      z3infer_project_from_window_title.<locals>.<listcomp>zgoogle chromeZchromezmozilla firefoxZfirefoxzmicrosoft edgeZedgezvisual studio codecodeZvscodec                    s   g | ]}|   kr|qS r   r*   r"   Zdropr   r   r%   A   s      r'   >   untitlednew tabz\.[a-zA-Z0-9]{1,6}$)r-   r.   r+   research)r2   tr/   r0   r   r5   r   infer_project_from_window_title2   s.           r;   )existing	file_pathwindow_titler   c                 C   sL   | pd  } | r |  dkr | S t|p(d}|r4|S t|p<d}|rH|S dS )z
    Single source of truth for project grouping.
    Priority:
      1) existing (if non-empty and not 'general')
      2) infer from file_path
      3) infer from window_title
      4) 'general'
    r   Zgeneral)r   r+   r1   r;   )r<   r=   r>   fpZwtr   r   r   choose_project_nameK   s    	r@   z'/api/activity-categories/{developer_id})developer_id
start_dateend_datedbc           !         s  zt  }|r"t|dd}nttjjddddd}|rTt|dd}nttj}td}||| ||d	 }t
d|d  d|d	  d
|d  d|d  d|d  
 td}	||	| ||d }
i i i i d}ddddddddddddd}d}|
D ]}|d }|d |d	 |d p<d|d pHd|d pTd|d rj|d  nd|d |d t||d pd|d pd|d |d d}||d |d }|d }||krd}|d  pd }||| kr@||d dd|d! |d! ||d" |d# |d$ |d% |d& |d' g d(|| |< || | }|d)  |d) 7  < |d*  d	7  < |d! |d+< |d& |d&< |d, |d- |d) |d! d. || d/  d	7  < || d)  |d) 7  < ||d) 7 }qi }| D ]\}}g }| D ]\}}|d) }|d0krFt|d0 d|d1< |d1  d2|d3< n`|d4krt|d4 d	}t|d0 d|d1< | d5|d3< n&t|d0 d|d1< t|d	 d6|d3< |d,d || q|jd7d8 d9d: |||< q|D ]}|| d) }|dkr|| d; nd|| d<< |d0 }t|d|| d1< |d0krRt|d	 d2|| d3< n@|d4krzt|d4 d	 d5|| d3< nt|d	 d6|| d3< qd=d> | D }| | | d?|t|d0 d||t|d@W S  tk
r }  ztdAdBt|  dCW 5 d} ~ X Y nX dS )Da   
    Get activities categorized into Productive, Browser, Server, Non-work.
    - Groups raw events by (category + window_title) for detail.
    - Ensures every activity has a reasonable project_name (inferred if needed).
    Durations are in SECONDS.
    Z+00:00r   ZhourZminutesecondZmicroseconda  
            SELECT 
                COUNT(*) as total_records,
                SUM(duration) as total_duration,
                AVG(duration) as avg_duration,
                MIN(duration) as min_duration,
                MAX(duration) as max_duration
            FROM activity_records
            WHERE developer_id = :dev_id
              AND timestamp >= :start_date
              AND timestamp <= :end_date
        dev_idrB   rC   zDebug - Total records: z, Total duration:    z, Avg:    z, Min:    z, Max:    a  
            SELECT 
                id,
                developer_id,
                application_name,
                window_title,
                duration,
                timestamp,
                url,
                file_path,
                project_name,
                project_type,
                category
            FROM activity_records
            WHERE developer_id = :dev_id
              AND timestamp >= :start_date
              AND timestamp <= :end_date
            ORDER BY timestamp DESC
        )
productivebrowserservernon-work)countduration   r      N      	   
   )idrA   application_namer>   rT   	timestampurlr=   project_nameproject_typeZexisting_categoryr>   r\   categoryrP   ZUntitledr]   subcategory
confidencer^   r=   r_   r`   )r>   r\   rT   activity_countZfirst_timestamplast_timestampra   rb   rc   r^   r=   r_   r`   individual_activitiesrT   rd   re   rf   r[   )r[   rT   r]   rS   r   Zduration_hoursr   Zduration_displayr   r   r   c                 S   s   | d S )NrT   r   )xr   r   r   <lambda>      z,get_categorized_activities.<locals>.<lambda>T)keyreversed   
percentagec                 S   s   i | ]\}}||d d qS )NrZ   r   )r#   catitemsr   r   r   
<dictcomp>  s      z.get_categorized_activities.<locals>.<dictcomp>startend)rA   
date_rangeZ
statisticstotal_duration_hoursactivities_by_categorytop_activities_by_categoryZproductivity_score  zError categorizing activities: Zstatus_codeZdetail)r   r   fromisoformatr-   nowr	   utcr   executefetchoneprintfetchall	isoformatr@   get_detailed_categoryr   appendro   roundpopsortcalculate_productivity_score	Exceptionr   str)!rA   rB   rC   rD   categorizerrr   rs   Zdebug_queryZdebug_resultZqueryresultZactivities_groupedcategory_statsZtotal_durationrowZraw_projectZactivityZcat_infora   Zwindow_title_keygrprv   rn   Zgrouped_dictro   _gdZminsZdurZhoursrw   er   r   r   get_categorized_activitiesa   s    4
 


"


r   z./api/update-activity-categories/{developer_id}c              
      s6  zt  }|rt|ddnttjjddddd}|rNt|ddn
ttj}|td| ||d	 }d}|D ]R}	|
|	d pd|	d	 pd}
|td
|
d |
d |
d |	d d |d7 }q~|  d|d| ddW S  tk
r0 } z"|  tddt| dW 5 d}~X Y nX dS )zJUpdate the category/subcategory/confidence columns for the selected range.rE   rF   r   rG   z
            SELECT id, window_title, application_name
            FROM activity_records
            WHERE developer_id = :dev_id
              AND timestamp >= :start_date
              AND timestamp <= :end_date
        rI   rK   r   rL   z
                UPDATE activity_records
                   SET category = :category,
                       subcategory = :subcategory,
                       category_confidence = :confidence
                 WHERE id = :id
            ra   rb   rc   )ra   rb   rc   r[   TzSuccessfully updated z activities)successZupdated_countmessagerx   zError updating categories: ry   N)r   r   rz   r-   r{   r	   r|   r}   r   r   r   Zcommitr   Zrollbackr   r   )rA   rB   rC   rD   r   rr   rs   r   updatedr   rn   r   r   r   r   update_activity_categories+  s0    "


 
r   z/api/category-summary)rB   rC   rD   c              
      sT  z| rt | ddnt tjjddddd}|rJt |ddn
t tj}|td||d }g d }}|D ]D}|d pd}	|	|d |d |	t
|	d	 d|d
 d ||	7 }q~|D ]&}
|rt
|
d | d dnd|
d< q| | d|t
|d	 ddW S  tk
rN } ztddt| dW 5 d}~X Y nX dS )z;Aggregate across all developers by category for the period.rE   rF   r   rG   a  
            SELECT 
                CASE 
                    WHEN category IN ('productive', 'browser', 'server', 'non-work') THEN category
                    ELSE 'browser'
                END as category,
                COUNT(*) as activity_count,
                SUM(duration) as total_duration,
                COUNT(DISTINCT developer_id) as developer_count
            FROM activity_records
            WHERE timestamp >= :start_date
              AND timestamp <= :end_date
            GROUP BY 
                CASE 
                    WHEN category IN ('productive', 'browser', 'server', 'non-work') THEN category
                    ELSE 'browser'
                END
            ORDER BY total_duration DESC
        )rB   rC   rL   rK   r   rM   )ra   rd   total_duration_secondsru   Zdeveloper_countr   rl   rm   rq   )rt   summaryru   rx   z Error getting category summary: ry   N)r   rz   r-   r{   r	   r|   r}   r   r   r   r   r   r   r   r   )rB   rC   rD   rr   rs   r   r   totalr   r   itemr   r   r   r   get_category_summaryU  s6    "


$r   )r   r   c           	      C   s   |  di  dd}|  di  dd}|  di  dd}|  di  dd}|| |d  }|| | | }|| }|dkr|| d nd}tt|dd	S )
NrO   rT   r   rQ   rP   rR   g      ?rl   rL   )getr   min)	r   Zproductive_timeZserver_timeZbrowser_timeZnon_work_timeZproductive_totalZ
total_timeZ	work_timeZscorer   r   r   r     s    r   z#/api/debug-durations/{developer_id})rA   rD   c           	         sp  z.| tdd| i }g }|D ]}|d p2d}||d |d t|d|dkrht|d d dnt|d dt|d	 pdd|d
 r|d
  nd|d r|d  ndd q$| tdd| i }|d pdd }|d |d |d pdt|dt|d	 pdd|d
 |d dd|ddW S  tk
rj } ztddt	| dW 5 d}~X Y nX dS )z.Quick look at durations & top windows (today).a  
            SELECT 
                window_title,
                COUNT(*) as count,
                SUM(duration) as total_duration,
                AVG(duration) as avg_duration,
                MIN(timestamp) as first_seen,
                MAX(timestamp) as last_seen
            FROM activity_records
            WHERE developer_id = :dev_id
              AND timestamp >= CURRENT_DATE
            GROUP BY window_title
            ORDER BY total_duration DESC
            LIMIT 20
        rJ   rL   r   rK   r   r   r   rM   rN   NrV   )r>   rd   r   Ztotal_duration_displayZavg_duration_secondsZ
first_seenZ	last_seena  
            SELECT 
                COUNT(*) as total_records,
                COUNT(DISTINCT window_title) as unique_windows,
                SUM(duration) as total_duration_seconds,
                AVG(duration) as avg_duration_seconds,
                MIN(duration) as min_duration_seconds,
                MAX(duration) as max_duration_seconds
            FROM activity_records
            WHERE developer_id = :dev_id
              AND timestamp >= CURRENT_DATE
        r   z0ActivityWatch records activities every 6 seconds)Ztotal_recordsZunique_windowsr   ru   Zavg_duration_per_record_secondsZmin_duration_secondsZmax_duration_secondsZrecording_intervalz@Durations are in seconds. ActivityWatch polls every few seconds.)r   Ztop_activities_groupedZnoterx   zError debugging durations: ry   )
r}   r   r   r   r   r   r~   r   r   r   )	rA   rD   r   Z
activitiesr   Ztotal_secondsZstatsZtotal_hoursr   r   r   r   debug_activity_durations  sD    
*




r   )$Zfastapir   r   r   r   Zsqlalchemy.ormr   Z
sqlalchemyr   r   r	   typingr
   r   r   Zdatabaser   Zactivity_categorizerr   osr8   Zrouterfloatr   r   r   r1   r;   r@   r   r   Zpostr   r   r   r   r   r   r   r   <module>   s^   
 J)7