U
    “ZÞhy-  ã                   @   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-   ÚbucketsÚbucket_countÚdescriptionÚ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ÚinfoÚ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   r:   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)rE   )Ú	host_portr&   r'   Úresult©r   r   r   Úcheck_host_porte   s
    z>DeveloperDiscovery.scan_network_range.<locals>.check_host_portrF   é2   r   é   ©Úmax_workersc                    s   i | ]}ˆ  ˆ |¡|“qS r   )Úsubmit)Ú.0Úhp)rN   Úexecutorr   r   Ú
<dictcomp>u   s   ÿ
 z9DeveloperDiscovery.scan_network_range.<locals>.<dictcomp>zDiscovered ActivityWatch: r0   z at r&   r(   r'   )r   Úranger   r   r   rL   r   rC   )r   rI   rJ   Ú
discoveredZhost_port_combinationsÚir&   r'   Zfuture_to_host_portÚfuturerL   r   )rN   rV   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   )r]   r^   r"   r*   r/   zLocal Machine)r   rE   r   r   r   )r   rY   Zlocalhost_addressesr&   r'   rL   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|jdd|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,
                    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
                ORDER BY last_seen DESC
            rP   )ÚdaysÚ
last_monthr,   r
   zFrom database records (z activities)NÚdatabase)r/   r0   r&   r'   r"   r-   r3   Ú	last_seenÚactivity_countÚsourcer5   z!Error discovering from database: )r   Ú
sqlalchemyr`   r   r>   r   Úexecuter   Údeveloper_idre   rd   r?   r   r   Úerror)r   r`   Úqueryrb   rL   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'   rf   rc   re   r   rd   z$Total unique developers discovered: )r   rC   r_   Úextendr=   rm   r%   r\   r7   r;   Úvaluesr   )r   rn   ro   rp   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 )Nrf   rc   r&   r,   Zdatabase_onlyr5   r'   r.   ZofflineÚlast_checked)r7   rE   r   r>   r?   )rt   rL   rM   r   r   Úcheck_statusý   s    zADeveloperDiscovery.refresh_developer_status.<locals>.check_statusé
   rQ   )r   r;   Úmap)r   rx   rz   rV   Zupdated_developersr   rM   r   Úrefresh_developer_statusû   s    
z+DeveloperDiscovery.refresh_developer_status)N)N)FTT)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   r   Ústrr%   Úintr   r   rE   r:   r\   r_   rm   Úboolrw   r}   r   r   r   r   r	      s   
"
$/  ÿ ÿ1r	   )r6   r   Ú	threadingr   r   Útypingr   r   r   r9   ÚloggingÚconcurrent.futuresr   r   ZpsutilÚ	getLoggerr~   r   r	   r   r   r   r   Ú<module>   s   
