Pragmatic Python V

Ten Minute Crafts

Shuvo Saha
intelligentmachines
10 min readAug 31, 2020

--

This is part 5 of the series. To go back to part 1, click here.

This section aims to build on the foundations built in the course so far to create some small but helpful programs that show the power of Python. This is the beauty of Python and programming, in general; you can easily build on the work of people before you to create some truly amazing things.

All the code here is available in this Github Repository. Working with Github is outside of the scope of this tutorial but you can just download the zip file from the site. Some of the code here may seem complex at first, but I assure you it’s nothing to be afraid of. All of it was built line by line and seeing it all at once makes it look scarier than it is. Take it one line at a time, read the explanations following the code, and go back to the previous parts of the series if needed.

Money Manager

There are a plethora of budget apps on Android and iOS built using Java and Swift. We’re going to build a simple budget app that we can tailor to our needs in the future.

Let’s try to plan out a solution. Remember that in all cases, it’s really helpful to understand the problem first. Thankfully, I’m your client and I can tell you exactly what I want.

I want the program to:

  • Allow me to input a variable salary and variable expenses.
  • Deduct the expenses and add the salary to the budget.
  • Let me view the updated budget.
  • Save the budget for future use.

Try to implement your solution to this problem and show it to me in the comments!

Money Managed

Before we begin, we need to install Pandas (bundled with Miniconda), a nifty data analysis library. We’re only going to be using it to store CSV files so it’s similar to using a gaming PC for browsing the internet. Open up Anaconda prompt or the terminal and type in conda install pandas and press enter. You might have to press Y on your keyboard to continue the installation.

After you’re done installing, start VS Code and make a new file. Name it whatever you want and type the following blocks of code serially on the editor. Remember that, for you, the terminal will mainly be used to run your code not edit it.

# Some Helpful Libraries
import pandas as pd

Next, we need to see if we already have a stored budget file. If we don’t, we can create a new budget variable and then save it after we’re done.

# Try opening the csv file
try:
# if file found open the file and set the budget column to the
# budget variable
df = pd.read_csv("budget.csv", index_col=None)
budget = df.loc[0, "Budget"]
except FileNotFoundError:
# If file is not found, set budget to 0 and create a dataframe
# with our budget and savings
budget = 0
df = pd.DataFrame({"Budget": [0]})

The try and except block is a way to find out if there are any errors in our code and do something if there is. The first (try) part tries something and if it runs into an error, it goes into the second (except) part. We can specify what type of error we want to catch in the except block by putting the name of the error beside it. I only know the name of the error because I tried running the code without the error catcher.

Inside the try block, we create a variable called df and store a dataframe object. This is a special object that Pandas provides us and it’s optimized for spreadsheets and tables. pd.read_csv()reads a CSV file and turns it into a dataframe. It also works with a multitude of other file types. We also specify that we don’t have an index column.

We then look for the budget, contained in the 1st row (Pandas indexes start from 0 just like Python) and the column called “Budget” using the df.loc[row, column] method. This is similar to selecting an element from a list. Note that you can override the default index in Pandas and make your index using strings, numbers, etc. Then the row should refer to your newly made index. So, we type in df.loc[0, “budget”].

If the file isn’t found, we just set our budget to 0 and create a new Pandas dataframe, which has only one column and one row, using the pd.DataFrame({key: [value, value2...], key2: [value3, value4...]...}) method. This is similar to the creation of a dictionary with the keys just being column names.

# Take user input and store it in an integer
user_intent = ""
while user_intent != "quit": user_intent = str.lower(input('''What would you like to do
today?\n'''))
if user_intent == "expense":
expense = int(input("How much did you spend?\n"))
budget -= expense
print(f"Expense of BDT {expense} deducted\n")
elif user_intent == "salary":
salary = int(input("How much did you get?\n"))
budget += salary
print(f"Salary payment of BDT {salary} added\n")
elif user_intent == "budget":
print(f"Your current budget is {budget}\n")
elif user_intent == "quit":
print("Quitting the application")
df.loc[0, "Budget"] = budget
df.to_csv("budget.csv", index=False)
else:
print("You typed something wrong.\n")

This part seems complex at first, but breaking it down shows a pretty simple user input system. We first create an empty string and initialize a while loop. As long as the user doesn’t type “quit”, the while loop will keep going.

At the start of the loop, it’ll ask the user to input something. str.lower() turns the input into all lowercase. This is in case the user messes up and types any letter uppercase. \n in a string works similar to pressing enter in a document. This has the added annoyance that typing “\” doesn’t put a forward slash inside strings and you’ll need to type "\\" instead.

If the input is “expense”, then we ask the user to input the expense amount and deduct the value from our budget. Note that we could return an error if the user types in anything that isn’t a number. Try implementing it yourself.

If the input is “salary”, then we ask the amount and add it to our budget.

If the input is “budget”, then we show the current budget to the user.

If the input is “quit”, then we save our budget variable inside the dataframe and save it our computer (in the same directory as the Python script we’re running) using the dataframe.to_csv(name) method. We also tell Pandas to discard the index as it’s automatically created when the file is opened. Note that when we opened the dataframe, we called the read_csv method on Pandas itself: pd.read_csv(name) and on closing, we’re calling the method on the dataframe:df.to_csv(name). Typing in "quit" also stops the loop.

If the input is anything else, then we return an error message.

The full code is given below and also available on the aforementioned Github repo:

Money Managed Better

To illustrate how easy it is to add to our program, I also decided to add a savings component, fixed salaries that are easy to input, a help system, and a reset system for a negative budget. Make sure to go through this step-by-step and try to figure out what each line of code does.

For an even better system, we could add online storage in the form of Google Sheets (future article?) or go all out and use a fully-featured online database such as Google Cloud. We could even add a GUI and make it more like a computer app.

