From 616710f42ecce1f25f2c97e3ab10e7cc25f69465 Mon Sep 17 00:00:00 2001 From: Donne Martin Date: Fri, 3 Mar 2017 18:54:08 -0800 Subject: [PATCH] Add Online Chat solution --- .../online_chat/__init__.py | 0 .../online_chat/online_chat.ipynb | 182 ++++++++++++++++++ .../online_chat/online_chat.py | 91 +++++++++ 3 files changed, 273 insertions(+) create mode 100644 solutions/object_oriented_design/online_chat/__init__.py create mode 100644 solutions/object_oriented_design/online_chat/online_chat.ipynb create mode 100644 solutions/object_oriented_design/online_chat/online_chat.py diff --git a/solutions/object_oriented_design/online_chat/__init__.py b/solutions/object_oriented_design/online_chat/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/solutions/object_oriented_design/online_chat/online_chat.ipynb b/solutions/object_oriented_design/online_chat/online_chat.ipynb new file mode 100644 index 00000000..bcc10f28 --- /dev/null +++ b/solutions/object_oriented_design/online_chat/online_chat.ipynb @@ -0,0 +1,182 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This notebook was prepared by [Donne Martin](https://github.com/donnemartin). Source and license info is on [GitHub](https://github.com/donnemartin/system-design-primer-primer)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Design an online chat" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Constraints and assumptions\n", + "\n", + "* Assume we'll focus on the following workflows:\n", + " * Text conversations only\n", + " * Users\n", + " * Add a user\n", + " * Remove a user\n", + " * Update a user\n", + " * Add to a user's friends list\n", + " * Add friend request\n", + " * Approve friend request\n", + " * Reject friend request\n", + " * Remove from a user's friends list\n", + " * Create a group chat\n", + " * Invite friends to a group chat\n", + " * Post a message to a group chat\n", + " * Private 1-1 chat\n", + " * Invite a friend to a private chat\n", + " * Post a meesage to a private chat\n", + "* No need to worry about scaling initially" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Solution" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting online_chat.py\n" + ] + } + ], + "source": [ + "%%writefile online_chat.py\n", + "from abc import ABCMeta\n", + "\n", + "\n", + "class UserService(object):\n", + "\n", + " __metaclass__ = Singleton\n", + "\n", + " def __init__(self):\n", + " self.users_by_id = {} # key: user id, value: User\n", + "\n", + " def add_user(self, user_id, name, pass_hash): # ...\n", + " def remove_user(self, user_id): # ...\n", + " def add_friend_request(self, from_user_id, to_user_id): # ...\n", + " def approve_friend_request(self, from_user_id, to_user_id): # ...\n", + " def reject_friend_request(self, from_user_id, to_user_id): # ...\n", + "\n", + "\n", + "class User(object):\n", + "\n", + " def __init__(self, user_id, name, pass_hash):\n", + " self.user_id = user_id\n", + " self.name = name\n", + " self.pass_hash = pass_hash\n", + " self.friends_by_id = {} # key: friend id, value: User\n", + " self.friend_ids_to_private_chats = {} # key: friend id, value: private chats\n", + " self.group_chats_by_id = {} # key: chat id, value: GroupChat\n", + " self.received_friend_requests_by_friend_id = {} # key: friend id, value: AddRequest\n", + " self.sent_friend_requests_by_friend_id = {} # key: friend id, value: AddRequest\n", + "\n", + " def message_user(self, friend_id, message): # ...\n", + " def message_group(self, group_id, message): # ...\n", + " def send_friend_request(self, friend_id): # ...\n", + " def receive_friend_request(self, friend_id): # ...\n", + " def approve_friend_request(self, friend_id): # ...\n", + " def reject_friend_request(self, friend_id): # ...\n", + "\n", + "\n", + "class Chat(metaclass=ABCMeta):\n", + "\n", + " def __init__(self, chat_id):\n", + " self.chat_id = chat_id\n", + " self.users = []\n", + " self.messages = []\n", + "\n", + "\n", + "class PrivateChat(Chat):\n", + "\n", + " def __init__(self, first_user, second_user):\n", + " super(PrivateChat, self).__init__()\n", + " self.users.append(first_user)\n", + " self.users.append(second_user)\n", + "\n", + "\n", + "class GroupChat(Chat):\n", + "\n", + " def add_user(self, user): # ...\n", + " def remove_user(self, user): # ... \n", + "\n", + "\n", + "class Message(object):\n", + "\n", + " def __init__(self, message_id, message, timestamp):\n", + " self.message_id = message_id\n", + " self.message = message\n", + " self.timestamp = timestamp\n", + "\n", + "\n", + "class AddRequest(object):\n", + "\n", + " def __init__(self, from_user_id, to_user_id, request_status, timestamp):\n", + " self.from_user_id = from_user_id\n", + " self.to_user_id = to_user_id\n", + " self.request_status = request_status\n", + " self.timestamp = timestamp\n", + "\n", + "\n", + "class RequestStatus(Enum):\n", + "\n", + " UNREAD = 0\n", + " READ = 1\n", + " ACCEPTED = 2\n", + " REJECTED = 3\n", + "\n", + "\n", + "class Singleton(type):\n", + "\n", + " _instances = {}\n", + " def __call__(cls, *args, **kwargs):\n", + " if cls not in cls._instances:\n", + " cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)\n", + " return cls._instances[cls]" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.4.3" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/solutions/object_oriented_design/online_chat/online_chat.py b/solutions/object_oriented_design/online_chat/online_chat.py new file mode 100644 index 00000000..266af291 --- /dev/null +++ b/solutions/object_oriented_design/online_chat/online_chat.py @@ -0,0 +1,91 @@ +from abc import ABCMeta + + +class UserService(object): + + __metaclass__ = Singleton + + def __init__(self): + self.users_by_id = {} # key: user id, value: User + + def add_user(self, user_id, name, pass_hash): # ... + def remove_user(self, user_id): # ... + def add_friend_request(self, from_user_id, to_user_id): # ... + def approve_friend_request(self, from_user_id, to_user_id): # ... + def reject_friend_request(self, from_user_id, to_user_id): # ... + + +class User(object): + + def __init__(self, user_id, name, pass_hash): + self.user_id = user_id + self.name = name + self.pass_hash = pass_hash + self.friends_by_id = {} # key: friend id, value: User + self.friend_ids_to_private_chats = {} # key: friend id, value: private chats + self.group_chats_by_id = {} # key: chat id, value: GroupChat + self.received_friend_requests_by_friend_id = {} # key: friend id, value: AddRequest + self.sent_friend_requests_by_friend_id = {} # key: friend id, value: AddRequest + + def message_user(self, friend_id, message): # ... + def message_group(self, group_id, message): # ... + def send_friend_request(self, friend_id): # ... + def receive_friend_request(self, friend_id): # ... + def approve_friend_request(self, friend_id): # ... + def reject_friend_request(self, friend_id): # ... + + +class Chat(metaclass=ABCMeta): + + def __init__(self, chat_id): + self.users = [] + self.chat_id = chat_id + self.messages = [] + + +class PrivateChat(Chat): + + def __init__(self, first_user, second_user): + super(PrivateChat, self).__init__() + self.users.append(first_user) + self.users.append(second_user) + + +class GroupChat(Chat): + + def add_user(self, user): # ... + def remove_user(self, user): # ... + + +class Message(object): + + def __init__(self, message_id, message, timestamp): + self.message_id = message_id + self.message = message + self.timestamp = timestamp + + +class AddRequest(object): + + def __init__(self, from_user_id, to_user_id, request_status, timestamp): + self.from_user_id = from_user_id + self.to_user_id = to_user_id + self.request_status = request_status + self.timestamp = timestamp + + +class RequestStatus(Enum): + + UNREAD = 0 + READ = 1 + ACCEPTED = 2 + REJECTED = 3 + + +class Singleton(type): + + _instances = {} + def __call__(cls, *args, **kwargs): + if cls not in cls._instances: + cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) + return cls._instances[cls] \ No newline at end of file