From 6294e6f8fa511dda1946846048d33a96c3aaed54 Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Sat, 28 Sep 2019 14:03:27 -0700 Subject: [PATCH 01/47] config api --- Procfile | 1 + README.md | 3 +++ app.py | 21 +++++++++++++++++++++ models.py | 1 + runtime.txt | 1 + 5 files changed, 27 insertions(+) create mode 100644 Procfile create mode 100644 runtime.txt diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..8001d1a --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: gunicorn app:app \ No newline at end of file diff --git a/README.md b/README.md index 8d0860b..5f80c44 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,9 @@ ## Resources +### Login + + ### Users ###### User Endpoints diff --git a/app.py b/app.py index b99c362..d7c5e88 100644 --- a/app.py +++ b/app.py @@ -1,12 +1,33 @@ import os from flask import Flask +from flask_sqlalchemy import SQLAlchemy +from flask_marshmallow import Marshmallow app = Flask(__name__) +# base directory +basedir = os.path.abspath(os.path.dirname(__file__)) + +# dev database +DATABASE = 'postgresql://localhost/browser-go' + +# config database +app.config['SQLALCHEMY_DATABASE_URI'] = DATABASE +app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False + +# init database +db = SQLAlchemy(app) + +# init marshmallow +ma = Marshmallow(app) + +# dev server DEBUG = True PORT = 8000 +# Routes + @app.route('/') def hello_world(): return 'Hello World' diff --git a/models.py b/models.py index e69de29..e9ba0dd 100644 --- a/models.py +++ b/models.py @@ -0,0 +1 @@ +from app import db, ma diff --git a/runtime.txt b/runtime.txt new file mode 100644 index 0000000..07261fe --- /dev/null +++ b/runtime.txt @@ -0,0 +1 @@ +python-3.7.4 \ No newline at end of file From 21dbf5b8accfec54ec7b8e9e1ddebfec7def504a Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Sat, 28 Sep 2019 15:53:27 -0700 Subject: [PATCH 02/47] init modules --- games/__init__.py | 0 home/__init__.py | 0 models/__init__.py | 0 rooms/__init__.py | 0 users/__init__.py | 0 5 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 games/__init__.py create mode 100644 home/__init__.py create mode 100644 models/__init__.py create mode 100644 rooms/__init__.py create mode 100644 users/__init__.py diff --git a/games/__init__.py b/games/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/home/__init__.py b/home/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/models/__init__.py b/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/rooms/__init__.py b/rooms/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/users/__init__.py b/users/__init__.py new file mode 100644 index 0000000..e69de29 From f169a9394141563355d72647a21f5d0a5df7cf40 Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Tue, 1 Oct 2019 16:32:11 -0700 Subject: [PATCH 03/47] stub blueprints --- games/game.py | 7 +++++++ home/home.py | 7 +++++++ requirements.txt | 1 + rooms/room.py | 7 +++++++ users/user.py | 11 +++++++++++ 5 files changed, 33 insertions(+) create mode 100644 games/game.py create mode 100644 home/home.py create mode 100644 rooms/room.py create mode 100644 users/user.py diff --git a/games/game.py b/games/game.py new file mode 100644 index 0000000..3f289de --- /dev/null +++ b/games/game.py @@ -0,0 +1,7 @@ +from flask import Blueprint + +game = Blueprint('games', __name__) + +@game.route('/') +def func(): + pass \ No newline at end of file diff --git a/home/home.py b/home/home.py new file mode 100644 index 0000000..22fa366 --- /dev/null +++ b/home/home.py @@ -0,0 +1,7 @@ +from flask import Blueprint + +home = Blueprint('home', __name__) + +@home.route('/home') +def func(): + pass \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 56ac562..23595ca 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,6 +8,7 @@ Jinja2==2.10.1 MarkupSafe==1.1.1 marshmallow==3.2.0 marshmallow-sqlalchemy==0.19.0 +numpy==1.17.2 six==1.12.0 SQLAlchemy==1.3.8 Werkzeug==0.16.0 diff --git a/rooms/room.py b/rooms/room.py new file mode 100644 index 0000000..13081d1 --- /dev/null +++ b/rooms/room.py @@ -0,0 +1,7 @@ +from flask import Blueprint + +room = Blueprint('rooms', __name__) + +@room.route('/') +def func(): + pass \ No newline at end of file diff --git a/users/user.py b/users/user.py new file mode 100644 index 0000000..fd9b6d3 --- /dev/null +++ b/users/user.py @@ -0,0 +1,11 @@ +from flask import Blueprint + +user = Blueprint('users', __name__) + +@user.route('/') +def func(): + pass + +@user.route('/') +def func(): + pass \ No newline at end of file From 740e2b13d4d70745ae6297abb0dd19fb3ed3f7e1 Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Tue, 1 Oct 2019 17:08:53 -0700 Subject: [PATCH 04/47] init db migrate --- app.py | 6 ++- migrations/README | 1 + migrations/alembic.ini | 45 ++++++++++++++++++ migrations/env.py | 96 +++++++++++++++++++++++++++++++++++++++ migrations/script.py.mako | 24 ++++++++++ requirements.txt | 5 ++ 6 files changed, 175 insertions(+), 2 deletions(-) create mode 100644 migrations/README create mode 100644 migrations/alembic.ini create mode 100644 migrations/env.py create mode 100644 migrations/script.py.mako diff --git a/app.py b/app.py index d7c5e88..bada1e9 100644 --- a/app.py +++ b/app.py @@ -2,7 +2,8 @@ import os from flask import Flask from flask_sqlalchemy import SQLAlchemy -from flask_marshmallow import Marshmallow +# from flask_marshmallow import Marshmallow +from flask_migrate import Migrate app = Flask(__name__) @@ -18,9 +19,10 @@ app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False # init database db = SQLAlchemy(app) +migrate = Migrate(app, db) # init marshmallow -ma = Marshmallow(app) +# ma = Marshmallow(app) # dev server DEBUG = True diff --git a/migrations/README b/migrations/README new file mode 100644 index 0000000..98e4f9c --- /dev/null +++ b/migrations/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/migrations/alembic.ini b/migrations/alembic.ini new file mode 100644 index 0000000..f8ed480 --- /dev/null +++ b/migrations/alembic.ini @@ -0,0 +1,45 @@ +# A generic, single database configuration. + +[alembic] +# template used to generate migration files +# file_template = %%(rev)s_%%(slug)s + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/migrations/env.py b/migrations/env.py new file mode 100644 index 0000000..79b8174 --- /dev/null +++ b/migrations/env.py @@ -0,0 +1,96 @@ +from __future__ import with_statement + +import logging +from logging.config import fileConfig + +from sqlalchemy import engine_from_config +from sqlalchemy import pool + +from alembic import context + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +fileConfig(config.config_file_name) +logger = logging.getLogger('alembic.env') + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +from flask import current_app +config.set_main_option( + 'sqlalchemy.url', current_app.config.get( + 'SQLALCHEMY_DATABASE_URI').replace('%', '%%')) +target_metadata = current_app.extensions['migrate'].db.metadata + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline(): + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, target_metadata=target_metadata, literal_binds=True + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online(): + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + + # this callback is used to prevent an auto-migration from being generated + # when there are no changes to the schema + # reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html + def process_revision_directives(context, revision, directives): + if getattr(config.cmd_opts, 'autogenerate', False): + script = directives[0] + if script.upgrade_ops.is_empty(): + directives[:] = [] + logger.info('No changes in schema detected.') + + connectable = engine_from_config( + config.get_section(config.config_ini_section), + prefix='sqlalchemy.', + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure( + connection=connection, + target_metadata=target_metadata, + process_revision_directives=process_revision_directives, + **current_app.extensions['migrate'].configure_args + ) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/migrations/script.py.mako b/migrations/script.py.mako new file mode 100644 index 0000000..2c01563 --- /dev/null +++ b/migrations/script.py.mako @@ -0,0 +1,24 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + + +def upgrade(): + ${upgrades if upgrades else "pass"} + + +def downgrade(): + ${downgrades if downgrades else "pass"} diff --git a/requirements.txt b/requirements.txt index 23595ca..454ea1d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,14 +1,19 @@ +alembic==1.2.1 Click==7.0 Flask==1.1.1 Flask-Cors==3.0.8 flask-marshmallow==0.10.1 +Flask-Migrate==2.5.2 Flask-SQLAlchemy==2.4.1 itsdangerous==1.1.0 Jinja2==2.10.1 +Mako==1.1.0 MarkupSafe==1.1.1 marshmallow==3.2.0 marshmallow-sqlalchemy==0.19.0 numpy==1.17.2 +python-dateutil==2.8.0 +python-editor==1.0.4 six==1.12.0 SQLAlchemy==1.3.8 Werkzeug==0.16.0 From ac02af8cbb504a19e0b22267edf908a2d4a2899c Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Tue, 1 Oct 2019 20:09:46 -0700 Subject: [PATCH 05/47] create basic Game model --- app.py | 4 ++-- models/Branch.py | 6 ++++++ models/Game.py | 42 ++++++++++++++++++++++++++++++++++++++++++ models/GameRoom.py | 0 models/Message.py | 0 models/Move.py | 0 models/TimeSettings.py | 0 models/User.py | 0 8 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 models/Branch.py create mode 100644 models/Game.py create mode 100644 models/GameRoom.py create mode 100644 models/Message.py create mode 100644 models/Move.py create mode 100644 models/TimeSettings.py create mode 100644 models/User.py diff --git a/app.py b/app.py index bada1e9..7544776 100644 --- a/app.py +++ b/app.py @@ -2,7 +2,7 @@ import os from flask import Flask from flask_sqlalchemy import SQLAlchemy -# from flask_marshmallow import Marshmallow +from flask_marshmallow import Marshmallow from flask_migrate import Migrate app = Flask(__name__) @@ -22,7 +22,7 @@ db = SQLAlchemy(app) migrate = Migrate(app, db) # init marshmallow -# ma = Marshmallow(app) +ma = Marshmallow(app) # dev server DEBUG = True diff --git a/models/Branch.py b/models/Branch.py new file mode 100644 index 0000000..72f717f --- /dev/null +++ b/models/Branch.py @@ -0,0 +1,6 @@ +from ..app import db, ma + +class Branch(db.Model): + __table_args__ = {'extend_existing': True} + + id = db.Column(db.Integer, primary_key=True) \ No newline at end of file diff --git a/models/Game.py b/models/Game.py new file mode 100644 index 0000000..65d366c --- /dev/null +++ b/models/Game.py @@ -0,0 +1,42 @@ +from ..app import db, ma +import enum + +class Players(enum.Enum): + BLACK = "The player taking black stones" + WHITE = "The player taking white stones" + +class WinType(enum.Enum): + DRAW = "The game is a draw" + RESIGN = "The game ended in resignation" + SCORE = "The game ended by counting points" + TIME = "The game ended in loss by time out" + VOID = "The game was suspended" + +class Game(db.Model): + __table_args__ = {'extend_existing': True} + + id = db.Column(db.Integer, primary_key=True) + date = db.Column(db.DateTime()) + komi = db.Column(db.Decimal(2,1)) + handicap = db.Column(db.Integer) + board_size = db.Column(db.Integer) + win_type = db.Column(db.Enum(WinType)) + winner = db.Column(db.Enum(Players)) + score = db.Column(db.Decimal(2,1)) + white_captures = db.Column(db.Integer) + black_captures = db.Column(db.Integer) + application = db.Column(db.String(40)) + application_version = db.Column(db.String(20)) + event = db.Column(db.String(40)) + name = db.Column(db.String(40)) + description = db.Column(db.String(200)) + round = db.Column(db.Integer()) + + # foreign keys + # game_room + # time_settings + # player_black + # player_white + + def __init__(self): + pass \ No newline at end of file diff --git a/models/GameRoom.py b/models/GameRoom.py new file mode 100644 index 0000000..e69de29 diff --git a/models/Message.py b/models/Message.py new file mode 100644 index 0000000..e69de29 diff --git a/models/Move.py b/models/Move.py new file mode 100644 index 0000000..e69de29 diff --git a/models/TimeSettings.py b/models/TimeSettings.py new file mode 100644 index 0000000..e69de29 diff --git a/models/User.py b/models/User.py new file mode 100644 index 0000000..e69de29 From 6a91b1c08ae09b6149c8a3fe580d1f5862d82799 Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Tue, 1 Oct 2019 20:14:21 -0700 Subject: [PATCH 06/47] create basic GameRoom model --- models/GameRoom.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/models/GameRoom.py b/models/GameRoom.py index e69de29..be88609 100644 --- a/models/GameRoom.py +++ b/models/GameRoom.py @@ -0,0 +1,16 @@ +from ..app import db, ma +import enum + +class Languages(enum.Enum): + EN: "English" + +class GameRoom(db.Model): + __table_args__ = {'extend_existing': True} + + name = db.Column(db.String(40)) + description = db.Column(db.String(200)) + private = db.Column(db.Boolean()) + language = db.Column(db.Enum(Languages)) + + def __init__(self): + pass \ No newline at end of file From a1c58e40cbedb185e662081a94c38b7097637b44 Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Tue, 1 Oct 2019 20:18:41 -0700 Subject: [PATCH 07/47] create basic Message model --- models/Message.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/models/Message.py b/models/Message.py index e69de29..f3e8396 100644 --- a/models/Message.py +++ b/models/Message.py @@ -0,0 +1,21 @@ +from ..app import db, ma +import enum + +class Players(enum.Enum): + BLACK = "The player taking black stones" + WHITE = "The player taking white stones" + +class Branch(db.Model): + __table_args__ = {'extend_existing': True} + + id = db.Column(db.Integer, primary_key=True) + player = db.Column(db.Enum(Players)) + x_point = db.Column(db.Integer) + y_point = db.Column(db.Integer) + move_number = db.Column(db.Integer) + + # foreign keys + game = db.Column(db.Integer, db.ForeignKey("game.id")) + + def __init__(self): + pass \ No newline at end of file From d6b306da99100f5e52e10ec7899ce0a93785be07 Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Tue, 1 Oct 2019 20:24:15 -0700 Subject: [PATCH 08/47] create basic Move model --- models/Move.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/models/Move.py b/models/Move.py index e69de29..f3e8396 100644 --- a/models/Move.py +++ b/models/Move.py @@ -0,0 +1,21 @@ +from ..app import db, ma +import enum + +class Players(enum.Enum): + BLACK = "The player taking black stones" + WHITE = "The player taking white stones" + +class Branch(db.Model): + __table_args__ = {'extend_existing': True} + + id = db.Column(db.Integer, primary_key=True) + player = db.Column(db.Enum(Players)) + x_point = db.Column(db.Integer) + y_point = db.Column(db.Integer) + move_number = db.Column(db.Integer) + + # foreign keys + game = db.Column(db.Integer, db.ForeignKey("game.id")) + + def __init__(self): + pass \ No newline at end of file From 52ad450fb9c0f165551f57ea027e452ee12182c6 Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Tue, 1 Oct 2019 20:26:40 -0700 Subject: [PATCH 09/47] recreate basic Message model --- models/Message.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/models/Message.py b/models/Message.py index f3e8396..b055a17 100644 --- a/models/Message.py +++ b/models/Message.py @@ -5,17 +5,15 @@ class Players(enum.Enum): BLACK = "The player taking black stones" WHITE = "The player taking white stones" -class Branch(db.Model): +class Message(db.Model): __table_args__ = {'extend_existing': True} id = db.Column(db.Integer, primary_key=True) - player = db.Column(db.Enum(Players)) - x_point = db.Column(db.Integer) - y_point = db.Column(db.Integer) - move_number = db.Column(db.Integer) + date = db.Column(db.DateTime()) + content = db.Column(db.String(200)) - # foreign keys - game = db.Column(db.Integer, db.ForeignKey("game.id")) + # foreing key + game = db.Column(db.Integer, db.ForeignKey("move.id")) def __init__(self): pass \ No newline at end of file From 56ffeb2a0169efd51178f16b03aeeb61cfb55b44 Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Tue, 1 Oct 2019 20:42:21 -0700 Subject: [PATCH 10/47] create basic TimeSettings model --- models/Message.py | 2 +- models/TimeSettings.py | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/models/Message.py b/models/Message.py index b055a17..0c50d42 100644 --- a/models/Message.py +++ b/models/Message.py @@ -13,7 +13,7 @@ class Message(db.Model): content = db.Column(db.String(200)) # foreing key - game = db.Column(db.Integer, db.ForeignKey("move.id")) + move = db.Column(db.Integer, db.ForeignKey("move.id")) def __init__(self): pass \ No newline at end of file diff --git a/models/TimeSettings.py b/models/TimeSettings.py index e69de29..1a18d04 100644 --- a/models/TimeSettings.py +++ b/models/TimeSettings.py @@ -0,0 +1,25 @@ +from ..app import db, ma +import enum + +class TimeTypes(enum.Enum): + BYOYOMI = "Counting by time period" + ABSOLUTE = "One period to use time" + HOURGLASS = "Absolute time for both players" + NONE = "Untimed" + +class TimeSettings(db.Model): + __table_args__ = {'extend_existing': True} + + id = db.Column(db.Integer, primary_key=True) + main_time = db.Column(db.Enum(Time)) + time_period = db.Column(db.Integer) # number of periods + period_length = db.Column(db.Integer) # seconds + overtime = db.Column(db.Enum()) + overtime_period = db.Column(db.Integer) # number of overtime periods + overtime_length = db.Column(db.Integer) # seconds + + # foreing key + game = db.Column(db.Integer, db.ForeignKey("game.id")) + + def __init__(self): + pass \ No newline at end of file From e3d47844bee2f7107da0d30b5d7821d6e93e55d8 Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Wed, 2 Oct 2019 13:55:09 -0700 Subject: [PATCH 11/47] make db migrations --- __init__.py | 0 app.py | 22 +++++- migrations/versions/9519d28f13f0_.py | 109 +++++++++++++++++++++++++++ models/Branch.py | 6 -- models/Game.py | 24 +++--- models/GameRoom.py | 12 +-- models/Message.py | 13 ++-- models/Move.py | 10 ++- models/TimeSettings.py | 10 +-- models/User.py | 58 ++++++++++++++ requirements.txt | 12 +++ 11 files changed, 237 insertions(+), 39 deletions(-) create mode 100644 __init__.py create mode 100644 migrations/versions/9519d28f13f0_.py delete mode 100644 models/Branch.py diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app.py b/app.py index 7544776..8e7d1be 100644 --- a/app.py +++ b/app.py @@ -1,11 +1,18 @@ import os from flask import Flask + +# ! SQLAlchemy > Marshmallow - these must be imported in this order from flask_sqlalchemy import SQLAlchemy from flask_marshmallow import Marshmallow + from flask_migrate import Migrate +from flask_bcrypt import Bcrypt +from flask_cors import CORS + app = Flask(__name__) +CORS(app) # base directory basedir = os.path.abspath(os.path.dirname(__file__)) @@ -17,13 +24,26 @@ DATABASE = 'postgresql://localhost/browser-go' app.config['SQLALCHEMY_DATABASE_URI'] = DATABASE app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False +#init bcrypt +bcrypt = Bcrypt(app) + # init database db = SQLAlchemy(app) -migrate = Migrate(app, db) # init marshmallow ma = Marshmallow(app) +# init all db models +from .models.User import User +from .models.GameRoom import GameRoom +from .models.TimeSettings import TimeSettings +from .models.Game import Game +from .models.Move import Move +from .models.Message import Message + +migrate = Migrate(app, db) + + # dev server DEBUG = True PORT = 8000 diff --git a/migrations/versions/9519d28f13f0_.py b/migrations/versions/9519d28f13f0_.py new file mode 100644 index 0000000..1f327ae --- /dev/null +++ b/migrations/versions/9519d28f13f0_.py @@ -0,0 +1,109 @@ +"""empty message + +Revision ID: 9519d28f13f0 +Revises: +Create Date: 2019-10-02 13:54:17.877590 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '9519d28f13f0' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('game_rooms', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('name', sa.String(length=40), nullable=False), + sa.Column('description', sa.String(length=200), nullable=False), + sa.Column('private', sa.Boolean(), nullable=False), + sa.Column('language', sa.Enum(name='languages'), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('time_settings', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('main_time', sa.Enum('BYOYOMI', 'ABSOLUTE', 'HOURGLASS', 'NONE', name='timetypes'), nullable=False), + sa.Column('time_period', sa.Integer(), nullable=True), + sa.Column('period_length', sa.Integer(), nullable=True), + sa.Column('overtime', sa.Enum('BYOYOMI', 'ABSOLUTE', 'HOURGLASS', 'NONE', name='timetypes'), nullable=False), + sa.Column('overtime_period', sa.Integer(), nullable=True), + sa.Column('overtime_length', sa.Integer(), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('users', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('email', sa.String(length=255), nullable=False), + sa.Column('password', sa.DateTime(), nullable=False), + sa.Column('registered_on', sa.DateTime(), nullable=False), + sa.Column('admin', sa.Boolean(), nullable=False), + sa.Column('rank', sa.Enum('D7', 'D6', 'D5', 'D4', 'D3', 'D2', 'D1', 'K1', 'K2', 'K3', 'K4', 'K5', 'K6', 'K7', 'K8', 'K9', 'K10', 'K11', 'K12', 'K13', 'K14', 'K15', 'K16', 'K17', 'K18', 'K19', 'K20', 'K21', 'K22', 'K23', 'K24', 'K25', 'K26', 'K27', 'K28', 'K29', 'K30', name='ranks'), nullable=True), + sa.Column('elo', sa.Integer(), nullable=True), + sa.Column('rank_certainty', sa.Boolean(), nullable=False), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('email') + ) + op.create_table('games', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('date', sa.DateTime(), nullable=True), + sa.Column('komi', sa.Numeric(precision=2, scale=1), nullable=False), + sa.Column('handicap', sa.Integer(), nullable=False), + sa.Column('board_size', sa.Integer(), nullable=False), + sa.Column('win_type', sa.Enum('DRAW', 'RESIGN', 'SCORE', 'TIME', 'VOID', name='wintype'), nullable=True), + sa.Column('winner', sa.Enum('BLACK', 'WHITE', 'VOID', name='players'), nullable=True), + sa.Column('score', sa.Numeric(precision=2, scale=1), nullable=True), + sa.Column('white_captures', sa.Integer(), nullable=True), + sa.Column('black_captures', sa.Integer(), nullable=True), + sa.Column('application', sa.String(length=40), nullable=True), + sa.Column('application_version', sa.String(length=20), nullable=True), + sa.Column('event', sa.String(length=40), nullable=True), + sa.Column('name', sa.String(length=40), nullable=True), + sa.Column('description', sa.String(length=200), nullable=True), + sa.Column('round', sa.Integer(), nullable=True), + sa.Column('game_room', sa.Integer(), nullable=True), + sa.Column('time_settings', sa.Integer(), nullable=True), + sa.Column('player_black', sa.Integer(), nullable=True), + sa.Column('player_white', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(['game_room'], ['game_rooms.id'], ), + sa.ForeignKeyConstraint(['player_black'], ['users.id'], ), + sa.ForeignKeyConstraint(['player_white'], ['users.id'], ), + sa.ForeignKeyConstraint(['time_settings'], ['time_settings.id'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('moves', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('player', sa.Enum('BLACK', 'WHITE', name='players'), nullable=True), + sa.Column('x_point', sa.Integer(), nullable=True), + sa.Column('y_point', sa.Integer(), nullable=True), + sa.Column('move_number', sa.Integer(), nullable=True), + sa.Column('game', sa.Integer(), nullable=False), + sa.Column('preceding_move', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(['game'], ['games.id'], ), + sa.ForeignKeyConstraint(['preceding_move'], ['moves.id'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('messages', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('date', sa.DateTime(), nullable=False), + sa.Column('content', sa.String(length=200), nullable=False), + sa.Column('move', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['move'], ['moves.id'], ), + sa.PrimaryKeyConstraint('id') + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('messages') + op.drop_table('moves') + op.drop_table('games') + op.drop_table('users') + op.drop_table('time_settings') + op.drop_table('game_rooms') + # ### end Alembic commands ### diff --git a/models/Branch.py b/models/Branch.py deleted file mode 100644 index 72f717f..0000000 --- a/models/Branch.py +++ /dev/null @@ -1,6 +0,0 @@ -from ..app import db, ma - -class Branch(db.Model): - __table_args__ = {'extend_existing': True} - - id = db.Column(db.Integer, primary_key=True) \ No newline at end of file diff --git a/models/Game.py b/models/Game.py index 65d366c..f8ec676 100644 --- a/models/Game.py +++ b/models/Game.py @@ -4,6 +4,7 @@ import enum class Players(enum.Enum): BLACK = "The player taking black stones" WHITE = "The player taking white stones" + VOID = "The game was a draw or voided" class WinType(enum.Enum): DRAW = "The game is a draw" @@ -13,16 +14,17 @@ class WinType(enum.Enum): VOID = "The game was suspended" class Game(db.Model): - __table_args__ = {'extend_existing': True} + __tablename__ = "games" + __table_args__ = {'extend_existing': True} - id = db.Column(db.Integer, primary_key=True) + id = db.Column(db.Integer, primary_key=True, autoincrement=True) date = db.Column(db.DateTime()) - komi = db.Column(db.Decimal(2,1)) - handicap = db.Column(db.Integer) - board_size = db.Column(db.Integer) + komi = db.Column(db.Numeric(2,1), nullable=False) + handicap = db.Column(db.Integer, nullable=False) + board_size = db.Column(db.Integer, nullable=False) win_type = db.Column(db.Enum(WinType)) winner = db.Column(db.Enum(Players)) - score = db.Column(db.Decimal(2,1)) + score = db.Column(db.Numeric(2,1)) white_captures = db.Column(db.Integer) black_captures = db.Column(db.Integer) application = db.Column(db.String(40)) @@ -30,13 +32,13 @@ class Game(db.Model): event = db.Column(db.String(40)) name = db.Column(db.String(40)) description = db.Column(db.String(200)) - round = db.Column(db.Integer()) + round = db.Column(db.Integer) # foreign keys - # game_room - # time_settings - # player_black - # player_white + game_room = db.Column(db.Integer, db.ForeignKey("game_rooms.id")) + time_settings = db.Column(db.Integer, db.ForeignKey("time_settings.id")) + player_black = db.Column(db.Integer, db.ForeignKey("users.id")) + player_white = db.Column(db.Integer, db.ForeignKey("users.id")) def __init__(self): pass \ No newline at end of file diff --git a/models/GameRoom.py b/models/GameRoom.py index be88609..5da8f4e 100644 --- a/models/GameRoom.py +++ b/models/GameRoom.py @@ -5,12 +5,14 @@ class Languages(enum.Enum): EN: "English" class GameRoom(db.Model): - __table_args__ = {'extend_existing': True} + __tablename__ = "game_rooms" + __table_args__ = {'extend_existing': True} - name = db.Column(db.String(40)) - description = db.Column(db.String(200)) - private = db.Column(db.Boolean()) - language = db.Column(db.Enum(Languages)) + id = db.Column(db.Integer, primary_key=True, autoincrement=True) + name = db.Column(db.String(40), nullable=False) + description = db.Column(db.String(200), nullable=False) + private = db.Column(db.Boolean(), nullable=False, default=False) + language = db.Column(db.Enum(Languages), nullable=False) def __init__(self): pass \ No newline at end of file diff --git a/models/Message.py b/models/Message.py index 0c50d42..7a273a4 100644 --- a/models/Message.py +++ b/models/Message.py @@ -6,14 +6,15 @@ class Players(enum.Enum): WHITE = "The player taking white stones" class Message(db.Model): - __table_args__ = {'extend_existing': True} + __tablename__ = "messages" + __table_args__ = {'extend_existing': True} - id = db.Column(db.Integer, primary_key=True) - date = db.Column(db.DateTime()) - content = db.Column(db.String(200)) + id = db.Column(db.Integer, primary_key=True, autoincrement=True) + date = db.Column(db.DateTime(), nullable=False) + content = db.Column(db.String(200), nullable=False) - # foreing key - move = db.Column(db.Integer, db.ForeignKey("move.id")) + # foreign key + move = db.Column(db.Integer, db.ForeignKey("moves.id"), nullable=False) def __init__(self): pass \ No newline at end of file diff --git a/models/Move.py b/models/Move.py index f3e8396..dc71f49 100644 --- a/models/Move.py +++ b/models/Move.py @@ -5,17 +5,19 @@ class Players(enum.Enum): BLACK = "The player taking black stones" WHITE = "The player taking white stones" -class Branch(db.Model): - __table_args__ = {'extend_existing': True} +class Move(db.Model): + __tablename__ = "moves" + __table_args__ = {'extend_existing': True} - id = db.Column(db.Integer, primary_key=True) + id = db.Column(db.Integer, primary_key=True, autoincrement=True) player = db.Column(db.Enum(Players)) x_point = db.Column(db.Integer) y_point = db.Column(db.Integer) move_number = db.Column(db.Integer) # foreign keys - game = db.Column(db.Integer, db.ForeignKey("game.id")) + game = db.Column(db.Integer, db.ForeignKey("games.id"), nullable=False) + preceding_move = db.Column(db.Integer, db.ForeignKey("moves.id")) def __init__(self): pass \ No newline at end of file diff --git a/models/TimeSettings.py b/models/TimeSettings.py index 1a18d04..bdf142e 100644 --- a/models/TimeSettings.py +++ b/models/TimeSettings.py @@ -8,18 +8,16 @@ class TimeTypes(enum.Enum): NONE = "Untimed" class TimeSettings(db.Model): - __table_args__ = {'extend_existing': True} + __table_args__ = {'extend_existing': True} - id = db.Column(db.Integer, primary_key=True) - main_time = db.Column(db.Enum(Time)) + id = db.Column(db.Integer, primary_key=True, autoincrement=True) + main_time = db.Column(db.Enum(TimeTypes), nullable=False) time_period = db.Column(db.Integer) # number of periods period_length = db.Column(db.Integer) # seconds - overtime = db.Column(db.Enum()) + overtime = db.Column(db.Enum(TimeTypes), nullable=False) overtime_period = db.Column(db.Integer) # number of overtime periods overtime_length = db.Column(db.Integer) # seconds - # foreing key - game = db.Column(db.Integer, db.ForeignKey("game.id")) def __init__(self): pass \ No newline at end of file diff --git a/models/User.py b/models/User.py index e69de29..b2888eb 100644 --- a/models/User.py +++ b/models/User.py @@ -0,0 +1,58 @@ +from ..app import db, ma, bcrypt +import enum + +class Ranks(enum.Enum): # with minimal Elo rating + D7 = "Seven Dan" # Elo 2700+ + D6 = "Six Dan" + D5 = "Five Dan" # Elo 2500 + D4 = "Four Dan" + D3 = "Three Dan" + D2 = "Two Dan" + D1 = "One Dan" + K1 = "One Kyu" # Elo 2000 + K2 = "Two Kyu" + K3 = "Three Kyu" + K4 = "Four Kyu" + K5 = "Five Kyu" + K6 = "Six Kyu" # Elo 1500 + K7 = "Seven Kyu" + K8 = "Eight Kyu" + K9 = "Nine Kyu" + K10 = "Ten Kyu" + K11 = "Eleven Kyu" # ELo 1000 + K12 = "Twelve Kyu" + K13 = "Thirteen Kyu" + K14 = "Fourteen Kyu" + K15 = "Fifteen Kyu" + K16 = "Sixteen Kyu" # Elo 500 + K17 = "Seventeen Kyu" + K18 = "Eighteen Kyu" + K19 = "Nineteen Kyu" + K20 = "Twenty Kyu" + K21 = "Twenty-One Kyu" # Elo 0 + K22 = "Twenty-Two Kyu" + K23 = "Twenty-Three Kyu" + K24 = "Twenty-Four Kyu" + K25 = "Twenty-Five Kyu" + K26 = "Twenty-Six Kyu" # Elo -500 + K27 = "Twenty-Seven Kyu" + K28 = "Twenty-Eight Kyu" + K29 = "Twenty-Nine Kyu" + K30 = "Thirty Kyu" # Elo -900 + +class User(db.Model): + __tablename__ = "users" + + id = db.Column(db.Integer, primary_key=True, autoincrement=True) + email = db.Column(db.String(255), unique=True, nullable=False) + password = db.Column(db.DateTime, nullable=False) + registered_on = db.Column(db.DateTime, nullable=False) + admin = db.Column(db.Boolean, nullable=False, default=False) + rank = db.Column(db.Enum(Ranks)) + elo = db.Column(db.Integer) + rank_certainty = db.Column(db.Boolean, nullable=False, default=False) + + def __init__(self): + self.password = bcrypt.generate_password_hash( + password, app.config.get('BCRYPT_LOG_ROUNDS') + ).decode() diff --git a/requirements.txt b/requirements.txt index 454ea1d..547158f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,19 +1,31 @@ alembic==1.2.1 +astroid==2.3.1 +bcrypt==3.1.7 +cffi==1.12.3 Click==7.0 Flask==1.1.1 +Flask-Bcrypt==0.7.1 Flask-Cors==3.0.8 flask-marshmallow==0.10.1 Flask-Migrate==2.5.2 Flask-SQLAlchemy==2.4.1 +isort==4.3.21 itsdangerous==1.1.0 Jinja2==2.10.1 +lazy-object-proxy==1.4.2 Mako==1.1.0 MarkupSafe==1.1.1 marshmallow==3.2.0 marshmallow-sqlalchemy==0.19.0 +mccabe==0.6.1 numpy==1.17.2 +psycopg2==2.8.3 +pycparser==2.19 +pylint==2.4.2 python-dateutil==2.8.0 python-editor==1.0.4 six==1.12.0 SQLAlchemy==1.3.8 +typed-ast==1.4.0 Werkzeug==0.16.0 +wrapt==1.11.2 From 9ce70cff4aea6e836dec60c9a23a8881f822ded7 Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Wed, 2 Oct 2019 14:50:56 -0700 Subject: [PATCH 12/47] add is_main to Move --- migrations/versions/16c70b11242e_.py | 28 ++++++++++++++++++++++++++++ models/Move.py | 1 + 2 files changed, 29 insertions(+) create mode 100644 migrations/versions/16c70b11242e_.py diff --git a/migrations/versions/16c70b11242e_.py b/migrations/versions/16c70b11242e_.py new file mode 100644 index 0000000..af28b38 --- /dev/null +++ b/migrations/versions/16c70b11242e_.py @@ -0,0 +1,28 @@ +"""empty message + +Revision ID: 16c70b11242e +Revises: 9519d28f13f0 +Create Date: 2019-10-02 14:50:27.180692 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '16c70b11242e' +down_revision = '9519d28f13f0' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('moves', sa.Column('is_main', sa.Boolean(), nullable=False)) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('moves', 'is_main') + # ### end Alembic commands ### diff --git a/models/Move.py b/models/Move.py index dc71f49..15f92b0 100644 --- a/models/Move.py +++ b/models/Move.py @@ -14,6 +14,7 @@ class Move(db.Model): x_point = db.Column(db.Integer) y_point = db.Column(db.Integer) move_number = db.Column(db.Integer) + is_main = db.Column(db.Boolean(), nullable=False, default=True) # foreign keys game = db.Column(db.Integer, db.ForeignKey("games.id"), nullable=False) From 2fe0e4d78b7959b09b030dbfa0cd9630863857f3 Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Wed, 2 Oct 2019 16:48:10 -0700 Subject: [PATCH 13/47] add moves.is_pass --- models/Move.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/models/Move.py b/models/Move.py index 15f92b0..d3dd264 100644 --- a/models/Move.py +++ b/models/Move.py @@ -14,7 +14,8 @@ class Move(db.Model): x_point = db.Column(db.Integer) y_point = db.Column(db.Integer) move_number = db.Column(db.Integer) - is_main = db.Column(db.Boolean(), nullable=False, default=True) + is_pass = db.Column(db.Boolean, nullable=False, default=False) + is_main = db.Column(db.Boolean, nullable=False, default=True) # foreign keys game = db.Column(db.Integer, db.ForeignKey("games.id"), nullable=False) From 836b0bb7ddd1ef7f2142a3d31dde233228c9cc48 Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Fri, 4 Oct 2019 11:24:11 -0700 Subject: [PATCH 14/47] config tests --- app.py | 17 +++++++++-------- config.py | 32 ++++++++++++++++++++++++++++++++ manage.py | 39 +++++++++++++++++++++++++++++++++++++++ models.py | 1 - models/User.py | 7 ++++++- requirements.txt | 2 ++ tests/__init__.py | 0 tests/base.py | 14 ++++++++++++++ tests/test_config.py | 14 ++++++++++++++ 9 files changed, 116 insertions(+), 10 deletions(-) create mode 100644 config.py create mode 100644 manage.py delete mode 100644 models.py create mode 100644 tests/__init__.py create mode 100644 tests/base.py create mode 100644 tests/test_config.py diff --git a/app.py b/app.py index 8e7d1be..876a94f 100644 --- a/app.py +++ b/app.py @@ -14,15 +14,13 @@ from flask_cors import CORS app = Flask(__name__) CORS(app) -# base directory -basedir = os.path.abspath(os.path.dirname(__file__)) - -# dev database -DATABASE = 'postgresql://localhost/browser-go' - # config database -app.config['SQLALCHEMY_DATABASE_URI'] = DATABASE -app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False +app_settings = os.getenv( + 'APP_SETTINGS', + 'project.server.config.DevelopmentConfig' +) + +app.config.from_object(app_settings) #init bcrypt bcrypt = Bcrypt(app) @@ -54,5 +52,8 @@ PORT = 8000 def hello_world(): return 'Hello World' +@app.route('/api') + + if __name__ == '__main__': app.run(debug=DEBUG, port=PORT) \ No newline at end of file diff --git a/config.py b/config.py new file mode 100644 index 0000000..e6c7f95 --- /dev/null +++ b/config.py @@ -0,0 +1,32 @@ + +# local db +DATABASE = 'postgresql://localhost/browser-go' + +class BaseConfig: + """Base configuration.""" + SECRET_KEY = os.getenv('SECRET_KEY') + DEBUG = False + BCRYPT_LOG_ROUNDS = 13 + SQLALCHEMY_TRACK_MODIFICATIONS = False + +class DevelopmentConfig(BaseConfig): + """Development configuration.""" + DEBUG = True + BCRYPT_LOG_ROUNDS = 4 + SQLALCHEMY_DATABASE_URI = DATABASE + + +class TestingConfig(BaseConfig): + """Testing configuration.""" + DEBUG = True + TESTING = True + BCRYPT_LOG_ROUNDS = 4 + SQLALCHEMY_DATABASE_URI = DATABASE + PRESERVE_CONTEXT_ON_EXCEPTION = False + + +class ProductionConfig(BaseConfig): + """Production configuration.""" + SECRET_KEY = '' + DEBUG = False + SQLALCHEMY_DATABASE_URI = 'postgresql:///' \ No newline at end of file diff --git a/manage.py b/manage.py new file mode 100644 index 0000000..70172ba --- /dev/null +++ b/manage.py @@ -0,0 +1,39 @@ +import os +import unittest + +from flask_script import Manager +from flask_migrate import Migrate, MigrateCommand + +from app import app, db + +migrate = Migrate(app, db) +manager = Manager(app) + +# migrations +manager.add_command('db', MigrateCommand) + + +@manager.command +def test(): + """Runs the unit tests without test coverage.""" + tests = unittest.TestLoader().discover('browser-go-api/tests', pattern='test*.py') + result = unittest.TextTestRunner(verbosity=2).run(tests) + if result.wasSuccessful(): + return 0 + return 1 + + +@manager.command +def create_db(): + """Creates the db tables.""" + db.create_all() + + +@manager.command +def drop_db(): + """Drops the db tables.""" + db.drop_all() + + +if __name__ == '__main__': + manager.run() \ No newline at end of file diff --git a/models.py b/models.py deleted file mode 100644 index e9ba0dd..0000000 --- a/models.py +++ /dev/null @@ -1 +0,0 @@ -from app import db, ma diff --git a/models/User.py b/models/User.py index b2888eb..94754bb 100644 --- a/models/User.py +++ b/models/User.py @@ -1,4 +1,5 @@ from ..app import db, ma, bcrypt +import datetime import enum class Ranks(enum.Enum): # with minimal Elo rating @@ -39,6 +40,7 @@ class Ranks(enum.Enum): # with minimal Elo rating K28 = "Twenty-Eight Kyu" K29 = "Twenty-Nine Kyu" K30 = "Thirty Kyu" # Elo -900 + RU = "Unknown Rank" class User(db.Model): __tablename__ = "users" @@ -52,7 +54,10 @@ class User(db.Model): elo = db.Column(db.Integer) rank_certainty = db.Column(db.Boolean, nullable=False, default=False) - def __init__(self): + def __init__(self, email, password, admin=False,): + self.email = email self.password = bcrypt.generate_password_hash( password, app.config.get('BCRYPT_LOG_ROUNDS') ).decode() + self.registered_on = datetime.datetime.now() + self.admin = admin diff --git a/requirements.txt b/requirements.txt index 547158f..f300d47 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,7 +8,9 @@ Flask-Bcrypt==0.7.1 Flask-Cors==3.0.8 flask-marshmallow==0.10.1 Flask-Migrate==2.5.2 +Flask-Script==2.0.6 Flask-SQLAlchemy==2.4.1 +Flask-Testing==0.7.1 isort==4.3.21 itsdangerous==1.1.0 Jinja2==2.10.1 diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/base.py b/tests/base.py new file mode 100644 index 0000000..1bed7a2 --- /dev/null +++ b/tests/base.py @@ -0,0 +1,14 @@ +from flask_testing import TestCase +from ..app import app, db + +class BaseTestCase(TestCase): + """ Base Tests """ + + def create_app(self): + pass + + def setUp(self): + pass + + def tearDown(self): + pass \ No newline at end of file diff --git a/tests/test_config.py b/tests/test_config.py new file mode 100644 index 0000000..df01f5a --- /dev/null +++ b/tests/test_config.py @@ -0,0 +1,14 @@ +import unittest + +from flask import current_app +from flask_testing import TestCase + +from ..app import app + +class TestDevelopmentConfig(TestCase): + def create_app(self): + app.config.from_object('browser-go-api.config.DevelopmentConfig') + return app + + def test_app_is_development(self): + self.assertFalse(app.config['SECRET_KEY'] is 'my_precious') \ No newline at end of file From df04427ea0b586702dffe4b2298109f4ef043058 Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Fri, 4 Oct 2019 15:00:47 -0700 Subject: [PATCH 15/47] create api blueprint --- {games => api}/__init__.py | 0 api/api.py | 14 ++++++++++++++ {home => api/games}/__init__.py | 0 {games => api/games}/game.py | 0 {rooms => api/home}/__init__.py | 0 {home => api/home}/home.py | 0 {users => api/rooms}/__init__.py | 0 {rooms => api/rooms}/room.py | 0 api/users/__init__.py | 0 api/users/user_endpoint.py | 5 +++++ app.py | 16 +++++++--------- config.py | 2 +- models.py | 6 ++++++ models/Game.py | 2 +- models/GameRoom.py | 2 +- models/Message.py | 2 +- models/Move.py | 2 +- models/TimeSettings.py | 2 +- models/User.py | 2 +- users/user.py | 11 ----------- 20 files changed, 39 insertions(+), 27 deletions(-) rename {games => api}/__init__.py (100%) create mode 100644 api/api.py rename {home => api/games}/__init__.py (100%) rename {games => api/games}/game.py (100%) rename {rooms => api/home}/__init__.py (100%) rename {home => api/home}/home.py (100%) rename {users => api/rooms}/__init__.py (100%) rename {rooms => api/rooms}/room.py (100%) create mode 100644 api/users/__init__.py create mode 100644 api/users/user_endpoint.py create mode 100644 models.py delete mode 100644 users/user.py diff --git a/games/__init__.py b/api/__init__.py similarity index 100% rename from games/__init__.py rename to api/__init__.py diff --git a/api/api.py b/api/api.py new file mode 100644 index 0000000..5b1e268 --- /dev/null +++ b/api/api.py @@ -0,0 +1,14 @@ +from flask import Blueprint, request, jsonify, session +from .users.user_endpoint import UserEndpoint + +api = Blueprint('api', __name__, url_prefix='/api') + + +@api.route('/home', methods=['GET']) +def api_home(): + response = {"message": "home page"} + return jsonify(response) + +@api.route('/users') +def api_users(): + return jsonify(UserEndpoint.users()) diff --git a/home/__init__.py b/api/games/__init__.py similarity index 100% rename from home/__init__.py rename to api/games/__init__.py diff --git a/games/game.py b/api/games/game.py similarity index 100% rename from games/game.py rename to api/games/game.py diff --git a/rooms/__init__.py b/api/home/__init__.py similarity index 100% rename from rooms/__init__.py rename to api/home/__init__.py diff --git a/home/home.py b/api/home/home.py similarity index 100% rename from home/home.py rename to api/home/home.py diff --git a/users/__init__.py b/api/rooms/__init__.py similarity index 100% rename from users/__init__.py rename to api/rooms/__init__.py diff --git a/rooms/room.py b/api/rooms/room.py similarity index 100% rename from rooms/room.py rename to api/rooms/room.py diff --git a/api/users/__init__.py b/api/users/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/api/users/user_endpoint.py b/api/users/user_endpoint.py new file mode 100644 index 0000000..e147fed --- /dev/null +++ b/api/users/user_endpoint.py @@ -0,0 +1,5 @@ + +class UserEndpoint(object): + def users(): + response = {"message": "users page"} + return response diff --git a/app.py b/app.py index 876a94f..a689d04 100644 --- a/app.py +++ b/app.py @@ -17,12 +17,12 @@ CORS(app) # config database app_settings = os.getenv( 'APP_SETTINGS', - 'project.server.config.DevelopmentConfig' + 'config.DevelopmentConfig' ) app.config.from_object(app_settings) -#init bcrypt +# init bcrypt bcrypt = Bcrypt(app) # init database @@ -32,12 +32,7 @@ db = SQLAlchemy(app) ma = Marshmallow(app) # init all db models -from .models.User import User -from .models.GameRoom import GameRoom -from .models.TimeSettings import TimeSettings -from .models.Game import Game -from .models.Move import Move -from .models.Message import Message +import models migrate = Migrate(app, db) @@ -52,7 +47,10 @@ PORT = 8000 def hello_world(): return 'Hello World' -@app.route('/api') +# Blue prints +from api.api import api + +app.register_blueprint(api) if __name__ == '__main__': diff --git a/config.py b/config.py index e6c7f95..0b3f00f 100644 --- a/config.py +++ b/config.py @@ -1,4 +1,4 @@ - +import os # local db DATABASE = 'postgresql://localhost/browser-go' diff --git a/models.py b/models.py new file mode 100644 index 0000000..5fa00f1 --- /dev/null +++ b/models.py @@ -0,0 +1,6 @@ +from models.User import User +from models.GameRoom import GameRoom +from models.TimeSettings import TimeSettings +from models.Game import Game +from models.Move import Move +from models.Message import Message \ No newline at end of file diff --git a/models/Game.py b/models/Game.py index f8ec676..742205e 100644 --- a/models/Game.py +++ b/models/Game.py @@ -1,4 +1,4 @@ -from ..app import db, ma +from app import db, ma import enum class Players(enum.Enum): diff --git a/models/GameRoom.py b/models/GameRoom.py index 5da8f4e..3e5239c 100644 --- a/models/GameRoom.py +++ b/models/GameRoom.py @@ -1,4 +1,4 @@ -from ..app import db, ma +from app import db, ma import enum class Languages(enum.Enum): diff --git a/models/Message.py b/models/Message.py index 7a273a4..3a81404 100644 --- a/models/Message.py +++ b/models/Message.py @@ -1,4 +1,4 @@ -from ..app import db, ma +from app import db, ma import enum class Players(enum.Enum): diff --git a/models/Move.py b/models/Move.py index d3dd264..a3afe12 100644 --- a/models/Move.py +++ b/models/Move.py @@ -1,4 +1,4 @@ -from ..app import db, ma +from app import db, ma import enum class Players(enum.Enum): diff --git a/models/TimeSettings.py b/models/TimeSettings.py index bdf142e..0c8c5bb 100644 --- a/models/TimeSettings.py +++ b/models/TimeSettings.py @@ -1,4 +1,4 @@ -from ..app import db, ma +from app import db, ma import enum class TimeTypes(enum.Enum): diff --git a/models/User.py b/models/User.py index 94754bb..83a9302 100644 --- a/models/User.py +++ b/models/User.py @@ -1,4 +1,4 @@ -from ..app import db, ma, bcrypt +from app import db, ma, bcrypt import datetime import enum diff --git a/users/user.py b/users/user.py deleted file mode 100644 index fd9b6d3..0000000 --- a/users/user.py +++ /dev/null @@ -1,11 +0,0 @@ -from flask import Blueprint - -user = Blueprint('users', __name__) - -@user.route('/') -def func(): - pass - -@user.route('/') -def func(): - pass \ No newline at end of file From 75f244ae9f2baad88d680d1950e7bc1c29437576 Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Fri, 4 Oct 2019 23:25:01 -0700 Subject: [PATCH 16/47] add auth routes --- auth/__init__.py | 0 auth/auth.py | 14 ++++++++++++++ models/User.py | 35 +++++++++++++++++++++++++++++++++++ requirements.txt | 1 + 4 files changed, 50 insertions(+) create mode 100644 auth/__init__.py create mode 100644 auth/auth.py diff --git a/auth/__init__.py b/auth/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/auth/auth.py b/auth/auth.py new file mode 100644 index 0000000..ffee7a5 --- /dev/null +++ b/auth/auth.py @@ -0,0 +1,14 @@ +from flask import Blueprint, request, jsonify, session +from ..models.User import User + +auth = Blueprint('auth', __name__, url_prefix='/auth') + +@api.route('/signup', methods=['POST']) +def auth_signup(): + response = {"message": "signup post"} + return jsonify(response) + +@api.route('/login', methods=['POST']) +def auth_login(): + response = {"message": "login post"} + return jsonify(response) \ No newline at end of file diff --git a/models/User.py b/models/User.py index 83a9302..4ae7c17 100644 --- a/models/User.py +++ b/models/User.py @@ -1,6 +1,7 @@ from app import db, ma, bcrypt import datetime import enum +import jwt class Ranks(enum.Enum): # with minimal Elo rating D7 = "Seven Dan" # Elo 2700+ @@ -61,3 +62,37 @@ class User(db.Model): ).decode() self.registered_on = datetime.datetime.now() self.admin = admin + + def encode_auth_token(self, user_id): + """ + Generates the Auth Token + :return: string + """ + try: + payload = { + 'exp': datetime.datetime.utcnow() + datetime.timedelta(days=0, seconds=5), + 'iat': datetime.datetime.utcnow(), + 'sub': user_id + } + return jwt.encode( + payload, + app.config.get('SECRET_KEY'), + algorithm='HS256' + ) + except Exception as e: + return e + + @staticmethod + def decode_auth_token(auth_token): + """ + Decodes the auth token + :param auth_token: + :return: integer|string + """ + try: + payload = jwt.decode(auth_token, app.config.get('SECRET_KEY')) + return payload['sub'] + except jwt.ExpiredSignatureError: + return 'Signature expired. Please log in again.' + except jwt.InvalidTokenError: + return 'Invalid token. Please log in again.' diff --git a/requirements.txt b/requirements.txt index f300d47..fab2ace 100644 --- a/requirements.txt +++ b/requirements.txt @@ -23,6 +23,7 @@ mccabe==0.6.1 numpy==1.17.2 psycopg2==2.8.3 pycparser==2.19 +PyJWT==1.7.1 pylint==2.4.2 python-dateutil==2.8.0 python-editor==1.0.4 From c8d56b5e1e46af34b1f16402e45c042395821d4c Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Fri, 4 Oct 2019 23:29:43 -0700 Subject: [PATCH 17/47] register auth blueprint --- app.py | 3 ++- auth/auth.py | 6 +++--- models/User.py | 34 +++++++++++++++++----------------- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/app.py b/app.py index a689d04..10fb0ea 100644 --- a/app.py +++ b/app.py @@ -49,9 +49,10 @@ def hello_world(): # Blue prints from api.api import api +from auth.auth import auth app.register_blueprint(api) - +app.register_blueprint(auth) if __name__ == '__main__': app.run(debug=DEBUG, port=PORT) \ No newline at end of file diff --git a/auth/auth.py b/auth/auth.py index ffee7a5..94e1723 100644 --- a/auth/auth.py +++ b/auth/auth.py @@ -1,14 +1,14 @@ from flask import Blueprint, request, jsonify, session -from ..models.User import User +import models auth = Blueprint('auth', __name__, url_prefix='/auth') -@api.route('/signup', methods=['POST']) +@auth.route('/signup', methods=['POST']) def auth_signup(): response = {"message": "signup post"} return jsonify(response) -@api.route('/login', methods=['POST']) +@auth.route('/login', methods=['POST']) def auth_login(): response = {"message": "login post"} return jsonify(response) \ No newline at end of file diff --git a/models/User.py b/models/User.py index 4ae7c17..b71c9c6 100644 --- a/models/User.py +++ b/models/User.py @@ -64,23 +64,23 @@ class User(db.Model): self.admin = admin def encode_auth_token(self, user_id): - """ - Generates the Auth Token - :return: string - """ - try: - payload = { - 'exp': datetime.datetime.utcnow() + datetime.timedelta(days=0, seconds=5), - 'iat': datetime.datetime.utcnow(), - 'sub': user_id - } - return jwt.encode( - payload, - app.config.get('SECRET_KEY'), - algorithm='HS256' - ) - except Exception as e: - return e + """ + Generates the Auth Token + :return: string + """ + try: + payload = { + 'exp': datetime.datetime.utcnow() + datetime.timedelta(days=0, seconds=5), + 'iat': datetime.datetime.utcnow(), + 'sub': user_id + } + return jwt.encode( + payload, + app.config.get('SECRET_KEY'), + algorithm='HS256' + ) + except Exception as e: + return e @staticmethod def decode_auth_token(auth_token): From 7c14f451bb5dc686a8e2320d4c958e5ed1622018 Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Sat, 5 Oct 2019 17:49:49 -0700 Subject: [PATCH 18/47] create app factory pattern --- api/api.py | 1 - app.py | 58 ------------------------------- auth/auth.py | 4 +-- __init__.py => config/__init__.py | 0 config.py => config/config.py | 1 + config/models_mount.py | 7 ++++ database.py | 15 ++++++++ models.py | 6 ---- models/User.py | 2 +- server.py | 36 +++++++++++++++++++ 10 files changed, 62 insertions(+), 68 deletions(-) delete mode 100644 app.py rename __init__.py => config/__init__.py (100%) rename config.py => config/config.py (97%) create mode 100644 config/models_mount.py create mode 100644 database.py delete mode 100644 models.py create mode 100644 server.py diff --git a/api/api.py b/api/api.py index 5b1e268..e27790a 100644 --- a/api/api.py +++ b/api/api.py @@ -3,7 +3,6 @@ from .users.user_endpoint import UserEndpoint api = Blueprint('api', __name__, url_prefix='/api') - @api.route('/home', methods=['GET']) def api_home(): response = {"message": "home page"} diff --git a/app.py b/app.py deleted file mode 100644 index 10fb0ea..0000000 --- a/app.py +++ /dev/null @@ -1,58 +0,0 @@ -import os - -from flask import Flask - -# ! SQLAlchemy > Marshmallow - these must be imported in this order -from flask_sqlalchemy import SQLAlchemy -from flask_marshmallow import Marshmallow - -from flask_migrate import Migrate - -from flask_bcrypt import Bcrypt -from flask_cors import CORS - -app = Flask(__name__) -CORS(app) - -# config database -app_settings = os.getenv( - 'APP_SETTINGS', - 'config.DevelopmentConfig' -) - -app.config.from_object(app_settings) - -# init bcrypt -bcrypt = Bcrypt(app) - -# init database -db = SQLAlchemy(app) - -# init marshmallow -ma = Marshmallow(app) - -# init all db models -import models - -migrate = Migrate(app, db) - - -# dev server -DEBUG = True -PORT = 8000 - -# Routes - -@app.route('/') -def hello_world(): - return 'Hello World' - -# Blue prints -from api.api import api -from auth.auth import auth - -app.register_blueprint(api) -app.register_blueprint(auth) - -if __name__ == '__main__': - app.run(debug=DEBUG, port=PORT) \ No newline at end of file diff --git a/auth/auth.py b/auth/auth.py index 94e1723..c1de946 100644 --- a/auth/auth.py +++ b/auth/auth.py @@ -1,6 +1,6 @@ from flask import Blueprint, request, jsonify, session -import models - +from models import User +print(User) auth = Blueprint('auth', __name__, url_prefix='/auth') @auth.route('/signup', methods=['POST']) diff --git a/__init__.py b/config/__init__.py similarity index 100% rename from __init__.py rename to config/__init__.py diff --git a/config.py b/config/config.py similarity index 97% rename from config.py rename to config/config.py index 0b3f00f..55c5173 100644 --- a/config.py +++ b/config/config.py @@ -14,6 +14,7 @@ class DevelopmentConfig(BaseConfig): DEBUG = True BCRYPT_LOG_ROUNDS = 4 SQLALCHEMY_DATABASE_URI = DATABASE + PORT = 8000 class TestingConfig(BaseConfig): diff --git a/config/models_mount.py b/config/models_mount.py new file mode 100644 index 0000000..fa36978 --- /dev/null +++ b/config/models_mount.py @@ -0,0 +1,7 @@ +if __name__ == '__main__': + from ..models.User import User + from ..models.GameRoom import GameRoom + from ..models.TimeSettings import TimeSettings + from ..models.Game import Game + from ..models.Move import Move + from ..models.Message import Message \ No newline at end of file diff --git a/database.py b/database.py new file mode 100644 index 0000000..c43b153 --- /dev/null +++ b/database.py @@ -0,0 +1,15 @@ + +# ! SQLAlchemy > Marshmallow - these must be imported in this order +from flask_sqlalchemy import SQLAlchemy +from flask_marshmallow import Marshmallow +from flask_bcrypt import Bcrypt + +# init bcrypt +def bcrypt(app): + Bcrypt(app) + +# init database +db = SQLAlchemy() + +# init marshmallow +ma = Marshmallow() diff --git a/models.py b/models.py deleted file mode 100644 index 5fa00f1..0000000 --- a/models.py +++ /dev/null @@ -1,6 +0,0 @@ -from models.User import User -from models.GameRoom import GameRoom -from models.TimeSettings import TimeSettings -from models.Game import Game -from models.Move import Move -from models.Message import Message \ No newline at end of file diff --git a/models/User.py b/models/User.py index b71c9c6..a22ff84 100644 --- a/models/User.py +++ b/models/User.py @@ -1,4 +1,4 @@ -from app import db, ma, bcrypt +from database import db, ma, bcrypt import datetime import enum import jwt diff --git a/server.py b/server.py new file mode 100644 index 0000000..c82ac0d --- /dev/null +++ b/server.py @@ -0,0 +1,36 @@ +import os +from database import db, ma, bcrypt + +from flask import Flask + +from flask_migrate import Migrate + +from flask_cors import CORS + +# Blueprints +from api.api import api +from auth.auth import auth + +import config.models_mount + +def create_app(): + app = Flask(__name__) + bcrypt(app) + CORS(app) + app_settings = os.getenv( + 'APP_SETTINGS', + 'config.config.DevelopmentConfig' + ) + + app.config.from_object(app_settings) + db.init_app(app) + ma.init_app(app) + app.register_blueprint(api) + app.register_blueprint(auth) + migrate = Migrate(app, db) + return app + + +if __name__ == '__main__': + app = create_app() + app.run(port=8000, debug=True) \ No newline at end of file From cc3b9eebf5044866ba27ef0c60dc217368789c47 Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Sat, 5 Oct 2019 22:40:02 -0700 Subject: [PATCH 19/47] debug server problems, implement auth route --- config/__init__.py => __init__.py | 0 api/users/user_endpoint.py | 4 ++- app.py | 20 +++++++++++ auth/auth.py | 43 ++++++++++++++++++++--- configuration/__init__.py | 0 {config => configuration}/config.py | 0 {config => configuration}/models_mount.py | 0 database.py | 5 --- manage.py | 11 +++++- migrations/versions/50aab465cf44_.py | 28 +++++++++++++++ migrations/versions/9519d28f13f0_.py | 2 +- migrations/versions/bdc3cd5d7499_.py | 28 +++++++++++++++ migrations/versions/bfa4b406e22f_.py | 28 +++++++++++++++ models/User.py | 25 +++++++++++-- server.py | 31 ++++------------ 15 files changed, 185 insertions(+), 40 deletions(-) rename config/__init__.py => __init__.py (100%) create mode 100644 app.py create mode 100644 configuration/__init__.py rename {config => configuration}/config.py (100%) rename {config => configuration}/models_mount.py (100%) create mode 100644 migrations/versions/50aab465cf44_.py create mode 100644 migrations/versions/bdc3cd5d7499_.py create mode 100644 migrations/versions/bfa4b406e22f_.py diff --git a/config/__init__.py b/__init__.py similarity index 100% rename from config/__init__.py rename to __init__.py diff --git a/api/users/user_endpoint.py b/api/users/user_endpoint.py index e147fed..7b89b6f 100644 --- a/api/users/user_endpoint.py +++ b/api/users/user_endpoint.py @@ -1,5 +1,7 @@ +from models.User import User, user_schema class UserEndpoint(object): def users(): - response = {"message": "users page"} + user = User.query.first() + response = user_schema.dumps(user) return response diff --git a/app.py b/app.py new file mode 100644 index 0000000..e1b6270 --- /dev/null +++ b/app.py @@ -0,0 +1,20 @@ +import os +from database import db, ma + +from flask import Flask + +from configuration.config import DevelopmentConfig + +from flask_bcrypt import Bcrypt +from flask_cors import CORS + +def create_app(): + app = Flask(__name__) + CORS(app) + + app.config.from_object(DevelopmentConfig) + db.init_app(app) + ma.init_app(app) + return app + +bcrypt = Bcrypt(create_app()) \ No newline at end of file diff --git a/auth/auth.py b/auth/auth.py index c1de946..b199d24 100644 --- a/auth/auth.py +++ b/auth/auth.py @@ -1,12 +1,47 @@ from flask import Blueprint, request, jsonify, session -from models import User -print(User) + +from database import db +from models.User import User + auth = Blueprint('auth', __name__, url_prefix='/auth') @auth.route('/signup', methods=['POST']) def auth_signup(): - response = {"message": "signup post"} - return jsonify(response) + data = request.get_json() + user = User.query.filter_by(email=data.get('email')).first() + if not user: + try: + print('getting here 1') + user = User( + email = data['email'], + password = data['password'], + ) + print('getting here 2') + db.session.add(user) + print('wtf') + db.session.commit() + print('user') + auth_token = user.encode_auth_token(user.id) + print('getting here 4') + response = { + 'status': 'success', + 'message': 'Succesfully registered.', + 'auth_token': auth_token.decode() + } + return jsonify(response), 201 + except Exception as e: + print(e.__dict__) + response = { + 'status': 'fail', + 'message': 'There was an error. Please try again.' + } + return jsonify(response), 401 + else: + response = { + 'status': 'fail', + 'message': 'User already exists. Please login.' + } + return jsonify(response), 202 @auth.route('/login', methods=['POST']) def auth_login(): diff --git a/configuration/__init__.py b/configuration/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/config/config.py b/configuration/config.py similarity index 100% rename from config/config.py rename to configuration/config.py diff --git a/config/models_mount.py b/configuration/models_mount.py similarity index 100% rename from config/models_mount.py rename to configuration/models_mount.py diff --git a/database.py b/database.py index c43b153..093d45e 100644 --- a/database.py +++ b/database.py @@ -2,12 +2,7 @@ # ! SQLAlchemy > Marshmallow - these must be imported in this order from flask_sqlalchemy import SQLAlchemy from flask_marshmallow import Marshmallow -from flask_bcrypt import Bcrypt -# init bcrypt -def bcrypt(app): - Bcrypt(app) - # init database db = SQLAlchemy() diff --git a/manage.py b/manage.py index 70172ba..35d0d9d 100644 --- a/manage.py +++ b/manage.py @@ -4,11 +4,20 @@ import unittest from flask_script import Manager from flask_migrate import Migrate, MigrateCommand -from app import app, db +from database import db +from app import create_app +app = create_app() migrate = Migrate(app, db) manager = Manager(app) +from models.Game import Game +from models.GameRoom import GameRoom +from models.Message import Message +from models.Move import Move +from models.TimeSettings import TimeSettings +from models.User import User + # migrations manager.add_command('db', MigrateCommand) diff --git a/migrations/versions/50aab465cf44_.py b/migrations/versions/50aab465cf44_.py new file mode 100644 index 0000000..6aade5b --- /dev/null +++ b/migrations/versions/50aab465cf44_.py @@ -0,0 +1,28 @@ +"""empty message + +Revision ID: 50aab465cf44 +Revises: bfa4b406e22f +Create Date: 2019-10-05 22:37:07.643522 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '50aab465cf44' +down_revision = 'bfa4b406e22f' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('users', sa.Column('password', sa.String(length=255), nullable=False)) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('users', 'password') + # ### end Alembic commands ### diff --git a/migrations/versions/9519d28f13f0_.py b/migrations/versions/9519d28f13f0_.py index 1f327ae..8633673 100644 --- a/migrations/versions/9519d28f13f0_.py +++ b/migrations/versions/9519d28f13f0_.py @@ -39,7 +39,7 @@ def upgrade(): op.create_table('users', sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), sa.Column('email', sa.String(length=255), nullable=False), - sa.Column('password', sa.DateTime(), nullable=False), + sa.Column('password', sa.Integer(length=255), nullable=False), sa.Column('registered_on', sa.DateTime(), nullable=False), sa.Column('admin', sa.Boolean(), nullable=False), sa.Column('rank', sa.Enum('D7', 'D6', 'D5', 'D4', 'D3', 'D2', 'D1', 'K1', 'K2', 'K3', 'K4', 'K5', 'K6', 'K7', 'K8', 'K9', 'K10', 'K11', 'K12', 'K13', 'K14', 'K15', 'K16', 'K17', 'K18', 'K19', 'K20', 'K21', 'K22', 'K23', 'K24', 'K25', 'K26', 'K27', 'K28', 'K29', 'K30', name='ranks'), nullable=True), diff --git a/migrations/versions/bdc3cd5d7499_.py b/migrations/versions/bdc3cd5d7499_.py new file mode 100644 index 0000000..d094cdc --- /dev/null +++ b/migrations/versions/bdc3cd5d7499_.py @@ -0,0 +1,28 @@ +"""empty message + +Revision ID: bdc3cd5d7499 +Revises: 16c70b11242e +Create Date: 2019-10-05 22:34:04.358841 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'bdc3cd5d7499' +down_revision = '16c70b11242e' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('moves', sa.Column('is_pass', sa.Boolean(), nullable=False)) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('moves', 'is_pass') + # ### end Alembic commands ### diff --git a/migrations/versions/bfa4b406e22f_.py b/migrations/versions/bfa4b406e22f_.py new file mode 100644 index 0000000..525da5f --- /dev/null +++ b/migrations/versions/bfa4b406e22f_.py @@ -0,0 +1,28 @@ +"""empty message + +Revision ID: bfa4b406e22f +Revises: bdc3cd5d7499 +Create Date: 2019-10-05 22:36:45.420054 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = 'bfa4b406e22f' +down_revision = 'bdc3cd5d7499' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('users', 'password') + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('users', sa.Column('password', postgresql.TIMESTAMP(), autoincrement=False, nullable=False)) + # ### end Alembic commands ### diff --git a/models/User.py b/models/User.py index a22ff84..203c25a 100644 --- a/models/User.py +++ b/models/User.py @@ -1,4 +1,6 @@ -from database import db, ma, bcrypt +from database import db, ma +from app import bcrypt +from configuration import config import datetime import enum import jwt @@ -48,7 +50,7 @@ class User(db.Model): id = db.Column(db.Integer, primary_key=True, autoincrement=True) email = db.Column(db.String(255), unique=True, nullable=False) - password = db.Column(db.DateTime, nullable=False) + password = db.Column(db.String(255), nullable=False) registered_on = db.Column(db.DateTime, nullable=False) admin = db.Column(db.Boolean, nullable=False, default=False) rank = db.Column(db.Enum(Ranks)) @@ -56,10 +58,13 @@ class User(db.Model): rank_certainty = db.Column(db.Boolean, nullable=False, default=False) def __init__(self, email, password, admin=False,): + print('user init') self.email = email + print('user email init') self.password = bcrypt.generate_password_hash( - password, app.config.get('BCRYPT_LOG_ROUNDS') + password, 13 ).decode() + print('user password init') self.registered_on = datetime.datetime.now() self.admin = admin @@ -96,3 +101,17 @@ class User(db.Model): return 'Signature expired. Please log in again.' except jwt.InvalidTokenError: return 'Invalid token. Please log in again.' + +class UserSchema(ma.ModelSchema): + class Meta: + fields = ( + 'id', + 'name', + 'registered_on', + 'rank', + 'rank_certainty', + 'elo' + ) + +user_schema = UserSchema() +users_schema = UserSchema(many=True) \ No newline at end of file diff --git a/server.py b/server.py index c82ac0d..d38827c 100644 --- a/server.py +++ b/server.py @@ -1,36 +1,17 @@ -import os -from database import db, ma, bcrypt - -from flask import Flask - -from flask_migrate import Migrate - -from flask_cors import CORS +from app import create_app, db # Blueprints from api.api import api from auth.auth import auth -import config.models_mount +import configuration.models_mount +from flask_migrate import Migrate -def create_app(): - app = Flask(__name__) - bcrypt(app) - CORS(app) - app_settings = os.getenv( - 'APP_SETTINGS', - 'config.config.DevelopmentConfig' - ) - - app.config.from_object(app_settings) - db.init_app(app) - ma.init_app(app) - app.register_blueprint(api) - app.register_blueprint(auth) - migrate = Migrate(app, db) - return app if __name__ == '__main__': app = create_app() + app.register_blueprint(api) + app.register_blueprint(auth) + migrate = Migrate(app, db) app.run(port=8000, debug=True) \ No newline at end of file From a2697b8a119d4f1e8be2825f5066207f4d793573 Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Sat, 5 Oct 2019 23:17:13 -0700 Subject: [PATCH 20/47] config CORS for dev --- api/users/user_endpoint.py | 6 +++--- app.py | 4 ++-- models/User.py | 3 ++- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/api/users/user_endpoint.py b/api/users/user_endpoint.py index 7b89b6f..7d80d6e 100644 --- a/api/users/user_endpoint.py +++ b/api/users/user_endpoint.py @@ -1,7 +1,7 @@ -from models.User import User, user_schema +from models.User import User, user_schema, users_schema class UserEndpoint(object): def users(): - user = User.query.first() - response = user_schema.dumps(user) + user = User.query.all() + response = users_schema.dumps(user) return response diff --git a/app.py b/app.py index e1b6270..e2c5604 100644 --- a/app.py +++ b/app.py @@ -10,8 +10,8 @@ from flask_cors import CORS def create_app(): app = Flask(__name__) - CORS(app) - + CORS(app, origins="http://localhost:3004") + app.config['CORS_HEADERS'] = 'Content-Type' app.config.from_object(DevelopmentConfig) db.init_app(app) ma.init_app(app) diff --git a/models/User.py b/models/User.py index 203c25a..7fd6fff 100644 --- a/models/User.py +++ b/models/User.py @@ -106,7 +106,8 @@ class UserSchema(ma.ModelSchema): class Meta: fields = ( 'id', - 'name', + 'name', + 'email', 'registered_on', 'rank', 'rank_certainty', From 257fd6a533f24c64113c609b1f9fd9bcdc3add36 Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Sat, 5 Oct 2019 23:57:36 -0700 Subject: [PATCH 21/47] add username --- migrations/versions/0e0f8ad1362d_.py | 30 ++++++++++++++++++++++++++++ migrations/versions/9519d28f13f0_.py | 2 +- models/User.py | 10 +++++----- 3 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 migrations/versions/0e0f8ad1362d_.py diff --git a/migrations/versions/0e0f8ad1362d_.py b/migrations/versions/0e0f8ad1362d_.py new file mode 100644 index 0000000..62a07e0 --- /dev/null +++ b/migrations/versions/0e0f8ad1362d_.py @@ -0,0 +1,30 @@ +"""empty message + +Revision ID: 0e0f8ad1362d +Revises: 50aab465cf44 +Create Date: 2019-10-05 23:21:57.620808 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '0e0f8ad1362d' +down_revision = '50aab465cf44' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('users', sa.Column('username', sa.String(length=255), nullable=False)) + op.create_unique_constraint(None, 'users', ['username']) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint(None, 'users', type_='unique') + op.drop_column('users', 'username') + # ### end Alembic commands ### diff --git a/migrations/versions/9519d28f13f0_.py b/migrations/versions/9519d28f13f0_.py index 8633673..51b4535 100644 --- a/migrations/versions/9519d28f13f0_.py +++ b/migrations/versions/9519d28f13f0_.py @@ -39,7 +39,7 @@ def upgrade(): op.create_table('users', sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), sa.Column('email', sa.String(length=255), nullable=False), - sa.Column('password', sa.Integer(length=255), nullable=False), + sa.Column('password', sa.String(length=255), nullable=False), sa.Column('registered_on', sa.DateTime(), nullable=False), sa.Column('admin', sa.Boolean(), nullable=False), sa.Column('rank', sa.Enum('D7', 'D6', 'D5', 'D4', 'D3', 'D2', 'D1', 'K1', 'K2', 'K3', 'K4', 'K5', 'K6', 'K7', 'K8', 'K9', 'K10', 'K11', 'K12', 'K13', 'K14', 'K15', 'K16', 'K17', 'K18', 'K19', 'K20', 'K21', 'K22', 'K23', 'K24', 'K25', 'K26', 'K27', 'K28', 'K29', 'K30', name='ranks'), nullable=True), diff --git a/models/User.py b/models/User.py index 7fd6fff..33a61d2 100644 --- a/models/User.py +++ b/models/User.py @@ -49,6 +49,7 @@ class User(db.Model): __tablename__ = "users" id = db.Column(db.Integer, primary_key=True, autoincrement=True) + username = db.Column(db.String(255), unique=True, nullable=False, autoincrement=True) email = db.Column(db.String(255), unique=True, nullable=False) password = db.Column(db.String(255), nullable=False) registered_on = db.Column(db.DateTime, nullable=False) @@ -57,14 +58,13 @@ class User(db.Model): elo = db.Column(db.Integer) rank_certainty = db.Column(db.Boolean, nullable=False, default=False) - def __init__(self, email, password, admin=False,): - print('user init') + def __init__(self, username, email, password, rank='RU', admin=False): + self.username = username self.email = email - print('user email init') self.password = bcrypt.generate_password_hash( password, 13 ).decode() - print('user password init') + self.rank = rank self.registered_on = datetime.datetime.now() self.admin = admin @@ -106,7 +106,7 @@ class UserSchema(ma.ModelSchema): class Meta: fields = ( 'id', - 'name', + 'username', 'email', 'registered_on', 'rank', From 5b17dedcad5fa392798fa56daf5d1db8f1d20841 Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Sun, 6 Oct 2019 13:44:38 -0700 Subject: [PATCH 22/47] implement auth with username --- app.py | 3 ++- auth/auth.py | 57 +++++++++++++++++++++++++++++--------------------- models/User.py | 24 +++++++++++---------- 3 files changed, 48 insertions(+), 36 deletions(-) diff --git a/app.py b/app.py index e2c5604..4a7d7c9 100644 --- a/app.py +++ b/app.py @@ -10,7 +10,8 @@ from flask_cors import CORS def create_app(): app = Flask(__name__) - CORS(app, origins="http://localhost:3004") + CORS(app, resources={r"/api/*": {"origins": "http://localhost:3000"}, + r"/auth/*": {"origins": "http://localhost:3000"}}) app.config['CORS_HEADERS'] = 'Content-Type' app.config.from_object(DevelopmentConfig) db.init_app(app) diff --git a/auth/auth.py b/auth/auth.py index b199d24..af58cc6 100644 --- a/auth/auth.py +++ b/auth/auth.py @@ -10,33 +10,42 @@ def auth_signup(): data = request.get_json() user = User.query.filter_by(email=data.get('email')).first() if not user: - try: - print('getting here 1') - user = User( - email = data['email'], - password = data['password'], - ) - print('getting here 2') - db.session.add(user) - print('wtf') - db.session.commit() - print('user') - auth_token = user.encode_auth_token(user.id) - print('getting here 4') - response = { - 'status': 'success', - 'message': 'Succesfully registered.', - 'auth_token': auth_token.decode() - } - return jsonify(response), 201 - except Exception as e: - print(e.__dict__) + user = User.query.filter_by(username=data.get('username')).first() + if not user: + try: + print('getting here 1') + print(data) + user = User( + username = data['username'], + email = data['email'], + password = data['password'], + ) + print('getting here 2') + db.session.add(user) + print('wtf') + db.session.commit() + print('user') + auth_token = user.encode_auth_token(user.id) + print('getting here 4') + response = { + 'status': 'success', + 'message': 'Succesfully registered.', + 'auth_token': auth_token.decode() + } + return jsonify(response), 201 + except Exception as e: + print(e.__dict__) + response = { + 'status': 'fail', + 'message': 'There was an error. Please try again.' + } + return jsonify(response), 401 + else: # username is taken response = { 'status': 'fail', - 'message': 'There was an error. Please try again.' + 'message': 'User already exists. Please login.' } - return jsonify(response), 401 - else: + else: # email is taken response = { 'status': 'fail', 'message': 'User already exists. Please login.' diff --git a/models/User.py b/models/User.py index 33a61d2..3fd732d 100644 --- a/models/User.py +++ b/models/User.py @@ -1,8 +1,10 @@ from database import db, ma +from marshmallow import fields from app import bcrypt from configuration import config import datetime import enum +import json import jwt class Ranks(enum.Enum): # with minimal Elo rating @@ -58,7 +60,9 @@ class User(db.Model): elo = db.Column(db.Integer) rank_certainty = db.Column(db.Boolean, nullable=False, default=False) - def __init__(self, username, email, password, rank='RU', admin=False): + def __init__(self, username, email, password, rank=Ranks.K1, admin=False): + print(rank) + print(Ranks) self.username = username self.email = email self.password = bcrypt.generate_password_hash( @@ -103,16 +107,14 @@ class User(db.Model): return 'Invalid token. Please log in again.' class UserSchema(ma.ModelSchema): - class Meta: - fields = ( - 'id', - 'username', - 'email', - 'registered_on', - 'rank', - 'rank_certainty', - 'elo' - ) + id = fields.Int() + username = fields.Str() + email = fields.Str() + registered_on = fields.Date() + rank = fields.Str() + rank_certainty = fields.Bool() + elo = fields.Int() + user_schema = UserSchema() users_schema = UserSchema(many=True) \ No newline at end of file From 8dcb80e456aa3361cf8ba5a3a6028d745b3a828f Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Sun, 6 Oct 2019 15:39:22 -0700 Subject: [PATCH 23/47] setup login --- auth/auth.py | 32 +++++++++++++++++++++++--------- models/User.py | 5 +++-- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/auth/auth.py b/auth/auth.py index af58cc6..085ded3 100644 --- a/auth/auth.py +++ b/auth/auth.py @@ -1,5 +1,4 @@ from flask import Blueprint, request, jsonify, session - from database import db from models.User import User @@ -13,20 +12,14 @@ def auth_signup(): user = User.query.filter_by(username=data.get('username')).first() if not user: try: - print('getting here 1') - print(data) user = User( username = data['username'], email = data['email'], password = data['password'], ) - print('getting here 2') db.session.add(user) - print('wtf') db.session.commit() - print('user') auth_token = user.encode_auth_token(user.id) - print('getting here 4') response = { 'status': 'success', 'message': 'Succesfully registered.', @@ -54,5 +47,26 @@ def auth_signup(): @auth.route('/login', methods=['POST']) def auth_login(): - response = {"message": "login post"} - return jsonify(response) \ No newline at end of file + # get the post data + data = request.get_json() + try: + # fetch the user data + print('getting here') + user = User.query.filter_by(email=data['email']).first() + print(user.username) + auth_token = user.encode_auth_token(user.id) + print(auth_token) + if auth_token: + response = { + 'status': 'success', + 'message': 'Successfully logged in.', + 'auth_token': auth_token.decode() + } + return jsonify(response), 200 + except Exception as e: + print(e) + response = { + 'status': 'fail', + 'message': 'Try again' + } + return jsonify(response), 500 \ No newline at end of file diff --git a/models/User.py b/models/User.py index 3fd732d..12ca239 100644 --- a/models/User.py +++ b/models/User.py @@ -6,6 +6,7 @@ import datetime import enum import json import jwt +import os class Ranks(enum.Enum): # with minimal Elo rating D7 = "Seven Dan" # Elo 2700+ @@ -85,7 +86,7 @@ class User(db.Model): } return jwt.encode( payload, - app.config.get('SECRET_KEY'), + os.environ.get('SECRET_KEY'), algorithm='HS256' ) except Exception as e: @@ -99,7 +100,7 @@ class User(db.Model): :return: integer|string """ try: - payload = jwt.decode(auth_token, app.config.get('SECRET_KEY')) + payload = jwt.decode(auth_token, os.environ.get('SECRET_KEY')) return payload['sub'] except jwt.ExpiredSignatureError: return 'Signature expired. Please log in again.' From 317c2c34006d0eb9e4b44e9644bb4db203e859c4 Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Sun, 6 Oct 2019 22:51:36 -0700 Subject: [PATCH 24/47] test authorization with user endpoint --- api/api.py | 4 ++++ api/users/user_endpoint.py | 12 ++++++++++-- auth/auth.py | 4 ++-- models/User.py | 6 +++--- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/api/api.py b/api/api.py index e27790a..59fe130 100644 --- a/api/api.py +++ b/api/api.py @@ -11,3 +11,7 @@ def api_home(): @api.route('/users') def api_users(): return jsonify(UserEndpoint.users()) + +@api.route('/user') +def api_user(): + return jsonify(UserEndpoint.user()) \ No newline at end of file diff --git a/api/users/user_endpoint.py b/api/users/user_endpoint.py index 7d80d6e..c5413ab 100644 --- a/api/users/user_endpoint.py +++ b/api/users/user_endpoint.py @@ -1,7 +1,15 @@ from models.User import User, user_schema, users_schema +from flask import request, jsonify, Response, json class UserEndpoint(object): def users(): - user = User.query.all() - response = users_schema.dumps(user) + users = User.query.all() + response = users_schema.dumps(users) return response + def user(): + + auth_header = request.headers.get('Authorization') or None + auth_token = auth_header.split(" ")[1] + user = User.decode_auth_token(auth_token) or None + response = json.dumps(user) + return response \ No newline at end of file diff --git a/auth/auth.py b/auth/auth.py index 085ded3..1ead3fb 100644 --- a/auth/auth.py +++ b/auth/auth.py @@ -23,7 +23,7 @@ def auth_signup(): response = { 'status': 'success', 'message': 'Succesfully registered.', - 'auth_token': auth_token.decode() + 'token': auth_token.decode() } return jsonify(response), 201 except Exception as e: @@ -60,7 +60,7 @@ def auth_login(): response = { 'status': 'success', 'message': 'Successfully logged in.', - 'auth_token': auth_token.decode() + 'token': auth_token.decode() } return jsonify(response), 200 except Exception as e: diff --git a/models/User.py b/models/User.py index 12ca239..917df4c 100644 --- a/models/User.py +++ b/models/User.py @@ -80,9 +80,9 @@ class User(db.Model): """ try: payload = { - 'exp': datetime.datetime.utcnow() + datetime.timedelta(days=0, seconds=5), + 'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=4), 'iat': datetime.datetime.utcnow(), - 'sub': user_id + 'user': user_schema.dumps(self) } return jwt.encode( payload, @@ -101,7 +101,7 @@ class User(db.Model): """ try: payload = jwt.decode(auth_token, os.environ.get('SECRET_KEY')) - return payload['sub'] + return payload['user'] except jwt.ExpiredSignatureError: return 'Signature expired. Please log in again.' except jwt.InvalidTokenError: From 1f877fddbe48ffbd65511ae66f0b7008b09d0126 Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Mon, 7 Oct 2019 10:09:49 -0700 Subject: [PATCH 25/47] init socketio --- app.py | 12 +++++++----- requirements.txt | 3 +++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/app.py b/app.py index 4a7d7c9..4e9ec07 100644 --- a/app.py +++ b/app.py @@ -7,15 +7,17 @@ from configuration.config import DevelopmentConfig from flask_bcrypt import Bcrypt from flask_cors import CORS +from flask_socketio import SocketIO + +app = Flask(__name__) +bcrypt = Bcrypt(app) +app.config['CORS_HEADERS'] = 'Content-Type' +app.config.from_object(DevelopmentConfig) +socketio = SocketIO(app) def create_app(): - app = Flask(__name__) CORS(app, resources={r"/api/*": {"origins": "http://localhost:3000"}, r"/auth/*": {"origins": "http://localhost:3000"}}) - app.config['CORS_HEADERS'] = 'Content-Type' - app.config.from_object(DevelopmentConfig) db.init_app(app) ma.init_app(app) return app - -bcrypt = Bcrypt(create_app()) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index fab2ace..e4a0a43 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,6 +9,7 @@ Flask-Cors==3.0.8 flask-marshmallow==0.10.1 Flask-Migrate==2.5.2 Flask-Script==2.0.6 +Flask-SocketIO==4.2.1 Flask-SQLAlchemy==2.4.1 Flask-Testing==0.7.1 isort==4.3.21 @@ -27,6 +28,8 @@ PyJWT==1.7.1 pylint==2.4.2 python-dateutil==2.8.0 python-editor==1.0.4 +python-engineio==3.9.3 +python-socketio==4.3.1 six==1.12.0 SQLAlchemy==1.3.8 typed-ast==1.4.0 From 44d365199f906c17084419d883c28cab6ecb3c4e Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Mon, 7 Oct 2019 10:19:48 -0700 Subject: [PATCH 26/47] confirm socketio run app --- app.py | 6 ++++-- requirements.txt | 2 ++ server.py | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/app.py b/app.py index 4e9ec07..c0d99e9 100644 --- a/app.py +++ b/app.py @@ -16,8 +16,10 @@ app.config.from_object(DevelopmentConfig) socketio = SocketIO(app) def create_app(): - CORS(app, resources={r"/api/*": {"origins": "http://localhost:3000"}, - r"/auth/*": {"origins": "http://localhost:3000"}}) + CORS(app, resources={ + r"/api/*": {"origins": "http://localhost:3000"}, + r"/auth/*": {"origins": "http://localhost:3000"} + }) db.init_app(app) ma.init_app(app) return app diff --git a/requirements.txt b/requirements.txt index e4a0a43..0af54ec 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,6 +12,8 @@ Flask-Script==2.0.6 Flask-SocketIO==4.2.1 Flask-SQLAlchemy==2.4.1 Flask-Testing==0.7.1 +gevent==1.4.0 +greenlet==0.4.15 isort==4.3.21 itsdangerous==1.1.0 Jinja2==2.10.1 diff --git a/server.py b/server.py index d38827c..23b7e45 100644 --- a/server.py +++ b/server.py @@ -1,4 +1,4 @@ -from app import create_app, db +from app import create_app, db, socketio # Blueprints from api.api import api @@ -14,4 +14,4 @@ if __name__ == '__main__': app.register_blueprint(api) app.register_blueprint(auth) migrate = Migrate(app, db) - app.run(port=8000, debug=True) \ No newline at end of file + socketio.run(app) \ No newline at end of file From 73a7373478489352ce331148fe02c92f42cd1d84 Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Mon, 7 Oct 2019 13:55:43 -0700 Subject: [PATCH 27/47] establish initial socket connection --- api/api.py | 14 +++++++++++++- app.py | 6 +++--- configuration/config.py | 2 +- requirements.txt | 4 +++- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/api/api.py b/api/api.py index 59fe130..6c795b8 100644 --- a/api/api.py +++ b/api/api.py @@ -1,5 +1,7 @@ from flask import Blueprint, request, jsonify, session from .users.user_endpoint import UserEndpoint +from app import socketio +from flask_socketio import emit api = Blueprint('api', __name__, url_prefix='/api') @@ -14,4 +16,14 @@ def api_users(): @api.route('/user') def api_user(): - return jsonify(UserEndpoint.user()) \ No newline at end of file + return jsonify(UserEndpoint.user()) + +@socketio.on('connect') +def handle_connection(): + print('connected') + socketio.emit('connect', payload={'data':'connection'}) + +@socketio.on('message') +def handle_message(message): + print(message) + emit('message return', {'data':'a message was sent'}) \ No newline at end of file diff --git a/app.py b/app.py index c0d99e9..fe753db 100644 --- a/app.py +++ b/app.py @@ -13,12 +13,12 @@ app = Flask(__name__) bcrypt = Bcrypt(app) app.config['CORS_HEADERS'] = 'Content-Type' app.config.from_object(DevelopmentConfig) -socketio = SocketIO(app) - +socketio = SocketIO(app, cors_allowed_origins="http://localhost:3000") +# cors_allowed_origins="http://localhost:3000" def create_app(): CORS(app, resources={ r"/api/*": {"origins": "http://localhost:3000"}, - r"/auth/*": {"origins": "http://localhost:3000"} + r"/auth/*": {"origins": "http://localhost:3000"}, }) db.init_app(app) ma.init_app(app) diff --git a/configuration/config.py b/configuration/config.py index 55c5173..4912a7b 100644 --- a/configuration/config.py +++ b/configuration/config.py @@ -14,7 +14,7 @@ class DevelopmentConfig(BaseConfig): DEBUG = True BCRYPT_LOG_ROUNDS = 4 SQLALCHEMY_DATABASE_URI = DATABASE - PORT = 8000 + PORT = 5000 class TestingConfig(BaseConfig): diff --git a/requirements.txt b/requirements.txt index 0af54ec..432ba18 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,6 +3,8 @@ astroid==2.3.1 bcrypt==3.1.7 cffi==1.12.3 Click==7.0 +dnspython==1.16.0 +eventlet==0.25.1 Flask==1.1.1 Flask-Bcrypt==0.7.1 Flask-Cors==3.0.8 @@ -12,7 +14,6 @@ Flask-Script==2.0.6 Flask-SocketIO==4.2.1 Flask-SQLAlchemy==2.4.1 Flask-Testing==0.7.1 -gevent==1.4.0 greenlet==0.4.15 isort==4.3.21 itsdangerous==1.1.0 @@ -23,6 +24,7 @@ MarkupSafe==1.1.1 marshmallow==3.2.0 marshmallow-sqlalchemy==0.19.0 mccabe==0.6.1 +monotonic==1.5 numpy==1.17.2 psycopg2==2.8.3 pycparser==2.19 From 2daacc05c90d818b2ec2d22b2e0d4daa84122d1b Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Mon, 7 Oct 2019 16:28:11 -0700 Subject: [PATCH 28/47] implement bidrectional socketio --- api/api.py | 12 ------------ server.py | 7 +++++-- websockets/__init__.py | 0 websockets/socket.py | 17 +++++++++++++++++ 4 files changed, 22 insertions(+), 14 deletions(-) create mode 100644 websockets/__init__.py create mode 100644 websockets/socket.py diff --git a/api/api.py b/api/api.py index 6c795b8..78efbbb 100644 --- a/api/api.py +++ b/api/api.py @@ -1,7 +1,5 @@ from flask import Blueprint, request, jsonify, session from .users.user_endpoint import UserEndpoint -from app import socketio -from flask_socketio import emit api = Blueprint('api', __name__, url_prefix='/api') @@ -17,13 +15,3 @@ def api_users(): @api.route('/user') def api_user(): return jsonify(UserEndpoint.user()) - -@socketio.on('connect') -def handle_connection(): - print('connected') - socketio.emit('connect', payload={'data':'connection'}) - -@socketio.on('message') -def handle_message(message): - print(message) - emit('message return', {'data':'a message was sent'}) \ No newline at end of file diff --git a/server.py b/server.py index 23b7e45..bebf6c1 100644 --- a/server.py +++ b/server.py @@ -1,9 +1,12 @@ -from app import create_app, db, socketio +from app import create_app, db # Blueprints from api.api import api from auth.auth import auth +# Web sockets +from websockets.socket import socketio + import configuration.models_mount from flask_migrate import Migrate @@ -14,4 +17,4 @@ if __name__ == '__main__': app.register_blueprint(api) app.register_blueprint(auth) migrate = Migrate(app, db) - socketio.run(app) \ No newline at end of file + socketio.run(app, debug=True) \ No newline at end of file diff --git a/websockets/__init__.py b/websockets/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/websockets/socket.py b/websockets/socket.py new file mode 100644 index 0000000..5de2d8c --- /dev/null +++ b/websockets/socket.py @@ -0,0 +1,17 @@ +from app import socketio +from flask_socketio import send, emit + +@socketio.on('connect') +def handle_connection(): + print(''' + + wow, there was a socketio connection! + + cool + ''') + send({'data':'connection'}) + +@socketio.on('message') +def handle_message(message): + print(message) + emit('message return', {'data':'a message was sent'}) \ No newline at end of file From 2bafe9102c7bc696262cd14ec60e6c47712b0fb4 Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Tue, 8 Oct 2019 16:56:28 -0700 Subject: [PATCH 29/47] add room endpoints --- api/api.py | 22 ++++++++++++++++++---- api/rooms/room.py | 7 ------- api/rooms/room_endpoint.py | 9 +++++++++ websockets/socket.py | 12 ++++++++++-- 4 files changed, 37 insertions(+), 13 deletions(-) delete mode 100644 api/rooms/room.py create mode 100644 api/rooms/room_endpoint.py diff --git a/api/api.py b/api/api.py index 78efbbb..3a88fc3 100644 --- a/api/api.py +++ b/api/api.py @@ -1,5 +1,6 @@ from flask import Blueprint, request, jsonify, session from .users.user_endpoint import UserEndpoint +from .users.room_endpoint import RoomEndpoint api = Blueprint('api', __name__, url_prefix='/api') @@ -8,10 +9,23 @@ def api_home(): response = {"message": "home page"} return jsonify(response) -@api.route('/users') -def api_users(): +@api.route('/users', methods=['GET']) +def api_get_users(): return jsonify(UserEndpoint.users()) -@api.route('/user') -def api_user(): +@api.route('/user', methods=['GET']) +def api_get_user(): return jsonify(UserEndpoint.user()) + +@api.route('/rooms', methods=['GET']) +def api_get_rooms(): + return RoomEndpoint.get_rooms() + +@api.route('/room', methods=['GET']) +def api_get_room(): + return RoomEndpoint.get_room() + +# protected route +@api.route('/room', methods=['POST']) +def api_post_room(): + return pass \ No newline at end of file diff --git a/api/rooms/room.py b/api/rooms/room.py deleted file mode 100644 index 13081d1..0000000 --- a/api/rooms/room.py +++ /dev/null @@ -1,7 +0,0 @@ -from flask import Blueprint - -room = Blueprint('rooms', __name__) - -@room.route('/') -def func(): - pass \ No newline at end of file diff --git a/api/rooms/room_endpoint.py b/api/rooms/room_endpoint.py new file mode 100644 index 0000000..5ba537d --- /dev/null +++ b/api/rooms/room_endpoint.py @@ -0,0 +1,9 @@ +from models.User import User, user_schema, users_schema +from models.Room import Room +from flask import request, jsonify, Response, json + +class RoomEndpoint(object): + def get_rooms(): + pass + def get_room(): + pass \ No newline at end of file diff --git a/websockets/socket.py b/websockets/socket.py index 5de2d8c..12c5e9d 100644 --- a/websockets/socket.py +++ b/websockets/socket.py @@ -9,9 +9,17 @@ def handle_connection(): cool ''') - send({'data':'connection'}) + emit('message', {'data':'connection'}) @socketio.on('message') def handle_message(message): print(message) - emit('message return', {'data':'a message was sent'}) \ No newline at end of file + emit('message return', {'message':'a message was sent'}, broadcast=True) + +@socketio.on('connect', namespace='/newroom') +def handle_connection(): + print(''' + + look cool a namespaced socketio connection! + + ''') \ No newline at end of file From 33584d6147e65edda878c377b7ab241d259cea4f Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Tue, 8 Oct 2019 17:18:40 -0700 Subject: [PATCH 30/47] refactor api endpoints --- api/api.py | 29 ++++++++--------------------- api/rooms/api_rooms.py | 19 +++++++++++++++++++ api/rooms/room_endpoint.py | 9 --------- api/users/api_users.py | 25 +++++++++++++++++++++++++ api/users/user_endpoint.py | 15 --------------- server.py | 5 ++--- 6 files changed, 54 insertions(+), 48 deletions(-) create mode 100644 api/rooms/api_rooms.py delete mode 100644 api/rooms/room_endpoint.py create mode 100644 api/users/api_users.py delete mode 100644 api/users/user_endpoint.py diff --git a/api/api.py b/api/api.py index 3a88fc3..8703d27 100644 --- a/api/api.py +++ b/api/api.py @@ -1,31 +1,18 @@ from flask import Blueprint, request, jsonify, session -from .users.user_endpoint import UserEndpoint -from .users.room_endpoint import RoomEndpoint +from .users.api_users import api_users +from .rooms.api_rooms import api_rooms api = Blueprint('api', __name__, url_prefix='/api') +def register_api_endpoints(app): + app.register_blueprint(api_users) + app.register_blueprint(api_rooms) + app.register_blueprint(api) + return app + @api.route('/home', methods=['GET']) def api_home(): response = {"message": "home page"} return jsonify(response) -@api.route('/users', methods=['GET']) -def api_get_users(): - return jsonify(UserEndpoint.users()) -@api.route('/user', methods=['GET']) -def api_get_user(): - return jsonify(UserEndpoint.user()) - -@api.route('/rooms', methods=['GET']) -def api_get_rooms(): - return RoomEndpoint.get_rooms() - -@api.route('/room', methods=['GET']) -def api_get_room(): - return RoomEndpoint.get_room() - -# protected route -@api.route('/room', methods=['POST']) -def api_post_room(): - return pass \ No newline at end of file diff --git a/api/rooms/api_rooms.py b/api/rooms/api_rooms.py new file mode 100644 index 0000000..19c3346 --- /dev/null +++ b/api/rooms/api_rooms.py @@ -0,0 +1,19 @@ +from models.User import User, user_schema, users_schema +from models.GameRoom import GameRoom +from flask import Blueprint, request, jsonify, session + +api_rooms = Blueprint('api_rooms', __name__, url_prefix='/api/rooms') + +@api_rooms.route('/', methods=['GET']) +def get_room(): + pass + +@api_rooms.route('/', methods=['GET']) +def get_rooms(): + response = {"status" : "success"} + return jsonify(response) + +# protected route +@api_rooms.route('/', methods=['POST']) +def post_room(): + pass \ No newline at end of file diff --git a/api/rooms/room_endpoint.py b/api/rooms/room_endpoint.py deleted file mode 100644 index 5ba537d..0000000 --- a/api/rooms/room_endpoint.py +++ /dev/null @@ -1,9 +0,0 @@ -from models.User import User, user_schema, users_schema -from models.Room import Room -from flask import request, jsonify, Response, json - -class RoomEndpoint(object): - def get_rooms(): - pass - def get_room(): - pass \ No newline at end of file diff --git a/api/users/api_users.py b/api/users/api_users.py new file mode 100644 index 0000000..e6d4183 --- /dev/null +++ b/api/users/api_users.py @@ -0,0 +1,25 @@ +from models.User import User, user_schema, users_schema +from flask import Blueprint, request, json, session, jsonify + +api_users = Blueprint('api_users', __name__, url_prefix='/api') + +@api_users.route('/users/', methods=['GET']) +def api_get_users(): + print('called one') + users = User.query.all() + response = users_schema.dumps(users) + return jsonify(response) + +@api_users.route('/users/account', methods=['GET']) +def api_get_user(): + print('called') + auth_header = request.headers.get('Authorization') or None + if auth_header: + auth_token = auth_header.split(" ")[1] + user = User.decode_auth_token(auth_token) or None + response = json.dumps(user) + else: + response = { + 'status': 'failed', + 'message': 'Please Log In'} + return jsonify(response) diff --git a/api/users/user_endpoint.py b/api/users/user_endpoint.py deleted file mode 100644 index c5413ab..0000000 --- a/api/users/user_endpoint.py +++ /dev/null @@ -1,15 +0,0 @@ -from models.User import User, user_schema, users_schema -from flask import request, jsonify, Response, json - -class UserEndpoint(object): - def users(): - users = User.query.all() - response = users_schema.dumps(users) - return response - def user(): - - auth_header = request.headers.get('Authorization') or None - auth_token = auth_header.split(" ")[1] - user = User.decode_auth_token(auth_token) or None - response = json.dumps(user) - return response \ No newline at end of file diff --git a/server.py b/server.py index bebf6c1..226bf96 100644 --- a/server.py +++ b/server.py @@ -1,7 +1,7 @@ from app import create_app, db # Blueprints -from api.api import api +from api.api import register_api_endpoints from auth.auth import auth # Web sockets @@ -14,7 +14,6 @@ from flask_migrate import Migrate if __name__ == '__main__': app = create_app() - app.register_blueprint(api) - app.register_blueprint(auth) + register_api_endpoints(app) migrate = Migrate(app, db) socketio.run(app, debug=True) \ No newline at end of file From 34182fdc23b1af0fb515ee48cebfcd857f479592 Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Tue, 8 Oct 2019 18:03:46 -0700 Subject: [PATCH 31/47] stub jwt_required decorator to protect routes --- api/decorators.py | 13 +++++++++++++ api/users/api_users.py | 3 +++ 2 files changed, 16 insertions(+) create mode 100644 api/decorators.py diff --git a/api/decorators.py b/api/decorators.py new file mode 100644 index 0000000..a34620f --- /dev/null +++ b/api/decorators.py @@ -0,0 +1,13 @@ +from flask import Blueprint, request, jsonify, session + +def jwt_required(): + def decorator(func): + def authorized(*args, **kwargs): + auth_header = request.headers.get('Authorization') or None + if auth_header: + # check secret on auth header + return func(*args, **kwargs) + else: + abort(401) + return authorized + return decorator \ No newline at end of file diff --git a/api/users/api_users.py b/api/users/api_users.py index e6d4183..d4f3f17 100644 --- a/api/users/api_users.py +++ b/api/users/api_users.py @@ -1,5 +1,6 @@ from models.User import User, user_schema, users_schema from flask import Blueprint, request, json, session, jsonify +from ..decorators import jwt_required api_users = Blueprint('api_users', __name__, url_prefix='/api') @@ -10,7 +11,9 @@ def api_get_users(): response = users_schema.dumps(users) return jsonify(response) + @api_users.route('/users/account', methods=['GET']) +@jwt_required() def api_get_user(): print('called') auth_header = request.headers.get('Authorization') or None From 8cd08567281b53c3fd24e48dfd849e7db9d1292c Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Tue, 8 Oct 2019 22:53:37 -0700 Subject: [PATCH 32/47] add rooms post route --- api/decorators.py | 10 ++++++++-- api/rooms/api_rooms.py | 26 +++++++++++++++++++++++++- models/GameRoom.py | 23 ++++++++++++++++++++--- models/User.py | 6 ++---- 4 files changed, 55 insertions(+), 10 deletions(-) diff --git a/api/decorators.py b/api/decorators.py index a34620f..58f1a65 100644 --- a/api/decorators.py +++ b/api/decorators.py @@ -1,12 +1,18 @@ from flask import Blueprint, request, jsonify, session +import os +import jwt def jwt_required(): def decorator(func): def authorized(*args, **kwargs): auth_header = request.headers.get('Authorization') or None + print(User.decode_auth_token(auth_header.split(" ")[1])) if auth_header: - # check secret on auth header - return func(*args, **kwargs) + auth_token = auth_header.split(" ")[1] + if jwt.decode(auth_token, os.environ.get('SECRET_KEY')): + return func(*args, **kwargs) + else: + abort(401) else: abort(401) return authorized diff --git a/api/rooms/api_rooms.py b/api/rooms/api_rooms.py index 19c3346..f470349 100644 --- a/api/rooms/api_rooms.py +++ b/api/rooms/api_rooms.py @@ -1,6 +1,7 @@ from models.User import User, user_schema, users_schema from models.GameRoom import GameRoom from flask import Blueprint, request, jsonify, session +from ..decorators import jwt_required api_rooms = Blueprint('api_rooms', __name__, url_prefix='/api/rooms') @@ -15,5 +16,28 @@ def get_rooms(): # protected route @api_rooms.route('/', methods=['POST']) +@jwt_required() def post_room(): - pass \ No newline at end of file + data = request.get_json() + try: + room = GameRoom( + name = data['name'], + description = data['description'], + private = data['private'], + language = data['language'] + ) + db.session.add(room) + db.session.commit() + response = { + 'status': 'success', + 'message': 'Succesfully registered.', + 'gameRoom': room.id + } + return jsonify(response), 201 + except Exception as e: + print(e.__dict__) + response = { + 'status': 'fail', + 'message': 'There was an error. Please try again.' + } + return jsonify(response), 401 \ No newline at end of file diff --git a/models/GameRoom.py b/models/GameRoom.py index 3e5239c..0a83fb3 100644 --- a/models/GameRoom.py +++ b/models/GameRoom.py @@ -1,8 +1,14 @@ from app import db, ma +# TODO User >---< GameRoom import enum +game_rooms_users = db.Table('game_rooms_users', + db.Column('user_id', db.Integer, db.ForeignKey('users.id'), primary_key=True), + db.Column('game_rooms_id', db.Integer, db.ForeignKey('game_rooms.id'), primary_key=True) +) + class Languages(enum.Enum): - EN: "English" + EN = "English" class GameRoom(db.Model): __tablename__ = "game_rooms" @@ -14,5 +20,16 @@ class GameRoom(db.Model): private = db.Column(db.Boolean(), nullable=False, default=False) language = db.Column(db.Enum(Languages), nullable=False) - def __init__(self): - pass \ No newline at end of file + # ! Foreign Keys + users = db.relationship( + 'User', + secondary=game_rooms_users, + lazy='subquery', + backref=db.backref('game_rooms', lazy=True) + ) + + def __init__(self, name, description, private=False, language=Languages.EN): + self.name = name + self.description = description + self.private = private + self.language = language diff --git a/models/User.py b/models/User.py index 917df4c..73c6bea 100644 --- a/models/User.py +++ b/models/User.py @@ -46,7 +46,7 @@ class Ranks(enum.Enum): # with minimal Elo rating K28 = "Twenty-Eight Kyu" K29 = "Twenty-Nine Kyu" K30 = "Thirty Kyu" # Elo -900 - RU = "Unknown Rank" + UR = "Unknown Rank" class User(db.Model): __tablename__ = "users" @@ -61,9 +61,7 @@ class User(db.Model): elo = db.Column(db.Integer) rank_certainty = db.Column(db.Boolean, nullable=False, default=False) - def __init__(self, username, email, password, rank=Ranks.K1, admin=False): - print(rank) - print(Ranks) + def __init__(self, username, email, password, rank=Ranks.UR, admin=False): self.username = username self.email = email self.password = bcrypt.generate_password_hash( From 9380f0485cbb4c5fb5e7a16283086dea3c5a5f64 Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Wed, 9 Oct 2019 00:08:37 -0700 Subject: [PATCH 33/47] debug auth --- api/api.py | 3 +++ api/decorators.py | 1 - api/rooms/api_rooms.py | 2 +- auth/auth.py | 1 + 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/api/api.py b/api/api.py index 8703d27..c1b926a 100644 --- a/api/api.py +++ b/api/api.py @@ -2,12 +2,15 @@ from flask import Blueprint, request, jsonify, session from .users.api_users import api_users from .rooms.api_rooms import api_rooms +from auth.auth import auth + api = Blueprint('api', __name__, url_prefix='/api') def register_api_endpoints(app): app.register_blueprint(api_users) app.register_blueprint(api_rooms) app.register_blueprint(api) + app.register_blueprint(auth) return app @api.route('/home', methods=['GET']) diff --git a/api/decorators.py b/api/decorators.py index 58f1a65..e971bec 100644 --- a/api/decorators.py +++ b/api/decorators.py @@ -6,7 +6,6 @@ def jwt_required(): def decorator(func): def authorized(*args, **kwargs): auth_header = request.headers.get('Authorization') or None - print(User.decode_auth_token(auth_header.split(" ")[1])) if auth_header: auth_token = auth_header.split(" ")[1] if jwt.decode(auth_token, os.environ.get('SECRET_KEY')): diff --git a/api/rooms/api_rooms.py b/api/rooms/api_rooms.py index f470349..37ac0ff 100644 --- a/api/rooms/api_rooms.py +++ b/api/rooms/api_rooms.py @@ -5,7 +5,7 @@ from ..decorators import jwt_required api_rooms = Blueprint('api_rooms', __name__, url_prefix='/api/rooms') -@api_rooms.route('/', methods=['GET']) +@api_rooms.route('/', methods=['GET']) def get_room(): pass diff --git a/auth/auth.py b/auth/auth.py index 1ead3fb..c263c6e 100644 --- a/auth/auth.py +++ b/auth/auth.py @@ -52,6 +52,7 @@ def auth_login(): try: # fetch the user data print('getting here') + print(data) user = User.query.filter_by(email=data['email']).first() print(user.username) auth_token = user.encode_auth_token(user.id) From 8fdd92f1c9ebccc9a76eff07a9cc74bcac1ed295 Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Wed, 9 Oct 2019 00:20:36 -0700 Subject: [PATCH 34/47] init namespace socket --- websockets/socket.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/websockets/socket.py b/websockets/socket.py index 12c5e9d..020a147 100644 --- a/websockets/socket.py +++ b/websockets/socket.py @@ -9,12 +9,12 @@ def handle_connection(): cool ''') - emit('message', {'data':'connection'}) + emit('message', {'data':'connection'}, broadcast=True) @socketio.on('message') def handle_message(message): print(message) - emit('message return', {'message':'a message was sent'}, broadcast=True) + emit('init namespace', {'namespace':'newroom'}) @socketio.on('connect', namespace='/newroom') def handle_connection(): From e5e7bb2a3f60e7a6b68cbdf7c7becce408afdb5a Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Wed, 9 Oct 2019 10:06:06 -0700 Subject: [PATCH 35/47] make migrations for GameRoomsUsers join table --- migrations/versions/02be4c8fbd69_.py | 34 ++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 migrations/versions/02be4c8fbd69_.py diff --git a/migrations/versions/02be4c8fbd69_.py b/migrations/versions/02be4c8fbd69_.py new file mode 100644 index 0000000..f4fb79e --- /dev/null +++ b/migrations/versions/02be4c8fbd69_.py @@ -0,0 +1,34 @@ +"""empty message + +Revision ID: 02be4c8fbd69 +Revises: 0e0f8ad1362d +Create Date: 2019-10-09 10:05:32.403967 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '02be4c8fbd69' +down_revision = '0e0f8ad1362d' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('game_rooms_users', + sa.Column('user_id', sa.Integer(), nullable=False), + sa.Column('game_rooms_id', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['game_rooms_id'], ['game_rooms.id'], ), + sa.ForeignKeyConstraint(['user_id'], ['users.id'], ), + sa.PrimaryKeyConstraint('user_id', 'game_rooms_id') + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('game_rooms_users') + # ### end Alembic commands ### From 69c33c3a75db18bb6eec27185a6df6e15c0e76c6 Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Wed, 9 Oct 2019 15:42:38 -0700 Subject: [PATCH 36/47] create room --- api/decorators.py | 4 +++- api/rooms/api_rooms.py | 10 +++++--- migrations/versions/bafcadd8390e_.py | 30 ++++++++++++++++++++++++ migrations/versions/e5b4175be075_.py | 35 ++++++++++++++++++++++++++++ models/GameRoom.py | 17 ++++++++++---- models/User.py | 2 +- 6 files changed, 88 insertions(+), 10 deletions(-) create mode 100644 migrations/versions/bafcadd8390e_.py create mode 100644 migrations/versions/e5b4175be075_.py diff --git a/api/decorators.py b/api/decorators.py index e971bec..18a4cd2 100644 --- a/api/decorators.py +++ b/api/decorators.py @@ -1,13 +1,15 @@ -from flask import Blueprint, request, jsonify, session +from flask import Blueprint, request, jsonify, session, abort import os import jwt def jwt_required(): def decorator(func): def authorized(*args, **kwargs): + print(request.headers) auth_header = request.headers.get('Authorization') or None if auth_header: auth_token = auth_header.split(" ")[1] + print(auth_token) if jwt.decode(auth_token, os.environ.get('SECRET_KEY')): return func(*args, **kwargs) else: diff --git a/api/rooms/api_rooms.py b/api/rooms/api_rooms.py index 37ac0ff..14039b4 100644 --- a/api/rooms/api_rooms.py +++ b/api/rooms/api_rooms.py @@ -1,6 +1,7 @@ +from flask import Blueprint, request, jsonify, session from models.User import User, user_schema, users_schema from models.GameRoom import GameRoom -from flask import Blueprint, request, jsonify, session +from database import db from ..decorators import jwt_required api_rooms = Blueprint('api_rooms', __name__, url_prefix='/api/rooms') @@ -18,13 +19,16 @@ def get_rooms(): @api_rooms.route('/', methods=['POST']) @jwt_required() def post_room(): + print('Hey it\'s a POST request') data = request.get_json() + print(data) try: room = GameRoom( name = data['name'], description = data['description'], - private = data['private'], - language = data['language'] + # TODO add support for private rooms and multiple languages + # private = data['private'], + # language = data['language'] ) db.session.add(room) db.session.commit() diff --git a/migrations/versions/bafcadd8390e_.py b/migrations/versions/bafcadd8390e_.py new file mode 100644 index 0000000..e25f01f --- /dev/null +++ b/migrations/versions/bafcadd8390e_.py @@ -0,0 +1,30 @@ +"""empty message + +Revision ID: bafcadd8390e +Revises: e5b4175be075 +Create Date: 2019-10-09 15:34:31.194998 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'bafcadd8390e' +down_revision = 'e5b4175be075' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('game_rooms', sa.Column('language', sa.Integer(), nullable=False)) + op.create_foreign_key(None, 'game_rooms', 'room_languages', ['language'], ['id']) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint(None, 'game_rooms', type_='foreignkey') + op.drop_column('game_rooms', 'language') + # ### end Alembic commands ### diff --git a/migrations/versions/e5b4175be075_.py b/migrations/versions/e5b4175be075_.py new file mode 100644 index 0000000..025736a --- /dev/null +++ b/migrations/versions/e5b4175be075_.py @@ -0,0 +1,35 @@ +"""empty message + +Revision ID: e5b4175be075 +Revises: 02be4c8fbd69 +Create Date: 2019-10-09 15:34:06.312258 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = 'e5b4175be075' +down_revision = '02be4c8fbd69' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('room_languages', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('name', sa.String(length=40), nullable=False), + sa.Column('iso', sa.String(length=2), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + op.drop_column('game_rooms', 'language') + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('game_rooms', sa.Column('language', postgresql.ENUM(name='languages'), autoincrement=False, nullable=False)) + op.drop_table('room_languages') + # ### end Alembic commands ### diff --git a/models/GameRoom.py b/models/GameRoom.py index 0a83fb3..ce6d477 100644 --- a/models/GameRoom.py +++ b/models/GameRoom.py @@ -1,14 +1,19 @@ from app import db, ma # TODO User >---< GameRoom -import enum +# ! Game Room >-< Users join table game_rooms_users = db.Table('game_rooms_users', db.Column('user_id', db.Integer, db.ForeignKey('users.id'), primary_key=True), db.Column('game_rooms_id', db.Integer, db.ForeignKey('game_rooms.id'), primary_key=True) ) -class Languages(enum.Enum): - EN = "English" +class RoomLanguages(db.Model): + __tablename__ = "room_languages" + + id = db.Column(db.Integer, primary_key=True, autoincrement=True) + name = db.Column(db.String(40), nullable=False) + iso = db.Column(db.String(2), nullable=False) + class GameRoom(db.Model): __tablename__ = "game_rooms" @@ -18,7 +23,6 @@ class GameRoom(db.Model): name = db.Column(db.String(40), nullable=False) description = db.Column(db.String(200), nullable=False) private = db.Column(db.Boolean(), nullable=False, default=False) - language = db.Column(db.Enum(Languages), nullable=False) # ! Foreign Keys users = db.relationship( @@ -27,8 +31,11 @@ class GameRoom(db.Model): lazy='subquery', backref=db.backref('game_rooms', lazy=True) ) + + language = db.Column(db.Integer, db.ForeignKey("room_languages.id"), nullable=False) + - def __init__(self, name, description, private=False, language=Languages.EN): + def __init__(self, name, description, private=False, language=1): self.name = name self.description = description self.private = private diff --git a/models/User.py b/models/User.py index 73c6bea..9273f31 100644 --- a/models/User.py +++ b/models/User.py @@ -61,7 +61,7 @@ class User(db.Model): elo = db.Column(db.Integer) rank_certainty = db.Column(db.Boolean, nullable=False, default=False) - def __init__(self, username, email, password, rank=Ranks.UR, admin=False): + def __init__(self, username, email, password, rank=Ranks.K1, admin=False): self.username = username self.email = email self.password = bcrypt.generate_password_hash( From e6fa8a7e31e40e470a2f5b51a9af0bedbf833fd8 Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Wed, 9 Oct 2019 16:27:41 -0700 Subject: [PATCH 37/47] serve gamerooms index --- api/rooms/api_rooms.py | 5 +++-- models/GameRoom.py | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/api/rooms/api_rooms.py b/api/rooms/api_rooms.py index 14039b4..8b3d68f 100644 --- a/api/rooms/api_rooms.py +++ b/api/rooms/api_rooms.py @@ -1,6 +1,6 @@ from flask import Blueprint, request, jsonify, session from models.User import User, user_schema, users_schema -from models.GameRoom import GameRoom +from models.GameRoom import GameRoom, rooms_schema, room_schema from database import db from ..decorators import jwt_required @@ -12,7 +12,8 @@ def get_room(): @api_rooms.route('/', methods=['GET']) def get_rooms(): - response = {"status" : "success"} + rooms = GameRoom.query.all() + response = rooms_schema.dumps(rooms) return jsonify(response) # protected route diff --git a/models/GameRoom.py b/models/GameRoom.py index ce6d477..fda05d2 100644 --- a/models/GameRoom.py +++ b/models/GameRoom.py @@ -1,4 +1,5 @@ from app import db, ma +from marshmallow import fields # TODO User >---< GameRoom # ! Game Room >-< Users join table @@ -40,3 +41,21 @@ class GameRoom(db.Model): self.description = description self.private = private self.language = language + +class LanguageSchema(ma.ModelSchema): + id = fields.Int() + name = fields.Str() + iso = fields.Str() + +language_schema = LanguageSchema() + +class RoomSchema(ma.ModelSchema): + id = fields.Int() + name = fields.Str() + description = fields.Str() + private = fields.Bool() + language = fields.Nested(LanguageSchema) + + +room_schema = RoomSchema() +rooms_schema = RoomSchema(many=True) \ No newline at end of file From df3313d08985b3e9cc4193ebbf1bf905b627fc83 Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Thu, 10 Oct 2019 14:57:29 -0700 Subject: [PATCH 38/47] refactor database models --- api/api.py | 2 + api/decorators.py | 2 - api/games/api_games.py | 53 +++++++++++++ api/games/game.py | 7 -- api/rooms/api_rooms.py | 19 +++-- api/users/api_users.py | 2 +- migrations/versions/02be4c8fbd69_.py | 34 -------- migrations/versions/0e0f8ad1362d_.py | 30 ------- migrations/versions/16c70b11242e_.py | 28 ------- migrations/versions/50aab465cf44_.py | 28 ------- .../{9519d28f13f0_.py => acabfc6d158d_.py} | 40 +++++++--- migrations/versions/bafcadd8390e_.py | 30 ------- migrations/versions/bdc3cd5d7499_.py | 28 ------- migrations/versions/bfa4b406e22f_.py | 28 ------- migrations/versions/e5b4175be075_.py | 35 -------- models/Game.py | 47 ++++++++--- models/Message.py | 7 +- models/Move.py | 7 +- models/TimeSettings.py | 17 ++-- models/User.py | 79 ++++++++++--------- 20 files changed, 190 insertions(+), 333 deletions(-) create mode 100644 api/games/api_games.py delete mode 100644 api/games/game.py delete mode 100644 migrations/versions/02be4c8fbd69_.py delete mode 100644 migrations/versions/0e0f8ad1362d_.py delete mode 100644 migrations/versions/16c70b11242e_.py delete mode 100644 migrations/versions/50aab465cf44_.py rename migrations/versions/{9519d28f13f0_.py => acabfc6d158d_.py} (80%) delete mode 100644 migrations/versions/bafcadd8390e_.py delete mode 100644 migrations/versions/bdc3cd5d7499_.py delete mode 100644 migrations/versions/bfa4b406e22f_.py delete mode 100644 migrations/versions/e5b4175be075_.py diff --git a/api/api.py b/api/api.py index c1b926a..0cbe65c 100644 --- a/api/api.py +++ b/api/api.py @@ -1,6 +1,7 @@ from flask import Blueprint, request, jsonify, session from .users.api_users import api_users from .rooms.api_rooms import api_rooms +from .games.api_games import api_games from auth.auth import auth @@ -9,6 +10,7 @@ api = Blueprint('api', __name__, url_prefix='/api') def register_api_endpoints(app): app.register_blueprint(api_users) app.register_blueprint(api_rooms) + app.register_blueprint(api_games) app.register_blueprint(api) app.register_blueprint(auth) return app diff --git a/api/decorators.py b/api/decorators.py index 18a4cd2..96d6865 100644 --- a/api/decorators.py +++ b/api/decorators.py @@ -5,11 +5,9 @@ import jwt def jwt_required(): def decorator(func): def authorized(*args, **kwargs): - print(request.headers) auth_header = request.headers.get('Authorization') or None if auth_header: auth_token = auth_header.split(" ")[1] - print(auth_token) if jwt.decode(auth_token, os.environ.get('SECRET_KEY')): return func(*args, **kwargs) else: diff --git a/api/games/api_games.py b/api/games/api_games.py new file mode 100644 index 0000000..d2c852a --- /dev/null +++ b/api/games/api_games.py @@ -0,0 +1,53 @@ +from flask import Blueprint, request, jsonify, session +from models.User import User, user_schema, users_schema +from models.GameRoom import GameRoom, rooms_schema, room_schema +from models.Game import Game +from database import db +from ..decorators import jwt_required +import jwt +import os +import json + +api_games = Blueprint('api_games', __name__, url_prefix='/api/games') + +@api_games.route('/', methods=['POST']) +@jwt_required() +def post_game(): + print('Hey it\'s a post request!') + data = request.get_json() + # TODO create decorator that returns user from header + auth_header = request.headers.get('Authorization') + user = jwt.decode(auth_header.split(" ")[1], os.environ.get('SECRET_KEY'))['user'] + print(user) + print('data') + print(data) + user_id = json.loads(user)['id'] + try: + game = Game( + name = data['name'], + description = data['description'], + board_size = data['boardSize'], + game_room = data['gameRoom'], + player_white = user_id + ) + print(game) + db.session.add(game) + print('game added') + db.session.commit() + print('game') + print(game) + response = { + 'status': 'success', + 'message': 'Game created', + 'game': game.id + } + return jsonify(response), 201 + except Exception as e: + print('error') + print(e) + print(e.__dict__) + response = { + 'status': 'fail', + 'message': 'There was an error. Please try again.' + } + return jsonify(response), 401 diff --git a/api/games/game.py b/api/games/game.py deleted file mode 100644 index 3f289de..0000000 --- a/api/games/game.py +++ /dev/null @@ -1,7 +0,0 @@ -from flask import Blueprint - -game = Blueprint('games', __name__) - -@game.route('/') -def func(): - pass \ No newline at end of file diff --git a/api/rooms/api_rooms.py b/api/rooms/api_rooms.py index 8b3d68f..f520612 100644 --- a/api/rooms/api_rooms.py +++ b/api/rooms/api_rooms.py @@ -1,14 +1,18 @@ from flask import Blueprint, request, jsonify, session from models.User import User, user_schema, users_schema from models.GameRoom import GameRoom, rooms_schema, room_schema +from models.Game import Game, games_schema from database import db from ..decorators import jwt_required api_rooms = Blueprint('api_rooms', __name__, url_prefix='/api/rooms') @api_rooms.route('/', methods=['GET']) -def get_room(): - pass +def get_room(room_id): + print(room_id) + games = Game.query.filter_by(game_room=room_id).all() + response = games_schema.dumps(games) + return jsonify(response) @api_rooms.route('/', methods=['GET']) def get_rooms(): @@ -25,11 +29,11 @@ def post_room(): print(data) try: room = GameRoom( - name = data['name'], - description = data['description'], - # TODO add support for private rooms and multiple languages - # private = data['private'], - # language = data['language'] + name = data['name'], + description = data['description'], + # TODO add support for private rooms and multiple languages + # private = data['private'], + # language = data['language'] ) db.session.add(room) db.session.commit() @@ -40,6 +44,7 @@ def post_room(): } return jsonify(response), 201 except Exception as e: + print(e) print(e.__dict__) response = { 'status': 'fail', diff --git a/api/users/api_users.py b/api/users/api_users.py index d4f3f17..7a77709 100644 --- a/api/users/api_users.py +++ b/api/users/api_users.py @@ -9,7 +9,7 @@ def api_get_users(): print('called one') users = User.query.all() response = users_schema.dumps(users) - return jsonify(response) + return jsonify(response), 200 @api_users.route('/users/account', methods=['GET']) diff --git a/migrations/versions/02be4c8fbd69_.py b/migrations/versions/02be4c8fbd69_.py deleted file mode 100644 index f4fb79e..0000000 --- a/migrations/versions/02be4c8fbd69_.py +++ /dev/null @@ -1,34 +0,0 @@ -"""empty message - -Revision ID: 02be4c8fbd69 -Revises: 0e0f8ad1362d -Create Date: 2019-10-09 10:05:32.403967 - -""" -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision = '02be4c8fbd69' -down_revision = '0e0f8ad1362d' -branch_labels = None -depends_on = None - - -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.create_table('game_rooms_users', - sa.Column('user_id', sa.Integer(), nullable=False), - sa.Column('game_rooms_id', sa.Integer(), nullable=False), - sa.ForeignKeyConstraint(['game_rooms_id'], ['game_rooms.id'], ), - sa.ForeignKeyConstraint(['user_id'], ['users.id'], ), - sa.PrimaryKeyConstraint('user_id', 'game_rooms_id') - ) - # ### end Alembic commands ### - - -def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.drop_table('game_rooms_users') - # ### end Alembic commands ### diff --git a/migrations/versions/0e0f8ad1362d_.py b/migrations/versions/0e0f8ad1362d_.py deleted file mode 100644 index 62a07e0..0000000 --- a/migrations/versions/0e0f8ad1362d_.py +++ /dev/null @@ -1,30 +0,0 @@ -"""empty message - -Revision ID: 0e0f8ad1362d -Revises: 50aab465cf44 -Create Date: 2019-10-05 23:21:57.620808 - -""" -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision = '0e0f8ad1362d' -down_revision = '50aab465cf44' -branch_labels = None -depends_on = None - - -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.add_column('users', sa.Column('username', sa.String(length=255), nullable=False)) - op.create_unique_constraint(None, 'users', ['username']) - # ### end Alembic commands ### - - -def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.drop_constraint(None, 'users', type_='unique') - op.drop_column('users', 'username') - # ### end Alembic commands ### diff --git a/migrations/versions/16c70b11242e_.py b/migrations/versions/16c70b11242e_.py deleted file mode 100644 index af28b38..0000000 --- a/migrations/versions/16c70b11242e_.py +++ /dev/null @@ -1,28 +0,0 @@ -"""empty message - -Revision ID: 16c70b11242e -Revises: 9519d28f13f0 -Create Date: 2019-10-02 14:50:27.180692 - -""" -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision = '16c70b11242e' -down_revision = '9519d28f13f0' -branch_labels = None -depends_on = None - - -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.add_column('moves', sa.Column('is_main', sa.Boolean(), nullable=False)) - # ### end Alembic commands ### - - -def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.drop_column('moves', 'is_main') - # ### end Alembic commands ### diff --git a/migrations/versions/50aab465cf44_.py b/migrations/versions/50aab465cf44_.py deleted file mode 100644 index 6aade5b..0000000 --- a/migrations/versions/50aab465cf44_.py +++ /dev/null @@ -1,28 +0,0 @@ -"""empty message - -Revision ID: 50aab465cf44 -Revises: bfa4b406e22f -Create Date: 2019-10-05 22:37:07.643522 - -""" -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision = '50aab465cf44' -down_revision = 'bfa4b406e22f' -branch_labels = None -depends_on = None - - -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.add_column('users', sa.Column('password', sa.String(length=255), nullable=False)) - # ### end Alembic commands ### - - -def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.drop_column('users', 'password') - # ### end Alembic commands ### diff --git a/migrations/versions/9519d28f13f0_.py b/migrations/versions/acabfc6d158d_.py similarity index 80% rename from migrations/versions/9519d28f13f0_.py rename to migrations/versions/acabfc6d158d_.py index 51b4535..bef6a4e 100644 --- a/migrations/versions/9519d28f13f0_.py +++ b/migrations/versions/acabfc6d158d_.py @@ -1,8 +1,8 @@ """empty message -Revision ID: 9519d28f13f0 +Revision ID: acabfc6d158d Revises: -Create Date: 2019-10-02 13:54:17.877590 +Create Date: 2019-10-10 14:25:27.198731 """ from alembic import op @@ -10,7 +10,7 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = '9519d28f13f0' +revision = 'acabfc6d158d' down_revision = None branch_labels = None depends_on = None @@ -18,12 +18,10 @@ depends_on = None def upgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.create_table('game_rooms', + op.create_table('room_languages', sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), sa.Column('name', sa.String(length=40), nullable=False), - sa.Column('description', sa.String(length=200), nullable=False), - sa.Column('private', sa.Boolean(), nullable=False), - sa.Column('language', sa.Enum(name='languages'), nullable=False), + sa.Column('iso', sa.String(length=2), nullable=False), sa.PrimaryKeyConstraint('id') ) op.create_table('time_settings', @@ -38,15 +36,33 @@ def upgrade(): ) op.create_table('users', sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('username', sa.String(length=255), autoincrement=True, nullable=False), sa.Column('email', sa.String(length=255), nullable=False), sa.Column('password', sa.String(length=255), nullable=False), sa.Column('registered_on', sa.DateTime(), nullable=False), sa.Column('admin', sa.Boolean(), nullable=False), - sa.Column('rank', sa.Enum('D7', 'D6', 'D5', 'D4', 'D3', 'D2', 'D1', 'K1', 'K2', 'K3', 'K4', 'K5', 'K6', 'K7', 'K8', 'K9', 'K10', 'K11', 'K12', 'K13', 'K14', 'K15', 'K16', 'K17', 'K18', 'K19', 'K20', 'K21', 'K22', 'K23', 'K24', 'K25', 'K26', 'K27', 'K28', 'K29', 'K30', name='ranks'), nullable=True), + sa.Column('rank', sa.Enum('D7', 'D6', 'D5', 'D4', 'D3', 'D2', 'D1', 'K1', 'K2', 'K3', 'K4', 'K5', 'K6', 'K7', 'K8', 'K9', 'K10', 'K11', 'K12', 'K13', 'K14', 'K15', 'K16', 'K17', 'K18', 'K19', 'K20', 'K21', 'K22', 'K23', 'K24', 'K25', 'K26', 'K27', 'K28', 'K29', 'K30', 'UR', name='ranks'), nullable=True), sa.Column('elo', sa.Integer(), nullable=True), sa.Column('rank_certainty', sa.Boolean(), nullable=False), sa.PrimaryKeyConstraint('id'), - sa.UniqueConstraint('email') + sa.UniqueConstraint('email'), + sa.UniqueConstraint('username') + ) + op.create_table('game_rooms', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('name', sa.String(length=40), nullable=False), + sa.Column('description', sa.String(length=200), nullable=False), + sa.Column('private', sa.Boolean(), nullable=False), + sa.Column('language', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['language'], ['room_languages.id'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('game_rooms_users', + sa.Column('user_id', sa.Integer(), nullable=False), + sa.Column('game_rooms_id', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['game_rooms_id'], ['game_rooms.id'], ), + sa.ForeignKeyConstraint(['user_id'], ['users.id'], ), + sa.PrimaryKeyConstraint('user_id', 'game_rooms_id') ) op.create_table('games', sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), @@ -81,6 +97,8 @@ def upgrade(): sa.Column('x_point', sa.Integer(), nullable=True), sa.Column('y_point', sa.Integer(), nullable=True), sa.Column('move_number', sa.Integer(), nullable=True), + sa.Column('is_pass', sa.Boolean(), nullable=False), + sa.Column('is_main', sa.Boolean(), nullable=False), sa.Column('game', sa.Integer(), nullable=False), sa.Column('preceding_move', sa.Integer(), nullable=True), sa.ForeignKeyConstraint(['game'], ['games.id'], ), @@ -103,7 +121,9 @@ def downgrade(): op.drop_table('messages') op.drop_table('moves') op.drop_table('games') + op.drop_table('game_rooms_users') + op.drop_table('game_rooms') op.drop_table('users') op.drop_table('time_settings') - op.drop_table('game_rooms') + op.drop_table('room_languages') # ### end Alembic commands ### diff --git a/migrations/versions/bafcadd8390e_.py b/migrations/versions/bafcadd8390e_.py deleted file mode 100644 index e25f01f..0000000 --- a/migrations/versions/bafcadd8390e_.py +++ /dev/null @@ -1,30 +0,0 @@ -"""empty message - -Revision ID: bafcadd8390e -Revises: e5b4175be075 -Create Date: 2019-10-09 15:34:31.194998 - -""" -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision = 'bafcadd8390e' -down_revision = 'e5b4175be075' -branch_labels = None -depends_on = None - - -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.add_column('game_rooms', sa.Column('language', sa.Integer(), nullable=False)) - op.create_foreign_key(None, 'game_rooms', 'room_languages', ['language'], ['id']) - # ### end Alembic commands ### - - -def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.drop_constraint(None, 'game_rooms', type_='foreignkey') - op.drop_column('game_rooms', 'language') - # ### end Alembic commands ### diff --git a/migrations/versions/bdc3cd5d7499_.py b/migrations/versions/bdc3cd5d7499_.py deleted file mode 100644 index d094cdc..0000000 --- a/migrations/versions/bdc3cd5d7499_.py +++ /dev/null @@ -1,28 +0,0 @@ -"""empty message - -Revision ID: bdc3cd5d7499 -Revises: 16c70b11242e -Create Date: 2019-10-05 22:34:04.358841 - -""" -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision = 'bdc3cd5d7499' -down_revision = '16c70b11242e' -branch_labels = None -depends_on = None - - -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.add_column('moves', sa.Column('is_pass', sa.Boolean(), nullable=False)) - # ### end Alembic commands ### - - -def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.drop_column('moves', 'is_pass') - # ### end Alembic commands ### diff --git a/migrations/versions/bfa4b406e22f_.py b/migrations/versions/bfa4b406e22f_.py deleted file mode 100644 index 525da5f..0000000 --- a/migrations/versions/bfa4b406e22f_.py +++ /dev/null @@ -1,28 +0,0 @@ -"""empty message - -Revision ID: bfa4b406e22f -Revises: bdc3cd5d7499 -Create Date: 2019-10-05 22:36:45.420054 - -""" -from alembic import op -import sqlalchemy as sa -from sqlalchemy.dialects import postgresql - -# revision identifiers, used by Alembic. -revision = 'bfa4b406e22f' -down_revision = 'bdc3cd5d7499' -branch_labels = None -depends_on = None - - -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.drop_column('users', 'password') - # ### end Alembic commands ### - - -def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.add_column('users', sa.Column('password', postgresql.TIMESTAMP(), autoincrement=False, nullable=False)) - # ### end Alembic commands ### diff --git a/migrations/versions/e5b4175be075_.py b/migrations/versions/e5b4175be075_.py deleted file mode 100644 index 025736a..0000000 --- a/migrations/versions/e5b4175be075_.py +++ /dev/null @@ -1,35 +0,0 @@ -"""empty message - -Revision ID: e5b4175be075 -Revises: 02be4c8fbd69 -Create Date: 2019-10-09 15:34:06.312258 - -""" -from alembic import op -import sqlalchemy as sa -from sqlalchemy.dialects import postgresql - -# revision identifiers, used by Alembic. -revision = 'e5b4175be075' -down_revision = '02be4c8fbd69' -branch_labels = None -depends_on = None - - -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.create_table('room_languages', - sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), - sa.Column('name', sa.String(length=40), nullable=False), - sa.Column('iso', sa.String(length=2), nullable=False), - sa.PrimaryKeyConstraint('id') - ) - op.drop_column('game_rooms', 'language') - # ### end Alembic commands ### - - -def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.add_column('game_rooms', sa.Column('language', postgresql.ENUM(name='languages'), autoincrement=False, nullable=False)) - op.drop_table('room_languages') - # ### end Alembic commands ### diff --git a/models/Game.py b/models/Game.py index 742205e..8cf4299 100644 --- a/models/Game.py +++ b/models/Game.py @@ -1,21 +1,24 @@ from app import db, ma +from marshmallow import fields import enum +from models.User import user_schema -class Players(enum.Enum): - BLACK = "The player taking black stones" - WHITE = "The player taking white stones" - VOID = "The game was a draw or voided" - -class WinType(enum.Enum): - DRAW = "The game is a draw" - RESIGN = "The game ended in resignation" - SCORE = "The game ended by counting points" - TIME = "The game ended in loss by time out" - VOID = "The game was suspended" class Game(db.Model): __tablename__ = "games" __table_args__ = {'extend_existing': True} + + class Players(enum.Enum): + BLACK = "The player taking black stones" + WHITE = "The player taking white stones" + VOID = "The game was a draw or voided" + + class WinType(enum.Enum): + DRAW = "The game is a draw" + RESIGN = "The game ended in resignation" + SCORE = "The game ended by counting points" + TIME = "The game ended in loss by time out" + VOID = "The game was suspended" id = db.Column(db.Integer, primary_key=True, autoincrement=True) date = db.Column(db.DateTime()) @@ -40,5 +43,23 @@ class Game(db.Model): player_black = db.Column(db.Integer, db.ForeignKey("users.id")) player_white = db.Column(db.Integer, db.ForeignKey("users.id")) - def __init__(self): - pass \ No newline at end of file + def __init__(self, name, description, board_size, game_room, player_white, komi=0.5, handicap=0, time_settings=1): + self.name = name + self.description = description + self.board_size = board_size + self.game_room = game_room + self.player_white = player_white + self.komi = komi + self.handicap = handicap + self.time_settings = time_settings + print('did it') + +class GameSchema(ma.ModelSchema): + id = fields.Int() + name = fields.Str() + description = fields.Str() + boardSize = fields.Int() + player = fields.Nested(user_schema) + +game_schema = GameSchema() +games_schema = GameSchema(many=True) \ No newline at end of file diff --git a/models/Message.py b/models/Message.py index 3a81404..c093ba9 100644 --- a/models/Message.py +++ b/models/Message.py @@ -1,14 +1,15 @@ from app import db, ma import enum -class Players(enum.Enum): - BLACK = "The player taking black stones" - WHITE = "The player taking white stones" class Message(db.Model): __tablename__ = "messages" __table_args__ = {'extend_existing': True} + class Players(enum.Enum): + BLACK = "The player taking black stones" + WHITE = "The player taking white stones" + id = db.Column(db.Integer, primary_key=True, autoincrement=True) date = db.Column(db.DateTime(), nullable=False) content = db.Column(db.String(200), nullable=False) diff --git a/models/Move.py b/models/Move.py index a3afe12..bbce1a3 100644 --- a/models/Move.py +++ b/models/Move.py @@ -1,14 +1,15 @@ from app import db, ma import enum -class Players(enum.Enum): - BLACK = "The player taking black stones" - WHITE = "The player taking white stones" class Move(db.Model): __tablename__ = "moves" __table_args__ = {'extend_existing': True} + class Players(enum.Enum): + BLACK = "The player taking black stones" + WHITE = "The player taking white stones" + id = db.Column(db.Integer, primary_key=True, autoincrement=True) player = db.Column(db.Enum(Players)) x_point = db.Column(db.Integer) diff --git a/models/TimeSettings.py b/models/TimeSettings.py index 0c8c5bb..c71f771 100644 --- a/models/TimeSettings.py +++ b/models/TimeSettings.py @@ -1,15 +1,17 @@ from app import db, ma import enum -class TimeTypes(enum.Enum): - BYOYOMI = "Counting by time period" - ABSOLUTE = "One period to use time" - HOURGLASS = "Absolute time for both players" - NONE = "Untimed" class TimeSettings(db.Model): + __tablename__ = "time_settings" __table_args__ = {'extend_existing': True} + class TimeTypes(enum.Enum): + BYOYOMI = "Counting by time period" + ABSOLUTE = "One period to use time" + HOURGLASS = "Absolute time for both players" + NONE = "Untimed" + id = db.Column(db.Integer, primary_key=True, autoincrement=True) main_time = db.Column(db.Enum(TimeTypes), nullable=False) time_period = db.Column(db.Integer) # number of periods @@ -19,5 +21,6 @@ class TimeSettings(db.Model): overtime_length = db.Column(db.Integer) # seconds - def __init__(self): - pass \ No newline at end of file + def __init__(self, main_time=TimeTypes.NONE, overtime=TimeTypes.NONE): + self.main_time = main_time + self.overtime = overtime \ No newline at end of file diff --git a/models/User.py b/models/User.py index 9273f31..110c1e2 100644 --- a/models/User.py +++ b/models/User.py @@ -8,49 +8,50 @@ import json import jwt import os -class Ranks(enum.Enum): # with minimal Elo rating - D7 = "Seven Dan" # Elo 2700+ - D6 = "Six Dan" - D5 = "Five Dan" # Elo 2500 - D4 = "Four Dan" - D3 = "Three Dan" - D2 = "Two Dan" - D1 = "One Dan" - K1 = "One Kyu" # Elo 2000 - K2 = "Two Kyu" - K3 = "Three Kyu" - K4 = "Four Kyu" - K5 = "Five Kyu" - K6 = "Six Kyu" # Elo 1500 - K7 = "Seven Kyu" - K8 = "Eight Kyu" - K9 = "Nine Kyu" - K10 = "Ten Kyu" - K11 = "Eleven Kyu" # ELo 1000 - K12 = "Twelve Kyu" - K13 = "Thirteen Kyu" - K14 = "Fourteen Kyu" - K15 = "Fifteen Kyu" - K16 = "Sixteen Kyu" # Elo 500 - K17 = "Seventeen Kyu" - K18 = "Eighteen Kyu" - K19 = "Nineteen Kyu" - K20 = "Twenty Kyu" - K21 = "Twenty-One Kyu" # Elo 0 - K22 = "Twenty-Two Kyu" - K23 = "Twenty-Three Kyu" - K24 = "Twenty-Four Kyu" - K25 = "Twenty-Five Kyu" - K26 = "Twenty-Six Kyu" # Elo -500 - K27 = "Twenty-Seven Kyu" - K28 = "Twenty-Eight Kyu" - K29 = "Twenty-Nine Kyu" - K30 = "Thirty Kyu" # Elo -900 - UR = "Unknown Rank" class User(db.Model): __tablename__ = "users" + class Ranks(enum.Enum): # with minimal Elo rating + D7 = "Seven Dan" # Elo 2700+ + D6 = "Six Dan" + D5 = "Five Dan" # Elo 2500 + D4 = "Four Dan" + D3 = "Three Dan" + D2 = "Two Dan" + D1 = "One Dan" + K1 = "One Kyu" # Elo 2000 + K2 = "Two Kyu" + K3 = "Three Kyu" + K4 = "Four Kyu" + K5 = "Five Kyu" + K6 = "Six Kyu" # Elo 1500 + K7 = "Seven Kyu" + K8 = "Eight Kyu" + K9 = "Nine Kyu" + K10 = "Ten Kyu" + K11 = "Eleven Kyu" # ELo 1000 + K12 = "Twelve Kyu" + K13 = "Thirteen Kyu" + K14 = "Fourteen Kyu" + K15 = "Fifteen Kyu" + K16 = "Sixteen Kyu" # Elo 500 + K17 = "Seventeen Kyu" + K18 = "Eighteen Kyu" + K19 = "Nineteen Kyu" + K20 = "Twenty Kyu" + K21 = "Twenty-One Kyu" # Elo 0 + K22 = "Twenty-Two Kyu" + K23 = "Twenty-Three Kyu" + K24 = "Twenty-Four Kyu" + K25 = "Twenty-Five Kyu" + K26 = "Twenty-Six Kyu" # Elo -500 + K27 = "Twenty-Seven Kyu" + K28 = "Twenty-Eight Kyu" + K29 = "Twenty-Nine Kyu" + K30 = "Thirty Kyu" # Elo -900 + UR = "Unknown Rank" + id = db.Column(db.Integer, primary_key=True, autoincrement=True) username = db.Column(db.String(255), unique=True, nullable=False, autoincrement=True) email = db.Column(db.String(255), unique=True, nullable=False) From 76b2afe94efa261c243738511f481f2cde0eb235 Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Thu, 10 Oct 2019 17:41:49 -0700 Subject: [PATCH 39/47] debug one to many relationships --- configuration/models_mount.py | 1 - manage.py | 1 - .../{acabfc6d158d_.py => b077aaee9ec8_.py} | 67 +++++++++---------- models/Game.py | 29 ++++++-- models/GameRoom.py | 26 ++----- models/Message.py | 2 +- models/Move.py | 10 ++- models/TimeSettings.py | 26 ------- models/User.py | 1 + 9 files changed, 71 insertions(+), 92 deletions(-) rename migrations/versions/{acabfc6d158d_.py => b077aaee9ec8_.py} (79%) delete mode 100644 models/TimeSettings.py diff --git a/configuration/models_mount.py b/configuration/models_mount.py index fa36978..a5299f7 100644 --- a/configuration/models_mount.py +++ b/configuration/models_mount.py @@ -1,7 +1,6 @@ if __name__ == '__main__': from ..models.User import User from ..models.GameRoom import GameRoom - from ..models.TimeSettings import TimeSettings from ..models.Game import Game from ..models.Move import Move from ..models.Message import Message \ No newline at end of file diff --git a/manage.py b/manage.py index 35d0d9d..e990a80 100644 --- a/manage.py +++ b/manage.py @@ -15,7 +15,6 @@ from models.Game import Game from models.GameRoom import GameRoom from models.Message import Message from models.Move import Move -from models.TimeSettings import TimeSettings from models.User import User # migrations diff --git a/migrations/versions/acabfc6d158d_.py b/migrations/versions/b077aaee9ec8_.py similarity index 79% rename from migrations/versions/acabfc6d158d_.py rename to migrations/versions/b077aaee9ec8_.py index bef6a4e..12c91b8 100644 --- a/migrations/versions/acabfc6d158d_.py +++ b/migrations/versions/b077aaee9ec8_.py @@ -1,8 +1,8 @@ """empty message -Revision ID: acabfc6d158d +Revision ID: b077aaee9ec8 Revises: -Create Date: 2019-10-10 14:25:27.198731 +Create Date: 2019-10-10 17:38:14.900202 """ from alembic import op @@ -10,7 +10,7 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = 'acabfc6d158d' +revision = 'b077aaee9ec8' down_revision = None branch_labels = None depends_on = None @@ -18,20 +18,12 @@ depends_on = None def upgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.create_table('room_languages', + op.create_table('game_rooms', sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), sa.Column('name', sa.String(length=40), nullable=False), - sa.Column('iso', sa.String(length=2), nullable=False), - sa.PrimaryKeyConstraint('id') - ) - op.create_table('time_settings', - sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), - sa.Column('main_time', sa.Enum('BYOYOMI', 'ABSOLUTE', 'HOURGLASS', 'NONE', name='timetypes'), nullable=False), - sa.Column('time_period', sa.Integer(), nullable=True), - sa.Column('period_length', sa.Integer(), nullable=True), - sa.Column('overtime', sa.Enum('BYOYOMI', 'ABSOLUTE', 'HOURGLASS', 'NONE', name='timetypes'), nullable=False), - sa.Column('overtime_period', sa.Integer(), nullable=True), - sa.Column('overtime_length', sa.Integer(), nullable=True), + sa.Column('description', sa.String(length=200), nullable=False), + sa.Column('private', sa.Boolean(), nullable=False), + sa.Column('language', sa.Enum('EN', name='languages'), nullable=False), sa.PrimaryKeyConstraint('id') ) op.create_table('users', @@ -48,15 +40,6 @@ def upgrade(): sa.UniqueConstraint('email'), sa.UniqueConstraint('username') ) - op.create_table('game_rooms', - sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), - sa.Column('name', sa.String(length=40), nullable=False), - sa.Column('description', sa.String(length=200), nullable=False), - sa.Column('private', sa.Boolean(), nullable=False), - sa.Column('language', sa.Integer(), nullable=False), - sa.ForeignKeyConstraint(['language'], ['room_languages.id'], ), - sa.PrimaryKeyConstraint('id') - ) op.create_table('game_rooms_users', sa.Column('user_id', sa.Integer(), nullable=False), sa.Column('game_rooms_id', sa.Integer(), nullable=False), @@ -81,16 +64,27 @@ def upgrade(): sa.Column('name', sa.String(length=40), nullable=True), sa.Column('description', sa.String(length=200), nullable=True), sa.Column('round', sa.Integer(), nullable=True), - sa.Column('game_room', sa.Integer(), nullable=True), - sa.Column('time_settings', sa.Integer(), nullable=True), + sa.Column('main_time', sa.Enum('BYOYOMI', 'ABSOLUTE', 'HOURGLASS', 'NONE', name='timetypes'), nullable=False), + sa.Column('time_period', sa.Integer(), nullable=True), + sa.Column('period_length', sa.Integer(), nullable=True), + sa.Column('overtime', sa.Enum('BYOYOMI', 'ABSOLUTE', 'HOURGLASS', 'NONE', name='timetypes'), nullable=False), + sa.Column('overtime_period', sa.Integer(), nullable=True), + sa.Column('overtime_length', sa.Integer(), nullable=True), + sa.Column('game_room_id', sa.Integer(), nullable=True), sa.Column('player_black', sa.Integer(), nullable=True), sa.Column('player_white', sa.Integer(), nullable=True), - sa.ForeignKeyConstraint(['game_room'], ['game_rooms.id'], ), + sa.ForeignKeyConstraint(['game_room_id'], ['game_rooms.id'], ), sa.ForeignKeyConstraint(['player_black'], ['users.id'], ), sa.ForeignKeyConstraint(['player_white'], ['users.id'], ), - sa.ForeignKeyConstraint(['time_settings'], ['time_settings.id'], ), sa.PrimaryKeyConstraint('id') ) + op.create_table('games_users', + sa.Column('user_id', sa.Integer(), nullable=False), + sa.Column('game_rooms_id', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['game_rooms_id'], ['games.id'], ), + sa.ForeignKeyConstraint(['user_id'], ['users.id'], ), + sa.PrimaryKeyConstraint('user_id', 'game_rooms_id') + ) op.create_table('moves', sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), sa.Column('player', sa.Enum('BLACK', 'WHITE', name='players'), nullable=True), @@ -99,18 +93,18 @@ def upgrade(): sa.Column('move_number', sa.Integer(), nullable=True), sa.Column('is_pass', sa.Boolean(), nullable=False), sa.Column('is_main', sa.Boolean(), nullable=False), - sa.Column('game', sa.Integer(), nullable=False), - sa.Column('preceding_move', sa.Integer(), nullable=True), - sa.ForeignKeyConstraint(['game'], ['games.id'], ), - sa.ForeignKeyConstraint(['preceding_move'], ['moves.id'], ), + sa.Column('game_id', sa.Integer(), nullable=False), + sa.Column('preceding_move_id', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(['game_id'], ['games.id'], ), + sa.ForeignKeyConstraint(['preceding_move_id'], ['moves.id'], ), sa.PrimaryKeyConstraint('id') ) op.create_table('messages', sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), sa.Column('date', sa.DateTime(), nullable=False), sa.Column('content', sa.String(length=200), nullable=False), - sa.Column('move', sa.Integer(), nullable=False), - sa.ForeignKeyConstraint(['move'], ['moves.id'], ), + sa.Column('move_id', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['move_id'], ['moves.id'], ), sa.PrimaryKeyConstraint('id') ) # ### end Alembic commands ### @@ -120,10 +114,9 @@ def downgrade(): # ### commands auto generated by Alembic - please adjust! ### op.drop_table('messages') op.drop_table('moves') + op.drop_table('games_users') op.drop_table('games') op.drop_table('game_rooms_users') - op.drop_table('game_rooms') op.drop_table('users') - op.drop_table('time_settings') - op.drop_table('room_languages') + op.drop_table('game_rooms') # ### end Alembic commands ### diff --git a/models/Game.py b/models/Game.py index 8cf4299..22eb89d 100644 --- a/models/Game.py +++ b/models/Game.py @@ -3,6 +3,11 @@ from marshmallow import fields import enum from models.User import user_schema +# ! Games >-< Users join table +games_users = db.Table('games_users', + db.Column('user_id', db.Integer, db.ForeignKey('users.id'), primary_key=True), + db.Column('game_rooms_id', db.Integer, db.ForeignKey('games.id'), primary_key=True) +) class Game(db.Model): __tablename__ = "games" @@ -20,6 +25,12 @@ class Game(db.Model): TIME = "The game ended in loss by time out" VOID = "The game was suspended" + class TimeTypes(enum.Enum): + BYOYOMI = "Counting by time period" + ABSOLUTE = "One period to use time" + HOURGLASS = "Absolute time for both players" + NONE = "Untimed" + id = db.Column(db.Integer, primary_key=True, autoincrement=True) date = db.Column(db.DateTime()) komi = db.Column(db.Numeric(2,1), nullable=False) @@ -36,14 +47,22 @@ class Game(db.Model): name = db.Column(db.String(40)) description = db.Column(db.String(200)) round = db.Column(db.Integer) + main_time = db.Column(db.Enum(TimeTypes), nullable=False) + time_period = db.Column(db.Integer) # number of periods + period_length = db.Column(db.Integer) # seconds + overtime = db.Column(db.Enum(TimeTypes), nullable=False) + overtime_period = db.Column(db.Integer) # number of overtime periods + overtime_length = db.Column(db.Integer) # seconds # foreign keys - game_room = db.Column(db.Integer, db.ForeignKey("game_rooms.id")) - time_settings = db.Column(db.Integer, db.ForeignKey("time_settings.id")) + game_room_id = db.Column(db.Integer, db.ForeignKey("game_rooms.id")) player_black = db.Column(db.Integer, db.ForeignKey("users.id")) player_white = db.Column(db.Integer, db.ForeignKey("users.id")) - def __init__(self, name, description, board_size, game_room, player_white, komi=0.5, handicap=0, time_settings=1): + def __init__( + self, name, description, board_size, game_room, player_white, + komi=0.5, handicap=0, main_time=TimeTypes.NONE, overtime=TimeTypes.NONE + ): self.name = name self.description = description self.board_size = board_size @@ -51,8 +70,8 @@ class Game(db.Model): self.player_white = player_white self.komi = komi self.handicap = handicap - self.time_settings = time_settings - print('did it') + self.main_time = main_time + self.overtime = overtime class GameSchema(ma.ModelSchema): id = fields.Int() diff --git a/models/GameRoom.py b/models/GameRoom.py index fda05d2..1877507 100644 --- a/models/GameRoom.py +++ b/models/GameRoom.py @@ -1,42 +1,30 @@ from app import db, ma from marshmallow import fields +import enum # TODO User >---< GameRoom -# ! Game Room >-< Users join table +# ! Game Rooms >-< Users join table game_rooms_users = db.Table('game_rooms_users', db.Column('user_id', db.Integer, db.ForeignKey('users.id'), primary_key=True), db.Column('game_rooms_id', db.Integer, db.ForeignKey('game_rooms.id'), primary_key=True) ) -class RoomLanguages(db.Model): - __tablename__ = "room_languages" - - id = db.Column(db.Integer, primary_key=True, autoincrement=True) - name = db.Column(db.String(40), nullable=False) - iso = db.Column(db.String(2), nullable=False) - - class GameRoom(db.Model): __tablename__ = "game_rooms" __table_args__ = {'extend_existing': True} + + class Languages(enum.Enum): + EN = "English" id = db.Column(db.Integer, primary_key=True, autoincrement=True) name = db.Column(db.String(40), nullable=False) description = db.Column(db.String(200), nullable=False) private = db.Column(db.Boolean(), nullable=False, default=False) + language = db.Column(db.Enum(Languages), nullable=False, default=Languages.EN) - # ! Foreign Keys - users = db.relationship( - 'User', - secondary=game_rooms_users, - lazy='subquery', - backref=db.backref('game_rooms', lazy=True) - ) - - language = db.Column(db.Integer, db.ForeignKey("room_languages.id"), nullable=False) - def __init__(self, name, description, private=False, language=1): + def __init__(self, name, description, private=False, language=Languages.EN): self.name = name self.description = description self.private = private diff --git a/models/Message.py b/models/Message.py index c093ba9..7524a9a 100644 --- a/models/Message.py +++ b/models/Message.py @@ -15,7 +15,7 @@ class Message(db.Model): content = db.Column(db.String(200), nullable=False) # foreign key - move = db.Column(db.Integer, db.ForeignKey("moves.id"), nullable=False) + move_id = db.Column(db.Integer, db.ForeignKey("moves.id"), nullable=False) def __init__(self): pass \ No newline at end of file diff --git a/models/Move.py b/models/Move.py index bbce1a3..4afbe68 100644 --- a/models/Move.py +++ b/models/Move.py @@ -19,8 +19,14 @@ class Move(db.Model): is_main = db.Column(db.Boolean, nullable=False, default=True) # foreign keys - game = db.Column(db.Integer, db.ForeignKey("games.id"), nullable=False) - preceding_move = db.Column(db.Integer, db.ForeignKey("moves.id")) + game_id = db.Column(db.Integer, db.ForeignKey("games.id"), nullable=False) + preceding_move_id = db.Column(db.Integer, db.ForeignKey("moves.id")) + + succeeding_moves = db.relationship( + 'Move', + lazy='subquery', + backref=db.backref('moves', lazy=True) + ) def __init__(self): pass \ No newline at end of file diff --git a/models/TimeSettings.py b/models/TimeSettings.py deleted file mode 100644 index c71f771..0000000 --- a/models/TimeSettings.py +++ /dev/null @@ -1,26 +0,0 @@ -from app import db, ma -import enum - - -class TimeSettings(db.Model): - __tablename__ = "time_settings" - __table_args__ = {'extend_existing': True} - - class TimeTypes(enum.Enum): - BYOYOMI = "Counting by time period" - ABSOLUTE = "One period to use time" - HOURGLASS = "Absolute time for both players" - NONE = "Untimed" - - id = db.Column(db.Integer, primary_key=True, autoincrement=True) - main_time = db.Column(db.Enum(TimeTypes), nullable=False) - time_period = db.Column(db.Integer) # number of periods - period_length = db.Column(db.Integer) # seconds - overtime = db.Column(db.Enum(TimeTypes), nullable=False) - overtime_period = db.Column(db.Integer) # number of overtime periods - overtime_length = db.Column(db.Integer) # seconds - - - def __init__(self, main_time=TimeTypes.NONE, overtime=TimeTypes.NONE): - self.main_time = main_time - self.overtime = overtime \ No newline at end of file diff --git a/models/User.py b/models/User.py index 110c1e2..5ee165b 100644 --- a/models/User.py +++ b/models/User.py @@ -62,6 +62,7 @@ class User(db.Model): elo = db.Column(db.Integer) rank_certainty = db.Column(db.Boolean, nullable=False, default=False) + def __init__(self, username, email, password, rank=Ranks.K1, admin=False): self.username = username self.email = email From b4bb9181a13bc93c21056de3560054b12eb0661e Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Thu, 10 Oct 2019 17:52:43 -0700 Subject: [PATCH 40/47] change model_id to model --- .../{b077aaee9ec8_.py => 45f01fb15e26_.py} | 22 +++++++++---------- models/Game.py | 2 +- models/GameRoom.py | 11 +--------- models/Message.py | 2 +- models/Move.py | 4 ++-- 5 files changed, 16 insertions(+), 25 deletions(-) rename migrations/versions/{b077aaee9ec8_.py => 45f01fb15e26_.py} (90%) diff --git a/migrations/versions/b077aaee9ec8_.py b/migrations/versions/45f01fb15e26_.py similarity index 90% rename from migrations/versions/b077aaee9ec8_.py rename to migrations/versions/45f01fb15e26_.py index 12c91b8..29db1f3 100644 --- a/migrations/versions/b077aaee9ec8_.py +++ b/migrations/versions/45f01fb15e26_.py @@ -1,8 +1,8 @@ """empty message -Revision ID: b077aaee9ec8 +Revision ID: 45f01fb15e26 Revises: -Create Date: 2019-10-10 17:38:14.900202 +Create Date: 2019-10-10 17:50:40.846864 """ from alembic import op @@ -10,7 +10,7 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = 'b077aaee9ec8' +revision = '45f01fb15e26' down_revision = None branch_labels = None depends_on = None @@ -70,10 +70,10 @@ def upgrade(): sa.Column('overtime', sa.Enum('BYOYOMI', 'ABSOLUTE', 'HOURGLASS', 'NONE', name='timetypes'), nullable=False), sa.Column('overtime_period', sa.Integer(), nullable=True), sa.Column('overtime_length', sa.Integer(), nullable=True), - sa.Column('game_room_id', sa.Integer(), nullable=True), + sa.Column('game_room', sa.Integer(), nullable=True), sa.Column('player_black', sa.Integer(), nullable=True), sa.Column('player_white', sa.Integer(), nullable=True), - sa.ForeignKeyConstraint(['game_room_id'], ['game_rooms.id'], ), + sa.ForeignKeyConstraint(['game_room'], ['game_rooms.id'], ), sa.ForeignKeyConstraint(['player_black'], ['users.id'], ), sa.ForeignKeyConstraint(['player_white'], ['users.id'], ), sa.PrimaryKeyConstraint('id') @@ -93,18 +93,18 @@ def upgrade(): sa.Column('move_number', sa.Integer(), nullable=True), sa.Column('is_pass', sa.Boolean(), nullable=False), sa.Column('is_main', sa.Boolean(), nullable=False), - sa.Column('game_id', sa.Integer(), nullable=False), - sa.Column('preceding_move_id', sa.Integer(), nullable=True), - sa.ForeignKeyConstraint(['game_id'], ['games.id'], ), - sa.ForeignKeyConstraint(['preceding_move_id'], ['moves.id'], ), + sa.Column('game', sa.Integer(), nullable=False), + sa.Column('preceding_move', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(['game'], ['games.id'], ), + sa.ForeignKeyConstraint(['preceding_move'], ['moves.id'], ), sa.PrimaryKeyConstraint('id') ) op.create_table('messages', sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), sa.Column('date', sa.DateTime(), nullable=False), sa.Column('content', sa.String(length=200), nullable=False), - sa.Column('move_id', sa.Integer(), nullable=False), - sa.ForeignKeyConstraint(['move_id'], ['moves.id'], ), + sa.Column('move', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['move'], ['moves.id'], ), sa.PrimaryKeyConstraint('id') ) # ### end Alembic commands ### diff --git a/models/Game.py b/models/Game.py index 22eb89d..0bcf5b4 100644 --- a/models/Game.py +++ b/models/Game.py @@ -55,7 +55,7 @@ class Game(db.Model): overtime_length = db.Column(db.Integer) # seconds # foreign keys - game_room_id = db.Column(db.Integer, db.ForeignKey("game_rooms.id")) + game_room = db.Column(db.Integer, db.ForeignKey("game_rooms.id")) player_black = db.Column(db.Integer, db.ForeignKey("users.id")) player_white = db.Column(db.Integer, db.ForeignKey("users.id")) diff --git a/models/GameRoom.py b/models/GameRoom.py index 1877507..084f04b 100644 --- a/models/GameRoom.py +++ b/models/GameRoom.py @@ -22,27 +22,18 @@ class GameRoom(db.Model): private = db.Column(db.Boolean(), nullable=False, default=False) language = db.Column(db.Enum(Languages), nullable=False, default=Languages.EN) - - def __init__(self, name, description, private=False, language=Languages.EN): self.name = name self.description = description self.private = private self.language = language -class LanguageSchema(ma.ModelSchema): - id = fields.Int() - name = fields.Str() - iso = fields.Str() - -language_schema = LanguageSchema() - class RoomSchema(ma.ModelSchema): id = fields.Int() name = fields.Str() description = fields.Str() private = fields.Bool() - language = fields.Nested(LanguageSchema) + language = fields.Str() room_schema = RoomSchema() diff --git a/models/Message.py b/models/Message.py index 7524a9a..c093ba9 100644 --- a/models/Message.py +++ b/models/Message.py @@ -15,7 +15,7 @@ class Message(db.Model): content = db.Column(db.String(200), nullable=False) # foreign key - move_id = db.Column(db.Integer, db.ForeignKey("moves.id"), nullable=False) + move = db.Column(db.Integer, db.ForeignKey("moves.id"), nullable=False) def __init__(self): pass \ No newline at end of file diff --git a/models/Move.py b/models/Move.py index 4afbe68..351b38e 100644 --- a/models/Move.py +++ b/models/Move.py @@ -19,8 +19,8 @@ class Move(db.Model): is_main = db.Column(db.Boolean, nullable=False, default=True) # foreign keys - game_id = db.Column(db.Integer, db.ForeignKey("games.id"), nullable=False) - preceding_move_id = db.Column(db.Integer, db.ForeignKey("moves.id")) + game = db.Column(db.Integer, db.ForeignKey("games.id"), nullable=False) + preceding_move = db.Column(db.Integer, db.ForeignKey("moves.id")) succeeding_moves = db.relationship( 'Move', From 085ae0f682b26050aae2d04ecb0bcb337b398c92 Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Thu, 10 Oct 2019 23:06:22 -0700 Subject: [PATCH 41/47] init room socket --- api/rooms/api_rooms.py | 4 +++- websockets/socket.py | 25 ++++++++++++++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/api/rooms/api_rooms.py b/api/rooms/api_rooms.py index f520612..29f5273 100644 --- a/api/rooms/api_rooms.py +++ b/api/rooms/api_rooms.py @@ -4,6 +4,7 @@ from models.GameRoom import GameRoom, rooms_schema, room_schema from models.Game import Game, games_schema from database import db from ..decorators import jwt_required +from websockets.socket import new_room_notice, join_room_notice api_rooms = Blueprint('api_rooms', __name__, url_prefix='/api/rooms') @@ -12,6 +13,7 @@ def get_room(room_id): print(room_id) games = Game.query.filter_by(game_room=room_id).all() response = games_schema.dumps(games) + join_room_notice(room_id) return jsonify(response) @api_rooms.route('/', methods=['GET']) @@ -40,8 +42,8 @@ def post_room(): response = { 'status': 'success', 'message': 'Succesfully registered.', - 'gameRoom': room.id } + new_room_notice(room_schema.dumps(room)) return jsonify(response), 201 except Exception as e: print(e) diff --git a/websockets/socket.py b/websockets/socket.py index 020a147..ec45d03 100644 --- a/websockets/socket.py +++ b/websockets/socket.py @@ -22,4 +22,27 @@ def handle_connection(): look cool a namespaced socketio connection! - ''') \ No newline at end of file + ''') + +def join_room_notice(room): + @socketio.on('connect', namespace=f'/{room}') + def handle_connection(): + print(f'joined room at {room}') + emit('join room', {'roomspace': f'{room}'}) + @socketio.on('join room', namespace=f'/{room}') + def connect_room(message): + print(f'connected with ${message}') + emit('connected', {'roomspace': f'/{room}'}) + +def new_game_notice(room, game): + @socketio.on('connect', namespace=f'/{room}') + def emit_game(game): + pass + + +def new_room_notice(room): + socketio.emit('new room', room, broadcast=True) + +# @socketio.on +# def room_socket(roomspace): +# pass \ No newline at end of file From 1a8c4de39414706fa62854a91a968849d1f19b5a Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Thu, 10 Oct 2019 23:31:27 -0700 Subject: [PATCH 42/47] serve new games via socket --- api/games/api_games.py | 6 ++++-- websockets/socket.py | 4 +--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/api/games/api_games.py b/api/games/api_games.py index d2c852a..54f0797 100644 --- a/api/games/api_games.py +++ b/api/games/api_games.py @@ -1,12 +1,13 @@ from flask import Blueprint, request, jsonify, session from models.User import User, user_schema, users_schema from models.GameRoom import GameRoom, rooms_schema, room_schema -from models.Game import Game +from models.Game import Game, game_schema from database import db from ..decorators import jwt_required import jwt import os import json +from websockets.socket import new_game_notice api_games = Blueprint('api_games', __name__, url_prefix='/api/games') @@ -35,7 +36,8 @@ def post_game(): print('game added') db.session.commit() print('game') - print(game) + print(game_schema.dumps(game)) + new_game_notice(room=game.game_room, game=game_schema.dumps(game)) response = { 'status': 'success', 'message': 'Game created', diff --git a/websockets/socket.py b/websockets/socket.py index ec45d03..12ea921 100644 --- a/websockets/socket.py +++ b/websockets/socket.py @@ -35,9 +35,7 @@ def join_room_notice(room): emit('connected', {'roomspace': f'/{room}'}) def new_game_notice(room, game): - @socketio.on('connect', namespace=f'/{room}') - def emit_game(game): - pass + socketio.emit('new game', game, broadcast=True, namespace=f'/{room}') def new_room_notice(room): From e378697974dcc5e88b397f38378dfd1e9c8bf9a2 Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Fri, 11 Oct 2019 11:55:35 -0700 Subject: [PATCH 43/47] serve basic game data --- api/games/api_games.py | 8 ++++++++ models/Game.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/api/games/api_games.py b/api/games/api_games.py index 54f0797..43029de 100644 --- a/api/games/api_games.py +++ b/api/games/api_games.py @@ -11,6 +11,14 @@ from websockets.socket import new_game_notice api_games = Blueprint('api_games', __name__, url_prefix='/api/games') +@api_games.route('/', methods=['GET']) +def get_room(game_id): + print(game_id) + game = Game.query.filter_by(id=game_id).first() + response = game_schema.dumps(game) + # join_game_notice(game_id) + return jsonify(response) + @api_games.route('/', methods=['POST']) @jwt_required() def post_game(): diff --git a/models/Game.py b/models/Game.py index 0bcf5b4..a18f9d9 100644 --- a/models/Game.py +++ b/models/Game.py @@ -77,7 +77,7 @@ class GameSchema(ma.ModelSchema): id = fields.Int() name = fields.Str() description = fields.Str() - boardSize = fields.Int() + board_size = fields.Int() player = fields.Nested(user_schema) game_schema = GameSchema() From 7eeac76af9def5e7dd06fa4375a8448c904eec86 Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Fri, 11 Oct 2019 13:56:28 -0700 Subject: [PATCH 44/47] establish game socketio room connection --- api/games/api_games.py | 16 ++++++---------- models/Game.py | 1 + websockets/socket.py | 17 ++++++++++++----- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/api/games/api_games.py b/api/games/api_games.py index 43029de..32bbd98 100644 --- a/api/games/api_games.py +++ b/api/games/api_games.py @@ -7,7 +7,7 @@ from ..decorators import jwt_required import jwt import os import json -from websockets.socket import new_game_notice +from websockets.socket import new_game_notice, join_game_notice api_games = Blueprint('api_games', __name__, url_prefix='/api/games') @@ -16,20 +16,20 @@ def get_room(game_id): print(game_id) game = Game.query.filter_by(id=game_id).first() response = game_schema.dumps(game) - # join_game_notice(game_id) + # TODO create decorator that returns user from header + auth_header = request.headers.get('Authorization') + user = jwt.decode(auth_header.split(" ")[1], os.environ.get('SECRET_KEY'))['user'] + print(user) + join_game_notice(game_id, user) return jsonify(response) @api_games.route('/', methods=['POST']) @jwt_required() def post_game(): - print('Hey it\'s a post request!') data = request.get_json() # TODO create decorator that returns user from header auth_header = request.headers.get('Authorization') user = jwt.decode(auth_header.split(" ")[1], os.environ.get('SECRET_KEY'))['user'] - print(user) - print('data') - print(data) user_id = json.loads(user)['id'] try: game = Game( @@ -39,12 +39,8 @@ def post_game(): game_room = data['gameRoom'], player_white = user_id ) - print(game) db.session.add(game) - print('game added') db.session.commit() - print('game') - print(game_schema.dumps(game)) new_game_notice(room=game.game_room, game=game_schema.dumps(game)) response = { 'status': 'success', diff --git a/models/Game.py b/models/Game.py index a18f9d9..16ac68c 100644 --- a/models/Game.py +++ b/models/Game.py @@ -79,6 +79,7 @@ class GameSchema(ma.ModelSchema): description = fields.Str() board_size = fields.Int() player = fields.Nested(user_schema) + game_room = fields.Int() game_schema = GameSchema() games_schema = GameSchema(many=True) \ No newline at end of file diff --git a/websockets/socket.py b/websockets/socket.py index 12ea921..515cb47 100644 --- a/websockets/socket.py +++ b/websockets/socket.py @@ -1,6 +1,8 @@ from app import socketio -from flask_socketio import send, emit +from flask_socketio import send, emit, join_room, leave_room +import json +# ! Basic Connection @socketio.on('connect') def handle_connection(): print(''' @@ -24,11 +26,9 @@ def handle_connection(): ''') +# ! Game Room Messages + def join_room_notice(room): - @socketio.on('connect', namespace=f'/{room}') - def handle_connection(): - print(f'joined room at {room}') - emit('join room', {'roomspace': f'{room}'}) @socketio.on('join room', namespace=f'/{room}') def connect_room(message): print(f'connected with ${message}') @@ -41,6 +41,13 @@ def new_game_notice(room, game): def new_room_notice(room): socketio.emit('new room', room, broadcast=True) +def join_game_notice(game_id, user): + @socketio.on('join game') + def return_join_game_notice(data): + game = data['game'] + join_room(game) + emit('join game', data, room=f'game') + # @socketio.on # def room_socket(roomspace): # pass \ No newline at end of file From 9d6d482c919c44d08fcf74ccddd8694acb35316d Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Fri, 11 Oct 2019 22:23:38 -0700 Subject: [PATCH 45/47] debug room socket conneciton --- app.py | 14 ++++++++++---- configuration/config.py | 3 ++- server.py | 2 -- websockets/socket.py | 3 +++ 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/app.py b/app.py index fe753db..5b00bf9 100644 --- a/app.py +++ b/app.py @@ -3,8 +3,6 @@ from database import db, ma from flask import Flask -from configuration.config import DevelopmentConfig - from flask_bcrypt import Bcrypt from flask_cors import CORS from flask_socketio import SocketIO @@ -12,9 +10,17 @@ from flask_socketio import SocketIO app = Flask(__name__) bcrypt = Bcrypt(app) app.config['CORS_HEADERS'] = 'Content-Type' -app.config.from_object(DevelopmentConfig) + +# ! Environment Variable +# TODO change to os.getenv('CONFIGURATION') +# TODO export CONFIGURATION='configuration.config.ProductionConfig' +app.config.from_object('configuration.config.DevelopmentConfig') + +# ! Environment Variable +# TODO change to os.getenv('ALLOWED_ORIGIN') +# TODO export ALLOWED_ORIGIN= whatever the react server is socketio = SocketIO(app, cors_allowed_origins="http://localhost:3000") -# cors_allowed_origins="http://localhost:3000" + def create_app(): CORS(app, resources={ r"/api/*": {"origins": "http://localhost:3000"}, diff --git a/configuration/config.py b/configuration/config.py index 4912a7b..efdd88e 100644 --- a/configuration/config.py +++ b/configuration/config.py @@ -1,5 +1,6 @@ import os # local db +# ! Environment Variable DATABASE = 'postgresql://localhost/browser-go' class BaseConfig: @@ -30,4 +31,4 @@ class ProductionConfig(BaseConfig): """Production configuration.""" SECRET_KEY = '' DEBUG = False - SQLALCHEMY_DATABASE_URI = 'postgresql:///' \ No newline at end of file + SQLALCHEMY_DATABASE_URI = 'postgresql:///' diff --git a/server.py b/server.py index 226bf96..a67d0ec 100644 --- a/server.py +++ b/server.py @@ -10,8 +10,6 @@ from websockets.socket import socketio import configuration.models_mount from flask_migrate import Migrate - - if __name__ == '__main__': app = create_app() register_api_endpoints(app) diff --git a/websockets/socket.py b/websockets/socket.py index 515cb47..d2a2d11 100644 --- a/websockets/socket.py +++ b/websockets/socket.py @@ -29,6 +29,9 @@ def handle_connection(): # ! Game Room Messages def join_room_notice(room): + @socketio.on('connect', namespace=f'/{room}') + def room_socket_mount(): + print(f'/{room} socket mounted') @socketio.on('join room', namespace=f'/{room}') def connect_room(message): print(f'connected with ${message}') From 864d33486d19841fc8ff9113576d6f2c4245494f Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Fri, 11 Oct 2019 23:04:11 -0700 Subject: [PATCH 46/47] move config to environment variables --- app.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/app.py b/app.py index 5b00bf9..c5f1062 100644 --- a/app.py +++ b/app.py @@ -12,19 +12,17 @@ bcrypt = Bcrypt(app) app.config['CORS_HEADERS'] = 'Content-Type' # ! Environment Variable -# TODO change to os.getenv('CONFIGURATION') -# TODO export CONFIGURATION='configuration.config.ProductionConfig' -app.config.from_object('configuration.config.DevelopmentConfig') +# TODO export CONFIGURATION_OBJECT='configuration.config.ProductionConfig' +app.config.from_object(os.getenv('CONFIGURATION_OBJECT')) # ! Environment Variable -# TODO change to os.getenv('ALLOWED_ORIGIN') # TODO export ALLOWED_ORIGIN= whatever the react server is -socketio = SocketIO(app, cors_allowed_origins="http://localhost:3000") +socketio = SocketIO(app, cors_allowed_origins=os.getenv('ALLOWED_ORIGIN')) def create_app(): CORS(app, resources={ - r"/api/*": {"origins": "http://localhost:3000"}, - r"/auth/*": {"origins": "http://localhost:3000"}, + r"/api/*": {"origins": os.getenv('ALLOWED_ORIGIN')}, + r"/auth/*": {"origins": os.getenv('ALLOWED_ORIGIN')}, }) db.init_app(app) ma.init_app(app) From f09cf80719e686b7834b37ac3a0d09cf6feeb052 Mon Sep 17 00:00:00 2001 From: Sorrel Bri Date: Sat, 12 Oct 2019 00:16:00 -0700 Subject: [PATCH 47/47] split socket concerns --- api/games/api_games.py | 2 +- api/rooms/api_rooms.py | 2 +- websockets/roomSocket.py | 24 ++++++++++++++++++++++++ websockets/socket.py | 31 +------------------------------ 4 files changed, 27 insertions(+), 32 deletions(-) create mode 100644 websockets/roomSocket.py diff --git a/api/games/api_games.py b/api/games/api_games.py index 32bbd98..a4853bb 100644 --- a/api/games/api_games.py +++ b/api/games/api_games.py @@ -7,7 +7,7 @@ from ..decorators import jwt_required import jwt import os import json -from websockets.socket import new_game_notice, join_game_notice +from websockets.roomSocket import new_game_notice, join_game_notice api_games = Blueprint('api_games', __name__, url_prefix='/api/games') diff --git a/api/rooms/api_rooms.py b/api/rooms/api_rooms.py index 29f5273..8f942fd 100644 --- a/api/rooms/api_rooms.py +++ b/api/rooms/api_rooms.py @@ -4,7 +4,7 @@ from models.GameRoom import GameRoom, rooms_schema, room_schema from models.Game import Game, games_schema from database import db from ..decorators import jwt_required -from websockets.socket import new_room_notice, join_room_notice +from websockets.roomSocket import new_room_notice, join_room_notice api_rooms = Blueprint('api_rooms', __name__, url_prefix='/api/rooms') diff --git a/websockets/roomSocket.py b/websockets/roomSocket.py new file mode 100644 index 0000000..a707602 --- /dev/null +++ b/websockets/roomSocket.py @@ -0,0 +1,24 @@ +from app import socketio +from flask_socketio import send, emit, join_room, leave_room +import json + +def join_room_notice(room): + @socketio.on('join room', namespace=f'/{room}') + def connect_room(message): + print(f'connected with ${message}') + emit('connected', {'roomspace': f'/{room}'}) + +def new_game_notice(room, game): + print('sending new game notice') + socketio.emit('new game', game, broadcast=True, namespace=f'/{room}') + + +def new_room_notice(room): + socketio.emit('new room', room, broadcast=True) + +def join_game_notice(game_id, user): + @socketio.on('join game') + def return_join_game_notice(data): + game = data['game'] + join_room(game) + emit('join game', data, room=f'game') diff --git a/websockets/socket.py b/websockets/socket.py index d2a2d11..af5e8c0 100644 --- a/websockets/socket.py +++ b/websockets/socket.py @@ -13,7 +13,7 @@ def handle_connection(): ''') emit('message', {'data':'connection'}, broadcast=True) -@socketio.on('message') +@socketio.on('send message') def handle_message(message): print(message) emit('init namespace', {'namespace':'newroom'}) @@ -25,32 +25,3 @@ def handle_connection(): look cool a namespaced socketio connection! ''') - -# ! Game Room Messages - -def join_room_notice(room): - @socketio.on('connect', namespace=f'/{room}') - def room_socket_mount(): - print(f'/{room} socket mounted') - @socketio.on('join room', namespace=f'/{room}') - def connect_room(message): - print(f'connected with ${message}') - emit('connected', {'roomspace': f'/{room}'}) - -def new_game_notice(room, game): - socketio.emit('new game', game, broadcast=True, namespace=f'/{room}') - - -def new_room_notice(room): - socketio.emit('new room', room, broadcast=True) - -def join_game_notice(game_id, user): - @socketio.on('join game') - def return_join_game_notice(data): - game = data['game'] - join_room(game) - emit('join game', data, room=f'game') - -# @socketio.on -# def room_socket(roomspace): -# pass \ No newline at end of file