Other use cases include having to calculate the tab after VAT and service charge and dividing it among my friends at restaurants, seeing how many days I have before I run out of money due to my excessive spending and visualizing my spending habits. All of these are possible with Python and are surprisingly easy too!

Excel File Merger

I don’t know about you but for some reason, I’ve had to merge upwards of 50 excel files at times during my two internships at two firms. While some of the other interns got annoyed at the non-use of Google Sheets, I just made a small script to do this for me. This script also works for other file types with slight modifications. The code here was heavily inspired by this article from this fantastic blog called Practical Business Python.

We will create a script that takes:

  • An input directory containing all the files we need, combined in a CSV format.
  • An output directory.
  • A file name for the combined CSV file.

The script will ignore the header row (1st row) and just combine the content of all the datasets. So ideally, we have CSV files with a similar number of columns and same column names in the same order. Although, with a bit more work, you could get rid of these restrictions!

Let’s import some libraries. Pathlib allows us to use the Path function which is useful because filepaths vary between Mac, Windows, and Linux. The OS module will be used to list all the files in a directory.

# Some helpful libraries
from pathlib import Path
import pandas as pd
import os

Next, we create a function that combines the files taking in 3 inputs: the source directory, the output directory, and the name of the output file.

def combine_excel(source, destination, output_file):
# make an empty dataframe
df_combined = pd.DataFrame()
# Create a path variable from the source directory name and
# use the listdir function from the os library
input_dir = os.listdir(Path(source))
for files in input_dir:
df = pd.read_csv(Path(source + '/' + files))
df_combined = pd.concat([df_combined,df],ignore_index=True)
df_combined.to_csv(
Path(destination + '/' + output_file + ".csv"), index=False)
print("File Saved")

os.listdir(path) allows us to create a list of all the filenames inside a folder. We then read the names of the files making sure to add the name of the source directory with a / and the name of the file. To illustrate why this is required, consider this example: our source directory is E:\Excelmerger and the files inside it are b.csv and c.csv. When we iterate over the files, we take the source (E:\Excelmerger), add a /and the name of the current file (b.csv) to get E:\Excelmerger\b.csvwhich is the correct path of the file.

The keen-eyed probably noticed that I added a forward slash,/, and the filepath still turned out to have a backward slash,\. On Mac, this stays as a forward slash. This is the magic of the Path() function, which directly refers to locations, so make sure to use / if you’re using Path().

pd.concat([df1, df2, df3…]) function from the Pandas library is used to concatenate two dataframes. This will create a new dataframe that, by default, assumes the header is the same and merges the rows. We also tell it to ignore the index since order doesn’t matter for our case; we just need the data in the files to appear one after another(it would matter if we needed a specific order between multiple datasets).

While saving the file, take care to add the extension at the end.

We should also take into account possible errors that can occur and make a new function to validate the errors.

def validate_inputs(source, destination, output_file):
# Assume there are no errors & error message is nonexistent
errors = False
error_message = []
# Path turns the source directory into a path variable that has
# a method called exists() that checks if its valid
# If the path doesn't exist, there is an error
if not (Path(source)).exists():
errors = True
error_message.append("Please select an input directory")
# This is similar to the above but for output directory
if not (Path(destination)).exists():
errors = True
error_message.append("Please select an output directory")
# This checks if the length of the output file is less than 1
# if it is 0 characters, that means field is empty
if len(output_file) < 1:
errors = True
error_message.append("Please enter a file name")
# This returns the errors boolean and the error message string
return(errors, error_message)

New functions used include the Path.exists() which checks if the filepath is valid and str.append() which allows us to add words to a string. We also use the len(string) function to find out if the user typed something.

# These take the inputs from the intent
source = input("Input_Directory: \n").replace('"', '')
destination = input("Output_Directory: \n").replace('"', "")
output_file = input("Output_Name: \n").replace('"', "")
# This triggers the validate input function
errors, error_message = validate_inputs(source, destination, output_file)
# If validate inputs returns an error, show the error message
# corresponding to that error
if errors:
print("Error: " + error_message)
else:
combine_excel(source, destination, output_file)

Note that we add a str.replace('old', 'new')when we take the input. This removes any in our filepath which occurs when we drag and drop folders onto the terminal. Yes, you can drag and drop the input and output directories to the terminal window if you don’t feel like typing!

The full script!

How about a gooey?

Remember that GUI we kept talking about? Let’s add it. We’ll leverage an easy to use library called appJar. Install it by typing pip install appjar in the Anaconda command prompt or terminal.

The code below has a lot of improvements: an error checker that makes sure all the files inside the source directory are CSV files and general improvements that a GUI adds, such as a quit button, traditional selection of filepaths (when you install a software and it asks for the installation directory) and so on.

To fully understand how the code below works, you might need to look at the appJar documentation.

If all of this was a bit too much to take in, I’d recommend going over the previous parts of the series and some Python programming problems online to get used to the syntax. These applications are fairly simple to understand as long as you get a hold of the syntax.

You should also try to implement these blocks of code yourself and look up anything that you found confusing in this article on the library docs, Stack Overflow, or just plain old Google. Learning through examples, implementation of the code by yourself, and fixing errors is a key skill!

Small update — I made this course as an undergraduate to teach the basics of programming to non-tech background individuals. However, I’ve graduated (yay) and due to lack of time, I haven’t been able to continue this series. If you do feel like I’ve helped you out, send me an email at shuvo.vertigo@gmail.com and let me know! Maybe your emails will spur me on to finish the series!

--

--

Shuvo Saha
intelligentmachines

A business grad who spends way too much time on his computer. Currently a Marketing Analyst at Wise!