diff --git a/app/main/routes.py b/app/main/routes.py index b9328b0..390869b 100644 --- a/app/main/routes.py +++ b/app/main/routes.py @@ -73,8 +73,9 @@ def game_dashboard(game_name): return render_template('game_bunny_dashboard.html', title='Game Dashboard', game=game, json=json, location_encoder=LocationEncoder) if role == Role.hunter: + hunter_delay = current_app.config['HUNTER_LOCATION_DELAY'] return render_template('game_hunter_dashboard.html', title='Game Dashboard', game=game, - json=json, location_encoder=LocationEncoder) + hunter_delay=hunter_delay, json=json, location_encoder=LocationEncoder) if role == Role.none: return render_template('game_hunter_dashboard.html', title='Game Dashboard', game=game, json=json, location_encoder=LocationEncoder) diff --git a/app/models/game.py b/app/models/game.py index 9324b16..6189739 100644 --- a/app/models/game.py +++ b/app/models/game.py @@ -29,16 +29,23 @@ class Game(db.Model): backref=db.backref('game', lazy='joined'), cascade="save-update, merge, delete, delete-orphan") - def last_player_locations(self): + def last_player_locations(self, offset): # pylint: disable=not-an-iterable - return [player.last_location() for player in self.players if player.user.locations] + return [player.last_location(offset=offset) for player in self.players if player.user.locations] - def last_locations(self, players): + def last_locations(self, players, offset=None): + ''' + Returns last locations for given players within time boundaries of game + + Parameters: + players (Player or User list): players for whom the last location is returned + offset (int): Offset in minutes. Only locations older than this amount of minutes will be returned. + ''' locations = [] for player in players: if isinstance(player, GamePlayer): player = player.user - location = player.last_location(self) + location = player.last_location(self, offset=offset) if location: locations.append(location) return locations diff --git a/app/models/game_player.py b/app/models/game_player.py index f156c85..7fb1c9c 100644 --- a/app/models/game_player.py +++ b/app/models/game_player.py @@ -37,9 +37,15 @@ class GamePlayer(db.Model): caught_by_players = association_proxy('player_caught_by_players', 'catching_player') caught_players = association_proxy('player_caught_players', 'caught_player') - def last_location(self): + def last_location(self, offset=None): + ''' + Returns game_player's last recorded location within game start- and end time. + + Parameters: + offset (int): Offset in minutes. Only locations older than this amount of minutes will be returned. + ''' # pylint: disable=no-member - return self.user.last_location(self.game) + return self.user.last_location(self.game, offset) def locations_during_game(self): # pylint: disable=no-member diff --git a/app/models/user.py b/app/models/user.py index b679d82..468cb31 100644 --- a/app/models/user.py +++ b/app/models/user.py @@ -1,5 +1,5 @@ from secrets import token_hex -from datetime import datetime +from datetime import datetime, timedelta from flask_login import UserMixin from sqlalchemy.ext.associationproxy import association_proxy @@ -42,23 +42,47 @@ class User(UserMixin, db.Model): return False return check_password_hash(self.password_hash, password) - def locations_during_game(self, game): + def locations_during_game(self, game, offset=0): + ''' + Returns users locations during game. + + Parameters: + game (Game): If specified, only locations within start- and endtime of game wil be returned + offset (int): Offset in minutes. Only locations older than this amount of minutes will be returned. + ''' # pylint: disable=not-an-iterable if not self.locations: return None if game is None: - return self.locations + if offset == 0: + return self.locations + return [location for location in self.locations + if datetime.utcnow() - location.timestamp > timedelta(minutes=offset)] game_start = game.start_time or datetime.min game_end = game.end_time or datetime.max - return [location for location in self.locations if location.timestamp > game_start and location.timestamp < game_end] - - def last_location(self, game=None): + if offset == 0: + return [location for location in self.locations + if location.timestamp > game_start and location.timestamp < game_end] + return [location for location in self.locations + if location.timestamp > game_start and location.timestamp < game_end + and datetime.utcnow() - location.timestamp > timedelta(minutes=offset)] + + def last_location(self, game=None, offset=0): + ''' + Returns users last recorded location. + + Parameters: + game (Game): If specified, only locations within start- and endtime of game wil be returned + offset (int): Offset in minutes. Only locations older than this amount of minutes will be returned. + ''' # pylint: disable=[not-an-iterable, unsubscriptable-object] if not self.locations: return None if game is None: - return self.locations[-1] - locations_during_game = self.locations_during_game(game) + locations = self.locations_during_game(game=None, offset=offset) + return locations[-1] if locations else None + + locations_during_game = self.locations_during_game(game, offset=offset) return locations_during_game[-1] if locations_during_game else None def role_in_game(self, game): diff --git a/app/templates/game_hunter_dashboard.html b/app/templates/game_hunter_dashboard.html index 369c183..e3f27c2 100644 --- a/app/templates/game_hunter_dashboard.html +++ b/app/templates/game_hunter_dashboard.html @@ -32,7 +32,7 @@ {{ bunny.player_caught_by_players | selectattr('catching_player', '==', player) | selectattr('review.name', '==', 'denied') |list|length}} / {{ bunny.player_caught_by_players | selectattr('catching_player', '==', player) | selectattr('review.name', '==', 'none') |list|length}} - {% with location = bunny.last_location() %} + {% with location = bunny.last_location(offset=hunter_delay) %} {% if location %}{{ moment(location.timestamp).fromNow()}}: {% endif %} {{ location }} {% endwith %} @@ -81,7 +81,7 @@ attribution: 'Kaartgegevens © Kadaster' }).addTo( map ); - var bunnies = JSON.parse('{{ json.dumps(game.last_locations(game.bunnies()), cls=location_encoder)|safe }}') + var bunnies = JSON.parse('{{ json.dumps(game.last_locations(game.bunnies(), offset=hunter_delay), cls=location_encoder)|safe }}') for (var i = 0; i < bunnies.length; i++){ addPlayerMarker(map, bunnies[i]) } diff --git a/config.py b/config.py index a08246b..83563ca 100644 --- a/config.py +++ b/config.py @@ -23,4 +23,6 @@ class Config(object): UPLOAD_FOLDER = os.environ.get('UPLOAD_FOLDER') or 'uploads' PLAYER_CAUGHT_PLAYER_PHOTO_DIR_NAME = os.environ.get('PLAYER_CAUGHT_PLAYER_PHOTO_DIR_NAME') or 'caught_bunny_photos' - ALLOWED_PHOTO_EXTENSIONS = os.environ.get('ALLOWED_EXTENSIONS') or {'png', 'jpg', 'jpeg', 'gif', 'tiff', 'heif', 'heic'} \ No newline at end of file + ALLOWED_PHOTO_EXTENSIONS = os.environ.get('ALLOWED_EXTENSIONS') or {'png', 'jpg', 'jpeg', 'gif', 'tiff', 'heif', 'heic'} + + HUNTER_LOCATION_DELAY = os.environ.get('HUNTER_LOCATION_DELAY') or 5