From 9d6670abdc365df9efdbdffb4ccc3bfa17d9d502 Mon Sep 17 00:00:00 2001 From: Burathar Date: Thu, 9 Jul 2020 15:01:18 +0200 Subject: [PATCH] implement issue #03 --- app/forms.py | 20 ++++- app/models.py | 9 +- app/routes.py | 33 ++++++-- app/templates/add_objective.html | 82 +++++++++++++++++++ app/templates/create_game.html | 16 +--- app/templates/game_dashboard.html | 29 ++++++- app/templates/register.html | 2 + ..._updated_to_database_documentation_1_3.py} | 12 +-- 8 files changed, 169 insertions(+), 34 deletions(-) create mode 100644 app/templates/add_objective.html rename migrations/versions/{68009992b106_update_up_to_1_3_association_tables.py => 3ef4c34115fd_updated_to_database_documentation_1_3.py} (95%) diff --git a/app/forms.py b/app/forms.py index 8f56215..313e7eb 100644 --- a/app/forms.py +++ b/app/forms.py @@ -1,7 +1,7 @@ from flask_wtf import FlaskForm -from wtforms import StringField, PasswordField, BooleanField, SubmitField, TextAreaField, DateTimeField, BooleanField, HiddenField -from wtforms.validators import DataRequired, EqualTo, ValidationError, Length -from app.models import Player +from wtforms import StringField, PasswordField, BooleanField, SubmitField, TextAreaField, DateTimeField, BooleanField, HiddenField, FloatField +from wtforms.validators import DataRequired, EqualTo, ValidationError, Length, NumberRange +from app.models import Player, Objective from pytz import timezone class LoginForm(FlaskForm): @@ -29,7 +29,7 @@ class CreateGameForm(FlaskForm): end_time_disabled = BooleanField('No end time') end_time = DateTimeField(id='datetimepicker_end', format="%d-%m-%Y %H:%M") timezone = HiddenField(validators=[DataRequired()]) - submit = SubmitField('Register') + submit = SubmitField('Create') def validate_start_time(self, start_time): self.date_time_validator(self.start_time_disabled, start_time) @@ -44,3 +44,15 @@ class CreateGameForm(FlaskForm): clientzone = timezone(self.timezone.data) date_time_utc = clientzone.localize(date_time.data).astimezone(timezone('UTC')) date_time.data = date_time_utc + +class AddObjectiveForm(FlaskForm): + objective_name = StringField('Objective Name', validators=[Length(min=0, max=64)]) + latitude = FloatField('Latitude', validators=[DataRequired(), NumberRange(min=-90, max=90)]) + longitude = FloatField('Longitude', validators=[DataRequired(), NumberRange(min=-180, max=180)]) + submit = SubmitField('Create') + + def validate_objective_name(self, objective_name): + if objective_name.data == '': return + objective = Objective.query.filter_by(name=objective_name.data).first() + if objective is not None: + raise ValidationError('Please use a different name.') \ No newline at end of file diff --git a/app/models.py b/app/models.py index 58f3e19..d63c732 100644 --- a/app/models.py +++ b/app/models.py @@ -76,6 +76,7 @@ class Game(db.Model): backref=db.backref('game', lazy='joined')) class Player(UserMixin, db.Model): + """ !Always call set_auth_hash() after creating new instance! """ __tablename__ = 'player' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(64), unique=True, nullable=False) @@ -138,11 +139,12 @@ def load_user(id): return Player.query.get(int(id)) class Objective(db.Model): + """ !Always call set_hash after() creating new instance! """ __tablename__ = 'objective' id = db.Column(db.Integer, primary_key=True) - name = db.Column(db.String(64), nullable=False) + name = db.Column(db.String(64)) game_id = db.Column(db.Integer, db.ForeignKey('game.id'), nullable=False) - hash = db.Column(db.String(128), unique=True, nullable=False) + hash = db.Column(db.String(32), unique=True, nullable=False) longitude = db.Column(db.Numeric(precision=15, scale=10, asdecimal=False, decimal_return_scale=None)) # maybe check asdecimal and decimal_return_scale later? latitude = db.Column(db.Numeric(precision=15, scale=10, asdecimal=False, decimal_return_scale=None)) objective_found_by = db.relationship( @@ -152,6 +154,9 @@ class Objective(db.Model): found_by = association_proxy('objective_found_by', 'player', creator=lambda player: PlayerFoundObjective(player=player)) + def set_hash(self): + self.hash = token_hex(16) + class Location(db.Model): __tablename__ = 'location' id = db.Column(db.Integer, primary_key=True) diff --git a/app/routes.py b/app/routes.py index 1a4c9fd..a31049f 100644 --- a/app/routes.py +++ b/app/routes.py @@ -2,11 +2,11 @@ from flask import render_template, flash, redirect, url_for, request from flask_login import login_user, logout_user, current_user, login_required from sqlalchemy import and_ from app import app, db -from app.models import Player, Game, Role, GamePlayer -from app.forms import LoginForm, RegistrationForm, CreateGameForm +from app.models import Player, Game, Role, GamePlayer, Objective +from app.forms import LoginForm, RegistrationForm, CreateGameForm, AddObjectiveForm -@app.route('/', methods=['GET']) -@app.route('/index', methods=['GET']) +@app.route('/') +@app.route('/index') @login_required def index(): return render_template("index.html", title='Home') @@ -26,6 +26,7 @@ def login(): return render_template('login.html', title='Sign In', form=form) @app.route('/logout') +@login_required def logout(): logout_user() return redirect(url_for('index')) @@ -38,15 +39,17 @@ def register(): if form.validate_on_submit(): player = Player(name=form.username.data) player.set_password(form.password.data) + player.set_auth_hash() db.session.add(player) db.session.commit() flash('Congratulations, you are now a registered user!') return redirect(url_for('login')) return render_template('register.html', title='Register', form=form) -@login_required @app.route('/create_game', methods=['GET', 'POST']) +@login_required def create_game(): + print(current_user.is_authenticated) form = CreateGameForm() if form.validate_on_submit(): game = Game(name=form.game_name.data, start_time=form.start_time.data, end_time=form.end_time.data) @@ -57,13 +60,27 @@ def create_game(): return redirect(url_for('game_dashboard', game_name=game.name)) return render_template('create_game.html', title='Create Game', form=form) -@login_required @app.route('/game//dashboard') +@login_required def game_dashboard(game_name): game = Game.query.filter(Game.game_players.any(and_(GamePlayer.player.has(Player.name == current_user.name), GamePlayer.role == 'owner'))).first_or_404() return render_template('game_dashboard.html', title = 'Game Dashboard', game=game) -@login_required @app.route('/game//player/') +@login_required def game_player(game_name, player_name): - return redirect(url_for('indsex')) \ No newline at end of file + return redirect(url_for('indsex')) + +@app.route('/game//add_objective', methods=['GET', 'POST']) +@login_required +def add_objective(game_name): + game = Game.query.filter_by(name = game_name).first_or_404() + form = AddObjectiveForm() + if form.validate_on_submit(): + objective = Objective(name=form.objective_name.data, longitude=form.longitude.data, latitude=form.latitude.data) + objective.set_hash() + game.objectives.append(objective) + db.session.commit() + flash(f"Objective has been added!") + return redirect(url_for('game_dashboard', game_name=game.name)) + return render_template('add_objective.html', title = f'Add Objective for {game_name}', form=form) \ No newline at end of file diff --git a/app/templates/add_objective.html b/app/templates/add_objective.html new file mode 100644 index 0000000..ed9ea84 --- /dev/null +++ b/app/templates/add_objective.html @@ -0,0 +1,82 @@ +{% extends "base.html" %} +{% import 'bootstrap/wtf.html' as wtf %} + +{% block head %} +{{ super() }} + + +{% endblock %} + +{% block app_content %} +

