├── .gitignore ├── binary_search_tree.py ├── custom_q.py ├── generate_dummy_data.py ├── hash_table.py ├── linked_list.py ├── server.py ├── sqlitedb.file └── stack.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 98 | __pypackages__/ 99 | 100 | # Celery stuff 101 | celerybeat-schedule 102 | celerybeat.pid 103 | 104 | # SageMath parsed files 105 | *.sage.py 106 | 107 | # Environments 108 | .env 109 | .venv 110 | env/ 111 | venv/ 112 | ENV/ 113 | env.bak/ 114 | venv.bak/ 115 | 116 | # Spyder project settings 117 | .spyderproject 118 | .spyproject 119 | 120 | # Rope project settings 121 | .ropeproject 122 | 123 | # mkdocs documentation 124 | /site 125 | 126 | # mypy 127 | .mypy_cache/ 128 | .dmypy.json 129 | dmypy.json 130 | 131 | # Pyre type checker 132 | .pyre/ 133 | 134 | # pytype static type analyzer 135 | .pytype/ 136 | 137 | # Cython debug symbols 138 | cython_debug/ 139 | -------------------------------------------------------------------------------- /binary_search_tree.py: -------------------------------------------------------------------------------- 1 | class Node: 2 | def __init__(self, data=None): 3 | self.data = data 4 | self.left = None 5 | self.right = None 6 | 7 | class BinarySearchTree: 8 | def __init__(self): 9 | self.root = None 10 | 11 | def _insert_recursive(self, data, node): 12 | if data["id"] < node.data["id"]: 13 | if node.left is None: 14 | node.left = Node(data) 15 | else: 16 | self._insert_recursive(data, node.left) 17 | elif data["id"] > node.data["id"]: 18 | if node.right is None: 19 | node.right = Node(data) 20 | else: 21 | self._insert_recursive(data, node.right) 22 | else: 23 | return 24 | 25 | def insert(self, data): 26 | if self.root is None: 27 | self.root = Node(data) 28 | else: 29 | self._insert_recursive(data, self.root) 30 | 31 | 32 | def _search_recursive(self, blog_post_id, node): 33 | 34 | if blog_post_id == node.data["id"]: 35 | return node.data 36 | 37 | if blog_post_id < node.data["id"] and node.left is not None: 38 | return self._search_recursive(blog_post_id, node.left) 39 | 40 | if blog_post_id > node.data["id"] and node.right is not None: 41 | return self._search_recursive(blog_post_id, node.right) 42 | 43 | return False 44 | 45 | def search(self, blog_post_id): 46 | blog_post_id = int(blog_post_id) 47 | if self.root is None: 48 | return False 49 | 50 | return self._search_recursive(blog_post_id, self.root) 51 | -------------------------------------------------------------------------------- /custom_q.py: -------------------------------------------------------------------------------- 1 | class Node: 2 | def __init__(self, data, next_node): 3 | self.data = data 4 | self.next_node = next_node 5 | 6 | class Queue: 7 | def __init__(self): 8 | self.head = None 9 | self.tail = None 10 | 11 | def enqueue(self, data): 12 | if self.tail is None and self.head is None: 13 | self.tail = self.head = Node(data, None) 14 | return 15 | 16 | self.tail.next_node = Node(data, None) 17 | self.tail = self.tail.next_node 18 | return 19 | 20 | def dequeue(self): 21 | if self.head is None: 22 | return None 23 | removed = self.head 24 | self.head = self.head.next_node 25 | if self.head is None: 26 | self.tail = None 27 | return removed 28 | -------------------------------------------------------------------------------- /generate_dummy_data.py: -------------------------------------------------------------------------------- 1 | from random import randrange 2 | from sqlite3 import Connection as SQLite3Connection 3 | from datetime import datetime 4 | from faker import Faker 5 | from sqlalchemy import event 6 | from sqlalchemy.engine import Engine 7 | from flask import Flask 8 | from flask_sqlalchemy import SQLAlchemy 9 | import server 10 | 11 | # app 12 | app = Flask(__name__) 13 | 14 | # config 15 | app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///sqlitedb.file" 16 | app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = 0 17 | 18 | # configure sqlite3 to enforce foreign key contraints 19 | @event.listens_for(Engine, "connect") 20 | def _set_sqlite_pragma(dbapi_connection, connection_record): 21 | if isinstance(dbapi_connection, SQLite3Connection): 22 | cursor = dbapi_connection.cursor() 23 | cursor.execute("PRAGMA foreign_keys=ON;") 24 | cursor.close() 25 | 26 | 27 | db = SQLAlchemy(app) 28 | now = datetime.now() 29 | 30 | faker = Faker() 31 | 32 | # create dummy users 33 | for i in range(200): 34 | name = faker.name() 35 | address = faker.address() 36 | phone = faker.msisdn() 37 | email = f'{name.replace(" ", "_")}@email.com' 38 | new_user = server.User(name=name, address=address, phone=phone, email=email) 39 | db.session.add(new_user) 40 | db.session.commit() 41 | 42 | # create dummy blog posts 43 | for i in range(200): 44 | title = faker.sentence(5) 45 | body = faker.paragraph(190) 46 | date = faker.date_time() 47 | user_id = randrange(1, 200) 48 | 49 | new_blog_post = server.BlogPost( 50 | title=title, body=body, date=date, user_id=user_id 51 | ) 52 | db.session.add(new_blog_post) 53 | db.session.commit() 54 | 55 | -------------------------------------------------------------------------------- /hash_table.py: -------------------------------------------------------------------------------- 1 | class Node: 2 | def __init__(self, data=None, next_node=None): 3 | self.data = data 4 | self.next_node = next_node 5 | 6 | 7 | class Data: 8 | def __init__(self, key, value): 9 | self.key = key 10 | self.value = value 11 | 12 | 13 | class HashTable: 14 | def __init__(self, table_size): 15 | self.table_size = table_size 16 | self.hash_table = [None] * table_size 17 | 18 | def custom_hash(self, key): 19 | hash_value = 0 20 | for i in key: 21 | hash_value += ord(i) 22 | hash_value = (hash_value * ord(i)) % self.table_size 23 | return hash_value 24 | 25 | def add_key_value(self, key, value): 26 | hashed_key = self.custom_hash(key) 27 | if self.hash_table[hashed_key] is None: 28 | self.hash_table[hashed_key] = Node(Data(key, value), None) 29 | else: 30 | node = self.hash_table[hashed_key] 31 | while node.next_node: 32 | node = node.next_node 33 | 34 | node.next_node = Node(Data(key, value), None) 35 | 36 | def get_value(self, key): 37 | hashed_key = self.custom_hash(key) 38 | if self.hash_table[hashed_key] is not None: 39 | node = self.hash_table[hashed_key] 40 | if node.next_node is None: 41 | return node.data.value 42 | while node.next_node: 43 | if key == node.data.key: 44 | return node.data.value 45 | node = node.next_node 46 | 47 | if key == node.data.key: 48 | return node.data.value 49 | return None 50 | 51 | def print_table(self): 52 | print("{") 53 | for i, val in enumerate(self.hash_table): 54 | if val is not None: 55 | llist_string = "" 56 | node = val 57 | if node.next_node: 58 | while node.next_node: 59 | llist_string += ( 60 | str(node.data.key) + " : " + str(node.data.value) + " --> " 61 | ) 62 | node = node.next_node 63 | llist_string += ( 64 | str(node.data.key) + " : " + str(node.data.value) + " --> None" 65 | ) 66 | print(f" [{i}] {llist_string}") 67 | else: 68 | print(f" [{i}] {val.data.key} : {val.data.value}") 69 | else: 70 | print(f" [{i}] {val}") 71 | print("}") 72 | -------------------------------------------------------------------------------- /linked_list.py: -------------------------------------------------------------------------------- 1 | class Node: 2 | def __init__(self, data=None, next_node=None): 3 | self.data = data 4 | self.next_node = next_node 5 | 6 | 7 | class LinkedList: 8 | def __init__(self): 9 | self.head = None 10 | self.last_node = None 11 | 12 | def to_list(self): 13 | l = [] 14 | if self.head is None: 15 | return l 16 | 17 | node = self.head 18 | while node: 19 | l.append(node.data) 20 | node = node.next_node 21 | return l 22 | 23 | def print_ll(self): 24 | ll_string = "" 25 | node = self.head 26 | if node is None: 27 | print(None) 28 | while node: 29 | ll_string += f" {str(node.data)} ->" 30 | node = node.next_node 31 | 32 | ll_string += " None" 33 | print(ll_string) 34 | 35 | def insert_beginning(self, data): 36 | if self.head is None: 37 | self.head = Node(data, None) 38 | self.last_node = self.head 39 | return 40 | 41 | new_node = Node(data, self.head) 42 | self.head = new_node 43 | 44 | def insert_at_end(self, data): 45 | if self.head is None: 46 | self.insert_beginning(data) 47 | return 48 | 49 | self.last_node.next_node = Node(data, None) 50 | self.last_node = self.last_node.next_node 51 | 52 | def get_user_by_id(self, user_id): 53 | node = self.head 54 | while node: 55 | if node.data["id"] is int(user_id): 56 | return node.data 57 | node = node.next_node 58 | return None 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /server.py: -------------------------------------------------------------------------------- 1 | from sqlite3 import Connection as SQLite3Connection 2 | from datetime import datetime 3 | from sqlalchemy import event 4 | from sqlalchemy.engine import Engine 5 | from flask import Flask, request, jsonify 6 | from flask_sqlalchemy import SQLAlchemy 7 | import linked_list 8 | import hash_table 9 | import binary_search_tree 10 | import custom_q 11 | import stack 12 | import random 13 | 14 | # app 15 | app = Flask(__name__) 16 | 17 | app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///sqlitedb.file" 18 | app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = 0 19 | 20 | # configure sqlite3 to enforce foreign key constraints 21 | @event.listens_for(Engine, "connect") 22 | def _set_sqlite_pragma(dbapi_connection, connection_record): 23 | if isinstance(dbapi_connection, SQLite3Connection): 24 | cursor = dbapi_connection.cursor() 25 | cursor.execute("PRAGMA foreign_keys=ON;") 26 | cursor.close() 27 | 28 | 29 | db = SQLAlchemy(app) 30 | now = datetime.now() 31 | 32 | # models 33 | class User(db.Model): 34 | __tablename__ = "user" 35 | id = db.Column(db.Integer, primary_key=True) 36 | name = db.Column(db.String(50)) 37 | email = db.Column(db.String(50)) 38 | address = db.Column(db.String(200)) 39 | phone = db.Column(db.String(50)) 40 | posts = db.relationship("BlogPost", cascade="all, delete") 41 | 42 | 43 | class BlogPost(db.Model): 44 | __tablename__ = "blog_post" 45 | id = db.Column(db.Integer, primary_key=True) 46 | title = db.Column(db.String(50)) 47 | body = db.Column(db.String(200)) 48 | date = db.Column(db.Date) 49 | user_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False) 50 | 51 | 52 | # routes 53 | @app.route("/user", methods=["POST"]) 54 | def create_user(): 55 | data = request.get_json() 56 | new_user = User( 57 | name=data["name"], 58 | email=data["email"], 59 | address=data["address"], 60 | phone=data["phone"], 61 | ) 62 | db.session.add(new_user) 63 | db.session.commit() 64 | return jsonify({"message": "User created"}), 200 65 | 66 | 67 | @app.route("/user/descending_id", methods=["GET"]) 68 | def get_all_users_descending(): 69 | users = User.query.all() 70 | all_users_ll = linked_list.LinkedList() 71 | 72 | for user in users: 73 | all_users_ll.insert_beginning( 74 | { 75 | "id": user.id, 76 | "name": user.name, 77 | "email": user.email, 78 | "address": user.address, 79 | "phone": user.phone, 80 | } 81 | ) 82 | 83 | return jsonify(all_users_ll.to_list()), 200 84 | 85 | 86 | @app.route("/user/ascending_id", methods=["GET"]) 87 | def get_all_users_ascending(): 88 | users = User.query.all() 89 | all_users_ll = linked_list.LinkedList() 90 | 91 | for user in users: 92 | all_users_ll.insert_at_end( 93 | { 94 | "id": user.id, 95 | "name": user.name, 96 | "email": user.email, 97 | "address": user.address, 98 | "phone": user.phone, 99 | } 100 | ) 101 | 102 | return jsonify(all_users_ll.to_list()), 200 103 | 104 | 105 | @app.route("/user/", methods=["GET"]) 106 | def get_one_user(user_id): 107 | users = User.query.all() 108 | 109 | all_users_ll = linked_list.LinkedList() 110 | 111 | for user in users: 112 | all_users_ll.insert_beginning( 113 | { 114 | "id": user.id, 115 | "name": user.name, 116 | "email": user.email, 117 | "address": user.address, 118 | "phone": user.phone, 119 | } 120 | ) 121 | 122 | user = all_users_ll.get_user_by_id(user_id) 123 | 124 | return jsonify(user), 200 125 | 126 | 127 | @app.route("/user/", methods=["DELETE"]) 128 | def delete_user(user_id): 129 | user = User.query.filter_by(id=user_id).first() 130 | db.session.delete(user) 131 | db.session.commit() 132 | return jsonify({}), 200 133 | 134 | 135 | @app.route("/blog_post/", methods=["POST"]) 136 | def create_blog_post(user_id): 137 | data = request.get_json() 138 | 139 | user = User.query.filter_by(id=user_id).first() 140 | if not user: 141 | return jsonify({"message": "user does not exist!"}), 400 142 | 143 | ht = hash_table.HashTable(10) 144 | 145 | ht.add_key_value("title", data["title"]) 146 | ht.add_key_value("body", data["body"]) 147 | ht.add_key_value("date", now) 148 | ht.add_key_value("user_id", user_id) 149 | 150 | new_blog_post = BlogPost( 151 | title=ht.get_value("title"), 152 | body=ht.get_value("body"), 153 | date=ht.get_value("date"), 154 | user_id=ht.get_value("user_id"), 155 | ) 156 | db.session.add(new_blog_post) 157 | db.session.commit() 158 | return jsonify({"message": "new blog post created"}), 200 159 | 160 | @app.route("/blog_post/", methods=["GET"]) 161 | def get_one_blog_post(blog_post_id): 162 | blog_posts = BlogPost.query.all() 163 | random.shuffle(blog_posts) 164 | 165 | bst = binary_search_tree.BinarySearchTree() 166 | 167 | for post in blog_posts: 168 | bst.insert({ 169 | "id" : post.id, 170 | "title" : post.title, 171 | "body" : post.body, 172 | "user_id" : post.user_id, 173 | }) 174 | 175 | post = bst.search(blog_post_id) 176 | 177 | if not post: 178 | return jsonify({"message": "post not found"}) 179 | 180 | return jsonify(post) 181 | 182 | @app.route("/blog_post/numeric_body", methods=["GET"]) 183 | def get_numeric_post_bodies(): 184 | blog_posts = BlogPost.query.all() 185 | 186 | q = custom_q.Queue() 187 | 188 | for post in blog_posts: 189 | q.enqueue(post) 190 | 191 | return_list = [] 192 | 193 | for _ in range(len(blog_posts)): 194 | post = q.dequeue() 195 | numeric_body = 0 196 | for char in post.data.body: 197 | numeric_body += ord(char) 198 | 199 | post.data.body = numeric_body 200 | 201 | return_list.append( 202 | { 203 | "id": post.data.id, 204 | "title" : post.data.title, 205 | "body" : post.data.body, 206 | "user_id" : post.data.user_id, 207 | } 208 | ) 209 | 210 | return jsonify(return_list) 211 | 212 | @app.route("/blog_post/delete_last_10", methods=["DELETE"]) 213 | def delete_last_10(): 214 | 215 | blog_posts = BlogPost.query.all() 216 | 217 | s = stack.Stack() 218 | 219 | for post in blog_posts: 220 | s.push(post) 221 | 222 | for _ in range(10): 223 | post_to_delete = s.pop() 224 | db.session.delete(post_to_delete.data) 225 | db.session.commit() 226 | 227 | return jsonify({"message" : "success"}) 228 | 229 | if __name__ == "__main__": 230 | app.run(debug=True) 231 | -------------------------------------------------------------------------------- /sqlitedb.file: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kantancoding/FlaskDS/3c5d40bda7fcd9fd1fe2ec34b69dd7a389b9b7d2/sqlitedb.file -------------------------------------------------------------------------------- /stack.py: -------------------------------------------------------------------------------- 1 | class Node: 2 | def __init__(self, data, next_node): 3 | self.data = data 4 | self.next_node = next_node 5 | 6 | 7 | class Stack: 8 | def __init__(self): 9 | self.top = None 10 | 11 | def peek(self): 12 | return self.top 13 | 14 | def push(self, data): 15 | next_node = self.top 16 | new_top = Node(data, next_node) 17 | self.top = new_top 18 | 19 | def pop(self): 20 | if self.top is None: 21 | return None 22 | removed = self.top 23 | self.top = self.top.next_node 24 | return removed 25 | --------------------------------------------------------------------------------