11from typing import Dict , Union
22from netunicorn .base import Task , Failure
3- from pprint import pprint
43import subprocess
4+ import pprint
55from ping3 import ping
66import csv
77import json
@@ -13,7 +13,7 @@ class AlexaWebsitesTask(Task):
1313 "pip install ping3"
1414 ]
1515
16- def __init__ (self , domain : str = None , filepath : str = None , output_path : str = None , top_k : int = 100 , * args , ** kwargs ):
16+ def __init__ (self , domain : str = None , filepath : str = "alexa_websites.csv" , output_path : str = None , top_k : int = 100 , * args , ** kwargs ):
1717 super ().__init__ (* args , ** kwargs )
1818 self .domain = domain
1919 self .filepath = filepath
@@ -31,7 +31,7 @@ def measure_ping(self) -> Union[Dict[str, float], Failure]:
3131 try :
3232 ping_value = ping (self .domain )
3333 if ping_value is None :
34- return Failure ({ "Ping returned None." } )
34+ return Failure ("Ping returned None." )
3535 return {"value" : ping_value * 1000 , "unit" : "ms" }
3636 except Exception as e :
3737 return Failure (f"Ping failed: { e } " )
@@ -46,20 +46,33 @@ def measure_dns_time(self) -> Union[Dict[str, float], Failure]:
4646 except Exception as e :
4747 return Failure (f"DNS resolution failed: { e } " )
4848
49- def measure_ttfb (self ) -> Union [Dict [str , float ], Failure ]:
49+ def measure_timing (self ) -> Union [Dict [str , Dict [ str , float ] ], Failure ]:
5050 try :
5151 result = subprocess .run ([
5252 "curl" ,
5353 "-o" , "/dev/null" ,
5454 "-s" ,
55- "-w" , "%{time_starttransfer}" ,
55+ "-w" ,
56+ (
57+ "time_appconnect: %{time_appconnect}\n "
58+ "time_connect: %{time_connect}\n "
59+ "time_namelookup: %{time_namelookup}\n "
60+ "time_pretransfer: %{time_pretransfer}\n "
61+ "time_redirect: %{time_redirect}\n "
62+ "time_starttransfer: %{time_starttransfer}\n "
63+ "time_total: %{time_total}\n "
64+ ),
5665 "-H" , "Cache-Control: no-cache" ,
57- f"http ://{ self .domain } " ,
66+ f"https ://{ self .domain } " ,
5867 ], capture_output = True , text = True , check = True )
59- ttfb = float (result .stdout .strip ()) * 1000 # s to ms
60- return {"value" : ttfb , "unit" : "ms" }
68+ metrics = {
69+ key .strip (): {"value" : float (value .strip ()) * 1000 , "unit" : "ms" }
70+ for line in result .stdout .splitlines ()
71+ for key , value in [line .split (": " , 1 )]
72+ }
73+ return metrics
6174 except Exception as e :
62- return Failure (f"TTFB measurement failed: { e } " )
75+ return Failure (f"Network Timing measurement failed: { e } " )
6376
6477 @staticmethod
6578 def load_websites (filepath : str , top_k : int ) -> list :
@@ -78,16 +91,14 @@ def run(self) -> Union[Dict[str, Dict], Failure]:
7891 if self .domain :
7992 # Run for a single domain
8093 return {
81- "domain" : self .domain ,
8294 "traceroute" : self .get_traceroute (),
8395 "ping_time" : self .measure_ping (),
8496 "dns_time" : self .measure_dns_time (),
85- "ttfb_time " : self .measure_ttfb (),
97+ "measure_timing " : self .measure_timing (),
8698 }
87-
88- elif self .filepath and self .output_path :
89- # Run for all websites in the file
90- websites = AlexaWebsitesTask .load_websites (self .filepath , self .top_k )
99+ else :
100+ # Run for all websites in a file
101+ websites = self .load_websites (self .filepath , self .top_k )
91102 print (f"Loaded { len (websites )} websites." )
92103
93104 results = {}
@@ -99,12 +110,15 @@ def run(self) -> Union[Dict[str, Dict], Failure]:
99110 except Exception as e :
100111 results [website ] = Failure (f"Failed to process { website } : { e } " )
101112
102- # Save results to a JSON file
103- with open (self .output_path , "w" ) as f :
104- json .dump (results , f , indent = 4 )
105-
106- print (f"Results saved to { self .output_path } " )
113+ # Save results to a JSON file if output_path is provided
114+ if self .output_path :
115+ print (f"Saving results to { self .output_path } " )
116+ try :
117+ with open (self .output_path , "w" ) as f :
118+ json .dump (results , f , indent = 4 )
119+ except Exception as e :
120+ return Failure (f"Failed to write results to file: { e } " )
121+ else :
122+ pprint .pp (results )
123+
107124 return results
108-
109- else :
110- raise Failure ("Either a domain or both a filepath and output_path must be provided." )
0 commit comments