Build a Budget App Project - create_spend_chart - Task 19,23,24

Hey there,
at this point, I dont know what else I could try than ask for help, from what I understand, everything should be fine but it just wont accept it:

Project:

Failed:19. The height of each bar on the create_spend_chart chart should be rounded down to the nearest 10.

Failed: 23. create_spend_chart chart should have each category name written vertically below the bar. Each line should have the same length, each category should be separated by two spaces, with additional two spaces after the final category.

Failed: 24. create_spend_chart should print a different chart representation. Check that all spacing is exact. Open your browser console with F12 for more details.

my attempt:

class Category:
    categories = []

    def __init__(self, category):
        self.ledger = []
        self.category = category
        self.ledger_print = ""

    def __str__(self):
        return self.create_ledger_print()

    def add_category(self):
        if self not in Category.categories:
            Category.categories.append(self)
        return

    def deposit(self, amount, description=""):
        self.ledger.append({"amount": amount, "description": description})
        self.add_category()
        return

    def withdraw(self, amount, description=""):
        neg_amount = amount * -1
        success = self.check_funds(amount)
        if success:
            self.ledger.append({"amount": neg_amount, "description": description})

        return success

    def transfer(self, amount, target_category):
        success = self.check_funds(amount)
        if success:
            withdraw_description = f"Transfer to {target_category.category}"
            target_category.add_category()
            self.withdraw(amount, withdraw_description)
            deposit_description = f"Transfer from {self.category}"
            target_category.deposit(amount, deposit_description)

        return success

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

        return success

    def get_balance(self):
        return sum(item["amount"] for item in self.ledger)

    def create_ledger_print(self):
        ledger_print = [self.category.center(30, "*"), "\n"]
        for item in self.ledger:
            description = f"{item['description'][:23]:<23}"
            amount = f"{item['amount']:.2f}"[:7]
            ledger_print.append(description + f"{amount:>7}" + "\n")
        ledger_print.append(f"Total: {self.get_balance():.2f}")

        return "".join(ledger_print)


