import json from collections import defaultdict from functools import wraps from operator import attrgetter VALID_HTTP_METHODS = ['GET', 'POST'] def dump(obj): return obj.__dict__ class User: def __init__(self, name): self.name = name self.owes = defaultdict(float) self.owed_by = defaultdict(float) self.balance = 0.0 def lend(self, borrower, amount): total_owed = self.owed_by[borrower] - self.owes[borrower] + amount if total_owed > 0: self.owed_by[borrower] = total_owed self.owes.pop(borrower, None) elif total_owed < 0: self.owes[borrower] = -total_owed self.owed_by.pop(borrower, None) else: self.owed_by.pop(borrower, None) self.owes.pop(borrower, None) self.balance += amount def borrow(self, lender, amount): self.lend(lender, -amount) @classmethod def from_dict(cls, d): user = cls(d['name']) user.owed_by.update(d['owed_by']) user.owes.update(d['owes']) user.balance = d['balance'] return user class Registry: def __init__(self): self.methods = {k: {} for k in VALID_HTTP_METHODS } def register_url(self, url, http_method): def actual_decorator(f): self.methods[http_method][url] = f @wraps(f) def _impl(f_self, *f_args, **f_kwargs): return f(f_self, *f_args, **f_kwargs) return actual_decorator class RestAPI: registry = Registry() def __init__(self, database=None): self._users = {user['name'] : User.from_dict(user) for user in database['users']} if database else {} @registry.register_url('/users', 'GET') def users(self, payload=None): if payload: requested_users = json.loads(payload)['users'] response = [user for user in self._users.values() if user.name in requested_users] else: response = [user for user in self._users.values()] return json.dumps({'users': response}, default=dump, sort_keys=True) @registry.register_url('/add', 'POST') def add(self, payload=None): payload = json.loads(payload) user = User(payload['user']) self._users[user.name] = user return json.dumps(user, default=dump) @registry.register_url('/iou', 'POST') def iou(self, payload=None): payload = json.loads(payload) lender = payload['lender'] borrower = payload['borrower'] amount = payload['amount'] self._users[lender].lend(borrower, amount) self._users[borrower].borrow(lender, amount) return json.dumps({'users': sorted([self._users[lender], self._users[borrower]], key=attrgetter('name'))}, default=dump, sort_keys=True) def get(self, url, payload=None): methods = self.registry.methods['GET'] if url in methods.keys(): return methods[url](self, payload) else: raise Exception('Error 404') def post(self, url, payload=None): methods = self.registry.methods['POST'] if url in methods.keys(): return methods[url](self, payload) else: raise Exception('Error 404')