From 48b7431c742976709f0bfc8095538806949b7521 Mon Sep 17 00:00:00 2001 From: Burathar Date: Wed, 29 Jul 2020 23:40:17 +0200 Subject: [PATCH] make game_player map self-updating --- app/main/routes.py | 39 ++++++++++++- app/static/assets/leaflet/utils.js | 11 ++++ app/templates/game_player.html | 90 +++++++++++++++++++++++++++--- 3 files changed, 131 insertions(+), 9 deletions(-) diff --git a/app/main/routes.py b/app/main/routes.py index df5507a..a077d35 100644 --- a/app/main/routes.py +++ b/app/main/routes.py @@ -93,7 +93,7 @@ def send_location(username): # Check if previous two locations are exactly the same, # if so, only update timestamp of last location if last_location: - if datetime.utcnow() - last_location.timestamp < timedelta(minutes=1): + if datetime.utcnow() - last_location.timestamp < timedelta(seconds=30): return '', 204 if (latitude == last_location.latitude and longitude == last_location.longitude and @@ -110,6 +110,43 @@ def send_location(username): db.session.commit() return '', 204 +@bp.route('/game//get_locations', methods=['POST']) +@login_required +def poll_locations(game_name): + game = Game.query.filter_by(name=game_name).first_or_404() + role = current_user.role_in_game(game) + if role is None or role == Role.none: + abort(403) + + payload = request.get_json() + if payload is None: + abort(400) + mode = get_value_if_key_exists(payload, 'mode', 'last') + last_update = get_value_if_key_exists(payload, 'last_update', 'none') + last_update = datetime.strptime(last_update, '%Y-%m-%d %H:%M:%S:%f') if last_update != 'none' else datetime.min + requested_users = get_value_if_key_exists(payload, 'requested_users', 'none') + #print(f'mode: {mode}\nlast_request: {last_update}\nrequested_users: {requested_users}') + response_objects = [] + if role == Role.owner: + for username in requested_users: + user = get_user_locations(game, username, mode, last_update) + if user: + response_objects.append(user) + response_objects = [obj for obj_list in response_objects for obj in obj_list] + return json.dumps(response_objects, cls=LocationEncoder) + +def get_value_if_key_exists(dictionary, key, default=None): + return dictionary[key] if key in dictionary else default + +def get_user_locations(game, username, mode, last_update): + user = User.query.filter_by(name=username).first() + if user is None: + return None + if mode == 'accumulative': + if game.end_time < last_update: + return [] + return [location for location in user.locations_during_game(game) if location.timestamp - last_update > timedelta(milliseconds=1)] + @bp.route('/user/') @login_required def user_profile(username): diff --git a/app/static/assets/leaflet/utils.js b/app/static/assets/leaflet/utils.js index 086f093..4a1d6d4 100644 --- a/app/static/assets/leaflet/utils.js +++ b/app/static/assets/leaflet/utils.js @@ -25,6 +25,15 @@ var bluePlayerIcon = new L.Icon({ shadowSize: [41, 41] }); +var bluePlayerIconMini = new L.Icon({ + iconUrl: '/static/assets/leaflet/images/person-marker-icon-2x-blue.png', + shadowUrl: '/static/assets/leaflet/images/marker-shadow.png', + iconSize: [10, 16.4], + iconAnchor: [5, 16.4], + popupAnchor: [1, -34], + shadowSize: [16.4, 16.4] +}); + var greenPlayerIcon = new L.Icon({ iconUrl: '/static/assets/leaflet/images/person-marker-icon-2x-green.png', shadowUrl: '/static/assets/leaflet/images/marker-shadow.png', @@ -50,6 +59,7 @@ function addObjectiveMarker(map, objective){ } else { objectiveMarker.bindTooltip(`${objective['name']}`).openPopup(); } + return objectiveMarker } function addPlayerMarker(map, player, icon=bluePlayerIcon){ @@ -61,6 +71,7 @@ function addPlayerMarker(map, player, icon=bluePlayerIcon){ var timestamp_local = moment(timestamp_utc).local().format('YYYY-MM-DD HH:mm'); playerMarker.bindTooltip(`${player['username']}
${timestamp_local}`).openPopup(); + return playerMarker } function getMap(){ diff --git a/app/templates/game_player.html b/app/templates/game_player.html index 1025887..401a636 100644 --- a/app/templates/game_player.html +++ b/app/templates/game_player.html @@ -9,6 +9,7 @@ {% endblock %} {% block app_content %} +

Player: {{ player.user.name }}


@@ -81,21 +82,94 @@ if (locations == null) { locations = [] } - - for (var i = 0; i < locations.length; i++) { - addPlayerMarker(map, locations[i]) + + var markers, lastMarker; + updateMarkers(); + var polyline; + updatePolyline(); + // zoom the map to the polyline + map.fitBounds(polyline.getBounds(), { + maxZoom : 13 + }); + + function updateMarkers(){ + if(markers != undefined){ + markers.forEach(function(marker){ + marker.remove() + }); + } + markers = [] + for (var i = 0; i < locations.length -1; i++) { + markers.push(addPlayerMarker(map, locations[i], bluePlayerIconMini)) + } + if(lastMarker != undefined){ + lastMarker.remove() + } + lastMarker = addPlayerMarker(map, locations[locations.length-1], bluePlayerIcon) } - if (locations.length > 0) { - var polyline = L.polyline(locations.map(l => [l.latitude, l.longitude]), { + function updatePolyline(){ + if(polyline != undefined){ + map.removeLayer(polyline) + } + if (locations.length == 0) { return } + polyline = L.polyline(locations.map(l => [l.latitude, l.longitude]), { color: 'blue', opacity: 0.6, }).addTo(map); + } + + // Poll Locations + var csrftoken = $('meta[name=csrf-token]').attr('content') + $.ajaxSetup({ + beforeSend: function(xhr, settings) { + if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) { + xhr.setRequestHeader("X-CSRFToken", csrftoken) + } + } + }) - // zoom the map to the polyline - map.fitBounds(polyline.getBounds(), { - maxZoom : 13 + function get_newest_date(locations){ + return new Date(Math.max.apply(null, locations.map(function(e) { + return new Date(e.timestamp_utc); + }))); + } + + setInterval(function() {pollLocations()}, 10 * 1000); + + function pollLocations() { + $.ajax({ + type: "POST", + url: "{{ url_for('main.poll_locations', game_name=player.game.name) }}", + data: JSON.stringify({ + + requested_users: ['{{ player.user.name }}'], + mode: 'accumulative', + last_update: moment(get_newest_date(locations)).format("YYYY-MM-DD HH:mm:ss:SSSS") + }), + contentType: "application/json; charset=utf-8", + dataType: 'json', + success: handleResponse, + error: function(error) { + console.log(error); + } + }); + } + + function handleResponse(data){ + data.filter(item => item.latitude && item.longitude && item.timestamp_utc && item.username) + .forEach(function (item, index) { + if (item.username == '{{ player.user.name }}' && new Date(item.timestamp_utc) > get_newest_date(locations) ){ + lastItem = locations[locations.length-1] + if (lastItem.latitude == item.latitude && lastItem.longitude == item.longitude){ + lastItem.timestamp_utc = item.timestamp_utc + } else{ + locations.push(item) + } + } }); + updatePolyline() + updateMarkers() } //Ajax for Generate Code button