├── README.md
├── time-calculator
├── main.py
├── README.md
├── time_calculator.py
└── test_module.py
├── arithmetic-formatter
├── main.py
├── arithmetic_arranger.py
├── README.md
└── test_module.py
├── probability-calculator
├── main.py
├── prob_calculator.py
├── test_module.py
└── README.md
├── polygon-area-calculator
├── main.py
├── shape_calculator.py
├── README.md
└── test_module.py
└── budget-app
├── main.py
├── budget.py
├── README.md
└── test_module.py
/README.md:
--------------------------------------------------------------------------------
1 | # Scientific Computing with Python
2 |
3 | This respository captures my personal solutions to the Scientific Computing with Python Certification projects.
4 | - Arithmetic Formatter
5 | - Time Calculator
6 | - Budget App
7 | - Polygon Area Calculator
8 | - Probability Calculator
9 |
--------------------------------------------------------------------------------
/time-calculator/main.py:
--------------------------------------------------------------------------------
1 | # This entrypoint file to be used in development. Start by reading README.md
2 | from time_calculator import add_time
3 | from unittest import main
4 |
5 |
6 | print(add_time("11:06 PM", "2:02"))
7 |
8 |
9 | # Run unit tests automatically
10 | main(module='test_module', exit=False)
--------------------------------------------------------------------------------
/arithmetic-formatter/main.py:
--------------------------------------------------------------------------------
1 | # This entrypoint file to be used in development. Start by reading README.md
2 | from arithmetic_arranger import arithmetic_arranger
3 | from unittest import main
4 |
5 |
6 | print(arithmetic_arranger(["32 + 698", "3801 - 2", "45 + 43", "123 + 49"]))
7 |
8 | # Run unit tests automatically
9 | main(module='test_module', exit=False)
10 |
--------------------------------------------------------------------------------
/probability-calculator/main.py:
--------------------------------------------------------------------------------
1 | # This entrypoint file to be used in development. Start by reading README.md
2 | import prob_calculator
3 | from unittest import main
4 |
5 | hat = prob_calculator.Hat(blue=4, red=2, green=6)
6 | probability = prob_calculator.experiment(
7 | hat=hat,
8 | expected_balls={"blue": 2,
9 | "red": 1},
10 | num_balls_drawn=4,
11 | num_experiments=3000)
12 | print("Probability:", probability)
13 |
14 | # Run unit tests automatically
15 | main(module='test_module', exit=False)
16 |
--------------------------------------------------------------------------------
/polygon-area-calculator/main.py:
--------------------------------------------------------------------------------
1 | # This entrypoint file to be used in development. Start by reading README.md
2 | import shape_calculator
3 | from unittest import main
4 |
5 |
6 | rect = shape_calculator.Rectangle(5, 10)
7 | print(rect.get_area())
8 | rect.set_width(3)
9 | print(rect.get_perimeter())
10 | print(rect)
11 |
12 |
13 | sq = shape_calculator.Square(9)
14 | print(sq.get_area())
15 | sq.set_side(4)
16 | print(sq.get_diagonal())
17 | print(sq)
18 |
19 |
20 |
21 | # Run unit tests automatically
22 | main(module='test_module', exit=False)
--------------------------------------------------------------------------------
/budget-app/main.py:
--------------------------------------------------------------------------------
1 | # This entrypoint file to be used in development. Start by reading README.md
2 | import budget
3 | from budget import create_spend_chart
4 | from unittest import main
5 |
6 | food = budget.Category("Food")
7 | food.deposit(1000, "initial deposit")
8 | food.withdraw(10.15, "groceries")
9 | food.withdraw(15.89, "restaurant and more food for dessert")
10 | print(food.get_balance())
11 | clothing = budget.Category("Clothing")
12 | food.transfer(50, clothing)
13 | clothing.withdraw(25.55)
14 | clothing.withdraw(100)
15 | auto = budget.Category("Auto")
16 | auto.deposit(1000, "initial deposit")
17 | auto.withdraw(15)
18 |
19 | print(food)
20 | print(clothing)
21 |
22 | print(create_spend_chart([food, clothing, auto]))
23 |
24 | # Run unit tests automatically
25 | main(module='test_module', exit=False)
--------------------------------------------------------------------------------
/probability-calculator/prob_calculator.py:
--------------------------------------------------------------------------------
1 | import copy
2 | import random
3 | # Consider using the modules imported above.
4 |
5 | class Hat:
6 |
7 | def __init__(self, **kwargs):
8 | self.contents = []
9 | for key, value in kwargs.items():
10 | for _ in range(value):
11 | self.contents.append(key)
12 |
13 | def draw(self, number):
14 | if number > len(self.contents):
15 | return self.contents
16 | balls = []
17 | for _ in range(number):
18 | choice = random.randrange(len(self.contents))
19 | balls.append(self.contents.pop(choice))
20 | return balls
21 |
22 | def experiment(hat, expected_balls, num_balls_drawn, num_experiments):
23 |
24 | expected_no_of_balls = []
25 | for key in expected_balls:
26 | expected_no_of_balls.append(expected_balls[key])
27 | successes = 0
28 |
29 | for _ in range(num_experiments):
30 | new_hat = copy.deepcopy(hat)
31 | balls = new_hat.draw(num_balls_drawn)
32 |
33 | no_of_balls = []
34 | for key in expected_balls:
35 | no_of_balls.append(balls.count(key))
36 |
37 | if no_of_balls >= expected_no_of_balls:
38 | successes += 1
39 |
40 | return successes/num_experiments
--------------------------------------------------------------------------------
/polygon-area-calculator/shape_calculator.py:
--------------------------------------------------------------------------------
1 | class Rectangle:
2 |
3 | def __init__(self, width, height):
4 | self.width = width
5 | self.height = height
6 |
7 | def __str__(self):
8 | return "Rectangle(width=" + str(self.width) + \
9 | ", height=" + str(self.height) + ")"
10 |
11 | def set_width(self, width):
12 | self.width = width
13 |
14 | def set_height(self, height):
15 | self.height = height
16 |
17 | def get_area(self):
18 | return self.width * self.height
19 |
20 | def get_perimeter(self):
21 | return 2 * (self.width + self.height)
22 |
23 | def get_diagonal(self):
24 | return (self.width ** 2 + self.height ** 2) ** .5
25 |
26 | def get_picture(self):
27 | if self.width > 50 or self.height > 50:
28 | return "Too big for picture."
29 | rectangle = ("*" * self.width + "\n") * self.height
30 | return rectangle
31 |
32 | def get_amount_inside(self, shape):
33 | max_width = self.width // shape.width
34 | max_height = self.height // shape.height
35 | return max_width * max_height
36 |
37 |
38 | class Square(Rectangle):
39 |
40 | def __init__(self, length):
41 | super().__init__(length, length)
42 |
43 | def __str__(self):
44 | return "Square(side=" + str(self.width) + ")"
45 |
46 | def set_side(self, side):
47 | self.width = side
48 | self.height = side
49 |
50 | def set_width(self, side):
51 | self.width = side
52 | self.height = side
53 |
54 | def set_height(self, side):
55 | self.width = side
56 | self.height = side
57 |
--------------------------------------------------------------------------------
/probability-calculator/test_module.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import prob_calculator
3 |
4 | prob_calculator.random.seed(95)
5 | class UnitTests(unittest.TestCase):
6 | def test_hat_class_contents(self):
7 | hat = prob_calculator.Hat(red=3,blue=2)
8 | actual = hat.contents
9 | expected = ["red","red","red","blue","blue"]
10 | self.assertEqual(actual, expected, 'Expected creation of hat object to add correct contents.')
11 |
12 | def test_hat_draw(self):
13 | hat = prob_calculator.Hat(red=5,blue=2)
14 | actual = hat.draw(2)
15 | expected = ['blue', 'red']
16 | self.assertEqual(actual, expected, 'Expected hat draw to return two random items from hat contents.')
17 | actual = len(hat.contents)
18 | expected = 5
19 | self.assertEqual(actual, expected, 'Expected hat draw to reduce number of items in contents.')
20 |
21 | def test_prob_experiment(self):
22 | hat = prob_calculator.Hat(blue=3,red=2,green=6)
23 | probability = prob_calculator.experiment(hat=hat, expected_balls={"blue":2,"green":1}, num_balls_drawn=4, num_experiments=1000)
24 | actual = probability
25 | expected = 0.272
26 | self.assertAlmostEqual(actual, expected, delta = 0.01, msg = 'Expected experiment method to return a different probability.')
27 | hat = prob_calculator.Hat(yellow=5,red=1,green=3,blue=9,test=1)
28 | probability = prob_calculator.experiment(hat=hat, expected_balls={"yellow":2,"blue":3,"test":1}, num_balls_drawn=20, num_experiments=100)
29 | actual = probability
30 | expected = 1.0
31 | self.assertAlmostEqual(actual, expected, delta = 0.01, msg = 'Expected experiment method to return a different probability.')
32 |
33 |
34 | if __name__ == "__main__":
35 | unittest.main()
36 |
--------------------------------------------------------------------------------
/time-calculator/README.md:
--------------------------------------------------------------------------------
1 | # Project Description
2 |
3 | ### Assignment
4 |
5 | Write a function named `add_time` that takes in two required parameters and one optional parameter:
6 | * a start time in the 12-hour clock format (ending in AM or PM)
7 | * a duration time that indicates the number of hours and minutes
8 | * (optional) a starting day of the week, case insensitive
9 |
10 | The function should add the duration time to the start time and return the result.
11 |
12 | If the result will be the next day, it should show `(next day)` after the time. If the result will be more than one day later, it should show `(n days later)` after the time, where "n" is the number of days later.
13 |
14 | If the function is given the optional starting day of the week parameter, then the output should display the day of the week of the result. The day of the week in the output should appear after the time and before the number of days later.
15 |
16 | Below are some examples of different cases the function should handle. Pay close attention to the spacing and punctuation of the results.
17 | ```py
18 | add_time("3:00 PM", "3:10")
19 | # Returns: 6:10 PM
20 |
21 | add_time("11:30 AM", "2:32", "Monday")
22 | # Returns: 2:02 PM, Monday
23 |
24 | add_time("11:43 AM", "00:20")
25 | # Returns: 12:03 PM
26 |
27 | add_time("10:10 PM", "3:30")
28 | # Returns: 1:40 AM (next day)
29 |
30 | add_time("11:43 PM", "24:20", "tueSday")
31 | # Returns: 12:03 AM, Thursday (2 days later)
32 |
33 | add_time("6:30 PM", "205:12")
34 | # Returns: 7:42 AM (9 days later)
35 | ```
36 |
37 | Do not import any Python libraries. Assume that the start times are valid times. The minutes in the duration time will be a whole number less than 60, but the hour can be any whole number.
38 |
39 |
40 |
41 | [FreeCodeCamp](https://www.freecodecamp.org/learn/scientific-computing-with-python/scientific-computing-with-python-projects/time-calculator)
42 |
--------------------------------------------------------------------------------
/time-calculator/time_calculator.py:
--------------------------------------------------------------------------------
1 | def add_time(start, duration, starting_day=""):
2 | # Separte the start into hours and minutes
3 | pieces = start.split()
4 | time = pieces[0].split(":")
5 | end = pieces[1]
6 |
7 | # Calculate 24-hour clock format
8 | if end == "PM" :
9 | hour = int(time[0]) + 12
10 | time[0] = str(hour)
11 |
12 | # Separate the duration into hours and minutes
13 | dur_time = duration.split(":")
14 |
15 | # Add hours and minutes
16 | new_hour = int(time[0]) + int(dur_time[0])
17 | new_minutes = int(time[1]) + int(dur_time[1])
18 |
19 | if new_minutes >= 60 :
20 | hours_add = new_minutes // 60
21 | new_minutes -= hours_add * 60
22 | new_hour += hours_add
23 |
24 | days_add = 0
25 | if new_hour > 24 :
26 | days_add = new_hour // 24
27 | new_hour -= days_add * 24
28 |
29 | # Find AM and PM
30 | # Return to 12-hour clock format
31 | if new_hour > 0 and new_hour < 12 :
32 | end = "AM"
33 | elif new_hour == 12 :
34 | end = "PM"
35 | elif new_hour > 12 :
36 | end = "PM"
37 | new_hour -= 12
38 | else : # new_hour == 0
39 | end = "AM"
40 | new_hour += 12
41 |
42 | if days_add > 0 :
43 | if days_add == 1 :
44 | days_later = " (next day)"
45 | else :
46 | days_later = " (" + str(days_add) + " days later)"
47 | else :
48 | days_later = ""
49 |
50 | week_days = ("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday")
51 |
52 | if starting_day :
53 | weeks = days_add // 7
54 | i = week_days.index(starting_day.lower().capitalize()) + (days_add - 7 * weeks)
55 | if i > 6 :
56 | i -= 7
57 | day = ", " + week_days[i]
58 | else :
59 | day = ""
60 |
61 | new_time= str(new_hour) + ":" + \
62 | (str(new_minutes) if new_minutes > 9 else ("0" + str(new_minutes))) + \
63 | " " + end + day + days_later
64 |
65 | return new_time
--------------------------------------------------------------------------------
/budget-app/budget.py:
--------------------------------------------------------------------------------
1 | class Category:
2 |
3 | def __init__(self, category):
4 | self.category = category
5 | self.ledger = []
6 |
7 |
8 | def __str__(self):
9 | s = self.category.center(30, "*") + "\n"
10 |
11 | for item in self.ledger:
12 | temp = f"{item['description'][:23]:23}{item['amount']:7.2f}"
13 | s += temp + "\n"
14 |
15 | s += "Total: " + str(self.get_balance())
16 | return s
17 |
18 | def deposit(self, amount, description=""):
19 | temp = {}
20 | temp['amount'] = amount
21 | temp['description'] = description
22 | self.ledger.append(temp)
23 |
24 |
25 | def withdraw(self, amount, description=""):
26 | if self.check_funds(amount):
27 | temp = {}
28 | temp['amount'] = 0 - amount
29 | temp['description'] = description
30 | self.ledger.append(temp)
31 | return True
32 | return False
33 |
34 |
35 | def get_balance(self):
36 | balance = 0
37 | for item in self.ledger:
38 | balance += item['amount']
39 | return balance
40 |
41 |
42 | def transfer(self, amount, budget_cat):
43 | if self.check_funds(amount):
44 | self.withdraw(amount, "Transfer to " + budget_cat.category)
45 | budget_cat.deposit(amount, "Transfer from " + self.category)
46 | return True
47 | return False
48 |
49 |
50 | def check_funds(self, amount):
51 | if amount > self.get_balance():
52 | return False
53 | return True
54 |
55 |
56 | def create_spend_chart(categories):
57 | spend = []
58 | for category in categories:
59 | temp = 0
60 | for item in category.ledger:
61 | if item['amount'] < 0:
62 | temp += abs(item['amount'])
63 | spend.append(temp)
64 |
65 | total = sum(spend)
66 | percentage = [i/total * 100 for i in spend]
67 |
68 | s = "Percentage spent by category"
69 | for i in range(100, -1, -10):
70 | s += "\n" + str(i).rjust(3) + "|"
71 | for j in percentage:
72 | if j > i:
73 | s += " o "
74 | else:
75 | s += " "
76 | # Spaces
77 | s += " "
78 | s += "\n ----------"
79 |
80 | cat_length = []
81 | for category in categories:
82 | cat_length.append(len(category.category))
83 | max_length = max(cat_length)
84 |
85 | for i in range(max_length):
86 | s += "\n "
87 | for j in range(len(categories)):
88 | if i < cat_length[j]:
89 | s += " " + categories[j].category[i] + " "
90 | else:
91 | s += " "
92 | # Spaces
93 | s += " "
94 |
95 | return s
96 |
--------------------------------------------------------------------------------
/arithmetic-formatter/arithmetic_arranger.py:
--------------------------------------------------------------------------------
1 | def arithmetic_arranger(problems, answer=False):
2 | # Check the number of problems
3 | if len(problems) > 5:
4 | return "Error: Too many problems."
5 |
6 | first_operand = []
7 | second_operand = []
8 | operator = []
9 |
10 | for problem in problems:
11 | pieces = problem.split()
12 | first_operand.append(pieces[0])
13 | operator.append(pieces[1])
14 | second_operand.append(pieces[2])
15 |
16 | # Check for * or /
17 | if "*" in operator or "/" in operator:
18 | return "Error: Operator must be '+' or '-'."
19 |
20 | # Check the digits
21 | for i in range(len(first_operand)):
22 | if not (first_operand[i].isdigit() and second_operand[i].isdigit()):
23 | return "Error: Numbers must only contain digits."
24 |
25 | # Check the length
26 | for i in range(len(first_operand)):
27 | if len(first_operand[i]) > 4 or len(second_operand[i]) > 4:
28 | return "Error: Numbers cannot be more than four digits."
29 |
30 | first_line = []
31 | second_line = []
32 | third_line = []
33 | fourth_line = []
34 |
35 | for i in range(len(first_operand)):
36 | if len(first_operand[i]) > len(second_operand[i]):
37 | first_line.append(" "*2 + first_operand[i])
38 | else:
39 | first_line.append(" "*(len(second_operand[i]) - len(first_operand[i]) + 2) + first_operand[i])
40 |
41 | for i in range(len(second_operand)):
42 | if len(second_operand[i]) > len(first_operand[i]):
43 | second_line.append(operator[i] + " " + second_operand[i])
44 | else:
45 | second_line.append(operator[i] + " "*(len(first_operand[i]) - len(second_operand[i]) + 1) + second_operand[i])
46 |
47 | for i in range(len(first_operand)):
48 | third_line.append("-"*(max(len(first_operand[i]), len(second_operand[i])) + 2))
49 |
50 | if answer:
51 | for i in range(len(first_operand)):
52 | if operator[i] == "+":
53 | ans = str(int(first_operand[i]) + int(second_operand[i]))
54 | else:
55 | ans = str(int(first_operand[i]) - int(second_operand[i]))
56 |
57 | if len(ans) > max(len(first_operand[i]), len(second_operand[i])):
58 | fourth_line.append(" " + ans)
59 | else:
60 | fourth_line.append(" "*(max(len(first_operand[i]), len(second_operand[i])) - len(ans) + 2) + ans)
61 | arranged_problems = " ".join(first_line) + "\n" + " ".join(second_line) + "\n" + " ".join(third_line) + "\n" + " ".join(fourth_line)
62 | else:
63 | arranged_problems = " ".join(first_line) + "\n" + " ".join(second_line) + "\n" + " ".join(third_line)
64 | return arranged_problems
65 |
--------------------------------------------------------------------------------
/polygon-area-calculator/README.md:
--------------------------------------------------------------------------------
1 | ### Assignment
2 |
3 | In this project you will use object oriented programming to create a Rectangle class and a Square class. The Square class should be a subclass of Rectangle and inherit methods and attributes.
4 |
5 | #### Rectangle class
6 | When a Rectangle object is created, it should be initialized with `width` and `height` attributes. The class should also contain the following methods:
7 | * `set_width`
8 | * `set_height`
9 | * `get_area`: Returns area (`width * height`)
10 | * `get_perimeter`: Returns perimeter (`2 * width + 2 * height`)
11 | * `get_diagonal`: Returns diagonal (`(width ** 2 + height ** 2) ** .5`)
12 | * `get_picture`: Returns a string that represents the shape using lines of "\*". The number of lines should be equal to the height and the number of "\*" in each line should be equal to the width. There should be a new line (`\n`) at the end of each line. If the width or height is larger than 50, this should return the string: "Too big for picture.".
13 | * `get_amount_inside`: Takes another shape (square or rectangle) as an argument. Returns the number of times the passed in shape could fit inside the shape (with no rotations). For instance, a rectangle with a width of 4 and a height of 8 could fit in two squares with sides of 4.
14 |
15 | Additionally, if an instance of a Rectangle is represented as a string, it should look like: `Rectangle(width=5, height=10)`
16 |
17 | #### Square class
18 | The Square class should be a subclass of Rectangle. When a Square object is created, a single side length is passed in. The `__init__` method should store the side length in both the `width` and `height` attributes from the Rectangle class.
19 |
20 | The Square class should be able to access the Rectangle class methods but should also contain a `set_side` method. If an instance of a Square is represented as a string, it should look like: `Square(side=9)`
21 |
22 | Additionally, the `set_width` and `set_height` methods on the Square class should set both the width and height.
23 |
24 | #### Usage example
25 | ```py
26 | rect = shape_calculator.Rectangle(10, 5)
27 | print(rect.get_area())
28 | rect.set_height(3)
29 | print(rect.get_perimeter())
30 | print(rect)
31 | print(rect.get_picture())
32 |
33 | sq = shape_calculator.Square(9)
34 | print(sq.get_area())
35 | sq.set_side(4)
36 | print(sq.get_diagonal())
37 | print(sq)
38 | print(sq.get_picture())
39 |
40 | rect.set_height(8)
41 | rect.set_width(16)
42 | print(rect.get_amount_inside(sq))
43 | ```
44 | That code should return:
45 | ```
46 | 50
47 | 26
48 | Rectangle(width=10, height=3)
49 | **********
50 | **********
51 | **********
52 |
53 | 81
54 | 5.656854249492381
55 | Square(side=4)
56 | ****
57 | ****
58 | ****
59 | ****
60 |
61 | 8
62 | ```
63 |
64 | The unit tests for this project are in `test_module.py`.
65 |
66 |
67 |
68 | [FreeCodeCamp](https://www.freecodecamp.org/learn/scientific-computing-with-python/scientific-computing-with-python-projects/polygon-area-calculator)
--------------------------------------------------------------------------------
/arithmetic-formatter/README.md:
--------------------------------------------------------------------------------
1 | # Project Description
2 |
3 | ### Assignment
4 |
5 | Students in primary school often arrange arithmetic problems vertically to make them easier to solve. For example, "235 + 52" becomes:
6 | ```
7 | 235
8 | + 52
9 | -----
10 | ```
11 |
12 | Create a function that receives a list of strings that are arithmetic problems and returns the problems arranged vertically and side-by-side. The function should optionally take a second argument. When the second argument is set to `True`, the answers should be displayed.
13 |
14 | ### For example
15 |
16 | Function Call:
17 | ```py
18 | arithmetic_arranger(["32 + 698", "3801 - 2", "45 + 43", "123 + 49"])
19 | ```
20 |
21 | Output:
22 | ```
23 | 32 3801 45 123
24 | + 698 - 2 + 43 + 49
25 | ----- ------ ---- -----
26 | ```
27 |
28 | Function Call:
29 | ```py
30 | arithmetic_arranger(["32 + 8", "1 - 3801", "9999 + 9999", "523 - 49"], True)
31 | ```
32 |
33 | Output:
34 | ```
35 | 32 1 9999 523
36 | + 8 - 3801 + 9999 - 49
37 | ---- ------ ------ -----
38 | 40 -3800 19998 474
39 | ```
40 |
41 | ### Rules
42 |
43 | The function will return the correct conversion if the supplied problems are properly formatted, otherwise, it will **return** a **string** that describes an error that is meaningful to the user.
44 |
45 |
46 | * Situations that will return an error:
47 | * If there are **too many problems** supplied to the function. The limit is **five**, anything more will return:
48 | `Error: Too many problems.`
49 | * The appropriate operators the function will accept are **addition** and **subtraction**. Multiplication and division will return an error. Other operators not mentioned in this bullet point will not need to be tested. The error returned will be:
50 | `Error: Operator must be '+' or '-'.`
51 | * Each number (operand) should only contain digits. Otherwise, the function will return:
52 | `Error: Numbers must only contain digits.`
53 | * Each operand (aka number on each side of the operator) has a max of four digits in width. Otherwise, the error string returned will be:
54 | `Error: Numbers cannot be more than four digits.`
55 | * If the user supplied the correct format of problems, the conversion you return will follow these rules:
56 | * There should be a single space between the operator and the longest of the two operands, the operator will be on the same line as the second operand, both operands will be in the same order as provided (the first will be the top one and the second will be the bottom.
57 | * Numbers should be right-aligned.
58 | * There should be four spaces between each problem.
59 | * There should be dashes at the bottom of each problem. The dashes should run along the entire length of each problem individually. (The example above shows what this should look like.)
60 |
61 | [FreeCodeCamp](https://www.freecodecamp.org/learn/scientific-computing-with-python/scientific-computing-with-python-projects/arithmetic-formatter)
62 |
--------------------------------------------------------------------------------
/arithmetic-formatter/test_module.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | from arithmetic_arranger import arithmetic_arranger
3 |
4 |
5 | # the test case
6 | class UnitTests(unittest.TestCase):
7 | def test_arrangement(self):
8 | actual = arithmetic_arranger(["3 + 855", "3801 - 2", "45 + 43", "123 + 49"])
9 | expected = " 3 3801 45 123\n+ 855 - 2 + 43 + 49\n----- ------ ---- -----"
10 | self.assertEqual(actual, expected, 'Expected different output when calling "arithmetic_arranger()" with ["3 + 855", "3801 - 2", "45 + 43", "123 + 49"]')
11 |
12 | actual = arithmetic_arranger(["11 + 4", "3801 - 2999", "1 + 2", "123 + 49", "1 - 9380"])
13 | expected = " 11 3801 1 123 1\n+ 4 - 2999 + 2 + 49 - 9380\n---- ------ --- ----- ------"
14 | self.assertEqual(actual, expected, 'Expected different output when calling "arithmetic_arranger()" with ["11 + 4", "3801 - 2999", "1 + 2", "123 + 49", "1 - 9380"]')
15 |
16 | def test_too_many_problems(self):
17 | actual = arithmetic_arranger(["44 + 815", "909 - 2", "45 + 43", "123 + 49", "888 + 40", "653 + 87"])
18 | expected = "Error: Too many problems."
19 | self.assertEqual(actual, expected, 'Expected calling "arithmetic_arranger()" with more than five problems to return "Error: Too many problems."')
20 |
21 | def test_incorrect_operator(self):
22 | actual = arithmetic_arranger(["3 / 855", "3801 - 2", "45 + 43", "123 + 49"])
23 | expected = "Error: Operator must be '+' or '-'."
24 | self.assertEqual(actual, expected, '''Expected calling "arithmetic_arranger()" with a problem that uses the "/" operator to return "Error: Operator must be '+' or '-'."''')
25 |
26 | def test_too_many_digits(self):
27 | actual = arithmetic_arranger(["24 + 85215", "3801 - 2", "45 + 43", "123 + 49"])
28 | expected = "Error: Numbers cannot be more than four digits."
29 | self.assertEqual(actual, expected, 'Expected calling "arithmetic_arranger()" with a problem that has a number over 4 digits long to return "Error: Numbers cannot be more than four digits."')
30 |
31 | def test_only_digits(self):
32 | actual = arithmetic_arranger(["98 + 3g5", "3801 - 2", "45 + 43", "123 + 49"])
33 | expected = "Error: Numbers must only contain digits."
34 | self.assertEqual(actual, expected, 'Expected calling "arithmetic_arranger()" with a problem that contains a letter character in the number to return "Error: Numbers must only contain digits."')
35 |
36 | def test_solutions(self):
37 | actual = arithmetic_arranger(["32 - 698", "1 - 3801", "45 + 43", "123 + 49"], True)
38 | expected = " 32 1 45 123\n- 698 - 3801 + 43 + 49\n----- ------ ---- -----\n -666 -3800 88 172"
39 | self.assertEqual(actual, expected, 'Expected solutions to be correctly displayed in output when calling "arithmetic_arranger()" with arithemetic problems and a second argument of `True`.')
40 |
41 | if __name__ == "__main__":
42 | unittest.main()
43 |
--------------------------------------------------------------------------------
/time-calculator/test_module.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | from time_calculator import add_time
3 |
4 |
5 | class UnitTests(unittest.TestCase):
6 |
7 | def test_same_period(self):
8 | actual = add_time("3:30 PM", "2:12")
9 | expected = "5:42 PM"
10 | self.assertEqual(actual, expected, 'Expected calling "add_time()" with "3:30 PM", "2:12" to return "5:42 PM"')
11 |
12 | def test_different_period(self):
13 | actual = add_time("11:55 AM", "3:12")
14 | expected = "3:07 PM"
15 | self.assertEqual(actual, expected, 'Expected calling "add_time()" with "11:55 AM", "3:12" to return "3:07 PM"')
16 |
17 | def test_next_day(self):
18 | actual = add_time("9:15 PM", "5:30")
19 | expected = "2:45 AM (next day)"
20 | self.assertEqual(actual, expected, 'Expected time to end with "(next day)" when it is the next day.')
21 |
22 | def test_period_change_at_twelve(self):
23 | actual = add_time("11:40 AM", "0:25")
24 | expected = "12:05 PM"
25 | self.assertEqual(actual, expected, 'Expected period to change from AM to PM at 12:00')
26 |
27 | def test_twenty_four(self):
28 | actual = add_time("2:59 AM", "24:00")
29 | expected = "2:59 AM (next day)"
30 | self.assertEqual(actual, expected, 'Expected calling "add_time()" with "2:59 AM", "24:00" to return "2:59 AM"')
31 |
32 | def test_two_days_later(self):
33 | actual = add_time("11:59 PM", "24:05")
34 | expected = "12:04 AM (2 days later)"
35 | self.assertEqual(actual, expected, 'Expected calling "add_time()" with "11:59 PM", "24:05" to return "12:04 AM (2 days later)"')
36 |
37 | def test_high_duration(self):
38 | actual = add_time("8:16 PM", "466:02")
39 | expected = "6:18 AM (20 days later)"
40 | self.assertEqual(actual, expected, 'Expected calling "add_time()" with "8:16 PM", "466:02" to return "6:18 AM (20 days later)"')
41 |
42 | def test_no_change(self):
43 | actual = add_time("5:01 AM", "0:00")
44 | expected = "5:01 AM"
45 | self.assertEqual(actual, expected, 'Expected adding 0:00 to return initial time.')
46 |
47 | def test_same_period_with_day(self):
48 | actual = add_time("3:30 PM", "2:12", "Monday")
49 | expected = "5:42 PM, Monday"
50 | self.assertEqual(actual, expected, 'Expected calling "add_time()" with "3:30 PM", "2:12", "Monday" to return "5:42 PM, Monday"')
51 |
52 | def test_twenty_four_with_day(self):
53 | actual = add_time("2:59 AM", "24:00", "saturDay")
54 | expected = "2:59 AM, Sunday (next day)"
55 | self.assertEqual(actual, expected, 'Expected calling "add_time()" with "2:59 AM", "24:00", "saturDay" to return "2:59 AM, Sunday (next day)"')
56 |
57 | def test_two_days_later_with_day(self):
58 | actual = add_time("11:59 PM", "24:05", "Wednesday")
59 | expected = "12:04 AM, Friday (2 days later)"
60 | self.assertEqual(actual, expected, 'Expected calling "add_time()" with "2:59 AM", "24:00", "Friday" to return "12:04 AM, Friday (2 days later)"')
61 |
62 | def test_high_duration_with_day(self):
63 | actual = add_time("8:16 PM", "466:02", "tuesday")
64 | expected = "6:18 AM, Monday (20 days later)"
65 | self.assertEqual(actual, expected, 'Expected calling "add_time()" with "8:16 PM", "466:02", "tuesday" to return "6:18 AM, Monday (20 days later)"')
66 |
67 | if __name__ == "__main__":
68 | unittest.main()
--------------------------------------------------------------------------------
/probability-calculator/README.md:
--------------------------------------------------------------------------------
1 | ### Assignment
2 |
3 | Suppose there is a hat containing 5 blue balls, 4 red balls, and 2 green balls. What is the probability that a random draw of 4 balls will contain at least 1 red ball and 2 green balls? While it would be possible to calculate the probability using advanced mathematics, an easier way is to write a program to perform a large number of experiments to estimate an approximate probability.
4 |
5 | For this project, you will write a program to determine the approximate probability of drawing certain balls randomly from a hat.
6 |
7 | First, create a `Hat` class in `prob_calculator.py`. The class should take a variable number of arguments that specify the number of balls of each color that are in the hat. For example, a class object could be created in any of these ways:
8 | ```
9 | hat1 = Hat(yellow=3, blue=2, green=6)
10 | hat2 = Hat(red=5, orange=4)
11 | hat3 = Hat(red=5, orange=4, black=1, blue=0, pink=2, striped=9)
12 | ```
13 |
14 | A hat will always be created with at least one ball. The arguments passed into the hat object upon creation should be converted to a `contents` instance variable. `contents` should be a list of strings containing one item for each ball in the hat. Each item in the list should be a color name representing a single ball of that color. For example, if your hat is `{"red": 2, "blue": 1}`, `contents` should be `["red", "red", "blue"]`.
15 |
16 | The `Hat` class should have a `draw` method that accepts an argument indicating the number of balls to draw from the hat. This method should remove balls at random from `contents` and return those balls as a list of strings. The balls should not go back into the hat during the draw, similar to an urn experiment without replacement. If the number of balls to draw exceeds the available quantity, return all the balls.
17 |
18 | Next, create an `experiment` function in `prob_calculator.py` (not inside the `Hat` class). This function should accept the following arguments:
19 | * `hat`: A hat object containing balls that should be copied inside the function.
20 | * `expected_balls`: An object indicating the exact group of balls to attempt to draw from the hat for the experiment. For example, to determine the probability of drawing 2 blue balls and 1 red ball from the hat, set `expected_balls` to `{"blue":2, "red":1}`.
21 | * `num_balls_drawn`: The number of balls to draw out of the hat in each experiment.
22 | * `num_experiments`: The number of experiments to perform. (The more experiments performed, the more accurate the approximate probability will be.)
23 |
24 | The `experiment` function should return a probability.
25 |
26 | For example, let's say that you want to determine the probability of getting at least 2 red balls and 1 green ball when you draw 5 balls from a hat containing 6 black, 4 red, and 3 green. To do this, we perform `N` experiments, count how many times `M` we get at least 2 red balls and 1 green ball, and estimate the probability as `M/N`. Each experiment consists of starting with a hat containing the specified balls, drawing a number of balls, and checking if we got the balls we were attempting to draw.
27 |
28 | Here is how you would call the `experiment` function based on the example above with 2000 experiments:
29 |
30 | ```
31 | hat = Hat(black=6, red=4, green=3)
32 | probability = experiment(hat=hat,
33 | expected_balls={"red":2,"green":1},
34 | num_balls_drawn=5,
35 | num_experiments=2000)
36 | ```
37 |
38 | Since this is based on random draws, the probability will be slightly different each time the code is run.
39 |
40 |
41 |
42 | [FreeCodeCamp](https://www.freecodecamp.org/learn/scientific-computing-with-python/scientific-computing-with-python-projects/probability-calculator)
--------------------------------------------------------------------------------
/budget-app/README.md:
--------------------------------------------------------------------------------
1 | ## Project Description
2 |
3 | ### Assignment
4 |
5 | Complete the `Category` class in `budget.py`. It should be able to instantiate objects based on different budget categories like *food*, *clothing*, and *entertainment*. When objects are created, they are passed in the name of the category. The class should have an instance variable called `ledger` that is a list. The class should also contain the following methods:
6 |
7 | * A `deposit` method that accepts an amount and description. If no description is given, it should default to an empty string. The method should append an object to the ledger list in the form of `{"amount": amount, "description": description}`.
8 | * A `withdraw` method that is similar to the `deposit` method, but the amount passed in should be stored in the ledger as a negative number. If there are not enough funds, nothing should be added to the ledger. This method should return `True` if the withdrawal took place, and `False` otherwise.
9 | * A `get_balance` method that returns the current balance of the budget category based on the deposits and withdrawals that have occurred.
10 | * A `transfer` method that accepts an amount and another budget category as arguments. The method should add a withdrawal with the amount and the description "Transfer to [Destination Budget Category]". The method should then add a deposit to the other budget category with the amount and the description "Transfer from [Source Budget Category]". If there are not enough funds, nothing should be added to either ledgers. This method should return `True` if the transfer took place, and `False` otherwise.
11 | * A `check_funds` method that accepts an amount as an argument. It returns `False` if the amount is greater than the balance of the budget category and returns `True` otherwise. This method should be used by both the `withdraw` method and `transfer` method.
12 |
13 | When the budget object is printed it should display:
14 | * A title line of 30 characters where the name of the category is centered in a line of `*` characters.
15 | * A list of the items in the ledger. Each line should show the description and amount. The first 23 characters of the description should be displayed, then the amount. The amount should be right aligned, contain two decimal places, and display a maximum of 7 characters.
16 | * A line displaying the category total.
17 |
18 | Here is an example of the output:
19 | ```
20 | *************Food*************
21 | initial deposit 1000.00
22 | groceries -10.15
23 | restaurant and more foo -15.89
24 | Transfer to Clothing -50.00
25 | Total: 923.96
26 | ```
27 |
28 | Besides the `Category` class, create a function (outside of the class) called `create_spend_chart` that takes a list of categories as an argument. It should return a string that is a bar chart.
29 |
30 | The chart should show the percentage spent in each category passed in to the function. The percentage spent should be calculated only with withdrawals and not with deposits. Down the left side of the chart should be labels 0 - 100. The "bars" in the bar chart should be made out of the "o" character. The height of each bar should be rounded down to the nearest 10. The horizontal line below the bars should go two spaces past the final bar. Each category name should be written vertically below the bar. There should be a title at the top that says "Percentage spent by category".
31 |
32 | This function will be tested with up to four categories.
33 |
34 | Look at the example output below very closely and make sure the spacing of the output matches the example exactly.
35 |
36 | ```
37 | Percentage spent by category
38 | 100|
39 | 90|
40 | 80|
41 | 70|
42 | 60| o
43 | 50| o
44 | 40| o
45 | 30| o
46 | 20| o o
47 | 10| o o o
48 | 0| o o o
49 | ----------
50 | F C A
51 | o l u
52 | o o t
53 | d t o
54 | h
55 | i
56 | n
57 | g
58 | ```
59 |
60 | The unit tests for this project are in `test_module.py`.
61 |
62 |
63 |
64 | [FreeCodeCamp](https://www.freecodecamp.org/learn/scientific-computing-with-python/scientific-computing-with-python-projects/budget-app)
65 |
--------------------------------------------------------------------------------
/polygon-area-calculator/test_module.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import shape_calculator
3 |
4 |
5 | class UnitTests(unittest.TestCase):
6 | def setUp(self):
7 | self.rect = shape_calculator.Rectangle(3, 6)
8 | self.sq = shape_calculator.Square(5)
9 |
10 | def test_subclass(self):
11 | actual = issubclass(shape_calculator.Square, shape_calculator.Rectangle)
12 | expected = True
13 | self.assertEqual(actual, expected, 'Expected Square class to be a subclass of the Rectangle class.')
14 |
15 | def test_distinct_classes(self):
16 | actual = shape_calculator.Square is not shape_calculator.Rectangle
17 | expected = True
18 | self.assertEqual(actual, expected, 'Expected Square class to be a distinct class from the Rectangle class.')
19 |
20 | def test_square_is_square_and_rectangle(self):
21 | actual = isinstance(self.sq, shape_calculator.Square) and isinstance(self.sq, shape_calculator.Rectangle)
22 | expected = True
23 | self.assertEqual(actual, expected, 'Expected square object to be an instance of the Square class and the Rectangle class.')
24 |
25 | def test_rectangle_string(self):
26 | actual = str(self.rect)
27 | expected = "Rectangle(width=3, height=6)"
28 | self.assertEqual(actual, expected, 'Expected string representation of rectangle to be "Rectangle(width=3, height=6)"')
29 |
30 | def test_square_string(self):
31 | actual = str(self.sq)
32 | expected = "Square(side=5)"
33 | self.assertEqual(actual, expected, 'Expected string representation of square to be "Square(side=5)"')
34 |
35 | def test_area(self):
36 | actual = self.rect.get_area()
37 | expected = 18
38 | self.assertEqual(actual, expected, 'Expected area of rectangle to be 18')
39 | actual = self.sq.get_area()
40 | expected = 25
41 | self.assertEqual(actual, expected, 'Expected area of square to be 25')
42 |
43 |
44 | def test_perimeter(self):
45 | actual = self.rect.get_perimeter()
46 | expected = 18
47 | self.assertEqual(actual, expected, 'Expected perimeter of rectangle to be 18')
48 | actual = self.sq.get_perimeter()
49 | expected = 20
50 | self.assertEqual(actual, expected, 'Expected perimeter of square to be 20')
51 |
52 | def test_diagonal(self):
53 | actual = self.rect.get_diagonal()
54 | expected = 6.708203932499369
55 | self.assertEqual(actual, expected, 'Expected diagonal of rectangle to be 6.708203932499369')
56 | actual = self.sq.get_diagonal()
57 | expected = 7.0710678118654755
58 | self.assertEqual(actual, expected, 'Expected diagonal of square to be 7.0710678118654755')
59 |
60 | def test_set_atributes(self):
61 | self.rect.set_width(7)
62 | self.rect.set_height(8)
63 | self.sq.set_side(2)
64 | actual = str(self.rect)
65 | expected = "Rectangle(width=7, height=8)"
66 | self.assertEqual(actual, expected, 'Expected string representation of rectangle after setting new values to be "Rectangle(width=7, height=8)"')
67 | actual = str(self.sq)
68 | expected = "Square(side=2)"
69 | self.assertEqual(actual, expected, 'Expected string representation of square after setting new values to be "Square(side=2)"')
70 | self.sq.set_width(4)
71 | actual = str(self.sq)
72 | expected = "Square(side=4)"
73 | self.assertEqual(actual, expected, 'Expected string representation of square after setting width to be "Square(side=4)"')
74 |
75 | def test_rectangle_picture(self):
76 | self.rect.set_width(7)
77 | self.rect.set_height(3)
78 | actual = self.rect.get_picture()
79 | expected = "*******\n*******\n*******\n"
80 | self.assertEqual(actual, expected, 'Expected rectangle picture to be different.')
81 |
82 | def test_squaree_picture(self):
83 | self.sq.set_side(2)
84 | actual = self.sq.get_picture()
85 | expected = "**\n**\n"
86 | self.assertEqual(actual, expected, 'Expected square picture to be different.')
87 |
88 | def test_big_picture(self):
89 | self.rect.set_width(51)
90 | self.rect.set_height(3)
91 | actual = self.rect.get_picture()
92 | expected = "Too big for picture."
93 | self.assertEqual(actual, expected, 'Expected message: "Too big for picture."')
94 |
95 | def test_get_amount_inside(self):
96 | self.rect.set_height(10)
97 | self.rect.set_width(15)
98 | actual = self.rect.get_amount_inside(self.sq)
99 | expected = 6
100 | self.assertEqual(actual, expected, 'Expected `get_amount_inside` to return 6.')
101 |
102 | def test_get_amount_inside_two_rectangles(self):
103 | rect2 = shape_calculator.Rectangle(4, 8)
104 | actual = rect2.get_amount_inside(self.rect)
105 | expected = 1
106 | self.assertEqual(actual, expected, 'Expected `get_amount_inside` to return 1.')
107 |
108 | def test_get_amount_inside_none(self):
109 | rect2 = shape_calculator.Rectangle(2, 3)
110 | actual = rect2.get_amount_inside(self.rect)
111 | expected = 0
112 | self.assertEqual(actual, expected, 'Expected `get_amount_inside` to return 0.')
113 |
114 | if __name__ == "__main__":
115 | unittest.main()
116 |
--------------------------------------------------------------------------------
/budget-app/test_module.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import budget
3 | from budget import create_spend_chart
4 |
5 |
6 | class UnitTests(unittest.TestCase):
7 | def setUp(self):
8 | self.food = budget.Category("Food")
9 | self.entertainment = budget.Category("Entertainment")
10 | self.business = budget.Category("Business")
11 |
12 | def test_deposit(self):
13 | self.food.deposit(900, "deposit")
14 | actual = self.food.ledger[0]
15 | expected = {"amount": 900, "description": "deposit"}
16 | self.assertEqual(actual, expected, 'Expected `deposit` method to create a specific object in the ledger instance variable.')
17 |
18 | def test_deposit_no_description(self):
19 | self.food.deposit(45.56)
20 | actual = self.food.ledger[0]
21 | expected = {"amount": 45.56, "description": ""}
22 | self.assertEqual(actual, expected, 'Expected calling `deposit` method with no description to create a blank description.')
23 |
24 | def test_withdraw(self):
25 | self.food.deposit(900, "deposit")
26 | self.food.withdraw(45.67, "milk, cereal, eggs, bacon, bread")
27 | actual = self.food.ledger[1]
28 | expected = {"amount": -45.67, "description": "milk, cereal, eggs, bacon, bread"}
29 | self.assertEqual(actual, expected, 'Expected `withdraw` method to create a specific object in the ledger instance variable.')
30 |
31 | def test_withdraw_no_description(self):
32 | self.food.deposit(900, "deposit")
33 | good_withdraw = self.food.withdraw(45.67)
34 | actual = self.food.ledger[1]
35 | expected = {"amount": -45.67, "description": ""}
36 | self.assertEqual(actual, expected, 'Expected `withdraw` method with no description to create a blank description.')
37 | self.assertEqual(good_withdraw, True, 'Expected `transfer` method to return `True`.')
38 |
39 | def test_get_balance(self):
40 | self.food.deposit(900, "deposit")
41 | self.food.withdraw(45.67, "milk, cereal, eggs, bacon, bread")
42 | actual = self.food.get_balance()
43 | expected = 854.33
44 | self.assertEqual(actual, expected, 'Expected balance to be 854.33')
45 |
46 | def test_transfer(self):
47 | self.food.deposit(900, "deposit")
48 | self.food.withdraw(45.67, "milk, cereal, eggs, bacon, bread")
49 | good_transfer = self.food.transfer(20, self.entertainment)
50 | actual = self.food.ledger[2]
51 | expected = {"amount": -20, "description": "Transfer to Entertainment"}
52 | self.assertEqual(actual, expected, 'Expected `transfer` method to create a specific ledger item in food object.')
53 | self.assertEqual(good_transfer, True, 'Expected `transfer` method to return `True`.')
54 | actual = self.entertainment.ledger[0]
55 | expected = {"amount": 20, "description": "Transfer from Food"}
56 | self.assertEqual(actual, expected, 'Expected `transfer` method to create a specific ledger item in entertainment object.')
57 |
58 | def test_check_funds(self):
59 | self.food.deposit(10, "deposit")
60 | actual = self.food.check_funds(20)
61 | expected = False
62 | self.assertEqual(actual, expected, 'Expected `check_funds` method to be False')
63 | actual = self.food.check_funds(10)
64 | expected = True
65 | self.assertEqual(actual, expected, 'Expected `check_funds` method to be True')
66 |
67 | def test_withdraw_no_funds(self):
68 | self.food.deposit(100, "deposit")
69 | good_withdraw = self.food.withdraw(100.10)
70 | self.assertEqual(good_withdraw, False, 'Expected `withdraw` method to return `False`.')
71 |
72 | def test_transfer_no_funds(self):
73 | self.food.deposit(100, "deposit")
74 | good_transfer = self.food.transfer(200, self.entertainment)
75 | self.assertEqual(good_transfer, False, 'Expected `transfer` method to return `False`.')
76 |
77 | def test_to_string(self):
78 | self.food.deposit(900, "deposit")
79 | self.food.withdraw(45.67, "milk, cereal, eggs, bacon, bread")
80 | self.food.transfer(20, self.entertainment)
81 | actual = str(self.food)
82 | expected = f"*************Food*************\ndeposit 900.00\nmilk, cereal, eggs, bac -45.67\nTransfer to Entertainme -20.00\nTotal: 834.33"
83 | self.assertEqual(actual, expected, 'Expected different string representation of object.')
84 |
85 | def test_create_spend_chart(self):
86 | self.food.deposit(900, "deposit")
87 | self.entertainment.deposit(900, "deposit")
88 | self.business.deposit(900, "deposit")
89 | self.food.withdraw(105.55)
90 | self.entertainment.withdraw(33.40)
91 | self.business.withdraw(10.99)
92 | actual = create_spend_chart([self.business, self.food, self.entertainment])
93 | 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 "
94 | self.assertEqual(actual, expected, 'Expected different chart representation. Check that all spacing is exact.')
95 |
96 | if __name__ == "__main__":
97 | unittest.main()
98 |
--------------------------------------------------------------------------------