3
\$\begingroup\$

I wanted to make use of the speedtest-cli while also incorporating pandas into the project. My initial goal, which I am posting here, was to take the values of the speed test and incorporate them into a CSV. I've been learning about Pandas and figured it would be a good way to accomplish the project.

import speedtest
import pandas as pd
import os
# Initial Speedtest variables
s = speedtest.Speedtest()
servers = []
# File path variables
localpath = ''
filepath = ''
filename = ''
# Functions #
def speedtest():
 """Makes use of the speed-test-cli python wrapper
 to get speed test data and returns in a dictionary"""
 s.get_servers(servers)
 s.get_best_server()
 s.download()
 s.upload()
 s.results.share()
 results_dict = s.results.dict()
 return results_dict
def convert_mbps(arg):
 arg = arg / 1000000
 return arg
def csv_upload(localpath, filepath, filename):
 """Attempts to append a csv file and, if none
 exists, creates one in a specified path"""
 try:
 df2 = pd.read_csv(localpath)
 df2 = df2.append(df)
 df2.to_csv(os.path.join(filepath, filename), index=False)
 except OSError:
 df.to_csv(os.path.join(filepath, filename), index=False)
# Speedtest and convert to Data Frame
result = speedtest()
df = pd.io.json.json_normalize(result)
df2 = None
# Uploads CSV
csv_upload(localpath, filepath, filename)

I've tried as best as I could to document throughout my code, as well as to not statically specify parameters within functions; something which I've been bad for in the past. I've also tried to incorporate in some exception handling, though this is something that I believe needs more work in my code.

I've tested the code within a cron, it does exactly what it seems to be functioning as I would like it to. Future developments will surround analyzing data collected from the CSV; trying to determine trends in bandwidth or latency as a function of time of day. Any feedback would be most appreciated.

asked May 15, 2018 at 17:20
\$\endgroup\$

1 Answer 1

3
\$\begingroup\$
  1. Don't use globals. E.g., you refer to df and df2 in csv_upload, why not make them parameters?

  2. Use the standard if __name__ == '__main__': guard around the script's main body.

  3. It's common to just accept a flat file path, and expect the calling code to perform any os.path.join in the creation of said path.

  4. "upload" sounds like network activity, but you're saving the data to disk. This should probably follow a more typical naming scheme of to_csv or save or dump or something of that sort.

  5. This seems like a prime candidate for being a class.

I'd suggest something like the following:

import speedtest
import pandas as pd
import os
class MySpeedtest:
 def __init__(servers, initial_data_path=None):
 # Initial Speedtest variables
 self.s = speedtest.Speedtest()
 self.servers = servers
 if initial_data_path is not None:
 self.df = pd.read_csv(initial_data_path)
 else:
 self.df = pd.DataFrame()
 def run_test(self):
 """Makes use of the speed-test-cli python wrapper
 to get speed test data and returns in a dictionary"""
 self.s.get_servers(self.servers)
 self.s.get_best_server()
 self.s.download()
 self.s.upload()
 self.s.results.share()
 results_dict = self.s.results.dict()
 self.df.append(pd.io.json.json_normalize(results_dict))
 @staticmethod
 def convert_mbps(arg):
 return arg / 1000000
 # Actually, this should probably be "arg / (2**20)"
 # 1 MB = 2**20 B
 def to_csv(self, path):
 """Attempts to append a csv file and, if none
 exists, creates one in a specified path"""
 self.df.to_csv(path, index=False)
if __name__ == '__main__':
 # TODO: populate a list of servers, define localpath/filepath/filename
 tester = MySpeedtest(servers, localpath)
 tester.run_test()
 tester.to_csv(os.path.join(filepath, filename))
answered May 15, 2018 at 19:45
\$\endgroup\$
2
  • \$\begingroup\$ I'll be the first to admit that classes are a weakness in how I code. I have implemented some trivial examples, but don't often come to the conclusion to use them within my own code. Considering that the initial script works fine, I'm assuming that a class would be useful within a project of a larger scope. Would you mind to give me a little bit of information on how incorporating the code into a class increases its usability? \$\endgroup\$ Commented May 16, 2018 at 14:04
  • 1
    \$\begingroup\$ It compartmentalizes your speedtest logic, including related state, into a single entity. Since the functions rely on sharing state back and forth, it seems reasonable to code them in such a way that their state is stored in a traditional fashion (as class instances). This also increases usability and testability by creating an entity that can be instantiated and exhaustively tested; when the code relied on global variables, it would've been difficult to rigorously test when embedded in a larger application (which could've changed the global variables between calls, for example) \$\endgroup\$ Commented May 16, 2018 at 17:02

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.