def gather_percentage(categories):
    spendings = {}
    percents = {}
    percents_values = []
    cats_upper = []

    sum_spendings = 0
    for category in categories:
        amount = sum(item["amount"] for item in category.ledger if item["amount"] < 0) * -1
        spendings[category.category] = amount
        sum_spendings += amount

    per_cent = sum_spendings / 100
    for category, total in spendings.items():
        percentage = int(max(0, (total / per_cent) // 10))
        percents[category] = percentage

    for item, key in percents.items():
        percents_values.append(key)
        cats_upper.append(str(item).upper())

    return percents_values, cats_upper


def construct_seperator(categories):
    seperator = [str(4 * " ")]
    for _ in categories:
        seperator.append(str(3 * "-"))
    seperator.append("-\n")
    target_length = len("".join(seperator))
    return "".join(seperator), (target_length - 1)


def fix_length(target_row_length, row):
    current_row_length = len("".join(row))
    if current_row_length < target_row_length:
        row.append(str((target_row_length - current_row_length) * " "))

    return row


def construct_percent_chart(percents_values, target_row_length):
    percent_chart = []

    for i in range(100, -1, -10):
        row = [f"{i:>3}|"]
        for item in percents_values:
            row.append(" o " if item >= (i // 10) else "  ")
        row = fix_length(target_row_length, row)
        row.append("\n")
        percent_chart.append("".join(row))

    return "".join(percent_chart)


def create_cat_chart(cats_upper, target_row_length):
    cat_chart = []
    target_column_length = 0

    for item in cats_upper:
        if len(item) > target_column_length:
            target_column_length = len(item)

    for i in range(0, target_column_length):
        row = []
        for j, item in enumerate(cats_upper):
            if j == 0:
                row.append(str(4 * " "))
            if i < len(item):
                row.append(f" {item[i]} ")
            else:
                row.append(str(3 * " "))
        row = fix_length(target_row_length, row)
        if i < (target_column_length - 1):
            row.append("\n")
        cat_chart.append("".join(row))

    return "".join(cat_chart)


def create_spend_chart(categories=Category.categories):
    spend_chart = ["Percentage spent by category", "\n"]

    percents_values, cats_upper = gather_percentage(categories)
    seperator, target_row_length = construct_seperator(categories)

    spend_chart.append(construct_percent_chart(percents_values, target_row_length))
    spend_chart.append(seperator)
    spend_chart.append(create_cat_chart(cats_upper, target_row_length))

    return "".join(spend_chart)

Thanks in advance

can you share a link to the project please?

Updated my query

have you opened the browser console to see a more detailed output from the tests as suggested?

The assertion error and diff gives you a lot of information to track down a problem. For example:

AssertionError: 'Year' != 'Years'
- Year
+ Years
?     +

Your output comes first, and the output that the test expects is second.

AssertionError: ‘Year’ != ‘Years’

Your output: Year does not equal what’s expected: Years

This is called a diff, and it shows you the differences between two files or blocks of code:

- Year
+ Years
?     +

- Dash indicates the incorrect output
+ Plus shows what it should be
? The Question mark line indicates the place of the character that’s different between the two lines. Here a + is placed under the missing s .

Here’s another example:

E       AssertionError: Expected different output when calling "arithmetic_arranger()" with ["3801 - 2", "123 + 49"]
E       assert '  3801      123    \n   - 2     + 49    \n------    -----    \n' == '  3801      123\n-    2    +  49\n------    -----'
E         -   3801      123
E         +   3801      123    
E         ?                ++++
E         - -    2    +  49
E         +    - 2     + 49    
E         - ------    -----
E         + ------    -----    
E         ?                +++++

The first line is long, and it helps to view it as 2 lines in fixed width characters, so you can compare it character by character:

'  3801      123    \n   - 2     + 49    \n------    -----    \n'
'  3801      123\n-    2    +  49\n------    -----'

Again, your output is first and the expected output is second. Here it’s easy to see extra spaces or \n characters.

E         -   3801      123
E         +   3801      123    
E         ?                ++++

Here the ? line indicates 4 extra spaces at the end of a line using four + symbols. Spaces are a little difficult to see this way, so it’s useful to use both formats together.

I hope this helps interpret your error!

Well, thanks for taking the time, I tried looking at the console before but the output didnt make much sense to me prior to your response.

Task 19 is still not working, I tried all kinds of variants to round the percentages, the console just shows a traceback error mentioning that values close to 10 fail, I can’t replicate though

Task 23 was my bad of making the categories uppercase, it works now that I just capitalize.

Task 24 was solved by sticking to the console but it feels bugged, this output is preferred by the Task (now correct) but not by the example or logic:

I have updated my code:

class Category:
    categories = []

    def __init__(self, category):
        self.ledger = []
        self.category = category
        self.ledger_print = ""

    def __str__(self):
        return self.create_ledger_print()

    def add_category(self):
        if self not in Category.categories:
            Category.categories.append(self)
        return

    def deposit(self, amount, description=""):
        self.ledger.append({"amount": amount, "description": description})
        self.add_category()
        return

    def withdraw(self, amount, description=""):
        neg_amount = amount * -1
        success = self.check_funds(amount)
        if success:
            self.ledger.append({"amount": neg_amount, "description": description})

        return success

    def transfer(self, amount, target_category):
        success = self.check_funds(amount)
        if success:
            withdraw_description = f"Transfer to {target_category.category}"
            target_category.add_category()
            self.withdraw(amount, withdraw_description)
            deposit_description = f"Transfer from {self.category}"
            target_category.deposit(amount, deposit_description)

        return success

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

        return success

    def get_balance(self):
        return sum(item["amount"] for item in self.ledger)

    def create_ledger_print(self):
        ledger_print = [self.category.center(30, "*"), "\n"]
        for item in self.ledger:
            description = f"{item['description'][:23]:<23}"
            amount = f"{item['amount']:.2f}"[:7]
            ledger_print.append(description + f"{amount:>7}" + "\n")
        ledger_print.append(f"Total: {self.get_balance():.2f}")

        return "".join(ledger_print)


def gather_percentage(categories):
    spendings = {}
    percents = {}
    percents_values = []
    cats_cap = []

    sum_spendings = 0
    for category in categories:
        amount = sum(item["amount"] for item in category.ledger if item["amount"] < 0) * -1
        spendings[category.category] = amount
        sum_spendings += amount

    per_cent = sum_spendings / 100
    for category, total in spendings.items():
        percentage = int(max(0, ((total / per_cent) // 10) * 10))
        percents[category] = percentage

    for item, key in percents.items():
        percents_values.append(key)
        cats_cap.append(str(item).capitalize())

    return percents_values, cats_cap


def construct_seperator(categories):
    seperator = [str(4 * " ")]
    for _ in categories:
        seperator.append(str(3 * "-"))
    seperator.append("-\n")
    target_length = len("".join(seperator))
    return "".join(seperator), (target_length - 1)


def fix_length(target_row_length, row):
    current_row_length = len("".join(row))
    if current_row_length < target_row_length:
        row.append(str((target_row_length - current_row_length) * " "))

    return row


def construct_percent_chart(percents_values, target_row_length):
    percent_chart = []

    for i in range(100, -1, -10):
        row = [f"{i:>3}| "] if i != 0 else [f"{i:>3}|"]
        for item in percents_values:
            row.append(" o " if item >= i else "  ")
        row = fix_length(target_row_length, row)
        row.append("\n")
        percent_chart.append("".join(row))

    return "".join(percent_chart)


def create_cat_chart(cats_cap, target_row_length):
    cat_chart = []
    target_column_length = 0

    for item in cats_cap:
        if len(item) > target_column_length:
            target_column_length = len(item)

    for i in range(0, target_column_length):
        row = []
        for j, item in enumerate(cats_cap):
            if j == 0:
                row.append(str(4 * " "))
            if i < len(item):
                row.append(f" {item[i]} ")
            else:
                row.append(str(3 * " "))
        row = fix_length(target_row_length, row)
        if i < (target_column_length - 1):
            row.append("\n")
        cat_chart.append("".join(row))

    return "".join(cat_chart)


def create_spend_chart(categories=Category.categories):
    spend_chart = ["Percentage spent by category", "\n"]

    percents_values, cats_cap = gather_percentage(categories)
    seperator, target_row_length = construct_seperator(categories)

    spend_chart.append(construct_percent_chart(percents_values, target_row_length))
    spend_chart.append(seperator)
    spend_chart.append(create_cat_chart(cats_cap, target_row_length))

    return "".join(spend_chart)

This is the chart used in the tests:

food = Category("Food")
entertainment = Category("Entertainment")
business = Category("Business")
food.deposit(900, "deposit")
entertainment.deposit(900, "deposit")
business.deposit(900, "deposit")
food.withdraw(78)
entertainment.withdraw(22)
business.withdraw(8)

chart = create_spend_chart([food, entertainment])
print(chart)

for some reason your chart is wonky in this case:

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  
    -------
     F  E  
     o  n  
     o  t  
     d  e  
        r  
        t  
        a  
        i  
        n  
        m  
        e  
        n  
        t  

yeah its weird:

the line changing is only this one:
upper-example:
row = [f"{i:>3}| “] if i != 0 else [f”{i:>3}|“]
lower-example:
row = [f”{i:>3}|"]

regarding task 19, how can I access the instructions for:

test_create_spend_chart_rounding_close_to_upper_and_lower_ten

or would you mind sending those as well?

Thanks again

that is one of the two charts used in test 19, but the other one is passing

if you want the other one it is

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)
print(create_spend_chart([business, food, entertainment]))

same setup, new test:

so again:
upper-example:
row = [f"{i:>3}| “] if i != 0 else [f”{i:>3}|“]
lower-example:
row = [f”{i:>3}|"]

after that confusion, I changed:

row.append(" o " if item >= (i // 10) else " “) # else 2 spaces
to
row.append(” o " if item >= (i // 10) else " ") # else 3 spaces

and voila, for some reason it worked finally

thanks for sticking with me

good job! debugging is hard, but you managed to fix it!

1 Like