Python Budget App - not understanding what is missing

What am I doing wrong here? It prints exactly as should each time. I see no difference between expected and actual…

FreeCodeCamp-BudgetApp:
TestModule:

Error 1: Line 102
self.assertEqual(actual, expected, ‘Expected different chart representation. Check that all spacing is exact.’)

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

Error 2: Line 91
self.assertEqual(actual, expected, ‘Expected different string representation of object.’)

Expected Result:
f"Food\ndeposit 900.00\nmilk, cereal, eggs, bac -45.67\nTransfer to Entertainme -20.00\nTotal: 834.33"

Here’s my code:

# Budget App

class Category:

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

    def __repr__(self):  # or __str__ ?
        s = f"{self.name:*^30}\n"
        # Here’s the grammar of a format specifier: [[fill]align][sign][#][0][width][grouping_option][.precision][type]

        for n in self.ledger:
            descriptions = (n['description'])[0:23]  # each character of the string up until the 23rd.
            amounts = str(n['amount'])  # entire amount as a string.
            d = (f"{descriptions: <23}")
            a = (f"{float(amounts): >#7.2f}\n")
            s += d
            s += (a)[0:7] + "\n"
        s += (f"Total: {self.balance:<#23.2f}\n")[0:30]
        #s += "\n"

        return s

    # DEFINE METHODS = FUNCTIONS (with self-contained variables)
    def get_balance(self):
        return self.balance

    def check_funds(self, amount):  # per category
        self.amount = amount

        if self.amount <= self.balance:
            return True
        else:
            return False

    def deposit(self, amount, description=""):
        self.balance += amount
        self.ledger.append({"amount": amount, "description": description})  # can take ONE argument
        return True

    def withdraw(self, amount, description=""):

        if self.check_funds(amount):
            self.balance -= amount
            self.ledger.append({"amount": -abs(amount), "description": description})
            return True
        else:
            amount = 0
            return False

    def transfer(self, amount, instance):

        if self.check_funds(amount):
            description_transfer = f"Transfer ${amount} from {self.name} to {instance.name}."
            self.withdraw(amount, f"Transfer to {instance.name}")
            instance.deposit(amount, f"Transfer from {self.name}")
            return True
        else:
            return False

    def print_ledger(self):
        print(self.__str__())

    def get_withdrawls(self):
        global cat_total
        cat_total = 0

        for item in self.ledger[0:]:
            if int(item['amount']) < 0:
                cat_total += item['amount']

        return cat_total

# -------------------OUTSIDE OF CLASS------------------- #
def get_totals(names):
    total = 0
    totals = []
    for each in names:
        total += Category.get_withdrawls(self=each)
        totals.append(Category.get_withdrawls(self=each))
    rounded = list(map(lambda x: math_reduce(x / total), totals))  # x is each in totals list, x/grand total for each.
    return rounded

def math_reduce(n):
    multiplier = 10
    return int(n * multiplier) / multiplier

# ***********************************^^^WORKS^^^*********************************** #

def create_spend_chart(categories):

    p = "Percentage spent by category\n"
    percentages = []
    names_list = []
    nums_list = []
    padded = []

    for cat in categories:  # use.method
        names_list.append(cat.name)
        nums_list.append(cat.get_withdrawls())
        height = (len(max(names_list, key=len)))
        padded = [name.ljust(height) for name in names_list]

    percentages.append(get_totals(categories))

    # PERCENTAGES FOR CHART
    percentages_big = []
    for i in percentages[0]:
        multiplied = i * 100
        percentages_big.append(multiplied)

    # THE CHART
    for n in range(100, -1, -10):
        p += f"{n:>3}" + "| "
        for percent in percentages_big:
            if percent >= n:
                p += "o  "
            else:
                p += "   "
        p += "\n"
    p += f"{'    ' + '-' + (('-' * 3) * len(categories))}\n"

    for name in zip(*padded):
        p += ('     ' + ('  '.join(name))) + '  \n'
    return p
# ***********************************END OF PROJECT*********************************** #

Thanks,
-T

From my limited experience, all I can say is that the format is not quite right. The ‘+’ signs from the testing module signals extra spaces that shouldn’t be there or something. All of the symbols on the left of the chart mean something, maybe experiment and see how you can reduce them.

I will have a further look when I have time. I hope that helps at least somewhat.

The expected chart ouput for spent by category chart is:

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  

Instead, you are returning:

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  

Are these two charts not exactly the same?

No.

Hi again, Randell,

I made the implied edits:
1. reduced spaces after last charted bar from 2 to 1 space.
2. added a vertical space after (below) the longest word length (now 27 lines instead of 26).

The test now throws the same 2 errors, but with more edit points indicated. Did I read the differencechecker incorrectly? I used the charts saying “Original Text” vs “Changed Text”, using the “changed text” as the desired target and the original as what I assumed was my erroneous output.

Here are the test failure outputs:

.F...F.....
======================================================================
FAIL: test_create_spend_chart (test_module.UnitTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/runner/boilerplate-budget-app-1/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[347 chars]  \n' != '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  
       u  o  n  
       s  o  t  
       i  d  e  
       n     r  
       e     t  
       s     a  
       s     i  
             n  
             m  
             e  
             n  
-            t  
?               -
+            t  -               
 : Expected different chart representation. Check that all spacing is exact.

======================================================================
FAIL: test_to_string (test_module.UnitTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/runner/boilerplate-budget-app-1/test_module.py", line 91, in test_to_string
    self.assertEqual(actual, expected, 'Expected different string representation of object.')
AssertionError: '****[92 chars]Transfer to Entertainme -20.00\nTotal: 834.33                 ' != '****[92 chars]Transfer to Entertainme -20.00\nTotal: 834.33'
  *************Food*************
  deposit                 900.00
  milk, cereal, eggs, bac -45.67
  Transfer to Entertainme -20.00
- Total: 834.33                 + Total: 834.33 : Expected different string representation of object.

----------------------------------------------------------------------
Ran 11 tests in 0.030s

FAILED (failures=2)
 
KeyboardInterrupt

Here is my current code from the create/spend chart:

def create_spend_chart(categories):

    p = "Percentage spent by category\n"
    percentages = []
    names_list = []
    nums_list = []
    padded = []

    for cat in categories:  # use.method
        names_list.append(cat.name)
        nums_list.append(cat.get_withdrawls())
        height = (len(max(names_list, key=len)))
        padded = [name.ljust(height+1) for name in names_list]  # NEW EDIT 2/24

    percentages.append(get_totals(categories))

    # PERCENTAGES FOR CHART
    percentages_big = []
    for i in percentages[0]:
        multiplied = i * 100
        percentages_big.append(multiplied)

    # THE CHART
    for n in range(100, -1, -10):
        p += f"{n:>3}" + "|"
        for percent in percentages_big:
            if percent >= n:
                p += " o "  # NEW EDIT 2/24
            else:
                p += "   "
        p += "\n"
    p += f"{'    ' + '-' + (('-' * 3) * len(categories))}\n"

    for name in zip(*padded):
        p += ('     ' + ('  '.join(name))) + '  \n'
    return p

Looks like you only have an extra line character at the end now. At least from using the original code you posted above and swapping out the create_spend_chart function you posted recently.

If you are using different code, make sure your replit is up to date and let us know.

SOLUTION!

The charts had identical spacing after all, BUT…

…after closer inspection of the test module:
-the test expected \n (new line) at the BEGINNING of each line, whereas
-my code had \n (new line) at the END of each line.

Total errors down from 2 to 1.

From test module:
 expected = "Percentage spent by category**\n**100|          \n 90|          \n 80|          \n 70|    o     \n 60|    o     \n 50|    o     \n 40|    o     \n 30|    o     \n 20|    o  o  \n 10|    o  o  \n  0| o  o  o  \n    ----------\n     B  F  E  \n     u  o  n  \n     s  o  t  \n     i  d  e  \n     n     r  \n     e     t  \n     s     a  \n     s     i  \n           n  \n           m  \n           e  \n           n  \n           t  "

vs my erroneous old code:

p = "Percentage spent by category**\n**" **# Mine ENDS with N)**
    percentages = []
    names_list = []
    nums_list = []
    padded = []

    for cat in categories:  # use.method
        names_list.append(cat.name)
        nums_list.append(cat.get_withdrawls())
        height_full = (len(max(names_list, key=len)))
        height_dif = (height_full-(len(cat.name)))
        padded = [name.ljust(height_full) for name in names_list]

    percentages.append(get_totals(categories))

    # PERCENTAGES FOR CHART
    percentages_big = []
    for i in percentages[0]:
        multiplied = i * 100
        percentages_big.append(multiplied)

    # THE CHART
    for n in range(100, -1, -10):
        p += f"{n:>3}" + "| "                                   
        for percent in percentages_big:
            if percent >= n:
                p += "o  "                                         
            else:
                p += "   "
        p += "\n"
    p += f"{'    ' + '-' + (('-' * 3) * len(categories))}\n"

    for name in zip(*padded):
        p += ('     ' + ('  '.join(name))) + **'  \n'** **# Mine ENDS with N)**
    return p

How particular! There is no way to know about this from the FCC instructions alone. A person MUST examine the test module to make this correction.
Thank you for your help, and for getting me to look closer at the test code.

PASSING CODE with \n at the BEGINNING of each printed line:


def create_spend_chart(categories):

    p = "Percentage spent by category"
    percentages = []
    names_list = []
    nums_list = []
    padded = []

    for cat in categories:  # use.method
        names_list.append(cat.name)
        nums_list.append(cat.get_withdrawls())
        height_full = (len(max(names_list, key=len)))
        height_dif = (height_full-(len(cat.name)))
        padded = [name.ljust(height_full) for name in names_list]

    percentages.append(get_totals(categories))

    # PERCENTAGES FOR CHART
    percentages_big = []
    for i in percentages[0]:
        multiplied = i * 100
        percentages_big.append(multiplied)

    # THE CHART
    for n in range(100, -1, -10):
        p += f"\n{n:>3}" + "| "                                       # NEW EDIT 2/24 *********
        for percent in percentages_big:
            if percent >= n:
                p += "o  "                                          # NEW EDIT 2/24 *********
            else:
                p += "   "
        #p += "\n"
    p += f"\n{'    ' + '-' + (('-' * 3) * len(categories))}"

    for name in zip(*padded):
        p += ('\n     ' + ('  '.join(name))) + '  '
    return p
# ***********************************END OF PROJECT*********************************** #

Line 91 (test module) error SOLVED:

The error was here:

    def __repr__(self):  # or __str__ ?
        s = f"{self.name:*^30}"
        # Here’s the grammar of a format specifier: [[fill]align][sign][#][0][width][grouping_option][.precision][type]

        for n in self.ledger:
            descriptions = (n['description'])[0:23]  # each character of the string up until the 23rd.
            amounts = str(n['amount'])  # entire amount as a string.
            d = (f"\n{descriptions: <23}")
            a = (f"{float(amounts): >#7.2f}")
            s += d
            s += a[0:7]
        s += (f"\nTotal: {self.balance:<#3.2f}")[0:30]  **# changed from <#23.2 to <#3.2**
        return s

In the line for the total and amount, my original code produced extra spaces after the total. The above is now correct, although I’m not sure HOW it got rid of the spaces, and since I wrote this stuff almost a year ago, I forgot how those ‘format specifiers’ even work. But it produced the right answer.

SEE BELOW for solution to Line 102 Error.
Thanks,
-T