Created
November 21, 2024 13:08
-
-
Save misaelbg/4f6e659fcf771d2d80e870e4725df241 to your computer and use it in GitHub Desktop.
Payment Challenge Code
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| """ | |
| Questions: | |
| 1. Complete the `MiniVenmo.create_user()` method to allow our application to create new users. | |
| 2. Complete the `User.pay()` method to allow users to pay each other. Consider the following: if user A is paying user B, user's A balance should be used if there's enough balance to cover the whole payment, if not, user's A credit card should be charged instead. | |
| 3. Venmo has the Feed functionality, that shows the payments that users have been doing in the app. If Bobby paid Carol $5, and then Carol paid Bobby $15, it should look something like this | |
| Bobby paid Carol $5.00 for Coffee | |
| Carol paid Bobby $15.00 for Lunch | |
| Implement the `User.retrieve_activity()` and `MiniVenmo.render_feed()` methods so the MiniVenmo application can render the feed. | |
| 4. Now users should be able to add friends. Implement the `User.add_friend()` method to allow users to add friends. | |
| 5. Now modify the methods involved in rendering the feed to also show when user's added each other as friends. | |
| """ | |
| """ | |
| MiniVenmo! Imagine that your phone and wallet are trying to have a beautiful | |
| baby. In order to make this happen, you must write a social payment app. | |
| Implement a program that will feature users, credit cards, and payment feeds. | |
| """ | |
| import re | |
| import unittest | |
| import uuid | |
| class UsernameException(Exception): | |
| pass | |
| class PaymentException(Exception): | |
| pass | |
| class CreditCardException(Exception): | |
| pass | |
| class Payment: | |
| def __init__(self, amount, actor, target, note): | |
| self.id = str(uuid.uuid4()) | |
| self.amount = float(amount) | |
| self.actor = actor | |
| self.target = target | |
| self.note = note | |
| class User: | |
| def __init__(self, username): | |
| self.credit_card_number = None | |
| self.balance = 0.0 | |
| self.friends = set() | |
| self.feed = [] | |
| if self._is_valid_username(username): | |
| self.username = username | |
| else: | |
| raise UsernameException('Username not valid.') | |
| def retrieve_feed(self): | |
| return self.feed | |
| def add_friend(self, new_friend): | |
| self.friends.add(new_friend) | |
| def add_to_balance(self, amount): | |
| self.balance += float(amount) | |
| def add_credit_card(self, credit_card_number): | |
| if self.credit_card_number is not None: | |
| raise CreditCardException('Only one credit card per user!') | |
| if self._is_valid_credit_card(credit_card_number): | |
| self.credit_card_number = credit_card_number | |
| else: | |
| raise CreditCardException('Invalid credit card number.') | |
| def pay(self, target, amount, note): | |
| if self.username == target.username: | |
| raise PaymentException("User cannot pay themselves.") | |
| if amount <= 0.0: | |
| raise PaymentException("Amount must be greater than zero.") | |
| if self.balance >= amount: | |
| self.pay_with_balance(target, amount, note) | |
| else: | |
| self.pay_with_card(target, amount, note) | |
| def pay_with_card(self, target, amount, note): | |
| amount = float(amount) | |
| if self.username == target.username: | |
| raise PaymentException('User cannot pay themselves.') | |
| elif amount <= 0.0: | |
| raise PaymentException('Amount must be a non-negative number.') | |
| elif self.credit_card_number is None: | |
| raise PaymentException('Must have a credit card to make a payment.') | |
| self._charge_credit_card(self.credit_card_number) | |
| payment = Payment(amount, self, target, note) | |
| target.add_to_balance(amount) | |
| return payment | |
| def pay_with_balance(self, target, amount, note): | |
| self.balance -= amount | |
| target.add_to_balance(mount) | |
| self.feed.append(f"Paid ${amount:.2f} to {target.username} for {note}") | |
| target.feed.append(f"Received ${amount:.2f} from {self.username} for {note}") | |
| def _is_valid_credit_card(self, credit_card_number): | |
| return credit_card_number in ["4111111111111111", "4242424242424242"] | |
| def _is_valid_username(self, username): | |
| return re.match('^[A-Za-z0-9_\\-]{4,15}$', username) | |
| def _charge_credit_card(self, credit_card_number): | |
| # magic method that charges a credit card thru the card processor | |
| if not self._is_valid_credit_card(credit_card_number): | |
| raise CreditCardException("Invalid credit vard number.") | |
| print(f"Charging credit card: {credit_card_number}") | |
| class MiniVenmo: | |
| def __init__(self): | |
| self.users = {} | |
| def create_user(self, username, balance, credit_card_number): | |
| user = User(username) | |
| user.add_to_balance(balance) | |
| user.add_credit_card(credit_card_number) | |
| self.users[username] = user | |
| return user | |
| def render_feed(self, feed): | |
| # Bobby paid Carol $5.00 for Coffee | |
| # Carol paid Bobby $15.00 for Lunch | |
| for payment in feed: | |
| print(f"{payment.actor.username} paid {payment.target.username} ${payment.amount:.2f} for {payment.note}") | |
| @classmethod | |
| def run(cls): | |
| venmo = cls() | |
| bobby = venmo.create_user("Bobby", 5.00, "4111111111111111") | |
| carol = venmo.create_user("Carol", 10.00, "4242424242424242") | |
| try: | |
| # should complete using balance | |
| bobby.pay(carol, 5.00, "Coffee") | |
| # should complete using card | |
| carol.pay(bobby, 15.00, "Lunch") | |
| except PaymentException as e: | |
| print(e) | |
| feed = bobby.retrieve_feed() | |
| venmo.render_feed(feed) | |
| bobby.add_friend(carol) | |
| class TestUser(unittest.TestCase): | |
| def setUp(self): | |
| self.sut = MiniVenmo() | |
| self.sut.create_user("Alice", 100.0, "4111111111111111") | |
| self.sut.create_user("Bob", 50.0, "4242424242424242") | |
| def test_this_works(self): | |
| with self.assertRaises(UsernameException): | |
| raise UsernameException() | |
| def test_create_user_valid(self): | |
| self.sut.create_user("valid_user", 100.0, "4111111111111111") | |
| self.assertIn("valid_user", self.sut.users) | |
| self.assertEqual(self.sut.users["valid_user"].balance, 100.0) | |
| def test_create_user_invalid_username(self): | |
| with self.assertRaises(UsernameException): | |
| self.sut.create_user("Bad@Name", 50.0, "4111111111111111") | |
| def test_pay_with_balance(self): | |
| alice = self.sut.users["Alice"] | |
| bob = self.sut.users["Bob"] | |
| alice.pay(bob, 50.0, "Dinner") | |
| # TODO: i miss test cases for payments | |
| if __name__ == '__main__': | |
| unittest.main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment