diff --git a/app/main/routes.py b/app/main/routes.py index 59970c1..c70ad9e 100644 --- a/app/main/routes.py +++ b/app/main/routes.py @@ -67,7 +67,7 @@ def game_dashboard(game_name): game=game, json=json, objective_encoder=ObjectiveMinimalEncoder, location_encoder=LocationEncoder) if role == Role.bunny: return render_template('game_bunny_dashboard.html', title='Game Dashboard', - game=game, json=json, objective_encoder=ObjectiveMinimalEncoder, location_encoder=LocationEncoder) + game=game, json=json, location_encoder=LocationEncoder) if role == Role.hunter: return render_template('game_hunter_dashboard.html', title='Game Dashboard', game=game, json=json, location_encoder=LocationEncoder) diff --git a/app/main/tests/test_routes.py b/app/main/tests/test_routes.py index f761372..ac56873 100644 --- a/app/main/tests/test_routes.py +++ b/app/main/tests/test_routes.py @@ -1,7 +1,7 @@ import unittest from app import create_app, db -from app.models import Player, Game, Role, GamePlayer, Objective, ObjectiveMinimalEncoder, LocationEncoder +from app.models import User, Game, Role, GamePlayer, Objective, ObjectiveMinimalEncoder, LocationEncoder import app.main.routes as routes from config import Config diff --git a/app/models/game_player.py b/app/models/game_player.py index 1765c62..be21a87 100644 --- a/app/models/game_player.py +++ b/app/models/game_player.py @@ -1,3 +1,5 @@ +import json + from sqlalchemy.ext.associationproxy import association_proxy from sqlalchemy.schema import UniqueConstraint @@ -5,6 +7,7 @@ from app import db from .role import Role from .notification_player import NotificationPlayer from .player_found_objective import PlayerFoundObjective +from .objective import Objective class GamePlayer(db.Model): __tablename__ = 'game_player' @@ -41,3 +44,16 @@ class GamePlayer(db.Model): def locations_during_game(self): # pylint: disable=no-member return self.user.locations_during_game(self.game) + + def encode_objectives(self): + # pylint: disable=no-member + objectives = ['['] + for objective in self.game.objectives: + obj = { + 'name' : objective.name, + 'longitude' : objective.longitude, + 'latitude' : objective.latitude, + 'found' : objective in self.found_objectives} + objectives.append(json.dumps(obj)) + objectives.append(',') + return ''.join(objectives)[:-1] + ']' diff --git a/app/models/objective.py b/app/models/objective.py index c75a8f3..45a3204 100644 --- a/app/models/objective.py +++ b/app/models/objective.py @@ -36,4 +36,4 @@ class ObjectiveMinimalEncoder(JSONEncoder): 'hash' : objective.hash, 'longitude' : objective.longitude, 'latitude' : objective.latitude - } + } \ No newline at end of file diff --git a/app/models/tests/test_game.py b/app/models/tests/test_game.py new file mode 100644 index 0000000..fb5709c --- /dev/null +++ b/app/models/tests/test_game.py @@ -0,0 +1,40 @@ +import unittest +from app import create_app, db +from app.models import User, Game, Role, GamePlayer +from config import Config + +class TestConfig(Config): + TESTING = True + WTF_CSRF_ENABLED = False + DEBUG = False + SQLALCHEMY_DATABASE_URI = 'sqlite://' + +class GameCase(unittest.TestCase): + # implement this: https://stackoverflow.com/questions/47294304/how-to-mock-current-user-in-flask-templates + def setUp(self): + self.app = create_app(TestConfig) + self.app_context = self.app.app_context() + self.app_context.push() + db.create_all() + + def tearDown(self): + db.session.remove() + db.drop_all() + self.app_context.pop() + + def test_is_game_owner(self): + g1 = Game(name='TestGame') + u1 = User(name='Henk') + u2 = User(name='Alfred') + + g1.players.append(GamePlayer(user=u1, role=Role.owner)) + g1.players.append(GamePlayer(user=u2, role=Role.bunny)) + + db.session.add(g1) + db.session.commit() + + self.assertTrue(g1.owned_by(u1)) + self.assertFalse(g1.owned_by(u2)) + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/app/models/tests/test_game_player.py b/app/models/tests/test_game_player.py new file mode 100644 index 0000000..5949ef3 --- /dev/null +++ b/app/models/tests/test_game_player.py @@ -0,0 +1,61 @@ +import unittest +import json +from app import create_app, db +from app.models import User, Game, Role, GamePlayer, Objective +from config import Config + +class TestConfig(Config): + TESTING = True + WTF_CSRF_ENABLED = False + DEBUG = False + SQLALCHEMY_DATABASE_URI = 'sqlite://' + +class GamePlayerCase(unittest.TestCase): + # implement this: https://stackoverflow.com/questions/47294304/how-to-mock-current-user-in-flask-templates + def setUp(self): + self.app = create_app(TestConfig) + self.app_context = self.app.app_context() + self.app_context.push() + db.create_all() + + def tearDown(self): + db.session.remove() + db.drop_all() + self.app_context.pop() + + def test_default(self): + g1 = Game(name='TestGame') + u1 = User(name='Henk') + p1 = GamePlayer(user=u1, role=Role.bunny) + + o1 = Objective(name='Objective 1') + o2 = Objective(name='Objective 2') + o3 = Objective(name='Objective 3') + + o1.set_hash() + o2.set_hash() + o3.set_hash() + + g1.players.append(p1) + g1.objectives.append(o1) + g1.objectives.append(o2) + g1.objectives.append(o3) + + p1.found_objectives.append(o1) + + db.session.add(g1) + db.session.commit() + + # Check if test initiaion succeeded + self.assertEqual(len(Game.query.first().objectives), 3) + self.assertEqual(User.query.first().user_games[0].found_objectives[0], o1) + + # The actual Test + player_objectives = ('[{"name": "Objective 1", "longitude": null, "latitude": null, "found": true},' + '{"name": "Objective 2", "longitude": null, "latitude": null, "found": false},' + '{"name": "Objective 3", "longitude": null, "latitude": null, "found": false}]') + self.assertEqual(p1.encode_objectives(), player_objectives) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/app/models/tests/test_objective.py b/app/models/tests/test_objective.py new file mode 100644 index 0000000..e04f7ee --- /dev/null +++ b/app/models/tests/test_objective.py @@ -0,0 +1,49 @@ +import unittest +from app import create_app, db +from app.models import User, Game, Role, GamePlayer, Objective +from config import Config + +class TestConfig(Config): + TESTING = True + WTF_CSRF_ENABLED = False + DEBUG = False + SQLALCHEMY_DATABASE_URI = 'sqlite://' + +class ObjectiveCase(unittest.TestCase): + # implement this: https://stackoverflow.com/questions/47294304/how-to-mock-current-user-in-flask-templates + def setUp(self): + self.app = create_app(TestConfig) + self.app_context = self.app.app_context() + self.app_context.push() + db.create_all() + + def tearDown(self): + db.session.remove() + db.drop_all() + self.app_context.pop() + + def test_is_objective_owner(self): + g1 = Game(name='TestGame') + g2 = Game(name='AnotherGame') + u1 = User(name='Henk') + + g1.players.append(GamePlayer(user=u1, role=Role.owner)) + g2.players.append(GamePlayer(user=u1, role=Role.bunny)) + + o1 = Objective(name='o1') + o1.set_hash() + o2 = Objective(name='o2') + o2.set_hash() + + g1.objectives.append(o1) + g2.objectives.append(o2) + + db.session.add(g1) + db.session.add(g2) + db.session.commit() + + self.assertTrue(o1.owned_by(u1)) + self.assertFalse(o2.owned_by(u1)) + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/app/models/tests/test_user.py b/app/models/tests/test_user.py new file mode 100644 index 0000000..55cb240 --- /dev/null +++ b/app/models/tests/test_user.py @@ -0,0 +1,75 @@ +import unittest +from app import create_app, db +from app.models import User, Game, GamePlayer, Role +from config import Config + +class TestConfig(Config): + TESTING = True + WTF_CSRF_ENABLED = False + DEBUG = False + SQLALCHEMY_DATABASE_URI = 'sqlite://' + +class UserCase(unittest.TestCase): + # implement this: https://stackoverflow.com/questions/47294304/how-to-mock-current-user-in-flask-templates + def setUp(self): + self.app = create_app(TestConfig) + self.app_context = self.app.app_context() + self.app_context.push() + db.create_all() + + def tearDown(self): + db.session.remove() + db.drop_all() + self.app_context.pop() + + def test_is_player_game_owner(self): + g1 = Game(name='TestGame') + g2 = Game(name='AnotherGame') + + u1 = User(name='Henk') + u2 = User(name='Alfred') + u3 = User(name='Sasha') + + g1.players.append(GamePlayer(user=u1, role=Role.owner)) + g1.players.append(GamePlayer(user=u2, role=Role.bunny)) + + g2.players.append(GamePlayer(user=u1, role=Role.hunter)) + g2.players.append(GamePlayer(user=u3, role=Role.bunny)) + + + db.session.add(g1) + db.session.add(g2) + db.session.commit() + + self.assertTrue(u1.owns_game_played_by(user=u2), "owner owns subject_player's game") + self.assertFalse(u1.owns_game_played_by(user=u3), "owner doesn't own subject_player's game") + self.assertTrue(u1.owns_game_played_by(user=u1), "owner owns it own's game") + + def test_role_in_game(self): + g1 = Game(name='TestGame') + + u1 = User(name='Henk') + u2 = User(name='Alfred') + u3 = User(name='Sasha') + u4 = User(name='Demian') + u5 = User(name='Karl') + + g1.players.append(GamePlayer(user=u1, role=Role.owner)) + g1.players.append(GamePlayer(user=u2, role=Role.bunny)) + g1.players.append(GamePlayer(user=u3, role=Role.hunter)) + g1.players.append(GamePlayer(user=u4, role=Role.none)) + + db.session.add(g1) + db.session.add(u5) + db.session.commit() + + self.assertEqual(u1.role_in_game(g1), Role.owner) + self.assertEqual(u2.role_in_game(g1), Role.bunny) + self.assertEqual(u3.role_in_game(g1), Role.hunter) + self.assertEqual(u4.role_in_game(g1), Role.none) + self.assertEqual(u5.role_in_game(g1), None) + with self.assertRaises(AttributeError): + g1.get_role_for_game(None) + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/app/static/assets/leaflet/utils.js b/app/static/assets/leaflet/utils.js new file mode 100644 index 0000000..b432ab0 --- /dev/null +++ b/app/static/assets/leaflet/utils.js @@ -0,0 +1,47 @@ +var greenIcon = new L.Icon({ + iconUrl: '/static/assets/leaflet/images/marker-icon-2x-green.png', + shadowUrl: '/static/assets/leaflet/images/marker-shadow.png', + iconSize: [25, 41], + iconAnchor: [12, 41], + popupAnchor: [1, -34], + shadowSize: [41, 41] +}); + +var goldIcon = new L.Icon({ + iconUrl: '/static/assets/leaflet/images/marker-icon-2x-gold.png', + shadowUrl: '/static/assets/leaflet/images/marker-shadow.png', + iconSize: [25, 41], + iconAnchor: [12, 41], + popupAnchor: [1, -34], + shadowSize: [41, 41] +}); + +function addObjectiveMarker(map, objective){ + var objectiveMarker = L.marker([ + objective['latitude'], + objective['longitude'] + ]) + if(objective['found']){ + console.log('test') + objectiveMarker.setIcon(goldIcon) + } + objectiveMarker.addTo(map); + + if(objective['hash']){ + objectiveMarker.bindTooltip(`${objective['name']}
+ ${objective['hash']}`).openPopup(); + } else { + objectiveMarker.bindTooltip(`${objective['name']}`).openPopup(); + } +} + +function addPlayerMarker(map, player){ + var playerMarker = L.marker([ + player['latitude'], + player['longitude'] + ], {icon: greenIcon}).addTo(map); + var timestamp_utc = moment.utc(player['timestamp_utc']).toDate() + var timestamp_local = moment(timestamp_utc).local().format('YYYY-MM-DD HH:mm'); + playerMarker.bindTooltip(`${player['username']}
+ ${timestamp_local}`).openPopup(); +} \ No newline at end of file diff --git a/app/templates/game_bunny_dashboard.html b/app/templates/game_bunny_dashboard.html index 1d77258..bb7c884 100644 --- a/app/templates/game_bunny_dashboard.html +++ b/app/templates/game_bunny_dashboard.html @@ -4,11 +4,12 @@ {{ super() }} + {% endblock %} {% block app_content %} -

{{ game.name }} Dashboard

- +

{{ game.name }}: {{ current_user.name }}

+{% include '_game_player_info.html' %}

Objective Locations:

{% if game.objectives %}
@@ -27,7 +28,7 @@ {{ objective.name }} {{ objective.latitude }} {{ objective.longitude }} - {{ 'Yes' if current_user.player_in(game) in objective.objective_found_by else 'No' }} + {{ 'Yes' if objective in current_user.player_in(game).found_objectives else 'No' }} {% endfor %} @@ -57,36 +58,15 @@ attribution: 'Kaartgegevens © Kadaster' }).addTo( map ); - var objectives = JSON.parse('{{ json.dumps(game.objectives, cls=objective_encoder)|safe }}') - + var string = '{{ current_user.player_in(game).encode_objectives() |safe }}' + var objectives = JSON.parse(string) for (var i = 0; i < objectives.length; i++){ - var objectiveMarker = L.marker([ - objectives[i]['latitude'], - objectives[i]['longitude'] - ]).addTo(map); - objectiveMarker.bindTooltip(`${objectives[i]['name']}
- ${objectives[i]['hash']}`).openPopup(); + addObjectiveMarker(map, objectives[i]) } - var greenIcon = new L.Icon({ - iconUrl: "{{ url_for('static', filename='assets/leaflet/images/marker-icon-2x-green.png') }}", - shadowUrl: "{{ url_for('static', filename='assets/leaflet/images/marker-shadow.png') }}", - iconSize: [25, 41], - iconAnchor: [12, 41], - popupAnchor: [1, -34], - shadowSize: [41, 41] - }); - - var players = JSON.parse('{{ json.dumps(game.last_player_locations(), cls=location_encoder)|safe }}') - for (var i = 0; i < players.length; i++){ - var playerMarker = L.marker([ - players[i]['latitude'], - players[i]['longitude'] - ], {icon: greenIcon}).addTo(map); - var timestamp_utc = moment.utc(players[i]['timestamp_utc']).toDate() - var timestamp_local = moment(timestamp_utc).local().format('YYYY-MM-DD HH:mm'); - playerMarker.bindTooltip(`${players[i]['username']}
- ${timestamp_local}`).openPopup(); + var self = JSON.parse('{{ json.dumps(current_user.last_location(game), cls=location_encoder)|safe }}') + if (self){ + addPlayerMarker(map, self) } diff --git a/app/templates/game_hunter_dashboard.html b/app/templates/game_hunter_dashboard.html index 7308053..bd19c18 100644 --- a/app/templates/game_hunter_dashboard.html +++ b/app/templates/game_hunter_dashboard.html @@ -4,6 +4,7 @@ {{ super() }} + {% endblock %} {% block app_content %} @@ -63,26 +64,15 @@ L.tileLayer( 'https://geodata.nationaalgeoregister.nl/tiles/service/wmts/brtachtergrondkaartpastel/EPSG:3857/{z}/{x}/{y}.png', { attribution: 'Kaartgegevens © Kadaster' }).addTo( map ); - - var greenIcon = new L.Icon({ - iconUrl: "{{ url_for('static', filename='assets/leaflet/images/marker-icon-2x-green.png') }}", - shadowUrl: "{{ url_for('static', filename='assets/leaflet/images/marker-shadow.png') }}", - iconSize: [25, 41], - iconAnchor: [12, 41], - popupAnchor: [1, -34], - shadowSize: [41, 41] - }); - var players = JSON.parse('{{ json.dumps(game.last_locations(game.bunnies()), cls=location_encoder)|safe }}') - for (var i = 0; i < players.length; i++){ - var playerMarker = L.marker([ - players[i]['latitude'], - players[i]['longitude'] - ], {icon: greenIcon}).addTo(map); - var timestamp_utc = moment.utc(players[i]['timestamp_utc']).toDate() - var timestamp_local = moment(timestamp_utc).local().format('YYYY-MM-DD HH:mm'); - playerMarker.bindTooltip(`${players[i]['username']}
- ${timestamp_local}`).openPopup(); + var bunnies = JSON.parse('{{ json.dumps(game.last_locations(game.bunnies()), cls=location_encoder)|safe }}') + for (var i = 0; i < bunnies.length; i++){ + addPlayerMarker(map, bunnies[i]) + } + + var self = JSON.parse('{{ json.dumps(current_user.last_location(game), cls=location_encoder)|safe }}') + if (self){ + addPlayerMarker(map, self) } diff --git a/app/templates/game_owner_dashboard.html b/app/templates/game_owner_dashboard.html index 6bffbc5..dc0e6ce 100644 --- a/app/templates/game_owner_dashboard.html +++ b/app/templates/game_owner_dashboard.html @@ -4,6 +4,7 @@ {{ super() }} + {% endblock %} {% block app_content %} @@ -102,33 +103,12 @@ var objectives = JSON.parse('{{ json.dumps(game.objectives, cls=objective_encoder)|safe }}') for (var i = 0; i < objectives.length; i++){ - var objectiveMarker = L.marker([ - objectives[i]['latitude'], - objectives[i]['longitude'] - ]).addTo(map); - objectiveMarker.bindTooltip(`${objectives[i]['name']}
- ${objectives[i]['hash']}`).openPopup(); + addObjectiveMarker(map, objectives[i]) } - - var greenIcon = new L.Icon({ - iconUrl: "{{ url_for('static', filename='assets/leaflet/images/marker-icon-2x-green.png') }}", - shadowUrl: "{{ url_for('static', filename='assets/leaflet/images/marker-shadow.png') }}", - iconSize: [25, 41], - iconAnchor: [12, 41], - popupAnchor: [1, -34], - shadowSize: [41, 41] - }); var players = JSON.parse('{{ json.dumps(game.last_player_locations(), cls=location_encoder)|safe }}') for (var i = 0; i < players.length; i++){ - var playerMarker = L.marker([ - players[i]['latitude'], - players[i]['longitude'] - ], {icon: greenIcon}).addTo(map); - var timestamp_utc = moment.utc(players[i]['timestamp_utc']).toDate() - var timestamp_local = moment(timestamp_utc).local().format('YYYY-MM-DD HH:mm'); - playerMarker.bindTooltip(`${players[i]['username']}
- ${timestamp_local}`).openPopup(); + addPlayerMarker(map, players[i]) } diff --git a/app/tests/test_models.py b/app/tests/test_models.py deleted file mode 100644 index 01071b6..0000000 --- a/app/tests/test_models.py +++ /dev/null @@ -1,113 +0,0 @@ -import unittest -from app import create_app, db -from app.models import User, Game, Role, GamePlayer, Objective, ObjectiveMinimalEncoder, LocationEncoder -from config import Config - -class TestConfig(Config): - TESTING = True - WTF_CSRF_ENABLED = False - DEBUG = False - SQLALCHEMY_DATABASE_URI = 'sqlite://' - -class ModelsCase(unittest.TestCase): - # implement this: https://stackoverflow.com/questions/47294304/how-to-mock-current-user-in-flask-templates - def setUp(self): - self.app = create_app(TestConfig) - self.app_context = self.app.app_context() - self.app_context.push() - db.create_all() - - def tearDown(self): - db.session.remove() - db.drop_all() - self.app_context.pop() - - def test_is_game_owner(self): - g1 = Game(name='TestGame') - u1 = User(name='Henk') - u2 = User(name='Alfred') - - g1.players.append(GamePlayer(player=u1, role=Role.owner)) - g1.players.append(GamePlayer(player=u2, role=Role.bunny)) - - db.session.add(g1) - db.session.commit() - - self.assertTrue(g1.is_game_owner(u1)) - self.assertFalse(g1.is_game_owner(u2)) - - def test_is_player_game_owner(self): - g1 = Game(name='TestGame') - g2 = Game(name='AnotherGame') - - u1 = User(name='Henk') - u2 = User(name='Alfred') - u3 = User(name='Sasha') - - g1.players.append(GamePlayer(player=u1, role=Role.owner)) - g1.players.append(GamePlayer(player=u2, role=Role.bunny)) - - g2.players.append(GamePlayer(player=u1, role=Role.hunter)) - g2.players.append(GamePlayer(player=u3, role=Role.bunny)) - - - db.session.add(g1) - db.session.add(g2) - db.session.commit() - - self.assertTrue(u1.owns_game_played_by(player=u2), "owner owns subject_player's game") - self.assertFalse(u1.owns_game_played_by(player=u3), "owner doesn't own subject_player's game") - self.assertTrue(u1.owns_game_played_by(player=u1), "owner owns it own's game") - - - def test_is_objective_owner(self): - g1 = Game(name='TestGame') - g2 = Game(name='AnotherGame') - u1 = User(name='Henk') - - g1.players.append(GamePlayer(player=u1, role=Role.owner)) - g2.players.append(GamePlayer(player=u1, role=Role.bunny)) - - o1 = Objective(name='o1') - o1.set_hash() - o2 = Objective(name='o2') - o2.set_hash() - - g1.objectives.append(o1) - g2.objectives.append(o2) - - db.session.add(g1) - db.session.add(g2) - db.session.commit() - - self.assertTrue(o1.owned_by(u1)) - self.assertFalse(o2.owned_by(u1)) - - def test_role_in_game(self): - g1 = Game(name='TestGame') - - u1 = User(name='Henk') - u2 = User(name='Alfred') - u3 = User(name='Sasha') - u4 = User(name='Demian') - u5 = User(name='Karl') - - g1.players.append(GamePlayer(player=u1, role=Role.owner)) - g1.players.append(GamePlayer(player=u2, role=Role.bunny)) - g1.players.append(GamePlayer(player=u3, role=Role.hunter)) - g1.players.append(GamePlayer(player=u4, role=Role.none)) - - db.session.add(g1) - db.session.add(u5) - db.session.commit() - - self.assertEqual(u1.role_in_game(g1), Role.owner) - self.assertEqual(u2.role_in_game(g1), Role.bunny) - self.assertEqual(u3.role_in_game(g1), Role.hunter) - self.assertEqual(u4.role_in_game(g1), Role.none) - self.assertEqual(u5.role_in_game(g1), None) - with self.assertRaises(AttributeError): - g1.get_role_for_game(None) - -if __name__ == '__main__': - unittest.main(verbosity=2)