o
    Yš…j&D  ã                   @   s(  d dl 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j	 
d¡ d dlmZmZmZmZ dd„ Zd	d
„ ZdZdZdZdZeeeed�ZdZdddœdddœgZdddœdddœdddœdddœdd dœd!d"dœd#d$dœd%d&dœd'd(dœg	Zd)Zd*d+„ Zd,d-„ Zd.d/„ Zd]d0d1„Zd2d3„ Zd4d5„ Z d6d7„ Z!d8d9„ Z"d:d;„ Z#d^d>d?„Z$d@dA„ Z%dBdCdDdEœZ&dFdG„ Z'd_dHdI„Z(dJdK„ Z)dLdM„ Z*dNdO„ Z+d^dPdQ„Z,d_dRdS„Z-e.dTk�re/dUƒ d dl0Z0d dl1Z1e1 2¡  e0 3¡ Z4e4 5dVdW¡Z6e7e4 5dXdY¡ƒZ8e6dZkrþe-e8ƒZ9ne4 5d[d\¡Z:e,e8e:ƒZ9e/e  ;e9¡ƒ dS dS )`é    N)Údefaultdict)ÚThreadPoolExecutor)ÚInfluxDBClientz/opt/ngon/apps)Úget_pod_miner_countsÚget_miner_modelsÚget_miner_type_specsÚget_worker_names_for_sitec                  C   s   t ƒ } dd„ |  ¡ D ƒS )zBGet miner hashrates from master config instead of hardcoded valuesc                 S   s   i | ]\}}||  d d¡“qS )Úspec_hashrater   )Úget)Ú.0Ú
miner_typeÚspecs© r   ú1/var/www/html/ngon/status/performance_data_cgi.pyÚ
<dictcomp>   s    ÿÿz'get_miner_hashrates.<locals>.<dictcomp>)r   Úitems)Úminer_specsr   r   r   Úget_miner_hashrates   s   þr   c                  C   s\   t ƒ } tƒ }tƒ }d}|  ¡ D ]\}}|dkr)| |d¡}| |d¡}||| 7 }q|d S )z4Calculate max potential hashrate in PH/s from configr   ÚM60é´   éè  )r   r   r   r   r
   )Úpod_miner_countsÚminer_types_mappingÚminer_hashratesÚtotal_hashrateÚpod_nameÚminer_countr   Úhashrate_per_minerr   r   r   Úcalculate_max_ph   s   €r   zXrehPqmCSlCkQnEAaFQEX75JD7J9lAsflaIS6TTYnIh3SetVyzEumX_dRMizxFEsEISG4VUdTd70rUHcJig7V6w==Úngonsolutionsz/https://us-central1-1.gcp.cloud2.influxdata.comÚMARAGON_Hashrate)ÚurlÚtokenÚorgz&/opt/ngon/config/reporting_config.jsonÚmarangonnd001ÚND)ÚnameÚregionÚmarangontx001ÚTXÚmaratxngonalpha001ÚButz)r&   ÚlabelÚmaratxngonhm002ÚFortsonÚmaratxngonjohn001ÚJohnÚmaratxngonosprey001ÚOspreyÚmaratxngonew001ÚEWÚmarandngondanielle001ÚDanÚmarandngonnate001ÚNateÚmaratxngongn001ÚGNÚmaratxngonwill001ÚWillz{base}/subaccount_stats/{name}c                  C   sV   t tƒ�} t | ¡ di ¡}W d  ƒ n1 sw   Y  | d¡p#d| d¡p)i fS )z@Base URL + {subaccount: api_key} from the Report Builder config.ÚfoundryNÚbase_urlzhttps://api.foundryusapool.comÚapi_keys)ÚopenÚREPORTING_CONFIGÚjsonÚloadr
   )Úfr=   r   r   r   Úload_foundry_configV   s   
ÿÿrE   c                 C   s¼   t | ddddd�}| | d ¡}|s|S tj|| d d�}z4tj||ddœd	d
�}| ¡  | ¡ }| | dd¡p:dd | dd¡pCd| dd¡pJdddœ¡ W |S  tjj	y]   Y |S w )uÉ   Live stats for one sub-account. Returns the account dict enriched with
    ph / workers / offline_15m / ok â€” ok=False on failure (or a missing key) so
    one dead sub-account doesn't break the rest.ç        r   F)ÚphÚworkersÚoffline_15mÚokr&   )Úbaser&   zngon-status)z	X-API-KEYz
