Code smells, third part
By Detlef Wuppermann
In part one and two of our series we discussed a small (Python) script. We went through the most obvious code smells and fixed them in the code below.
from datetime import datetime, timedelta
import sys
import requests
from requests.auth import HTTPBasicAuth
from configuration import http_user_service_url, http_user_service_username, http_user_service_password,
userdata_filename, age_in_years
import csv
f = open(userdata_filename, 'w')
writer = csv.writer(f)
now = datetime.now()
min_age = now - timedelta(days=age_in_years * 365)
authentication = HTTPBasicAuth(http_user_service_username, http_user_service_password)
response = requests.get(http_user_service_url, auth=authentication)
LOG.debug(f"Fetching data from user service with response code {response.code}")
if response.status_code != 200:
LOG.error(f"Could not make a successful request to ${http_user_service_url}. The error message is {response.text}".)
sys.exit(1)
response_dict = response.json()
all_users = response_dict['results']
filtered_users = 0
total_users = 0
for user in all_users:
total_users = total_users + 1
birth_date_str = user['birth_date']
birth_date = datetime.fromisoformat(birth_date_str)
if birth_date < min_age:
if user['country'] == "Spain":
new_user = {
"name": f"{user["title"]} {user["first_name"]} {user["last_name"]}",
"street": user["street"],
"city": f"{user["postal_code"]} {user["city"]}",
"country": user["country"],
}
if filtered_users == 0:
writer.writerow(new_user.keys())
writer.writerow(new_user.values())
filtered_users = filtered_users + 1
LOG.debug(f"User with id ${user['id']} added to the list.")
else:
LOG.debug(f"User with id ${user['id']} is not from Spain.")
else:
LOG.debug(f"User with id ${user['id']} is not old enough and will be skipped.")
LOG.debug(f"{filtered_users} from {total_users} total users found.")
f.close()
We are not done yet!
Let’s take a look at further improvements of our code.
Separate the parts of your code into small functions
If you take a look at the code you will notice that everything runs sequentially. Moreover, we do not distinguish between the different parts of the code. So let’s describe what the code really does:
- We have a section where we set up the file handler and the REST client.
- We iterate through the list of all users and filter for those that we are looking for.
- We write to the CSV file.
So let’s break down the code into the following part:
- We already did that: we moved the configuration in a dedicated file.
- We will have a “main” method that does the configuration of all the parts.
- We will have a function that reads the user data from the REST API.
- We will separate the logic where we filter the customers into two functions.
- We will have a function that iterates through the users and applies the filters.
- We will have a function that writes the filtered users to a file.
So let’s go to the refactored code and discuss the changes, in reality we did way more than what we promised.