diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..596eb2a --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.idea/ +*.pyc +venv/ \ No newline at end of file diff --git a/README.md b/README.md index 11da620..139597f 100755 --- a/README.md +++ b/README.md @@ -1,5 +1,2 @@ -# mock-machine-coding-2 -Welcome to the 2nd Mock Machine Coding Round by [workat.tech](http://workat.tech). -Please visit [this link](https://workat.tech/machine-coding/practice/splitwise-problem-0kp2yneec2q2) to participate. diff --git a/main.py b/main.py new file mode 100644 index 0000000..fe84599 --- /dev/null +++ b/main.py @@ -0,0 +1,61 @@ +# coding=utf-8 +# This is a sample Python script. + +# Press ⇧F10 to execute it or replace it with your code. +# Press Double ⇧ to search everywhere for classes, files, tool windows, actions, and settings. +from models.user_ledger import Ledger +from splitwise import SplitWise +from models.user import User + + +# Press the green button in the gutter to run the script. +if __name__ == '__main__': + splitwise = SplitWise() + ''' + Creating Users + ''' + u1 = User('Ram', 'ram@gmail.com', 1234567890) + u2 = User('Shyam', 'shyam@gmail.com', 9876543211) + u3 = User('Ghanshyam', 'ghanshyam@gmail.com', 432167890) + u4 = User('shyamshan', 'sh@gmail.com', 43216781290) + + ''' + Creating ledger entry for each User. + ''' + Ledger.crate_ledger_entry(u1, [u2, u3, u4]) + Ledger.crate_ledger_entry(u2, [u1, u3, u4]) + Ledger.crate_ledger_entry(u3, [u1, u2, u4]) + Ledger.crate_ledger_entry(u4, [u1, u2, u3]) + + ''' + Mapping username with object. + ''' + user_obj = {"u1": u1, "u2": u2, "u3": u3, "u4": u4} + + ''' + Start Taking input from here. + ''' + + while 1: + command = str(raw_input("Enter your input")) + command = command.split(' ') + users = [] + split_amount = [] + + if command[0] == 'EXPENSE': + paid_by = user_obj[command[1]] + total_amount = float(command[2]) + total_users = int(command[3]) + for i in range(0, total_users): + users.append(user_obj[command[i + 4]]) + split_type = command[total_users + 4] + + if split_type in ['PERCENT', 'EXACT']: + for i in range(0, total_users): + split_amount.append(float(command[total_users + 5 + i])) + splitwise.add_expense(paid_by, total_amount, users, split_type, split_amount=split_amount) + + elif command[0] == "SHOW" and len(command) > 1: + splitwise.show_balance_by_userid(command[1]) + else: + splitwise.show_all_balances() diff --git a/models/__init__.py b/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/models/expense/__init__.py b/models/expense/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/models/expense/equal_expense.py b/models/expense/equal_expense.py new file mode 100644 index 0000000..588f32a --- /dev/null +++ b/models/expense/equal_expense.py @@ -0,0 +1,16 @@ +from models.expense.expense import Expense +from models.user_ledger import Ledger +from utils.utils import is_approx + + +class EqualExpense(Expense): + def __init__(self, users, total_amount, paid_by, **kwargs): + Expense.__init__(self, total_amount, paid_by, kwargs.get('name'), kwargs.get('note')) + self.users = users + self.amount = total_amount / len(users) + self.update_ledger() + + def update_ledger(self): + for i in self.users: + if i.id != self.paid_by.id: + Ledger.update_ledger(self.paid_by, i, self.amount) diff --git a/models/expense/exact_expense.py b/models/expense/exact_expense.py new file mode 100644 index 0000000..8d33c0a --- /dev/null +++ b/models/expense/exact_expense.py @@ -0,0 +1,23 @@ +from models.expense.expense import Expense +from models.user_ledger import Ledger +from utils.utils import is_approx + + +class ExactExpense(Expense): + def __init__(self, users, total_amount, paid_by, amount_list, **kwargs): + Expense.__init__(self, total_amount, paid_by, kwargs.get('name'), kwargs.get('note')) + self.users = users + self.amount_list = amount_list + self.update_ledger() + + def update_ledger(self): + for i in range(0, len(self.users)): + if not self.users[i].id == self.paid_by.id: + Ledger.update_ledger(self.paid_by, self.users[i], self.amount_list[i]) + + @staticmethod + def validate(total_amount, amounts_list): + sum_amount = 0 + for amount in amounts_list: + sum_amount += amount + return is_approx(sum_amount, total_amount) diff --git a/models/expense/expense.py b/models/expense/expense.py new file mode 100644 index 0000000..95e039f --- /dev/null +++ b/models/expense/expense.py @@ -0,0 +1,27 @@ +from abc import ABCMeta, abstractmethod + + +class Expense: + id_count = 0 + user_expense = {} + settled = False + + def __init__(self, total_amount, paid_by, note, name): + self.id = Expense.id_count + Expense.id_count += 1 + self.name = name + self.paid_by = paid_by + self.total_amount = total_amount + self.note = note + + @abstractmethod + def update_ledger(self): + pass + + def mark_as_settled(self): + self.settled = True + + @staticmethod + @abstractmethod + def validate(total_amount, split_amount): + pass diff --git a/models/expense/percentage_expense.py b/models/expense/percentage_expense.py new file mode 100644 index 0000000..2ffb0a6 --- /dev/null +++ b/models/expense/percentage_expense.py @@ -0,0 +1,21 @@ +from models.expense.expense import Expense +from models.user_ledger import Ledger +from utils.utils import is_approx + + +class PercentageExpense(Expense): + def __init__(self, users, total_amount, paid_by, percentages, **kwargs): + Expense.__init__(self, total_amount, paid_by, kwargs.get('name'), kwargs.get('note')) + self.users = users + self.percentage = percentages + self.update_ledger() + + def update_ledger(self): + for i in range(0, len(self.users)): + if not self.users[i].id == self.paid_by.id: + amount = self.total_amount * (1 - self.percentage[i] / 100) + Ledger.update_ledger(self.paid_by, self.users[i], amount) + + @staticmethod + def validate(total_amount, percentages): + return is_approx(sum(percentages), 100) diff --git a/models/user.py b/models/user.py new file mode 100644 index 0000000..80804c9 --- /dev/null +++ b/models/user.py @@ -0,0 +1,23 @@ +class User: + id_count = 0 + + def __init__(self, name, email, mobile_no): + self.id = User.id_count + User.id_count += 1 + self.name = name + self.email = email + self.mobile_no = mobile_no + self.total_balance = 0 + self.total_expense = 0 + + def get_total_balance(self): + return self.total_balance + + def update_total_balance(self, amount): + self.total_balance += amount + + def get_total_expense(self): + return self.total_balance + + def update_total_expense(self, amount): + self.total_expense += amount \ No newline at end of file diff --git a/models/user_ledger.py b/models/user_ledger.py new file mode 100644 index 0000000..76c148a --- /dev/null +++ b/models/user_ledger.py @@ -0,0 +1,49 @@ +class Ledger: + def __init__(self): + pass + ''' + {u1 :{u2:-50} + {u2 :{u1:50} + ''' + transaction_entry = {} + + @staticmethod + def crate_ledger_entry(user, users): + Ledger.transaction_entry[user.id] = {} + for u in users: + Ledger.transaction_entry[user.id][u.id] = 0 + + + @staticmethod + def update_ledger(paid_by, user, amount): + if paid_by.id in Ledger.transaction_entry.keys(): + Ledger.transaction_entry[paid_by.id][user.id] -= round(amount, 2) + Ledger.transaction_entry[user.id][paid_by.id] += round(amount, 2) + else: + print("No Account for this user") + + @staticmethod + def get_user_balance(user_id): + flag=0 + if Ledger.transaction_entry.get(user_id): + ledger = Ledger.transaction_entry[user_id] + for id in ledger: + if ledger[id] > 0: + print "User ", user_id, " owes ", ledger[id], "rupees to ", user_id + flag=1 + if flag==0: + print("No Balance") + + @staticmethod + def get_all_balances(): + flag = 0 + if Ledger.transaction_entry: + for user in Ledger.transaction_entry: + transactions = Ledger.transaction_entry.get(user) + if transactions: + for key, val in transactions.items(): + if val<0: + print "User ", key, " owes ", abs(val), "rupees to ", user + flag = 1 + if flag == 0: + print("NO BALANCE") diff --git a/splitwise.py b/splitwise.py new file mode 100644 index 0000000..367a9c3 --- /dev/null +++ b/splitwise.py @@ -0,0 +1,29 @@ +from models.expense.equal_expense import EqualExpense +from models.expense.exact_expense import ExactExpense +from models.expense.percentage_expense import PercentageExpense +from models.user_ledger import Ledger + + +class SplitWise: + def __init__(self): + self.expenses = [] + self.user_ledgers = {} + + def add_expense(self, paid_by, total_paid_amount, users, split_type, **kwargs): + if split_type == 'EQUAL': + expense = EqualExpense(users, total_paid_amount, paid_by) + elif split_type == 'PERCENT' and PercentageExpense.validate(total_paid_amount, kwargs.get('split_amount')): + expense = PercentageExpense(users, total_paid_amount, paid_by, kwargs.get('split_amount')) + elif split_type == 'EXACT' and ExactExpense.validate(total_paid_amount, kwargs.get('split_amount')): + expense = ExactExpense(users, total_paid_amount, paid_by, kwargs.get('split_amount')) + else: + print("Unknown split type.") + return + self.expenses.append(expense) + print("Expense Added successfully!") + + def show_balance_by_userid(self, user_id): + Ledger.get_user_balance(int(user_id)) + + def show_all_balances(self): + Ledger.get_all_balances() diff --git a/utils/__init__.py b/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/utils/utils.py b/utils/utils.py new file mode 100644 index 0000000..aac3a16 --- /dev/null +++ b/utils/utils.py @@ -0,0 +1,2 @@ +def is_approx(a, b): + return int(a * 100) == int(b * 100)