mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-04-13 13:00:15 -04:00
888 lines
29 KiB
Markdown
888 lines
29 KiB
Markdown
---
|
||
id: 5e44413e903586ffb414c94e
|
||
title: Застосунок для ведення бюджету
|
||
challengeType: 23
|
||
forumTopicId: 462361
|
||
dashedName: budget-app
|
||
---
|
||
|
||
# --description--
|
||
|
||
Завершіть клас `Category`. Він повинен опрацьовувати об’єкти, базуючись на різних категоріях (наприклад, *їжа*, *одяг* та *розваги*). Коли об’єкти створені, вони передаються до назви категорії. Клас повинен мати поле класу під назвою `ledger`, яке є списком. Клас також повинен містити наступні методи:
|
||
|
||
- Метод `deposit`, що приймає суму та опис. Якщо жодного опису не дано, то він автоматично стає порожнім рядком. Цей метод повинен додавати об’єкти до списку головної книги у вигляді `{"amount": amount, "description": description}`.
|
||
- Метод `withdraw` схожий до методу `deposit`, але сума, яку він передає, повинна зберігатися у списку як від’ємне число. Якщо недостатньо коштів, то нічого додавати не потрібно. Цей метод повинен повертати `True`, якщо зняття дійсне, а в іншому випадку повертати `False`.
|
||
- Метод `get_balance`, що повертає поточний баланс категорії бюджету на основі депозитів і зняття.
|
||
- Метод `transfer`, що приймає суму або іншу бюджетну категорію як аргументи. Цей метод повинен додавати виведення суми та опис "Transfer to [Destination Budget Category]". Потім він повинен додати депозит до іншої категорії, що містить суму та опис "Transfer from [Source Budget Category]". Якщо коштів недостатньо, то нічого додавати не потрібно. Цей метод повинен повертати `True`, якщо переказ дійсний, а в іншому випадку повертати `False`.
|
||
- Метод `check_funds`, що приймає суму як аргумент. Він повертає `False`, якщо сума більша за баланс бюджетної категорії, а в іншому випадку повертає `True`. Цей метод варто використовувати обом методам `withdraw` та `transfer`.
|
||
|
||
Якщо бюджет надрукований, то повинні відображатися:
|
||
|
||
- Рядок заголовка із 30 символів, де назва категорії знаходиться у центрі рядка символів `*`.
|
||
- Список елементів у записі. Кожен рядок повинен показувати опис і суму. Повинні зображатися перші 23 символи опису, а потім сума. Сума повинна бути вирівняною за правим краєм, містити два десяткові знаки та відображати максимум 7 символів.
|
||
- Рядок, що показує загальну категорію.
|
||
|
||
Ось приклад використання:
|
||
|
||
```py
|
||
food = Category("Food")
|
||
food.deposit(1000, "deposit")
|
||
food.withdraw(10.15, "groceries")
|
||
food.withdraw(15.89, "restaurant and more food for dessert")
|
||
clothing = Category("Clothing")
|
||
food.transfer(50, clothing)
|
||
print(food)
|
||
```
|
||
|
||
І ось приклад виводу:
|
||
|
||
```bash
|
||
*************Food*************
|
||
initial deposit 1000.00
|
||
groceries -10.15
|
||
restaurant and more foo -15.89
|
||
Transfer to Clothing -50.00
|
||
Total: 923.96
|
||
```
|
||
|
||
Окрім класу `Category`, створіть функцію під назвою `create_spend_chart` (за межами класу), яка приймає список категорій як аргумент. Вона повинна повертати рядок зі стовпчиковою діаграмою.
|
||
|
||
Діаграма повинна показувати відсотки, витрачені в кожній категорії, переданій до функції. Відсоток витраченого повинен бути розрахований лише за рахунок зняття, але не з депозитів. Внизу лівої частини графіку повинні бути мітки 0 - 100. Стовпчики у стовпчиковій діаграмі повинні бути створені із символу «o». Висота кожного стовпчика повинна бути округлена до найближчого десятка. Горизонтальна лінія під стовпчиками повинна розташовуватись в двох пробілах від кінцевого стовпчика. Назва кожної категорії повинна бути написана вертикально під стовпчиком. Вгорі повинен бути заголовок "Percentage spent by category".
|
||
|
||
Ця функція буде тестуватися чотирма категоріями.
|
||
|
||
Уважно розгляньте приклад виводу нижче і переконайтеся, що інтервал виводу повністю відповідає прикладу.
|
||
|
||
```bash
|
||
Percentage spent by category
|
||
100|
|
||
90|
|
||
80|
|
||
70|
|
||
60| o
|
||
50| o
|
||
40| o
|
||
30| o
|
||
20| o o
|
||
10| o o o
|
||
0| o o o
|
||
----------
|
||
F C A
|
||
o l u
|
||
o o t
|
||
d t o
|
||
h
|
||
i
|
||
n
|
||
g
|
||
```
|
||
|
||
# --hints--
|
||
|
||
Метод `deposit` має створити певний об’єкт у змінній екземпляра головної книги.
|
||
|
||
```js
|
||
({
|
||
test: () => {
|
||
pyodide.FS.writeFile('/home/pyodide/budget.py', code);
|
||
pyodide.FS.writeFile('/home/pyodide/test_module.py',`
|
||
import unittest
|
||
import budget
|
||
from importlib import reload
|
||
|
||
reload(budget)
|
||
class UnitTests(unittest.TestCase):
|
||
maxDiff = None
|
||
def setUp(self):
|
||
self.food = budget.Category("Food")
|
||
|
||
def test_deposit(self):
|
||
self.food.deposit(900, "deposit")
|
||
actual = self.food.ledger[0]
|
||
expected = {"amount": 900, "description": "deposit"}
|
||
self.assertEqual(actual, expected, 'Expected "deposit" method to create a specific object in the ledger instance variable.')
|
||
`);
|
||
const testCode = `
|
||
from unittest import main
|
||
from importlib import reload
|
||
import test_module
|
||
|
||
reload(test_module)
|
||
t = main(module='test_module', exit=False)
|
||
t.result.wasSuccessful()
|
||
`;
|
||
const out = __pyodide.runPython(testCode);
|
||
assert(out);
|
||
}
|
||
})
|
||
```
|
||
|
||
Виклик методу `deposit` без опису має створити порожній опис.
|
||
|
||
```js
|
||
({
|
||
test: () => {
|
||
pyodide.FS.writeFile('/home/pyodide/budget.py', code);
|
||
pyodide.FS.writeFile('/home/pyodide/test_module.py',`
|
||
import unittest
|
||
import budget
|
||
from importlib import reload
|
||
|
||
reload(budget)
|
||
class UnitTests(unittest.TestCase):
|
||
maxDiff = None
|
||
def setUp(self):
|
||
self.food = budget.Category("Food")
|
||
|
||
def test_deposit_no_description(self):
|
||
self.food.deposit(45.56)
|
||
actual = self.food.ledger[0]
|
||
expected = {"amount": 45.56, "description": ""}
|
||
self.assertEqual(actual, expected, 'Expected calling "deposit" method with no description to create a blank description.')
|
||
`);
|
||
const testCode = `
|
||
from unittest import main
|
||
from importlib import reload
|
||
import test_module
|
||
|
||
reload(test_module)
|
||
t = main(module='test_module', exit=False)
|
||
t.result.wasSuccessful()
|
||
`;
|
||
const out = __pyodide.runPython(testCode);
|
||
assert(out);
|
||
}
|
||
})
|
||
```
|
||
|
||
Метод `withdraw` має створити певний об’єкт у змінній екземпляра `ledger`.
|
||
|
||
```js
|
||
({
|
||
test: () => {
|
||
pyodide.FS.writeFile('/home/pyodide/budget.py', code);
|
||
pyodide.FS.writeFile('/home/pyodide/test_module.py',`
|
||
import unittest
|
||
import budget
|
||
from importlib import reload
|
||
|
||
reload(budget)
|
||
class UnitTests(unittest.TestCase):
|
||
maxDiff = None
|
||
def setUp(self):
|
||
self.food = budget.Category("Food")
|
||
|
||
def test_withdraw(self):
|
||
self.food.deposit(900, "deposit")
|
||
self.food.withdraw(45.67, "milk, cereal, eggs, bacon, bread")
|
||
actual = self.food.ledger[1]
|
||
expected = {"amount": -45.67, "description": "milk, cereal, eggs, bacon, bread"}
|
||
self.assertEqual(actual, expected, 'Expected "withdraw" method to create a specific object in the ledger instance variable.')
|
||
`);
|
||
const testCode = `
|
||
from unittest import main
|
||
from importlib import reload
|
||
import test_module
|
||
|
||
reload(test_module)
|
||
t = main(module='test_module', exit=False)
|
||
t.result.wasSuccessful()
|
||
`;
|
||
const out = __pyodide.runPython(testCode);
|
||
assert(out);
|
||
}
|
||
})
|
||
```
|
||
|
||
Виклик методу `withdraw` без опису має створити порожній опис.
|
||
|
||
```js
|
||
({
|
||
test: () => {
|
||
pyodide.FS.writeFile('/home/pyodide/budget.py', code);
|
||
pyodide.FS.writeFile('/home/pyodide/test_module.py',`
|
||
import unittest
|
||
import budget
|
||
from importlib import reload
|
||
|
||
reload(budget)
|
||
class UnitTests(unittest.TestCase):
|
||
maxDiff = None
|
||
def setUp(self):
|
||
self.food = budget.Category("Food")
|
||
|
||
def test_withdraw_no_description(self):
|
||
self.food.deposit(900, "deposit")
|
||
good_withdraw = self.food.withdraw(45.67)
|
||
actual = self.food.ledger[1]
|
||
expected = {"amount": -45.67, "description": ""}
|
||
self.assertEqual(actual, expected, 'Expected "withdraw" method with no description to create a blank description.')
|
||
`);
|
||
const testCode = `
|
||
from unittest import main
|
||
from importlib import reload
|
||
import test_module
|
||
|
||
reload(test_module)
|
||
t = main(module='test_module', exit=False)
|
||
t.result.wasSuccessful()
|
||
`;
|
||
const out = __pyodide.runPython(testCode);
|
||
assert(out);
|
||
}
|
||
})
|
||
```
|
||
|
||
Метод `withdraw` має повернути `True`, якщо зняття відбулося.
|
||
|
||
```js
|
||
({
|
||
test: () => {
|
||
pyodide.FS.writeFile('/home/pyodide/budget.py', code);
|
||
pyodide.FS.writeFile('/home/pyodide/test_module.py',`
|
||
import unittest
|
||
import budget
|
||
from importlib import reload
|
||
|
||
reload(budget)
|
||
class UnitTests(unittest.TestCase):
|
||
maxDiff = None
|
||
def setUp(self):
|
||
self.food = budget.Category("Food")
|
||
|
||
def test_withdraw_no_description(self):
|
||
self.food.deposit(900, "deposit")
|
||
good_withdraw = self.food.withdraw(45.67)
|
||
self.assertEqual(good_withdraw, True, 'Expected "withdraw" method to return "True".')
|
||
`);
|
||
const testCode = `
|
||
from unittest import main
|
||
from importlib import reload
|
||
import test_module
|
||
|
||
reload(test_module)
|
||
t = main(module='test_module', exit=False)
|
||
t.result.wasSuccessful()
|
||
`;
|
||
const out = __pyodide.runPython(testCode);
|
||
assert(out);
|
||
}
|
||
})
|
||
```
|
||
|
||
Виклик `food.deposit(900, "deposit")` та `food.withdraw(45.67, "milk, cereal, eggs, bacon, bread")` має повернути баланс `854.33`.
|
||
|
||
```js
|
||
({
|
||
test: () => {
|
||
pyodide.FS.writeFile('/home/pyodide/budget.py', code);
|
||
pyodide.FS.writeFile('/home/pyodide/test_module.py',`
|
||
import unittest
|
||
import budget
|
||
from importlib import reload
|
||
|
||
reload(budget)
|
||
class UnitTests(unittest.TestCase):
|
||
maxDiff = None
|
||
def setUp(self):
|
||
self.food = budget.Category("Food")
|
||
|
||
def test_get_balance(self):
|
||
self.food.deposit(900, "deposit")
|
||
self.food.withdraw(45.67, "milk, cereal, eggs, bacon, bread")
|
||
actual = self.food.get_balance()
|
||
expected = 854.33
|
||
self.assertEqual(actual, expected, 'Expected balance to be 854.33')
|
||
`);
|
||
const testCode = `
|
||
from unittest import main
|
||
from importlib import reload
|
||
import test_module
|
||
|
||
reload(test_module)
|
||
t = main(module='test_module', exit=False)
|
||
t.result.wasSuccessful()
|
||
`;
|
||
const out = __pyodide.runPython(testCode);
|
||
assert(out);
|
||
}
|
||
})
|
||
```
|
||
|
||
Виклик методу `transfer` на об’єкті категорії має створити певний елемент головної книги в цьому об’єкті категорії.
|
||
|
||
```js
|
||
({
|
||
test: () => {
|
||
pyodide.FS.writeFile('/home/pyodide/budget.py', code);
|
||
pyodide.FS.writeFile('/home/pyodide/test_module.py',`
|
||
import unittest
|
||
import budget
|
||
from importlib import reload
|
||
|
||
reload(budget)
|
||
class UnitTests(unittest.TestCase):
|
||
maxDiff = None
|
||
def setUp(self):
|
||
self.food = budget.Category("Food")
|
||
self.entertainment = budget.Category("Entertainment")
|
||
|
||
def test_transfer(self):
|
||
self.food.deposit(900, "deposit")
|
||
self.food.withdraw(45.67, "milk, cereal, eggs, bacon, bread")
|
||
transfer_amount = 20
|
||
good_transfer = self.food.transfer(transfer_amount, self.entertainment)
|
||
actual = self.food.ledger[2]
|
||
expected = {"amount": -transfer_amount, "description": "Transfer to Entertainment"}
|
||
self.assertEqual(actual, expected, 'Expected "transfer" method to create a specific ledger item in food object.')
|
||
`);
|
||
const testCode = `
|
||
from unittest import main
|
||
from importlib import reload
|
||
import test_module
|
||
|
||
reload(test_module)
|
||
t = main(module='test_module', exit=False)
|
||
t.result.wasSuccessful()
|
||
`;
|
||
const out = __pyodide.runPython(testCode);
|
||
assert(out);
|
||
}
|
||
})
|
||
```
|
||
|
||
Метод `transfer` має повернути `True`, якщо переказ відбувся.
|
||
|
||
```js
|
||
({
|
||
test: () => {
|
||
pyodide.FS.writeFile('/home/pyodide/budget.py', code);
|
||
pyodide.FS.writeFile('/home/pyodide/test_module.py',`
|
||
import unittest
|
||
import budget
|
||
from importlib import reload
|
||
|
||
reload(budget)
|
||
class UnitTests(unittest.TestCase):
|
||
maxDiff = None
|
||
def setUp(self):
|
||
self.food = budget.Category("Food")
|
||
self.entertainment = budget.Category("Entertainment")
|
||
|
||
def test_transfer(self):
|
||
self.food.deposit(900, "deposit")
|
||
self.food.withdraw(45.67, "milk, cereal, eggs, bacon, bread")
|
||
transfer_amount = 20
|
||
good_transfer = self.food.transfer(transfer_amount, self.entertainment)
|
||
self.assertEqual(good_transfer, True, 'Expected "transfer" method to return "True".')
|
||
`);
|
||
const testCode = `
|
||
from unittest import main
|
||
from importlib import reload
|
||
import test_module
|
||
|
||
reload(test_module)
|
||
t = main(module='test_module', exit=False)
|
||
t.result.wasSuccessful()
|
||
`;
|
||
const out = __pyodide.runPython(testCode);
|
||
assert(out);
|
||
}
|
||
})
|
||
```
|
||
|
||
Виклик `transfer` на об’єкті категорії має зменшити баланс в об’єкті категорії.
|
||
|
||
```js
|
||
({
|
||
test: () => {
|
||
pyodide.FS.writeFile('/home/pyodide/budget.py', code);
|
||
pyodide.FS.writeFile('/home/pyodide/test_module.py',`
|
||
import unittest
|
||
import budget
|
||
from importlib import reload
|
||
|
||
reload(budget)
|
||
class UnitTests(unittest.TestCase):
|
||
maxDiff = None
|
||
def setUp(self):
|
||
self.food = budget.Category("Food")
|
||
self.entertainment = budget.Category("Entertainment")
|
||
|
||
def test_transfer(self):
|
||
self.food.deposit(900, "deposit")
|
||
self.food.withdraw(45.67, "milk, cereal, eggs, bacon, bread")
|
||
transfer_amount = 20
|
||
food_balance_before = self.food.get_balance()
|
||
good_transfer = self.food.transfer(transfer_amount, self.entertainment)
|
||
food_balance_after = self.food.get_balance()
|
||
self.assertEqual(food_balance_before - food_balance_after, transfer_amount, 'Expected "transfer" method to reduce balance in food object.')
|
||
`);
|
||
const testCode = `
|
||
from unittest import main
|
||
from importlib import reload
|
||
import test_module
|
||
|
||
reload(test_module)
|
||
t = main(module='test_module', exit=False)
|
||
t.result.wasSuccessful()
|
||
`;
|
||
const out = __pyodide.runPython(testCode);
|
||
assert(out);
|
||
}
|
||
})
|
||
```
|
||
|
||
Метод `transfer` має збільшити баланс об’єкта категорії, переданого як аргумент.
|
||
|
||
```js
|
||
({
|
||
test: () => {
|
||
pyodide.FS.writeFile('/home/pyodide/budget.py', code);
|
||
pyodide.FS.writeFile('/home/pyodide/test_module.py',`
|
||
import unittest
|
||
import budget
|
||
from importlib import reload
|
||
|
||
reload(budget)
|
||
class UnitTests(unittest.TestCase):
|
||
maxDiff = None
|
||
def setUp(self):
|
||
self.food = budget.Category("Food")
|
||
self.entertainment = budget.Category("Entertainment")
|
||
|
||
def test_transfer(self):
|
||
self.food.deposit(900, "deposit")
|
||
self.food.withdraw(45.67, "milk, cereal, eggs, bacon, bread")
|
||
transfer_amount = 20
|
||
entertainment_balance_before = self.entertainment.get_balance()
|
||
good_transfer = self.food.transfer(transfer_amount, self.entertainment)
|
||
entertainment_balance_after = self.entertainment.get_balance()
|
||
self.assertEqual(entertainment_balance_after - entertainment_balance_before, transfer_amount, 'Expected "transfer" method to increase balance in entertainment object.')
|
||
`);
|
||
const testCode = `
|
||
from unittest import main
|
||
from importlib import reload
|
||
import test_module
|
||
|
||
reload(test_module)
|
||
t = main(module='test_module', exit=False)
|
||
t.result.wasSuccessful()
|
||
`;
|
||
const out = __pyodide.runPython(testCode);
|
||
assert(out);
|
||
}
|
||
})
|
||
```
|
||
|
||
Метод `transfer` має створити певний елемент головної книги в об’єкті категорії, переданого як аргумент.
|
||
|
||
```js
|
||
({
|
||
test: () => {
|
||
pyodide.FS.writeFile('/home/pyodide/budget.py', code);
|
||
pyodide.FS.writeFile('/home/pyodide/test_module.py',`
|
||
import unittest
|
||
import budget
|
||
from importlib import reload
|
||
|
||
reload(budget)
|
||
class UnitTests(unittest.TestCase):
|
||
maxDiff = None
|
||
def setUp(self):
|
||
self.food = budget.Category("Food")
|
||
self.entertainment = budget.Category("Entertainment")
|
||
|
||
def test_transfer(self):
|
||
self.food.deposit(900, "deposit")
|
||
self.food.withdraw(45.67, "milk, cereal, eggs, bacon, bread")
|
||
transfer_amount = 20
|
||
good_transfer = self.food.transfer(transfer_amount, self.entertainment)
|
||
actual = self.entertainment.ledger[0]
|
||
expected = {"amount": transfer_amount, "description": "Transfer from Food"}
|
||
self.assertEqual(actual, expected, 'Expected "transfer" method to create a specific ledger item in entertainment object.')
|
||
`);
|
||
const testCode = `
|
||
from unittest import main
|
||
from importlib import reload
|
||
import test_module
|
||
|
||
reload(test_module)
|
||
t = main(module='test_module', exit=False)
|
||
t.result.wasSuccessful()
|
||
`;
|
||
const out = __pyodide.runPython(testCode);
|
||
assert(out);
|
||
}
|
||
})
|
||
```
|
||
|
||
Метод `check_funds` має повернути `False`, якщо сума, передана до методу, перевищує баланс категорії.
|
||
|
||
```js
|
||
({
|
||
test: () => {
|
||
pyodide.FS.writeFile('/home/pyodide/budget.py', code);
|
||
pyodide.FS.writeFile('/home/pyodide/test_module.py',`
|
||
import unittest
|
||
import budget
|
||
from importlib import reload
|
||
|
||
reload(budget)
|
||
class UnitTests(unittest.TestCase):
|
||
maxDiff = None
|
||
def setUp(self):
|
||
self.food = budget.Category("Food")
|
||
|
||
def test_check_funds(self):
|
||
self.food.deposit(10, "deposit")
|
||
actual = self.food.check_funds(20)
|
||
expected = False
|
||
self.assertEqual(actual, expected, 'Expected "check_funds" method to be False')
|
||
`);
|
||
const testCode = `
|
||
from unittest import main
|
||
from importlib import reload
|
||
import test_module
|
||
|
||
reload(test_module)
|
||
t = main(module='test_module', exit=False)
|
||
t.result.wasSuccessful()
|
||
`;
|
||
const out = __pyodide.runPython(testCode);
|
||
assert(out);
|
||
}
|
||
})
|
||
```
|
||
|
||
Метод `check_funds` має повернути `True`, якщо сума, передана до методу, не перевищує баланс категорії.
|
||
|
||
```js
|
||
({
|
||
test: () => {
|
||
pyodide.FS.writeFile('/home/pyodide/budget.py', code);
|
||
pyodide.FS.writeFile('/home/pyodide/test_module.py',`
|
||
import unittest
|
||
import budget
|
||
from importlib import reload
|
||
|
||
reload(budget)
|
||
class UnitTests(unittest.TestCase):
|
||
maxDiff = None
|
||
def setUp(self):
|
||
self.food = budget.Category("Food")
|
||
|
||
def test_check_funds(self):
|
||
self.food.deposit(10, "deposit")
|
||
actual = self.food.check_funds(10)
|
||
expected = True
|
||
self.assertEqual(actual, expected, 'Expected "check_funds" method to be True')
|
||
`);
|
||
const testCode = `
|
||
from unittest import main
|
||
from importlib import reload
|
||
import test_module
|
||
|
||
reload(test_module)
|
||
t = main(module='test_module', exit=False)
|
||
t.result.wasSuccessful()
|
||
`;
|
||
const out = __pyodide.runPython(testCode);
|
||
assert(out);
|
||
}
|
||
})
|
||
```
|
||
|
||
Метод `withdraw` має повернути `False`, якщо зняття не відбулося.
|
||
|
||
```js
|
||
({
|
||
test: () => {
|
||
pyodide.FS.writeFile('/home/pyodide/budget.py', code);
|
||
pyodide.FS.writeFile('/home/pyodide/test_module.py',`
|
||
import unittest
|
||
import budget
|
||
from importlib import reload
|
||
|
||
reload(budget)
|
||
class UnitTests(unittest.TestCase):
|
||
maxDiff = None
|
||
def setUp(self):
|
||
self.food = budget.Category("Food")
|
||
|
||
def test_withdraw_no_funds(self):
|
||
self.food.deposit(100, "deposit")
|
||
good_withdraw = self.food.withdraw(100.10)
|
||
self.assertEqual(good_withdraw, False, 'Expected "withdraw" method to return "False".')
|
||
`);
|
||
const testCode = `
|
||
from unittest import main
|
||
from importlib import reload
|
||
import test_module
|
||
|
||
reload(test_module)
|
||
t = main(module='test_module', exit=False)
|
||
t.result.wasSuccessful()
|
||
`;
|
||
const out = __pyodide.runPython(testCode);
|
||
assert(out);
|
||
}
|
||
})
|
||
```
|
||
|
||
Метод `transfer` має повернути `False`, якщо переказ не відбувся.
|
||
|
||
```js
|
||
({
|
||
test: () => {
|
||
pyodide.FS.writeFile('/home/pyodide/budget.py', code);
|
||
pyodide.FS.writeFile('/home/pyodide/test_module.py',`
|
||
import unittest
|
||
import budget
|
||
from importlib import reload
|
||
|
||
reload(budget)
|
||
class UnitTests(unittest.TestCase):
|
||
maxDiff = None
|
||
def setUp(self):
|
||
self.food = budget.Category("Food")
|
||
self.entertainment = budget.Category("Entertainment")
|
||
|
||
def test_transfer_no_funds(self):
|
||
self.food.deposit(100, "deposit")
|
||
good_transfer = self.food.transfer(200, self.entertainment)
|
||
self.assertEqual(good_transfer, False, 'Expected "transfer" method to return "False".')
|
||
`);
|
||
const testCode = `
|
||
from unittest import main
|
||
from importlib import reload
|
||
import test_module
|
||
|
||
reload(test_module)
|
||
t = main(module='test_module', exit=False)
|
||
t.result.wasSuccessful()
|
||
`;
|
||
const out = __pyodide.runPython(testCode);
|
||
assert(out);
|
||
}
|
||
})
|
||
```
|
||
|
||
Друк екземпляра `Category` має дати інше рядкове представлення об’єкта.
|
||
|
||
```js
|
||
({
|
||
test: () => {
|
||
pyodide.FS.writeFile('/home/pyodide/budget.py', code);
|
||
pyodide.FS.writeFile('/home/pyodide/test_module.py',`
|
||
import unittest
|
||
import budget
|
||
from importlib import reload
|
||
|
||
reload(budget)
|
||
class UnitTests(unittest.TestCase):
|
||
maxDiff = None
|
||
def setUp(self):
|
||
self.food = budget.Category("Food")
|
||
self.entertainment = budget.Category("Entertainment")
|
||
|
||
def test_to_string(self):
|
||
self.food.deposit(900, "deposit")
|
||
self.food.withdraw(45.67, "milk, cereal, eggs, bacon, bread")
|
||
self.food.transfer(20, self.entertainment)
|
||
actual = str(self.food)
|
||
expected = "*************Food*************\\ndeposit 900.00\\nmilk, cereal, eggs, bac -45.67\\nTransfer to Entertainme -20.00\\nTotal: 834.33"
|
||
self.assertEqual(actual, expected, 'Expected different string representation of object.')
|
||
`);
|
||
const testCode = `
|
||
from unittest import main
|
||
from importlib import reload
|
||
import test_module
|
||
|
||
reload(test_module)
|
||
t = main(module='test_module', exit=False)
|
||
t.result.wasSuccessful()
|
||
`
|
||
const out = __pyodide.runPython(testCode);
|
||
assert(out);
|
||
}
|
||
})
|
||
```
|
||
|
||
`create_spend_chart` має надрукувати інше представлення діаграми. Переконайтеся, що всі інтервали точні.
|
||
|
||
```js
|
||
({
|
||
test: () => {
|
||
pyodide.FS.writeFile('/home/pyodide/budget.py', code);
|
||
pyodide.FS.writeFile('/home/pyodide/test_module.py',`
|
||
import unittest
|
||
import budget
|
||
from importlib import reload
|
||
|
||
reload(budget)
|
||
class UnitTests(unittest.TestCase):
|
||
maxDiff = None
|
||
def setUp(self):
|
||
self.food = budget.Category("Food")
|
||
self.entertainment = budget.Category("Entertainment")
|
||
self.business = budget.Category("Business")
|
||
|
||
def test_create_spend_chart(self):
|
||
self.food.deposit(900, "deposit")
|
||
self.entertainment.deposit(900, "deposit")
|
||
self.business.deposit(900, "deposit")
|
||
self.food.withdraw(105.55)
|
||
self.entertainment.withdraw(33.40)
|
||
self.business.withdraw(10.99)
|
||
actual = budget.create_spend_chart([self.business, self.food, self.entertainment])
|
||
expected = "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 "
|
||
self.assertEqual(actual, expected, 'Expected different chart representation. Check that all spacing is exact.')
|
||
`);
|
||
|
||
const testCode = `
|
||
from unittest import main
|
||
from importlib import reload
|
||
import test_module
|
||
reload(test_module)
|
||
t = main(module='test_module', exit=False)
|
||
t.result.wasSuccessful()
|
||
`;
|
||
const out = __pyodide.runPython(testCode);
|
||
assert(out);
|
||
}
|
||
})
|
||
```
|
||
|
||
# --seed--
|
||
|
||
## --seed-contents--
|
||
|
||
```py
|
||
class Category:
|
||
pass
|
||
|
||
def create_spend_chart(categories):
|
||
pass
|
||
```
|
||
|
||
# --solutions--
|
||
|
||
```py
|
||
class Category:
|
||
|
||
def __init__(self, name):
|
||
self.name = name
|
||
self.ledger = []
|
||
self.balance = 0
|
||
self.spent = 0
|
||
|
||
def __str__(self):
|
||
first_line = f'{self.name.center(30, "*")}\n'
|
||
lines = ''
|
||
total = f'Total: {format(self.balance, ".2f")}'
|
||
|
||
for n in range(len(self.ledger)):
|
||
descr = self.ledger[n]["description"][:23]
|
||
am = format(float(self.ledger[n]["amount"]), ".2f")[:7]
|
||
lines = lines + f'{descr:<23}{am:>7}\n'
|
||
|
||
return f'{first_line}{lines}{total}'
|
||
|
||
def deposit(self, amount, description=''):
|
||
self.ledger.append({
|
||
'amount': float(amount),
|
||
'description': description
|
||
})
|
||
self.balance = self.balance + float(amount)
|
||
|
||
def withdraw(self, amount, description=''):
|
||
if self.check_funds(amount):
|
||
self.ledger.append({
|
||
'amount': -float(amount),
|
||
'description': description
|
||
})
|
||
self.balance = self.balance - float(amount)
|
||
self.spent = self.spent + float(amount)
|
||
return True
|
||
else:
|
||
return False
|
||
|
||
def get_balance(self):
|
||
return self.balance
|
||
|
||
def transfer(self, amount, category):
|
||
if self.check_funds(amount):
|
||
# withdraw
|
||
self.ledger.append({
|
||
'amount': -float(amount),
|
||
'description': f'Transfer to {category.name}'
|
||
})
|
||
self.balance = self.balance - float(amount)
|
||
# deposit
|
||
category.deposit(amount, f'Transfer from {self.name}')
|
||
|
||
return True
|
||
else:
|
||
return False
|
||
|
||
def check_funds(self, amount):
|
||
if float(amount) > self.balance:
|
||
return False
|
||
else:
|
||
return True
|
||
|
||
|
||
def create_spend_chart(categories):
|
||
total_expenses = 0
|
||
obj = {}
|
||
col1 = []
|
||
str = []
|
||
final_str = 'Percentage spent by category\n'
|
||
label_max_length = 0
|
||
label_strings = []
|
||
|
||
for category in categories:
|
||
total_expenses = total_expenses + category.spent
|
||
obj[category.name] = {'expenses': category.spent}
|
||
obj[category.name]['label'] = list(category.name)
|
||
if len(obj[category.name]['label']) > label_max_length:
|
||
label_max_length = len(obj[category.name]['label'])
|
||
|
||
for category in categories:
|
||
obj[category.name]['percent'] = (
|
||
(category.spent / total_expenses * 100) // 10) * 10
|
||
obj[category.name]['column'] = []
|
||
for i in range(0, 110, 10):
|
||
if obj[category.name]['percent'] >= i:
|
||
obj[category.name]['column'].insert(0, 'o')
|
||
else:
|
||
obj[category.name]['column'].insert(0, ' ')
|
||
|
||
for i in range(0, 110, 10):
|
||
col1.insert(0, i)
|
||
|
||
for i in range(11):
|
||
str.append("")
|
||
for key in obj:
|
||
str[i] += (f'{obj[key]["column"][i]} ')
|
||
final_str += f'{col1[i]:>3}| {str[i]}\n'
|
||
final_str += f' {"-"*(1+3*len(obj))}\n '
|
||
|
||
for i in range(label_max_length):
|
||
label_strings.append(' ')
|
||
for k in obj:
|
||
if len(obj[k]['label']) < label_max_length:
|
||
obj[k]['label'].extend(
|
||
f'{" "*(label_max_length-len(obj[k]["label"]))}')
|
||
|
||
label_strings[i] += f'{obj[k]["label"][i]} '
|
||
if i < label_max_length - 1:
|
||
label_strings[i] += '\n '
|
||
final_str += label_strings[i]
|
||
|
||
print(final_str)
|
||
return (final_str)
|
||
|
||
```
|