Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion examples/flask/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@
from flask import render_template

from model.post import Post
from model.user import User
from errors import register_error_handlers

from security.basic_authentication import generate_password_hash
from security.basic_authentication import init_basic_auth

app = Flask(__name__)

app = Flask(__name__)
auth = init_basic_auth()
register_error_handlers(app)


Expand Down Expand Up @@ -57,7 +61,19 @@ def update_post(post_id):
return json.dumps(post.save().to_dict())


@app.route("/api/users", methods = ["POST"])
def create_user():
user_data = request.get_json(force=True, silent=True)
if user_data == None:
return "Bad request", 400
hashed_password = generate_password_hash(user_data["password"])
user = User(user_data["username"], hashed_password)
user.save()
return json.dumps(user.to_dict()), 201


@app.route("/", methods = ["GET"])
@auth.login_required
def posts():
return render_template("index.html")

Expand Down
9 changes: 9 additions & 0 deletions examples/flask/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@
''')
conn.commit()

conn.cursor().execute('''
CREATE TABLE IF NOT EXISTS user
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
password TEXT NOT NULL
)
''')
conn.commit()

class SQLite(object):

Expand Down
2 changes: 1 addition & 1 deletion examples/flask/model/post.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from database import SQLite
from errors import NoValuePresentError
from errors import ApplicationError


class Post(object):
Expand Down
74 changes: 74 additions & 0 deletions examples/flask/model/user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
from database import SQLite
from errors import ApplicationError


class User(object):

def __init__(self, username, password, user_id=None):
self.id = user_id
self.username = username
self.password = password

def to_dict(self):
user_data = self.__dict__
del user_data["password"]
return user_data

def save(self):
with SQLite() as db:
cursor = db.execute(self.__get_save_query())
self.id = cursor.lastrowid
return self

@staticmethod
def delete(user_id):
result = None
with SQLite() as db:
result = db.execute("DELETE FROM user WHERE id = ?",
(user_id,))
if result.rowcount == 0:
raise ApplicationError("No value present", 404)

@staticmethod
def find(user_id):
result = None
with SQLite() as db:
result = db.execute(
"SELECT username, password, id FROM user WHERE id = ?",
(user_id,))
user = result.fetchone()
if user is None:
raise ApplicationError(
"Post with id {} not found".format(user_id), 404)
return User(*user)

@staticmethod
def find_by_username(username):
result = None
with SQLite() as db:
result = db.execute(
"SELECT username, password, id FROM user WHERE username = ?",
(username,))
user = result.fetchone()
if user is None:
raise ApplicationError(
"Post with name {} not found".format(username), 404)
return User(*user)

@staticmethod
def all():
with SQLite() as db:
result = db.execute(
"SELECT username, password, id FROM user").fetchall()
return [User(*row) for row in result]

def __get_save_query(self):
query = "{} INTO user {} VALUES {}"
if self.id == None:
args = (self.username, self.password)
query = query.format("INSERT", "(username, password)", args)
else:
args = (self.id, self.username, self.password)
query = query.format("REPLACE", "(id, username, password)", args)
return query

26 changes: 26 additions & 0 deletions examples/flask/security/basic_authentication.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from flask_httpauth import HTTPBasicAuth
from werkzeug.security import generate_password_hash, check_password_hash

from errors import ApplicationError
from model.user import User


def get_password_hash(password):
return generate_password_hash(password)


def __verify_password(username, password):
user = None
try:
user = User.find_by_username(username)
except ApplicationError as err:
if err.status_code != 404:
raise err

return user is not None and check_password_hash(user.password, password)


def init_basic_auth():
auth = HTTPBasicAuth()
auth.verify_password(__verify_password)
return auth
76 changes: 76 additions & 0 deletions homework_2/solution.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
from itertools import product

class InvalidMove(Exception): pass

class InvalidValue(Exception): pass

class InvalidKey(Exception): pass

class NotYourTurn(Exception): pass

class TicTacToeBoard:

VALID_CELLS = ["ABC", "123"]
VALID_MOVES = "OX"
BOARD_REPR = """
-------------
3 |{A3:^3}|{B3:^3}|{C3:^3}|
-------------
2 |{A2:^3}|{B2:^3}|{C2:^3}|
-------------
1 |{A1:^3}|{B1:^3}|{C1:^3}|
-------------
A B C
"""

def __init__(self):
self.__board = {k: "" for k in self.__valid_cells()}
self.__turn = ""

def __str__(self):
return TicTacToeBoard.BOARD_REPR.format(**self.__board)

def __getitem__(self, key):
return self.__board[key]

def __setitem__(self, cell, move):
self.__validate_move(cell, move)
self.__turn = move
self.__board[cell] = move

def game_status(self):
if self.__is_winner("X"):
return "X wins!"
if self.__is_winner("O"):
return "O wins!"
if "" in self.__board.values():
return "Game in progress."
return "Draw!"

def __valid_cells(self):
return ["{}{}".format(x, y) for (x, y) in
product(*TicTacToeBoard.VALID_CELLS)]

def __validate_move(self, cell, move):
if cell not in self.__valid_cells():
raise InvalidKey
if move not in TicTacToeBoard.VALID_MOVES:
raise InvalidValue
if self.__board[cell] is not "":
raise InvalidMove
if self.__turn != "" and self.__turn == move:
raise NotYourTurn

def __is_winner(self, player):
coords = self.__valid_cells()
all_cells = [self.__values(coords[i:9:3]) for i in [0, 1, 2]] +\
[self.__values(coords[i:i+3]) for i in [0, 3, 6]] +\
[self.__values(coords[0:12:4]), self.__values(coords[2:8:2])]

for cells in all_cells:
if all([cell == player for cell in cells]):
return True
return False

def __values(self, cells):
return [self.__board[cell] for cell in cells]
150 changes: 150 additions & 0 deletions homework_2/solution_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import unittest
import solution

from solution import TicTacToeBoard

class SolutionTest(unittest.TestCase):

def test_move_when_cell_number_before_letter_raises(self):
board = TicTacToeBoard()
with self.assertRaises(solution.InvalidKey):
board["1B"] = "X"

def test_move_when_cell_is_just_a_letter(self):
board = TicTacToeBoard()
with self.assertRaises(solution.InvalidKey):
board["B"] = "X"

def test_move_when_cell_is_just_a_number(self):
board = TicTacToeBoard()
with self.assertRaises(solution.InvalidKey):
board["1"] = "X"

def test_move_when_cell_is_invalid_letter(self):
board = TicTacToeBoard()
with self.assertRaises(solution.InvalidKey):
board["Z"] = "X"

def test_move_when_cell_is_invalid_number(self):
board = TicTacToeBoard()
with self.assertRaises(solution.InvalidKey):
board["9"] = "X"

def test_move_when_cell_has_invalid_letter(self):
board = TicTacToeBoard()
with self.assertRaises(solution.InvalidKey):
board["D1"] = "X"

def test_move_when_cell_has_invalid_number(self):
board = TicTacToeBoard()
with self.assertRaises(solution.InvalidKey):
board["B8"] = "X"

def test_move_when_invalid_value(self):
board = TicTacToeBoard()
with self.assertRaises(solution.InvalidValue):
board["B3"] = "d"

def test_can_move_when_cell_valid(self):
board = TicTacToeBoard()
board["B1"] = "X"
board["C1"] = "O"
self.assertEqual(board["B1"], "X")
self.assertEqual(board["C1"], "O")

def test_move_twice_on_same_cell(self):
board = TicTacToeBoard()
with self.assertRaises(solution.InvalidMove):
board["A1"] = 'X'
board["A1"] = 'O'

def test_move_twice_with_same_value(self):
board = TicTacToeBoard()
with self.assertRaises(solution.NotYourTurn):
board["A1"] = 'X'
board["B1"] = 'X'

def test_game_in_progress_when_board_instantiated(self):
board = TicTacToeBoard()
self.assertEqual("Game in progress.", board.game_status())

def test_print_empty_board(self):
board = TicTacToeBoard()
empty_board = """
-------------
3 | | | |
-------------
2 | | | |
-------------
1 | | | |
-------------
A B C
"""
self.assertEqual(empty_board.strip(), str(board).strip())

def test_print_full_board(self):
board = TicTacToeBoard()
board_str = """
-------------
3 | X | X | X |
-------------
2 | O | O | X |
-------------
1 | O | X | O |
-------------
A B C
"""
board["A3"] = "X"
board["A2"] = "O"
board["B3"] = "X"
board["B2"] = "O"
board["C3"] = "X"
board["C1"] = "O"
board["C2"] = "X"
board["A1"] = "O"
board["B1"] = "X"
self.assertEqual(board_str.strip(), str(board).strip())

def test_x_wins(self):
board = TicTacToeBoard()
board["A3"] = "X"
board["C3"] = "O"
board["B2"] = "X"
board["B1"] = "O"
board["C1"] = "X"

self.assertEqual("X wins!", board.game_status())

def test_o_wins(self):
board = TicTacToeBoard()
board["C3"] = "O"
board["A3"] = "X"
board["B2"] = "O"
board["B1"] = "X"
board["A1"] = "O"

self.assertEqual("O wins!", board.game_status())

def test_draw(self):
board = TicTacToeBoard()
board["A1"] = 'O'
board["B1"] = 'X'
board["A3"] = 'O'
board["A2"] = 'X'
board["C2"] = 'O'
board["C3"] = 'X'
board["B3"] = 'O'
board["B2"] = 'X'
board["C1"] = 'O'

self.assertEqual('Draw!', board.game_status())

def test_game_in_progress(self):
board = TicTacToeBoard()
board["A1"] = 'X'
board["A3"] = 'O'

self.assertEqual('Game in progress.', board.game_status())

if __name__ == "__main__":
unittest.main()