Add Objective

+
+
+
+
+ {{ form.hidden_tag() }} + {{ wtf.form_field(form.objective_name, class='form-control') }} + {{ wtf.form_field(form.latitude, type='number', value='52.2', min='-90', max='90', step='0.00001') }} + {{ wtf.form_field(form.longitude, class='form-control', type='number', value='5.3', min='-180', max='181', step='0.00001') }} + {{ wtf.form_field(form.submit, class='btn btn-primary') }} +
+ +
+
+ +
+
+ + +{% endblock %} + +{% block scripts %} +{{ super() }} + + +{% endblock %} \ No newline at end of file diff --git a/app/templates/create_game.html b/app/templates/create_game.html index 1fb6f7f..4fb4947 100644 --- a/app/templates/create_game.html +++ b/app/templates/create_game.html @@ -3,8 +3,7 @@ {% block head %} {{ super() }} - + {% endblock %} {% block app_content %} @@ -13,7 +12,7 @@

- {{ form.csrf_token() }} + {{ form.hidden_tag() }} {{ form.timezone }} {{ wtf.form_field(form.game_name, class='form-control') }} @@ -36,22 +35,13 @@ {{ wtf.form_field(form.end_time_disabled, class='form-control') }}
- - + {{ wtf.form_field(form.submit, class='btn btn-primary') }}
- - - - {% endblock %} -TODO: -https://eonasdan.github.io/bootstrap-datetimepicker/Options/#locale+ -- optie voor nu - {% block scripts %} {{ super() }} diff --git a/app/templates/game_dashboard.html b/app/templates/game_dashboard.html index ca190d6..5ecc547 100644 --- a/app/templates/game_dashboard.html +++ b/app/templates/game_dashboard.html @@ -30,7 +30,34 @@ - +

