Build a Budget App Project - Build a Budget App Project

Tell us what’s happening:

None of the tests for the create_spend_chart function (tests 17-24) are clearing (not even the title!!), even though the chart seemingly meets all the mentioned requirements, including spacing and formatting.

What’s worse is that when I open the dev console to look for Assertion Errors that could help, I am left with an Unspecified Assertion Error which isn’t helpful at all. I’ve included the code in it’s entirety below.

Your code so far

```import math

class Category:


    def __init__(self, name):
        self.name = name
        self.ledger = []

        
    def _round(self, value):
        num_decimal = str(value).split('.')
        try:
            if len(num_decimal[1]) < 2:
                num_str = str(value) + (2-len(num_decimal[1]))*'0'
            else:
                num_str = str(round(value,2))
        except:
            num_str = str(value) + '.00'
        return num_str

    def deposit(self, amount, description=None):
        if not description:
            description = ''
        self.ledger.append({'amount': amount, 'description': description})
        

    def withdraw(self, amount, description=None):
        if not description:
            description = ''
        if self.check_funds(amount):
            self.ledger.append({'amount': -amount, 'description': description})
            return True
        return False
    

    def transfer(self, amount, Category):
        if self.check_funds(amount):
            self.ledger.append({'amount': -amount, 'description': f'Transfer to {Category.name}'})
            Category.ledger.append({'amount': amount, 'description': f'Transfer from {self.name}'})
            return True
        return False


    def get_balance(self):
        self.balance = 0
        for fund in self.ledger:
            self.balance += fund['amount']
        return round(self.balance, 2)


    def check_funds(self, amount):
        if amount > self.get_balance():
            return False
        return True

    def __str__(self):
        ledger_str = (30-len(self.name))//2*'*' + self.name + (30-len(self.name))//2*'*'
        for fund in self.ledger:
            ledger_str += '\n'
            shorthand_description = ''
            shorthand_amount = ''
            for num, char in enumerate(fund['description']):
                if num < 23:
                    shorthand_description += str(char)
                    ledger_str += str(char)
            if len(self._round(fund['amount'])) <= 7:
                shorthand_amount = self._round(fund['amount'])
            else:
                char_list = []
                for num, char in enumerate(self._round(fund['amount'])):
                    char_list += char
                for item in char_list[-7:]:
                    shorthand_amount += item
            ledger_str += (30 - (len(shorthand_description)+len(shorthand_amount)))*' '+ shorthand_amount
        ledger_str += '\n' +'Total: ' + f'{self._round(self.get_balance())}'
        return ledger_str
    


def create_spend_chart(*categories):
    string = []
    string.append("Percentage spent by category")
    dash_length = 1
    withdrawl_total = 0
    category_total = []
    name_length = 0

    for category in categories:
        dash_length += 3
        total = 0
        if name_length < len(category.name):
            name_length = len(category.name)
        for item in category.ledger:
            if item['amount'] < 0:
                withdrawl_total += item['amount']
                total += item['amount']
        category_total.append(total)
    category_percentages = [math.floor(category/withdrawl_total*10)*10 for category in category_total]
    
    for i in range(11):
        percent = 100-i*10
        string.append((3-len(str(percent)))*' ' + str(percent) + "| ") 
    for line_number in range(len(string)):
        for percentage in category_percentages:
            
            o_number = percentage/10+1
            if line_number > 0:
                if 12-line_number <= o_number:
                    string[line_number] += 'o  '
                else:
                    string[line_number] += '   '          
    string.append('    ' + dash_length*'-')
    for n in range(name_length):
        string.append('     ')
    for line_number in range(13, len(string)):
        
        for category in categories:
            
            try:
                if category.name[line_number-13]:
                    string[line_number] += category.name[line_number-13] + '  '
            except:
                string[line_number] += '   '
    
    for line in string:
        print(line)

Your browser information:

User Agent is: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36 Edg/140.0.0.0

Challenge Information:

Build a Budget App Project - Build a Budget App Project

Ignore the Unspecified AssertionError, there’s another output in console, that should give some hints what’s happening:

> E
> ======================================================================
> ERROR: test_create_spend_chart (test_module.UnitTests.test_create_spend_chart)
> ----------------------------------------------------------------------
> Traceback (most recent call last):
>   File "/home/pyodide/test_module.py", line 15, in test_create_spend_chart
>     chart = budget.create_spend_chart([self.food])
>             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>   File "/home/pyodide/budget.py", line 92, in create_spend_chart
>     if name_length < len(category.name):
>                          ^^^^^^^^^^^^^
> AttributeError: 'list' object has no attribute 'name'
> 
> ----------------------------------------------------------------------
> Ran 1 test in 0.002s
> 
> FAILED (errors=1)

I see. I was doing some more tinkering with the code and found that I was working around the assumption that the function’s inputs could be listed purely as create_spend_chart(category1, category2, etc.) rather than as a list within, like in create_spend_chart([category1, category2, etc.]).

I went ahead and modified the function to work with a list input value instead, and now there is a specific AssertionError for what I can only guess is not having a title. However, shouldn’t my version of the title work just as well? The output appears identical to what it’s asking.

Here is the modified function:

def create_spend_chart(categories):
    
    string = []
    string.append("Percentage spent by category")
    dash_length = 1
    withdrawl_total = 0
    category_total = []
    name_length = 0
            
    for category in categories:
        dash_length += 3
        total = 0
        if name_length < len(category.name):
            name_length = len(category.name)
        for item in category.ledger:
            if item['amount'] < 0:
                withdrawl_total += item['amount']
                total += item['amount']
        category_total.append(total)
    category_percentages = [math.floor(category/withdrawl_total*10)*10 for category in category_total]
    
    for i in range(11):
        percent = 100-i*10
        string.append((3-len(str(percent)))*' ' + str(percent) + "| ") 
    for line_number in range(len(string)):
        for percentage in category_percentages:
            
            o_number = percentage/10+1
            if line_number > 0:
                if 12-line_number <= o_number:
                    string[line_number] += 'o  '
                else:
                    string[line_number] += '   '          
    string.append('    ' + dash_length*'-')
    for n in range(name_length):
        string.append('     ')
    for line_number in range(13, len(string)):
        
        for category in categories:
            try:
                if category.name[line_number-13]:
                    string[line_number] += category.name[line_number-13] + '  '
            except:
                string[line_number] += '   '
    
    for line in string:
        print(line)

What your create_spend_chart is returning?

Given the following prompts:

food = Category("Food")
entertainment = Category("Entertainment")
business = Category("Business")
food.deposit(900, "deposit")
entertainment.deposit(900, "deposit")
business.deposit(900, "deposit")
food.withdraw(105.55)
entertainment.withdraw(33.40)
business.withdraw(10.99)
create_spend_chart([business, food, entertainment])

It gives:

Percentage spent by category
100|          
 90|          
 80|          
 70|    o     
 60|    o     
 50|    o     
 40|    o     
 30|    o     
 20|    o  o  
 10|    o  o  
  0| o  o  o  
    ----------
     B  F  E  
     u  o  n  
     s  o  t  
     i  d  e  
     n     r  
     e     t  
     s     a  
     s     i  
           n  
           m  
           e  
           n  
           t  

Okay, that’s printed when it’s executed, but what the function returns?

Wow, I forgot that printing it isn’t exactly the same as returning the output…after fixing it to return all the lines at once instead of printing them individually, it passed all the tests!! Thank you for your help!!