User-Agenté   )ÚheadersÚtimeoutÚhashrate5minAvgg    €„.AÚactiveWorkersÚoffline15MinWorkerCountT)
Údictr
   ÚSTATS_ENDPOINTÚformatÚrequestsÚraise_for_statusrB   ÚupdateÚ
exceptionsÚRequestException)Úaccountr>   r?   ÚoutÚapi_keyr!   ÚresponseÚstatsr   r   r   Úfetch_pool_data^   s0   ÿÿ
üþþr_   c                 C   s\   t tƒ}|  ¡ D ]#\}}d|v r| ¡ d n|}t|ƒpi  d¡}|r+||  |7  < q|S )z¾Our own measured PH/s keyed by the Foundry sub-account each pod's site
    reports into (site.worker_name1), so region membership is a config edit
    rather than a hardcoded site list here.ú r   Úworker_name1)r   Úfloatr   Úsplitr   r
   )Úcontainers_hashrater[   r   rG   ÚsiterZ   r   r   r   Úours_by_pool_accountw   s   €rf   c                    sÐ   t ƒ \‰‰ tt }tt|ƒd��}t| ‡ ‡fdd„|¡ƒ}W d  ƒ n1 s(w   Y  t| p1i ƒ‰‡fdd„|D ƒ}dd„ |D ƒ}tdd„ |D ƒd	d„ d
d�}|t	dd„ |D ƒƒt	dd„ |D ƒƒ|dœdœS )zpCurrent pool hashrate split into regional accounts + a legacy roll-up.
    Sub-accounts are fetched in parallel.)Úmax_workersc                    s   t | ˆˆ ƒS ©N)r_   )Úa)r?   r>   r   r   Ú<lambda>Š   s    zrun_pool_data.<locals>.<lambda>Nc                    s,   g | ]}d |v rt |ˆ  |d d¡d�‘qS )r'   r&   rF   )Úours_ph)rR   r
   ©r   Úr)Úoursr   r   Ú
<listcomp>�   s   , z!run_pool_data.<locals>.<listcomp>c                 S   s   g | ]}d |v r|‘qS )r,   r   rl   r   r   r   ro   Ž   s    c                 S   s(   g | ]}|d  dks|d dkr|‘qS )rG   r   rH   r   rl   r   r   r   ro   ‘   s   ( c                 S   s   | d S )NrG   r   )rm   r   r   r   rj   ’   s    T)ÚkeyÚreversec                 s   ó   � | ]}|d  V  qdS )rG   Nr   rl   r   r   r   Ú	<genexpr>—   ó   € z run_pool_data.<locals>.<genexpr>c                 s   rr   )rH   Nr   rl   r   r   r   rs   ˜   rt   )rG   rH   Úaccounts)ÚregionsÚlegacy)
rE   ÚREGIONAL_ACCOUNTSÚLEGACY_ACCOUNTSr   ÚlenÚlistÚmaprf   ÚsortedÚsum)rd   ru   ÚexecutorÚresultsrv   rw   Ú
stragglersr   )r?   r>   rn   r   Úrun_pool_data„   s"   
ÿÿýþr‚   c                 C   s   | d S )zConvert hashrate to Petahashesl     �Iú5 r   )Úhashrater   r   r   Úto_ph�   s   r„   c                   C   s   t tƒ  ¡ ƒS )z»Return the set of pod names defined in master_config. Used to filter
    out phantom/legacy InfluxDB measurements (e.g. 'Ell', 'Ospre', 'Butz')
    that no longer correspond to real pods.)Úsetr   Úkeysr   r   r   r   Úget_known_pod_names¡   s   r‡   c                 C   s(   d| v r
|   ¡ d n| }|dv rdS |S )zDMap a pod name to its site, applying the Ellyson/Walker -> EW merge.r`   r   )ÚEllysonÚWalkerr4   )rc   )r   rK   r   r   r   Úsite_from_pod§   s   rŠ   c               
   C   sî   zat jddd�} |  ¡  |  ¡ }i }| di ¡}| ¡ D ]:\}}| di ¡}| ¡ D ]+\}}| di ¡}	|	 ¡ D ]\}
}| di ¡}| dd	¡pId	}|d	krT|d
 ||
< q8q*qt| ¡ ƒ}||fW S  tyv } z	tƒ W  Y d}~S d}~ww )z5Get current hashrate from Status API (real-time data)z http://localhost:5050/api/statusé
   )rN   ÚsitesÚgenerator_groupsÚpodsr^   rƒ   r   r   N)	rU   r
   rV   rB   r   r~   ÚvaluesÚ	ExceptionÚ get_current_hashrate_from_influx)r]   Údatard   rŒ   Ú	site_nameÚ	site_dataÚgroupsÚ
group_nameÚ
group_datarŽ   r   Úpod_datar^   Úhashrate_thr   Úer   r   r   Úget_current_hashrate®   s0   €üþ
€þr›   c            	      C   sd   d} t  ¡ j| td�}i }|D ]}|jD ]}| ¡ }| ¡ }t|ƒ}|||< qqt| 	¡ ƒ}||fS )z,Fallback: Get current hashrate from InfluxDBz‹
    from(bucket: "MARAGON_Hashrate")
      |> range(start: -1h)
      |> filter(fn: (r) => r["_field"] == "hashrate")
      |> last()
    ©r#   )
ÚclientÚ	query_apiÚqueryr#   ÚrecordsÚget_measurementÚ	get_valuer„   r~   r�   )	rŸ   Úresultrd   ÚtableÚrecordÚcontainer_nameÚraw_hashrate_valueÚhashrate_valuer   r   r   r   r‘   Ë   s   

ür‘   é   Ú1dc              	   C   sÎ   |dkr	d}d}nd}d}d| › d|› d|› d|› d�	}t  ¡ j|td	�}tƒ }i }|D ]8}|jD ]2}	|	 ¡ }
|
|vr<q1|	 ¡ }|d
urIt|ƒ}nd}|	 	¡  
¡ }|
|vrYg ||
< ||
  ||dœ¡ q1q,|S )z\Get all containers' hashrates for the specified number of days with configurable aggregationrª   ÚlastÚtrueú=
    from(bucket: "MARAGON_Hashrate")
      |> range(start: -zYd)
      |> filter(fn: (r) => r["_field"] == "hashrate")
      |> aggregateWindow(every: z, fn: z, createEmpty: z#)
      |> yield(name: "last")
    rœ   Nr   ©ÚtimeÚvalue)r�   rž   rŸ   r#   r‡   r    r¡   r¢   r„   Úget_timeÚ	isoformatÚappend)ÚdaysÚaggregationÚfn_typeÚcreate_emptyrŸ   r£   Ú
known_podsrd   r¤   r¥   r¦   r§   r¨   Ú
time_valuer   r   r   Úget_all_containers_hashrateà   s@   þüüü

ñrº   c                  C   sl   t tƒ} tƒ }tƒ }tƒ }| ¡ D ]"\}}|dkr3| |d¡}| |d¡}| t|ƒ  || d 7  < q| S )z>Calculate maximum potential hashrate based on installed minersr   r   r   r   )r   rb   r   r   r   r   r
   rŠ   )Úmax_hashrater   r   r   r   r   r   r   r   r   r   Úget_max_potential_hashrate
  s   €r¼   çffffffî?g)\�Âõ(ì?g333333ã?)r   ÚJProÚXPc            	      C   s|   t tƒ} tƒ }tƒ }tƒ }| ¡ D ]*\}}|dkr;| |d¡}| |d¡}t |d¡}| t|ƒ  || | d 7  < q| S )zMCalculate expected hashrate per site (nameplate * miner-type discount factor)r   r   r   r½   r   )	r   rb   r   r   r   r   r
   ÚEXPECTED_FACTORSrŠ   )	Úexpected_hashrater   r   r   r   r   r   r   Úfactorr   r   r   Úget_expected_hashrate"  s    €rÃ   c                 C   s¢   d| › d�}t  ¡ j|td�}tƒ }i }|D ]}|jD ]}| ¡ }||vr&q| ¡ }|dur4t|ƒ||< qqt	t
ƒ}	| ¡ D ]\}}
|	t|ƒ  |
7  < q>t|	ƒS )z6Get average hashrate per site for the specified periodr­   zMd)
      |> filter(fn: (r) => r["_field"] == "hashrate")
      |> mean()
    rœ   N)r�   rž   rŸ   r#   r‡   r    r¡   r¢   r„   r   rb   r   rŠ   rR   )r´   rŸ   r£   r¸   Úpod_avgr¤   r¥   r   Ú	raw_valueÚsite_avgÚavg_phr   r   r   Úget_site_avg_hashrate4  s(   þ
€ú	rÈ   c                 C   s2   t tƒ}|  ¡ D ]\}}|t|ƒ  |7  < q|S )z5Group containers by site name and sum their hashrates)r   rb   r   rŠ   )rd   Úgrouped_hashrateÚ	containerrƒ   r   r   r   Úgroup_and_sum_by_siteP  s   rË   c                    s¦   t tƒ}|  ¡ D ]H\}}t|ƒ}|D ]=‰ t‡ fdd„|| D ƒƒs-||  ˆ d ddœ¡ || D ]}|d ˆ d krN|d  ˆ d 7  < t|d dƒ|d< q1qq|S )z1Group containers by site name for historical datac                 3   s    � | ]}|d  ˆ d  kV  qdS )r¯   Nr   )r   Úd©Úentryr   r   rs   ]  s   € z/group_containers_by_site_30d.<locals>.<genexpr>r¯   r   r®   r°   )r   r{   r   rŠ   Úanyr³   Úround)Úcontainers_hashrate_30dÚgrouped_hashrate_30drÊ   r’   r“   rÌ   r   rÍ   r   Úgroup_containers_by_site_30dW  s   €ýýrÓ   c                 C   sj   i }|   ¡ D ]!\}}|D ]}|d }|d }||vrd||< ||  |7  < qqdd„ t|  ¡ ƒD ƒ}|S )zMCalculate total hashrate per day by summing up individual container hashratesr¯   r°   r   c                 S   s    g | ]\}}|t |d ƒdœ‘qS )r   r®   )rÐ   )r   ÚkÚvr   r   r   ro   r  s     z,calculate_total_hashrate.<locals>.<listcomp>)r   r}   )rd   r   rÊ   r’   rÎ   r¯   r°   Útotal_hashrate_listr   r   r   Úcalculate_total_hashratee  s   úr×   c              
   C   s¢   z7t ƒ \}}t| |ƒ}t|ƒ}t|ƒ}t|ƒ}t|ƒ}tƒ }	tƒ }
dt|ƒ|
t	|ƒt	|ƒ|t	|	ƒ|| dœdœW S  t
yP } zdt|ƒdœW  Y d}~S d}~ww )zSGet all performance data for specified number of days with configurable aggregationT)Útotal_hashrate_currentÚmax_phÚcontainers_hashrate_currentrÑ   Útotal_hashrate_30dÚmax_potential_hashrateÚpool_regionalr´   ©Úsuccessr’   F©rß   ÚerrorN)r›   rº   r×   r‚   rË   rÓ   r¼   r   rÐ   rR   r�   Ústr)r´   rµ   rØ   rÚ   Úcontainers_hashrate_periodÚtotal_hashrate_periodrÝ   Úgrouped_current_hashrateÚ"grouped_containers_hashrate_periodrÜ   rÙ   rš   r   r   r   Úget_performance_datau  s4   

øþþ€ÿrç   c              
   C   sf   zt ƒ }tƒ }t| ƒ}dt|ƒt|ƒ|| dœdœW S  ty2 } zdt|ƒdœW  Y d}~S d}~ww )zBGet site hashrate summary: nameplate, expected, and average actualT)rÜ   rÁ   Úsite_avg_hashrater´   rÞ   Frà   N)r¼   rÃ   rÈ   rR   r�   râ   )r´   Úmax_potentialÚexpectedrÆ   rš   r   r   r   Úget_summary_data˜  s"   üþ	þ€ÿrë   Ú__main__zContent-Type: application/json
ÚmodeÚfullr´   Ú7Úsummaryrµ   Ú1hrh   )r©   rª   )r©   )<rB   ÚsysrU   Úcollectionsr   Úconcurrent.futuresr   Úinfluxdb_clientr   Úpathr³   Úmanagers.config_managerr   r   r   r   r   r   r"   r#   r!   Úhashrate_bucketr�   rA   rx   ry   rS   rE   r_   rf   r‚   r„   r‡   rŠ   r›   r‘   rº   r¼   rÀ   rÃ   rÈ   rË   rÓ   r×   rç   rë   Ú__name__ÚprintÚcgiÚcgitbÚenableÚFieldStorageÚformÚgetvaluerí   Úintr´   r£   rµ   Údumpsr   r   r   r   Ú<module>   s„   þ÷

*ý


#


î