fishbowl/fishbowl/app.py

177 lines
5.2 KiB
Python

from uuid import uuid4
from random import choice
from os import environ
import crypt
from flask import Flask
from flask import redirect
from flask import render_template
from flask import request
from flask import session
from flask import url_for
from flask_bootstrap import Bootstrap
from flask_sqlalchemy import SQLAlchemy
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, BooleanField, PasswordField
from wtforms.validators import DataRequired, Length, Optional
import flask_sqlalchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = environ.get(
'DB_URI', 'sqlite:///fishbowl.db'
)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.secret_key = environ.get('SECRET_KEY', uuid4().hex)
bootstrap = Bootstrap(app)
db = SQLAlchemy(app)
class Game(db.Model):
id = db.Column(db.Integer, primary_key=True)
uuid = db.Column(db.String(32), unique=True, nullable=False)
password = db.Column(db.String(128))
admin_password = db.Column(db.String(128))
words = db.relationship(
'Word',
backref=db.backref('game', lazy=True),
)
@classmethod
def new(cls, password, admin_password=None):
game = Game(uuid=uuid4().hex)
salt = crypt.mksalt()
if password:
game.password = crypt.crypt(password, salt)
if admin_password:
admin_password = crypt.crypt(admin_password, salt)
return game
@classmethod
def by_uuid(cls, uuid):
return cls.query.filter_by(
uuid=uuid,
).one()
def get_url(self):
return url_for('.game', game_uuid=self.uuid)
def get_remaining_words(self):
return [
word for word in self.words
if not word.is_hidden and not word.is_picked
]
class Word(db.Model):
id = db.Column(db.Integer, primary_key=True)
text = db.Column(db.String(1024))
game_id = db.Column(db.Integer, db.ForeignKey('game.id'))
is_hidden = db.Column(db.Boolean, default=False)
is_picked = db.Column(db.Boolean, default=False)
class NewGameForm(FlaskForm):
game_password = PasswordField(
'Game password (optional)', validators=[Optional(), Length(5, 128)]
)
# admin_password = PasswordField(
# 'Admin password', validators=[DataRequired(), Length(5, 128)]
# )
submit = SubmitField(label='Create')
class GameLoginForm(FlaskForm):
game_password = PasswordField(
'Game password', validators=[DataRequired(), Length(5, 128)]
)
# admin_password = PasswordField(
# 'Admin password', validators=[DataRequired(), Length(5, 128)]
# )
submit = SubmitField(label='Login')
class AddWordForm(FlaskForm):
new_word = StringField('New word', validators=[Length(3, 1024)])
submit_new_word = SubmitField(label='Add new word')
class PlayGameForm(FlaskForm):
draw_word = SubmitField(label='Draw word')
reset_game = SubmitField(label='Reset game')
reset_confirm = BooleanField(label='Confirm reset')
@app.route('/', methods=['GET', 'POST'])
def index():
form = NewGameForm()
if request.method == 'POST':
if form.validate_on_submit():
game = Game.new(form.game_password.data)
db.session.add(game)
db.session.commit()
app.logger.info("Created game %i, %s", game.id, game.uuid)
return redirect(game.get_url())
else:
print("Form was not valid")
return render_template('index.html', form=form)
@app.route('/game/<game_uuid>', methods=['GET', 'POST'])
def game(game_uuid):
# Init login tracker
if 'logged_in' not in session:
session['logged_in'] = {}
game_login = GameLoginForm()
word_form = AddWordForm()
play_form = PlayGameForm()
try:
game = Game.by_uuid(game_uuid)
except flask_sqlalchemy.orm.exc.NoResultFound:
return redirect(url_for('.index'))
word = None
if request.method == 'POST':
# Check if trying to login
if game_login.submit.data and game_login.validate():
if crypt.crypt(game.password, game_login.game_password.data):
if not session['logged_in']:
session['logged_in'] = {}
session['logged_in'][game.uuid] = True
# Check if word is being added
if word_form.submit_new_word.data and word_form.validate():
word = Word(text=word_form.new_word.data)
game.words.append(word)
# We just drew a new word!
if play_form.draw_word.data:
words = game.get_remaining_words()
if words:
word = choice(words)
word.is_picked = True
else:
word = "There are no words. Add some and try again!"
# Reset the game
if play_form.reset_game.data and play_form.reset_confirm.data:
for unpick_word in game.words:
unpick_word.is_picked = False
db.session.add(game)
db.session.commit()
words = game.get_remaining_words()
return render_template(
'game.html',
game_login=game_login,
word_form=word_form,
play_form=play_form,
game=game,
word=word,
words=words,
)
if __name__ == '__main__':
db.create_all()
app.run(host='0.0.0.0', port=3000)