Objectives:

+

Add new objective

+{% if game.objectives %} +
+ + + + + + + + + + + + {% for objective in game.objectives %} + + + + + + + {% endfor %} + +
HashObjective NameLatitudeLongitudeAmount of players that found it
{{ objective.hash }}{{ objective.name }}{{ objective.latitude }}{{ objective.longitude }} Placeholder +
+
+{% endif %} {% endblock %} \ No newline at end of file diff --git a/app/templates/register.html b/app/templates/register.html index 659915d..1ecb3bf 100644 --- a/app/templates/register.html +++ b/app/templates/register.html @@ -8,4 +8,6 @@ {{ wtf.quick_form(form, button_map={'submit': 'primary'}) }} +
+

Already have an account? Click to Sign In!

{% endblock %} \ No newline at end of file diff --git a/migrations/versions/68009992b106_update_up_to_1_3_association_tables.py b/migrations/versions/3ef4c34115fd_updated_to_database_documentation_1_3.py similarity index 95% rename from migrations/versions/68009992b106_update_up_to_1_3_association_tables.py rename to migrations/versions/3ef4c34115fd_updated_to_database_documentation_1_3.py index dc8e108..55fbce6 100644 --- a/migrations/versions/68009992b106_update_up_to_1_3_association_tables.py +++ b/migrations/versions/3ef4c34115fd_updated_to_database_documentation_1_3.py @@ -1,8 +1,8 @@ -"""update up to 1.3+association tables +"""updated to database documentation 1.3 -Revision ID: 68009992b106 +Revision ID: 3ef4c34115fd Revises: -Create Date: 2020-07-03 23:14:41.035088 +Create Date: 2020-07-09 14:43:14.149353 """ from alembic import op @@ -10,7 +10,7 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = '68009992b106' +revision = '3ef4c34115fd' down_revision = None branch_labels = None depends_on = None @@ -64,9 +64,9 @@ def upgrade(): ) op.create_table('objective', sa.Column('id', sa.Integer(), nullable=False), - sa.Column('name', sa.String(length=64), nullable=False), + sa.Column('name', sa.String(length=64), nullable=True), sa.Column('game_id', sa.Integer(), nullable=False), - sa.Column('hash', sa.String(length=128), nullable=False), + sa.Column('hash', sa.String(length=32), nullable=False), sa.Column('longitude', sa.Numeric(precision=15, scale=10, asdecimal=False), nullable=True), sa.Column('latitude', sa.Numeric(precision=15, scale=10, asdecimal=False), nullable=True), sa.ForeignKeyConstraint(['game_id'], ['game.id'], ),