U
    ¾%Ðh™-  ã                   @   s€   d dl Z 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
Z
d dlmZmZ d dlZe
 e¡ZG dd„ dƒZdS )é    N)ÚdatetimeÚ	timedelta)ÚListÚDictÚOptional)ÚThreadPoolExecutorÚas_completedc                   @   sÈ   e Zd ZdZddd„Zee dœdd„Zeee	e
 dœd	d
„Ze
e	e dœdd„Zdeee ee
 dœdd„Zee
 dœdd„Zee
 dœdd„Zdeeeee
 dœdd„Zee
 ee
 dœdd„ZdS ) ÚDeveloperDiscoveryzKAutomatically discover ActivityWatch instances on network and local machineNc                 C   s&   || _ g | _dddddg| _d| _d S )Néà  iá  iâ  iã  iD  é   )Ú
db_sessionÚdiscovered_developersÚdefault_portsÚtimeout)Úselfr   © r   ú6/var/www/html/timesheet/backend/developer_discovery.pyÚ__init__   s    zDeveloperDiscovery.__init__)Úreturnc              
   C   s¾   g }znt  ¡ }t  |¡}|rH| d¡sHd | d¡dd… ¡}| |¡ ddddg}|D ]}||krX| |¡ qXW n> tk
r° } z t 	d	|› ¡ ddddg}W 5 d}~X Y nX |dd
… S )z5Get local network ranges to scan (simplified version)z127.Ú.Néÿÿÿÿz	192.168.1z	192.168.0z10.0.0z172.16.0z$Could not determine local networks: r   )
ÚsocketÚgethostnameÚgethostbynameÚ
startswithÚjoinÚsplitÚappendÚ	ExceptionÚloggerÚwarning)r   ÚnetworksÚhostnameZlocal_ipZbase_ipZcommon_networksÚnetworkÚer   r   r   Úget_local_networks   s    

z%DeveloperDiscovery.get_local_networks)ÚhostÚportr   c           
      C   s,  zàd|› d|› d}t j|| jd}|jdkrÞ| ¡ }t jd|› d|› d| jd}|jdkrf| ¡ ni }|  |¡p~| d|¡}|› d|› |||| d	d
¡|| d|› d|› ¡t| ¡ ƒt|ƒd|› t	 
¡  ¡ ddœW S W nF tk
r& }	 z&t d|› d|› d|	› ¡ W Y ¢dS d}	~	X Y nX dS )z7Check if ActivityWatch is running on specific host:portzhttp://ú:z/api/0/info)r   éÈ   z/api/0/bucketsr"   Ú_ÚversionÚunknownÚ	device_idzActivityWatch on Úonline)ÚidÚnamer&   r'   r+   r"   r-   ÚbucketsZbucket_countÚdescriptionZdiscovered_atÚstatuszNo ActivityWatch at z: N)ÚrequestsÚgetr   Ústatus_codeÚjsonÚextract_hostname_from_bucketsÚlistÚkeysÚlenr   ÚnowÚ	isoformatr   r   Údebug)
r   r&   r'   ÚurlÚresponseÚinfoZbuckets_responser1   r"   r$   r   r   r   Úcheck_activitywatch_instance2   s0    



ôz/DeveloperDiscovery.check_activitywatch_instance)r1   r   c                 C   s<   |  ¡ D ].}d|kr| d¡}t|ƒdkr|d   S qdS )z"Extract hostname from bucket namesr*   é   r   N)r:   r   r;   )r   r1   Úbucket_nameÚpartsr   r   r   r8   T   s    
z0DeveloperDiscovery.extract_hostname_from_buckets)Únetwork_baseÚportsr   c                    sÒ   |dkrˆj }g }‡fdd„‰ g }tddƒD ]*}|› d|› }|D ]}| ||f¡ qBq,tddf‰‡ ‡fd	d
„|D ƒ}t|ƒD ]B}	|	 ¡ }
|
r€| |
¡ t d|
d › d|
d › d|
d › ¡ q€W 5 Q R X |S )z0Scan a network range for ActivityWatch instancesNc                    s    | \}}ˆ   ||¡}|r|S d S )N)rB   )Ú	host_portr&   r'   Úresult©r   r   r   Úcheck_host_porte   s
    z>DeveloperDiscovery.scan_network_range.<locals>.check_host_portrC   é2   r   é   ©Úmax_workersc                    s   i | ]}ˆ  ˆ |¡|“qS r   )Úsubmit)Ú.0Úhp)rK   Úexecutorr   r   Ú
