Browse Source

implement issue #03

feature_tests
Burathar 4 years ago
parent
commit
9d6670abdc
  1. 20
      app/forms.py
  2. 9
      app/models.py
  3. 31
      app/routes.py
  4. 82
      app/templates/add_objective.html
  5. 16
      app/templates/create_game.html
  6. 29
      app/templates/game_dashboard.html
  7. 2
      app/templates/register.html
  8. 12
      migrations/versions/3ef4c34115fd_updated_to_database_documentation_1_3.py

20
app/forms.py

@ -1,7 +1,7 @@ @@ -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): @@ -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): @@ -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.')

9
app/models.py

@ -76,6 +76,7 @@ class Game(db.Model): @@ -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): @@ -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): @@ -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)

31
app/routes.py

@ -2,11 +2,11 @@ from flask import render_template, flash, redirect, url_for, request @@ -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(): @@ -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(): @@ -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(): @@ -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/<game_name>/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/<game_name>/player/<player_name>')
@login_required
def game_player(game_name, player_name):
return redirect(url_for('indsex'))
@app.route('/game/<game_name>/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)

82
app/templates/add_objective.html

@ -0,0 +1,82 @@ @@ -0,0 +1,82 @@
{% extends "base.html" %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block head %}
{{ super() }}
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.6.0/dist/leaflet.css"
integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ=="
crossorigin=""/>
<script src="https://unpkg.com/leaflet@1.6.0/dist/leaflet.js"
integrity="sha512-gZwIG9x3wUXg2hdXF6+rVkLF/0Vi9U8D2Ntg4Ga5I5BZpVkVxlJWbSQtXPSiUTtC0TjtGOmxa1AJPuV0CPthew=="
crossorigin=""></script>
{% endblock %}
{% block app_content %}
<h1>Add Objective</h1>
<hr>
<div class="row">
<div class="col-md-4 col-sm-6 col-xs-8">
<form action="" method="post" class="form" role="form">
{{ 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') }}
</form>
</div>
<div id="map" style=" height: 500px; border-radius: 10px; " class="col-md-6 col-xs-12"></div>
</div>
<hr>
{% endblock %}
{% block scripts %}
{{ super() }}
<script type="text/javascript", crossorigin="anonymous">
// Leaflet Map
var map = L.map( 'map', {
center: [52.2, 5.3],
minZoom: 6,
maxZoom: 19,
bounds: [[50.5, 3.25], [54, 7.6]],
zoom: 8
});
L.control.scale().addTo(map);
L.tileLayer( 'https://geodata.nationaalgeoregister.nl/tiles/service/wmts/brtachtergrondkaartpastel/EPSG:3857/{z}/{x}/{y}.png', {
attribution: 'Kaartgegevens &copy; <a href="kadaster.nl">Kadaster</a>'
}).addTo( map );
var objectiveMarker = L.marker([
$("#latitude")[0].value,
$("#longitude")[0].value
], {
draggable: true
}).addTo(map);
var round = function(value){
return (Math.round(value * 100000) / 100000).toFixed(5);
};
objectiveMarker.on('dragend', function(e){
var newPosition = e.target.getLatLng();
$("#latitude")[0].value = round(newPosition.lat);
$("#longitude")[0].value = round(newPosition.lng);
})
$("#latitude").change(function(e) {
var newLatLng = new L.LatLng(e.target.value, objectiveMarker._latlng.lng);
objectiveMarker.setLatLng(newLatLng);
});
$("#longitude").change(function(e) {
var newLatLng = new L.LatLng(objectiveMarker._latlng.lat, e.target.value);
objectiveMarker.setLatLng(newLatLng);
});
</script>
{% endblock %}

16
app/templates/create_game.html

@ -3,8 +3,7 @@ @@ -3,8 +3,7 @@
{% block head %}
{{ super() }}
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.47/css/bootstrap-datetimepicker.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.47/css/bootstrap-datetimepicker.min.css">
{% endblock %}
{% block app_content %}
@ -13,7 +12,7 @@ @@ -13,7 +12,7 @@
<div class="col-md-4 col-sm-6 col-xs-8">
<hr>
<form action="" method="post" class="form" role="form">
{{ form.csrf_token() }}
{{ form.hidden_tag() }}
{{ form.timezone }}
{{ wtf.form_field(form.game_name, class='form-control') }}
@ -36,22 +35,13 @@ @@ -36,22 +35,13 @@
{{ wtf.form_field(form.end_time_disabled, class='form-control') }}
</div>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
{{ wtf.form_field(form.submit, class='btn btn-primary') }}
</form>
<hr>
</div>
{% endblock %}
TODO:
https://eonasdan.github.io/bootstrap-datetimepicker/Options/#locale+
- optie voor nu
{% block scripts %}
{{ super() }}
<!-- TODO: Scripts downloaden naar repo? -->

29
app/templates/game_dashboard.html

@ -30,7 +30,34 @@ @@ -30,7 +30,34 @@
</tbody>
</table>
</div>
<h2>Objectives:</h2>
<p><a href="{{ url_for('add_objective', game_name = game.name) }}">Add new objective</a></p>
{% if game.objectives %}
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th scope="col">Hash</th>
<th scope="col">Objective Name</th>
<th scope="col">Latitude</th>
<th scope="col">Longitude</th>
<th scope="col">Amount of players that found it</th>
</tr>
</thead>
<tbody>
{% for objective in game.objectives %}
<tr>
<td>{{ objective.hash }}</td>
<td>{{ objective.name }}</td>
<td>{{ objective.latitude }}</td>
<td>{{ objective.longitude }}</td>
<td> Placeholder <td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endif %}
{% endblock %}

2
app/templates/register.html

@ -8,4 +8,6 @@ @@ -8,4 +8,6 @@
{{ wtf.quick_form(form, button_map={'submit': 'primary'}) }}
</div>
</div>
<br>
<p>Already have an account? <a href="{{ url_for('login') }}">Click to Sign In!</a></p>
{% endblock %}

12
migrations/versions/68009992b106_update_up_to_1_3_association_tables.py → migrations/versions/3ef4c34115fd_updated_to_database_documentation_1_3.py

@ -1,8 +1,8 @@ @@ -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 @@ -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(): @@ -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'], ),
Loading…
Cancel
Save