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   ú./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ÚgethostnameZgethostbynameÚ
startswithÚjoinÚsplitÚappendÚ	ExceptionÚloggerZ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   Zstatus_codeÚjsonÚextract_hostname_from_bucketsÚlistÚkeysÚlenr   ÚnowÚ	isoformatr   r   Údebug)
r   r$   r%   ZurlZresponseÚinfoZbuckets_responser/   r    r"   r   r   r   Úcheck_activitywatch_instance2   s0    



ôz/DeveloperDiscovery.check_activitywatch_instance)r/   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)r7   r   r8   )r   r/   Zbucket_nameÚpartsr   r   r   r5   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)r=   )Z	host_portr$   r%   Úresult©r   r   r   Úcheck_host_porte   s
    z>DeveloperDiscovery.scan_network_range.<locals>.check_host_portr>   é2   r   é   ©Úmax_workersc                    s   i | ]}ˆ  ˆ |¡|“qS r   )Zsubmit)Ú.0Zhp)rD   Úexecutorr   r   Ú
<dictcomp>u   s   ÿ
 z9DeveloperDiscovery.scan_network_range.<locals>.<dictcomp>zDiscovered ActivityWatch: r.   z at r$   r&   r%   )r   Úranger   r   r   rB   r   r<   )r   r@   rA   Ú
discoveredZhost_port_combinationsÚir$   r%   Zfuture_to_host_portZfuturerB   r   )rD   rJ   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Ú	localhostr.   )rP   rQ   r    r(   r-   zLocal Machine)r   r=   r   r   r   )r   rM   Zlocalhost_addressesr$   r%   rB   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
            rF   )ZdaysÚ
last_monthr*   r
   zFrom database records (z activities)NÚdatabase)r-   r.   r$   r%   r    r+   r0   Ú	last_seenÚactivity_countÚsourcer1   z!Error discovering from database: )r   Z
sqlalchemyrS   r   r9   r   Zexecuter   Zdeveloper_idrW   rV   r:   r   r   Úerror)r   rS   ZqueryrT   rB   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%   rX   rU   rW   r   rV   z$Total unique developers discovered: )r   r<   rR   Úextendr8   r[   r#   rO   r3   r6   Úvaluesr   )r   r\   r]   r^   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 )NrX   rU   r$   r*   Zdatabase_onlyr1   r%   r,   ZofflineZlast_checked)r3   r=   r   r9   r:   )rb   rB   rC   r   r   Úcheck_statusý   s    zADeveloperDiscovery.refresh_developer_status.<locals>.check_statusé
   rG   )r   r6   Úmap)r   rf   rg   rJ   Zupdated_developersr   rC   r   Úrefresh_developer_statusû   s    
z+DeveloperDiscovery.refresh_developer_status)N)N)FTT)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   r   Ústrr#   Úintr   r   r=   r5   rO   rR   r[   Úboolre   rj   r   r   r   r   r	      s   
"
$/  ÿ ÿ1r	   )r2   r   Ú	threadingr   r   Útypingr   r   r   r4   ZloggingÚconcurrent.futuresr   r   ZpsutilZ	getLoggerrk   r   r	   r   r   r   r   Ú<module>   s   