<dictcomp>u   s   ÿ
 z9DeveloperDiscovery.scan_network_range.<locals>.<dictcomp>zDiscovered ActivityWatch: r0   z at r&   r(   r'   )r   Úranger   r   r   rI   r   rA   )r   rF   rG   Ú
discoveredZhost_port_combinationsÚir&   r'   Zfuture_to_host_portÚfuturerI   r   )rK   rS   r   r   Úscan_network_range^   s&    þ
4z%DeveloperDiscovery.scan_network_rangec                 C   s    g }ddg}|D ]Š}| j D ]~}|  ||¡}|r|d dkrŠz2t ¡ |d< t ¡ |d< t ¡ › d|› |d< W n   d|d< d|d< Y nX | |¡  qqq|S )	z-Discover ActivityWatch instances on localhostú	127.0.0.1Ú	localhostr0   )rZ   r[   r"   r*   r/   zLocal Machine)r   rB   r   r   r   )r   rV   Zlocalhost_addressesr&   r'   rI   r   r   r   Údiscover_local_instances‚   s"    

z+DeveloperDiscovery.discover_local_instancesc                 C   sî   | j s
g S z¤ddlm} |dƒ}t ¡ tdd }| j  |d|i¡}g }|D ]\}| |j|j	pb|jdd|j
pv|j	pv|j|jd	|j› d
|jr–|j ¡ nd|jdddœ¡ qL|W S  tk
rè } zt d|› ¡ g  W Y ¢S d}~X Y nX dS )z2Discover developers from database activity recordsr   )Útexta÷  
                SELECT DISTINCT 
                    developer_id,
                    developer_name,
                    developer_hostname,
                    MAX(created_at) as last_seen,
                    COUNT(*) as activity_count
                FROM activity_records 
                WHERE developer_id IS NOT NULL 
                    AND created_at > :last_month
                GROUP BY developer_id, developer_name, developer_hostname
                ORDER BY last_seen DESC
            rM   )ÚdaysÚ
last_monthr,   r
   zFrom database records (z activities)NÚdatabase)r/   r0   r&   r'   r"   r-   r2   Ú	last_seenÚactivity_countÚsourcer3   z!Error discovering from database: )r   Ú
sqlalchemyr]   r   r<   r   Úexecuter   Údeveloper_idÚdeveloper_nameZdeveloper_hostnamerb   ra   r=   r   r   Úerror)r   r]   Úqueryr_   rI   Zdb_developersÚrowr$   r   r   r   Údiscover_from_database›   s4    
õ
z)DeveloperDiscovery.discover_from_databaseFT)Úscan_networkÚ
scan_localÚscan_databaser   c                 C   s†  g }|r:t  d¡ |  ¡ }| |¡ t  dt|ƒ› d¡ |rpt  d¡ |  ¡ }| |¡ t  dt|ƒ› d¡ |rÚt  d¡ |  ¡ }|dd… D ]F}t  d	|› d
¡ |  |¡}	| |	¡ t  dt|	ƒ› d|› d¡ q’i }
|D ]x}| d¡p|d › d|d › }||
kr||
|< qâ|
| }| d¡dkrâ| d¡sâ| dd¡|d< | d¡|d< qât	|
 
¡ ƒ| _t  dt| jƒ› ¡ | jS )z2Discover all available developers from all sourcesz,Discovering local ActivityWatch instances...zFound z local instancesz'Discovering developers from database...z developers in databasez/Scanning network for ActivityWatch instances...Né   zScanning network z.x...z instances on z.xr-   r&   r(   r'   rc   r`   rb   r   ra   z$Total unique developers discovered: )r   rA   r\   Úextendr;   rk   r%   rY   r5   r9   Úvaluesr   )r   rl   rm   rn   Zall_developersZ
local_devsZdb_devsr!   r#   Znetwork_devsZunique_developersÚdevÚkeyÚexistingr   r   r   Údiscover_all_developersË   s>    






"

z*DeveloperDiscovery.discover_all_developers)Ú
developersr   c              	      s6   ‡ fdd„}t dd}t| ||¡ƒ}W 5 Q R X |S )z(Refresh online status for all developersc                    s`   |   d¡dkr(|   d¡dkr(d| d< | S ˆ  | d | d ¡}|rDdnd	| d< t ¡  ¡ | d
< | S )Nrc   r`   r&   r,   Zdatabase_onlyr3   r'   r.   ZofflineZlast_checked)r5   rB   r   r<   r=   )rr   rI   rJ   r   r   Úcheck_statusþ   s    zADeveloperDiscovery.refresh_developer_status.<locals>.check_statusé
   rN   )r   r9   Úmap)r   rv   rw   rS   Zupdated_developersr   rJ   r   Úrefresh_developer_statusü   s    
z+DeveloperDiscovery.refresh_developer_status)N)N)FTT)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   r   Ústrr%   Úintr   r   rB   r8   rY   r\   rk   Úboolru   rz   r   r   r   r   r	      s   
"
$0  ÿ ÿ1r	   )r4   r   Ú	threadingr   r   Útypingr   r   r   r7   ÚloggingÚconcurrent.futuresr   r   ZpsutilÚ	getLoggerr{   r   r	   r   r   r   r   Ú<module>   s   
