Tell us what’s happening:
When I run my project in replit, I’m getting errors on the create_spend_chart test. I know it has to do with spacing and I tried fiddling everywhere I added spacing or justified text, but I cannot seem to decipher what exactly is wrong with my formatting. Even after looking at tutorials and the unittest documentation I’m still no closer to understanding what the differences are. I don’t know what all the pluses, minuses, carrots, and question marks mean. If anyone can look at my code and tell me what looks like the issue, I’d appreciate it. However what I’d really like is a resource that tells me how to read the unittest output or color code the differences so the information is easier to parse.
Error Message
FAIL: test_create_spend_chart (test_module.UnitTests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/runner/boilerplate-budget-app-2/test_module.py", line 102, in test_create_spend_chart
self.assertEqual(actual, expected, 'Expected different chart representation. Check that all spacing is exact.')
AssertionError: 'Perc[34 chars] \n 90| \n 80| \n 70| o[303 chars] t' != 'Perc[34 chars] \n 90| \n 80| \n 70| [340 chars] t '
Percentage spent by category
- 100|
+ 100|
? +
- 90|
+ 90|
? +
- 80|
+ 80|
? +
- 70| o
+ 70| o
? +
- 60| o
+ 60| o
? +
- 50| o
+ 50| o
? +
- 40| o
+ 40| o
? +
- 30| o
+ 30| o
? +
- 20| o o
+ 20| o o
? +
- 10| o o
+ 10| o o
? +
- 0| o o o
+ 0| o o o
? +
----------
- B F E
+ B F E
? ++
- u o n
+ u o n
? ++
- s o t
+ s o t
? ++
- i d e
+ i d e
? ++
- n r
+ n r
? ++
- e t
+ e t
? ++
- s a
+ s a
? ++
- s i
+ s i
? ++
- n
+ n
? ++
- m
+ m
? ++
- e
+ e
? ++
- n
+ n
? ++
- t+ t ? ++
: Expected different chart representation. Check that all spacing is exact.
----------------------------------------------------------------------
Ran 11 tests in 0.023s
FAILED (failures=1)
Your code so far
import re
class Category:
def __init__(self, category):
self.category = category
self.ledger = []
self.balance = 0.00
def deposit(self, amount, description=""):
"""Amount is an float amount.
Description is a string.
writes a transcation to the ledger"""
self.balance += amount
transaction = {"amount": amount, "description": description}
self.update_ledger(transaction)
def check_funds(self, amount):
"""Function to check if an amount for a transfer or withdrawal is valid.
Returns True if valid, False otherwise."""
if self.balance < amount:
return False
return True
def update_ledger(self, transaction):
"""updates ledger list with transaction statment.
transaction statement is a dictionary in the form of {"amount": amount, "description": description}
"""
self.ledger.append(transaction)
def withdraw(self, amount, description=""):
"""Amount is an float amount.
Description is a string.
writes a transcation to the ledger.
The transaction will be placed as a negative in the ledger.
returns True if the transaction succeeds
False if withdraw amount is greater than balance."""
if not self.check_funds(amount):
return False
else:
self.balance -= amount
transaction = {"amount": -amount, "description": description}
self.update_ledger(transaction)
return True
def get_balance(self):
return self.balance
def transfer(self, amount, budget_category):
"""amount is a floating point number.
budget_category is an object that gets an amount transferred to it.
Takes amount from current object and transfers it to the new object"""
if not self.check_funds(amount):
return False
# make withdral from this budget object
withdraw_message = f"Transfer to {budget_category.category}"
self.withdraw(amount, withdraw_message)
# make a deposit to the budget_category object
deposit_message = f"Transfer from {self.category}"
budget_category.deposit(amount, deposit_message)
return True
def __str__(self):
"""fomatting object ledger to be printed when object is passed through the print function"""
page_lines = []
header = self.category.center(30, "*")
page_lines.append(header)
for line in self.ledger:
description = line["description"][:23]
amount = "{:.2f}".format(line["amount"])
# move amount to be right justified relative to the header
amount = amount.rjust(30 - len(description))
page_line = description + amount
page_lines.append(page_line)
final_balance = "{:.2f}".format(self.balance)
page_total = f"Total: {final_balance}"
page_lines.append(page_total)
formatted_page = "\n".join(page_lines)
return formatted_page
def create_spend_chart(category_list):
"""category_list is a list of objects
creates a histogram based on the pecentage of the budget spent on that category rounded down
Returns a formatted histogram string"""
graph_title = "Percentage spent by category"
# create empty chart
chart = []
for i in range(100, -1, -10):
y_label = str(i) + "|"
if i != 100:
y_label = y_label.rjust(len(chart[0]))
chart.append(y_label)
# find percentages
overall_withdraws = 0
category_withdraws = defaultdict(int)
# total perecntage spent is total category withdraws divided by the overall withdraws rounded down
for budget_item in category_list:
for transaction in budget_item.ledger:
if transaction["amount"] < 0:
category_withdraws[budget_item.category] += - \
transaction["amount"]
overall_withdraws += -transaction["amount"]
# nested loop calulates percentages and fills chart accordingly
for budget_item in category_list:
percentage_spent_per_category = int(
(category_withdraws[budget_item.category] / overall_withdraws) * 100)
for i, chart_line in enumerate(chart):
chart_percent = re.findall("[0-9]+", chart_line)
chart_percent = int(chart_percent[0])
if percentage_spent_per_category >= chart_percent:
chart[i] += " o "
else:
chart[i] += " " * len(" o ")
# print(*chart, sep="\n")
# use regex to find the longest bar length horizontal & use that number plus two
# to create the underline that creates the x- axis
longest_bar = 0
longest_row = 0
for row in chart:
bar = re.findall("\so.*", row)
if len(row) > longest_row:
longest_row = len(row)
if bar != []:
bar_length = len(bar[0])
if bar_length > longest_bar:
longest_bar = bar_length
under_line = "-" * (longest_bar + 1)
# loop for x-axis labels
x_axis = ""
longest_string_length = 0
category_names = []
for word in category_list:
category_names.append(word.category)
if len(word.category) > longest_string_length:
longest_string_length = len(word.category)
i = 0
while i < longest_string_length:
letter_part = " " * ((longest_row - longest_bar) + 1)
for j in range(len(category_names)):
# put each letter for the category_names in a row with spacing. Make sure last letter has no spacing.
# handles unequal word lengths
try:
letter_part += category_names[j][i]
except IndexError:
letter_part += " "
if j != len(category_list) - 1:
letter_part += " "
else:
if i != (longest_string_length -1):
letter_part += "\n"
x_axis += letter_part
i += 1
formatted_chart = "\n".join(chart)
under_line = (" " * (longest_row - longest_bar)) + under_line
histogram_string = f"{graph_title}\n{formatted_chart}\n{under_line}\n{x_axis}"
return histogram_string
Your browser information:
User Agent is: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/118.0
Challenge: Scientific Computing with Python Projects - Budget App
Link to the challenge: