def post(self): api_assert(self.body['data'], 400, 'empty data') try: ap_datas = [] router_mac = Mac.correct(self.body['apMac']) signal = Signal(band='2.4', channel=self.body['band']) created_at = Time(int(self.body['time'])) for item in self.body['data']: device_mac = Mac.correct(item['clientMac']) rssis = {} if item.get('rss1') is not None: rssis['1'] = RSSI(float(item['rss1'])) if item.get('rss2') is not None: rssis['2'] = RSSI(float(item['rss2'])) if item.get('rss3') is not None: rssis['3'] = RSSI(float(item['rss3'])) if len(rssis) > 0: ap_datas.append( APData(router_mac=router_mac, device_mac=device_mac, created_at=created_at, rssis=rssis, signal=signal)) for ap_data in ap_datas: self.ap_data_dao.save(ap_data) except DBException as e: raise APIError(500, e.message) return 'ok'
def put(self, id): job = waker().scheduler.get_job(id) if(not job): api_assert(False, log_message="no such alarm.") return mod_args = {} for key in ["sound_id", "level", "repeat"]: if(key in self.body): mod_args[key] = self.body[key] else: mod_args[key] = job.kwargs[key] mc = music_controller() if not mc.exist(mod_args["sound_id"]): api_assert(False, log_message="such sound_id is not exists.") return mod_day_of_week = get_dayofweek_list_from_job(job) if("day_of_week" in self.body): mod_day_of_week = self.body["day_of_week"] mod_day_of_week = ",".join(mod_day_of_week) d = job.next_run_time.time() if("time" in self.body): d = datetime.strptime(self.body["time"], TIME_FORMAT) mod_trigger = CronTrigger(hour=d.hour, minute=d.minute, second=d.second, day_of_week=mod_day_of_week) job.modify(kwargs=mod_args) job.reschedule(mod_trigger)
def post(self): """Decline or accept invite * `id`: ID of game you want to respond to invite for * `decision`: Either "Accept" or "Decline" """ attrs = dict(self.body) with db_session: game = GameEntity.get(id=attrs['id']) me = PlayerEntity[self.get_current_user()] api_assert( game is not None, 400, log_message="No such game {} exists!".format(attrs['id']) ) player_invites = get_player_invitations(me.username) api_assert( attrs['id'] in player_invites, 400, log_message="This game is not in your list of invitations!" ) if attrs['decision'] == "Accept": game.accepted_players.add(me) return "You successfully joined game {}!".format(attrs['id']) elif attrs['decision'] == "Decline": game.cancelled = True return "You declined and the game ({}) has been cancelled!".format( attrs['id'] )
def post(self): """Decline or accept invite * `id`: ID of game you want to respond to invite for * `decision`: Either "Accept" or "Decline" """ attrs = dict(self.body) with db_session: game = GameEntity.get(id=attrs['id']) me = PlayerEntity[self.get_current_user()] api_assert(game is not None, 400, log_message="No such game {} exists!".format( attrs['id'])) player_invites = get_player_invitations(me.username) api_assert( attrs['id'] in player_invites, 400, log_message="This game is not in your list of invitations!") if attrs['decision'] == "Accept": game.accepted_players.add(me) return "You successfully joined game {}!".format(attrs['id']) elif attrs['decision'] == "Decline": game.cancelled = True return "You declined and the game ({}) has been cancelled!".format( attrs['id'])
def wrap(self, id): mc = music_controller() if not mc.exist(id): api_assert(False, status_code=404, log_message="no such sound_id.") return func(self, id)
def delete(self): """DELETE to remove yourself from current game""" # Get player and the game_id he's in player_name = self.get_current_user() player = Player(self.db_conn, "name", player_name) game_id = player["current_game_id"] api_assert(game_id, 409, log_message="You are not currently in a game.") with DBLock(): # Get game game = Game(self.db_conn, "game_id", game_id) # Get remaining set of players rem_players = list(set(game["players"]) - {player_name}) # Set new gamemaster if not rem_players: game["gamemaster"] = None # If gamemaster is leaving, assign to a random player elif game["gamemaster"] == player_name: game["gamemaster"] = choice(rem_players) # Set remaining players in game and add players' balls to game's # unclaimed set game["players"] = rem_players game["unclaimed_balls"] += player["balls"] # Set the player's game_id to None and his list of balls to empty player["current_game_id"] = None player["balls"] = [] return {"game_id": game_id}
def join_room(db, room_name, password, player_name): """Join room ``room_name`` Updates ``current_players`` entry for room ``room_name`` with player ``player_name`` to the database. :raises APIError: If a room with ``room_name`` does not exist; or if the password is incorrect for room ``room_name``, or if player ``player_name`` does not exist """ # Ensure that the following preconditions are met: # * Correct password to room is give if required # * The player is not already in a room room = get_room(db, room_name) api_assert(password == room['password'], 403, log_message="Bad password for room `{}`.".format(room_name)) player = get_player(db, player_name) api_assert( player_name not in room["current_players"], 409, log_message="Player `{}` already in room `{}`".format( player_name, room_name) ) # If they are met, add the player to the room room["current_players"] += [player_name] player["current_room"] = room_name
def post(self): """ post new alarm schedule. returns job id. ### input * `time`: colon(":") splitted time. (i.e. 09:30:00) this value means when alarm go off. * `day_of_week`: list of "day of week". * `sound_id`: random 32byte hexadecimal string. you can get filename->sound_id correspondence from /api/musics/. if not set this value, waker may choose music randomly from same "level". * `level`: type of alarm. (i.e. "alarm","notify","warning") * `repeat`: number of repeat sound. 0 means "repeats indefinitely". ### output job id. """ d = datetime.strptime(self.body["time"], TIME_FORMAT) day_of_week = ",".join(self.body["day_of_week"]) sound_id = None if("sound_id" in self.body): sound_id = self.body["sound_id"] mc = music_controller() if not mc.exist(sound_id): api_assert(False, log_message="such sound_id is not exists.") return level = self.body["level"] repeat = self.body["repeat"] job = waker().scheduler.add_job(waker.alarm_go_off, "cron", hour=d.hour, minute=d.minute, second=d.second, day_of_week=day_of_week, kwargs={"repeat":repeat, "level":level, "sound_id":sound_id}) return job.id
def get(self): """ GET to receive list of players in current room * `players` array includes ALL players (including owner) * `owner` field is useful for highlighting the room owner in the UI """ player_name = self.get_current_user() # The reason we don't use get_player in a case like # this, is because this is an authenticated method, # so a player with the name of the current user # SHOULD exist; if he doesn't, then that indicates # a problem on our side (if that happens, the user # will see a 500 Internal Server Error) which # is arguably appropriate in this case. player = Player(self.db_conn, "name", player_name) room_name = player["current_room"] api_assert(room_name, 400, log_message="You are not currently in" " a room.") # Again, same thing here; if the player has joined # a room, the room name must exist, otherwise, it # indicates a failure somewhere earlier down the line. room = Room(self.db_conn, "name", room_name) return { "players": room["current_players"], "owner": room["owner"] }
def post(self): """ POST the required credentials to get back a cookie * `username`: Username * `password`: Password """ api_assert(self.body['username'], 400, log_message="Username field is empty!") with db_session: player = PlayerEntity.get(username=self.body['username']) api_assert(player is not None, 400, log_message="No such player {}".format( self.body['username'])) password = self.body['password'] # Check if the given password hashed with the player's known # salt matches the stored password password_match = bcrypt.hashpw( password.encode(), player.salt.encode()) == player.password.encode() if password_match: self.set_secure_cookie( "user", self.body['username'], self.settings['app_config'].session_timeout_days) return {"username": player.username} else: raise APIError(400, log_message="Bad username/password combo")
def get(self, id): job = waker().scheduler.get_job(id) if(not job): api_assert(False, log_message="no such rule.") return job_dict = make_cronobj_from_job(job) return job_dict
def post(self): """ (Game host only) POST to finalize game """ attrs = dict(self.body) with db_session: game = GameEntity.get(id=attrs['id']) api_assert(game is not None, 400, log_message="No such game {} exists!".format( attrs['id'])) api_assert(game.host.username == self.get_current_user(), 403, log_message="Only the host of this game may edit it!") api_assert(game.cancelled is not True, 400, "This game has been cancelled. :(") api_assert( len(game.accepted_players) == len( [p for t in game.teams for p in t.users]), 400, "Not everyone has accepted invites for this game yet!") team_names = [team.name for team in game.teams] team_a = TeamEntity[team_names[0]] team_b = TeamEntity[team_names[1]] # Make sure final_score has legit team names final_score = attrs['final_score'] for team_name, score in final_score.iteritems(): api_assert(team_name in team_names, 400, "{} is not part of this game!".format(team_name)) # Set wins, losses and ties if final_score[team_a.name] > final_score[team_b.name]: team_a.wins += 1 team_b.losses += 1 elif final_score[team_a.name] < final_score[team_b.name]: team_b.wins += 1 team_a.losses += 1 else: team_a.ties += 1 team_b.ties += 1 # Calculate new points_ratio for team1, team2 in [(team_a, team_b), (team_b, team_a)]: total_games = team1.wins + team1.losses + team1.ties team1.points_ratio = ( (team1.points_ratio * max(total_games, 0)) + (final_score[team1.name] / final_score[team2.name])) / total_games # Set final score game.final_score = json.dumps(final_score) return "Game results recorded with final score: {}".format( json.dumps(final_score))
def delete(self, id): """ delete scheduled alarm. """ job = waker().scheduler.get_job(id) if (not job): api_assert(False, log_message="no such alarm.") return job.remove()
def get(self): try: result = yield self.api_client.update_cache() except AssertionError as e: api_assert(False, 403, e.message) except Exception as e: self.error(e) else: self.success(result)
def post(self): """ POST the required parameters to register the pocketing/unpocketing of a ball * `ball`: The ball that was pocketed/unpocketed """ gamemaster = self.get_current_user() ball = self.body['ball'] game_id = get_player(self.db_conn, gamemaster)["current_game_id"] game = Game(self.db_conn, "game_id", game_id) res = {"game_id": game_id} # Authenticate api_assert( game["gamemaster"] == gamemaster, 401, log_message="You are not the gamemaster of the current game" ) # If ball is already sunk, retable it if ball not in get_balls_on_table(self.db_conn, game_id): game = Game(self.db_conn, "game_id", game_id) # Look for ball in game's set of unclaimed balls if ball in game["orig_unclaimed_balls"]: game["unclaimed_balls"] += [ball] # If it's not there, look for it from each player's set else: for pname in game["players"]: p = Player(self.db_conn, "name", pname) if ball in p["orig_balls"]: p["balls"] += [ball] break res['message'] = "Ball {} was retabled.".format(ball) return res # Otherwise, sink the ball # Look for it in players' first for pname in Game(self.db_conn, "game_id", game_id)["players"]: p = get_player(self.db_conn, pname) if ball in p["balls"]: p["balls"] = list(set(p["balls"]) - {ball}) break else: # Remove ball from unclaimed g = Game(self.db_conn, "game_id", game_id) uballs = list(game["unclaimed_balls"]) uballs.remove(ball) game["unclaimed_balls"] = uballs res["message"] = "Ball {} was sunk.".format(ball) return res
def get(self): from_city = self.get_argument('from_city', None) to_city = self.get_argument('to_city', None) direction = "{0} - {1}".format(from_city, to_city) try: result = yield self.api_client.get_flights_hash(direction) except AssertionError as e: api_assert(False, 403, e.message) except Exception as e: self.error(e) else: self.success(result)
def delete(self): """ DELETE to end the game; this endpoint should only be triggered if GameState indicates there is a winner. """ player_name = self.get_current_user() player = get_player(self.db_conn, player_name) game_id = player["current_game_id"] api_assert(game_id, 400, log_message="You are not currently in" " a game.") game = get_game(self.db_conn, game_id) with DBLock(): is_gamemaster = game["gamemaster"] == player_name gamemaster_has_closed = game["gamemaster"] is None winner_name = get_game_winner(self.db_conn, game_id) # If there is no current winner, return an error if not winner_name: raise APIError( 409, log_message="The game currently has no winner." ) # Otherwise, clean up everything else: # We want the gamemaster to FIRST to do the cleanup if is_gamemaster: # Record the win for the player who won winner = get_player(self.db_conn, winner_name) winner["games_won"] += [game_id] game["winner"] = winner_name # Remove all players from game for pname in game["players"]: p = get_player(self.db_conn, pname) p["balls"] = [] p["orig_balls"] = [] # Clear the game's "current" attributes game["players"] = [] game["gamemaster"] = None # Remove the gamemaster's current_game_id player["current_game_id"] = None # Then the remainder of the players can truly leave the game # so they have a chance to query who the winner was from GameState # before having their current_game_id set to None elif gamemaster_has_closed: player["current_game_id"] = None return "Game {} was completed; {} was the winner.".format( game_id, winner_name )
def post(self): """ POST the required parameters to register the pocketing/unpocketing of a ball * `ball`: The ball that was pocketed/unpocketed """ gamemaster = self.get_current_user() ball = self.body['ball'] game_id = get_player(self.db_conn, gamemaster)["current_game_id"] game = Game(self.db_conn, "game_id", game_id) res = {"game_id": game_id} # Authenticate api_assert( game["gamemaster"] == gamemaster, 401, log_message="You are not the gamemaster of the current game") # If ball is already sunk, retable it if ball not in get_balls_on_table(self.db_conn, game_id): game = Game(self.db_conn, "game_id", game_id) # Look for ball in game's set of unclaimed balls if ball in game["orig_unclaimed_balls"]: game["unclaimed_balls"] += [ball] # If it's not there, look for it from each player's set else: for pname in game["players"]: p = Player(self.db_conn, "name", pname) if ball in p["orig_balls"]: p["balls"] += [ball] break res['message'] = "Ball {} was retabled.".format(ball) return res # Otherwise, sink the ball # Look for it in players' first for pname in Game(self.db_conn, "game_id", game_id)["players"]: p = get_player(self.db_conn, pname) if ball in p["balls"]: p["balls"] = list(set(p["balls"]) - {ball}) break else: # Remove ball from unclaimed g = Game(self.db_conn, "game_id", game_id) uballs = list(game["unclaimed_balls"]) uballs.remove(ball) game["unclaimed_balls"] = uballs res["message"] = "Ball {} was sunk.".format(ball) return res
def post(self): """ Does matchmaking by finding a rival team for the provided `team_name`, creates a new game with the two teams and returns the game_id for that game """ team_name = self.body['team_name'] with db_session: myteam = TeamEntity.get(name=team_name) api_assert( PlayerEntity[self.get_current_user()] in myteam.users, 403, log_message="You can only matchmake for teams that you are" " a part of!" ) overall_rankings = get_team_rankings(myteam) myranking = overall_rankings[myteam.name] ranking_vals = overall_rankings.values() rankings_by_ranking = invert_dict_nonunique(overall_rankings) who_you_verse_index = None print(overall_rankings) while (who_you_verse_index not in ranking_vals): print(who_you_verse_index) from time import sleep sleep(0.4) if len(rankings_by_ranking) == 1: who_you_verse_index = myranking else: who_you_verse_index = choice( [rval for rval in ranking_vals if rval != myranking and abs(rval - myranking) <= 10] ) rival_team_name = rankings_by_ranking[who_you_verse_index][0] if rival_team_name == myteam.name: rival_team_name = rankings_by_ranking[who_you_verse_index][1] rival_team = TeamEntity[rival_team_name] game = GameEntity( teams=[myteam, rival_team], host=PlayerEntity[self.get_current_user()], accepted_players=[PlayerEntity[self.get_current_user()]] ) commit() return {"game_id": game.id}
def delete(self): """ DELETE to end the game; this endpoint should only be triggered if GameState indicates there is a winner. """ player_name = self.get_current_user() player = get_player(self.db_conn, player_name) game_id = player["current_game_id"] api_assert(game_id, 400, log_message="You are not currently in" " a game.") game = get_game(self.db_conn, game_id) with DBLock(): is_gamemaster = game["gamemaster"] == player_name gamemaster_has_closed = game["gamemaster"] is None winner_name = get_game_winner(self.db_conn, game_id) # If there is no current winner, return an error if not winner_name: raise APIError(409, log_message="The game currently has no winner.") # Otherwise, clean up everything else: # We want the gamemaster to FIRST to do the cleanup if is_gamemaster: # Record the win for the player who won winner = get_player(self.db_conn, winner_name) winner["games_won"] += [game_id] game["winner"] = winner_name # Remove all players from game for pname in game["players"]: p = get_player(self.db_conn, pname) p["balls"] = [] p["orig_balls"] = [] # Clear the game's "current" attributes game["players"] = [] game["gamemaster"] = None # Remove the gamemaster's current_game_id player["current_game_id"] = None # Then the remainder of the players can truly leave the game # so they have a chance to query who the winner was from GameState # before having their current_game_id set to None elif gamemaster_has_closed: player["current_game_id"] = None return "Game {} was completed; {} was the winner.".format( game_id, winner_name)
def get_team_rankings(myteam, filter_for_matchmaking=True): team_name = myteam.name sport_name = myteam.sport.name if myteam is None: raise APIError( 400, log_message="Team with name {} does not exist!" .format(team_name) ) ### Figure out rival team # Find teams that are of the same sport and also # that they don't contain any players from myteam sport_teams = select(team for team in TeamEntity if team.sport.name == sport_name)[:] print sport_teams, [[player.username for player in team.users] for team in sport_teams ] if filter_for_matchmaking: myteam_names = [player.username for player in myteam.users] print myteam_names sport_teams = [team for team in sport_teams if all( player.username not in myteam_names for player in team.users )] print sport_teams sport_teams.append(myteam) num_teams = len(sport_teams) api_assert( num_teams > 1, 409, "There are no other teams with all different people!" ) overall_rankings = defaultdict(lambda: 0) teams_wlratio = sorted([ (team, float(team.wins) / (team.losses or 1)) for team in sport_teams ], key=lambda t: t[1], reverse=True) for i, (team, wlratio) in enumerate(teams_wlratio): overall_rankings[team.name] += i teams_pointsratio = sorted([ (team, team.points_ratio) for team in sport_teams ], key=lambda t: t[1], reverse=True) for i, (team, points_ratio) in enumerate(teams_pointsratio): overall_rankings[team.name] += i return overall_rankings
def post(self): """ post new alarm schedule. returns job id. ### input * `time`: ISO8601 formatted string. this value means when alarm go off. if not set, alarm will go off immediately. * `sound_id`: random 32byte hexadecimal string. you can get filename->sound_id correspondence from /api/musics/. if not set this value, waker may choose music randomly from same "level". * `level`: type of alarm. (i.e. "alarm","notify","warning") * `repeat`: number of repeat sound. 0 means "repeats indefinitely". ### output job id. """ d = None if ("time" in self.body): d = iso8601.parse_date(self.body["time"]) if d < datetime.datetime.now(tz=TIMEZONE): api_assert(False, log_message="time must be later than present.") return sound_id = None if ("sound_id" in self.body): sound_id = self.body["sound_id"] mc = music_controller() if not mc.exist(sound_id): api_assert(False, log_message="such sound_id is not exists.") return level = self.body["level"] repeat = self.body["repeat"] job = None if (d): job = waker().scheduler.add_job(waker.alarm_go_off, "date", run_date=d, kwargs={ "repeat": repeat, "level": level, "sound_id": sound_id }) else: job = waker().scheduler.add_job(waker.alarm_go_off, kwargs={ "repeat": repeat, "level": level, "sound_id": sound_id }) return job.id
def put(self): """ PUT the required parameters to permanently register a new player * `username` * `first`: First name * `last`: Last name * `password`: Password for future logins * `gender`: M/F * `birthday`: e.g., "1993-05-16" * `city` * `country` * `bio` """ attrs = dict(self.body) # Set salt and password salt = bcrypt.gensalt(rounds=12) attrs['password'] = bcrypt.hashpw( password=attrs['password'].encode(), salt=salt ).decode() attrs['salt'] = salt.decode() # Create player with db_session: api_assert( attrs['username'], 400, log_message="Provided username is empty!" ) if PlayerEntity.get(username=attrs['username']): raise APIError( 409, log_message="Player with username {} already exists!" .format(attrs['username']) ) player = PlayerEntity(**attrs) # Log the user in self.set_secure_cookie( "user", attrs['username'], self.settings['app_config'].session_timeout_days ) return {"username": player.username}
def post(self): """ Does matchmaking by finding a rival team for the provided `team_name`, creates a new game with the two teams and returns the game_id for that game """ team_name = self.body['team_name'] with db_session: myteam = TeamEntity.get(name=team_name) api_assert( PlayerEntity[self.get_current_user()] in myteam.users, 403, log_message="You can only matchmake for teams that you are" " a part of!") overall_rankings = get_team_rankings(myteam) myranking = overall_rankings[myteam.name] ranking_vals = overall_rankings.values() rankings_by_ranking = invert_dict_nonunique(overall_rankings) who_you_verse_index = None print(overall_rankings) while (who_you_verse_index not in ranking_vals): print(who_you_verse_index) from time import sleep sleep(0.4) if len(rankings_by_ranking) == 1: who_you_verse_index = myranking else: who_you_verse_index = choice([ rval for rval in ranking_vals if rval != myranking and abs(rval - myranking) <= 10 ]) rival_team_name = rankings_by_ranking[who_you_verse_index][0] if rival_team_name == myteam.name: rival_team_name = rankings_by_ranking[who_you_verse_index][1] rival_team = TeamEntity[rival_team_name] game = GameEntity( teams=[myteam, rival_team], host=PlayerEntity[self.get_current_user()], accepted_players=[PlayerEntity[self.get_current_user()]]) commit() return {"game_id": game.id}
def get_team_rankings(myteam, filter_for_matchmaking=True): team_name = myteam.name sport_name = myteam.sport.name if myteam is None: raise APIError( 400, log_message="Team with name {} does not exist!".format(team_name)) ### Figure out rival team # Find teams that are of the same sport and also # that they don't contain any players from myteam sport_teams = select(team for team in TeamEntity if team.sport.name == sport_name)[:] print sport_teams, [[player.username for player in team.users] for team in sport_teams] if filter_for_matchmaking: myteam_names = [player.username for player in myteam.users] print myteam_names sport_teams = [ team for team in sport_teams if all( player.username not in myteam_names for player in team.users) ] print sport_teams sport_teams.append(myteam) num_teams = len(sport_teams) api_assert(num_teams > 1, 409, "There are no other teams with all different people!") overall_rankings = defaultdict(lambda: 0) teams_wlratio = sorted([(team, float(team.wins) / (team.losses or 1)) for team in sport_teams], key=lambda t: t[1], reverse=True) for i, (team, wlratio) in enumerate(teams_wlratio): overall_rankings[team.name] += i teams_pointsratio = sorted([(team, team.points_ratio) for team in sport_teams], key=lambda t: t[1], reverse=True) for i, (team, points_ratio) in enumerate(teams_pointsratio): overall_rankings[team.name] += i return overall_rankings
def get(self): """ GET to receive list of players in current game * `players` array includes ALL players (including gamemaster) * `gamemaster` field is useful for highlighting the gamemaster in the UI """ player_name = self.get_current_user() player = Player(self.db_conn, "name", player_name) game_id = player["current_game_id"] api_assert(game_id, 400, log_message="You are not currently in" " a game.") game = Game(self.db_conn, "game_id", game_id) return {"players": game["players"], "gamemaster": game["gamemaster"]}
def get(self, id): """ get scheduled alarm. * `time`: ISO8601 formatted string. this value means when alarm go off. * `id`: random 32byte hexadecimal string. * `sound_id`: random 32byte hexadecimal string. * `level`: type of alarm. (i.e. "alarm","notify","warning") * `repeat`: number of repeat sound. 0 means "repeats indefinitely". """ job = waker().scheduler.get_job(id) if (not job): api_assert(False, log_message="no such alarm.") return job_dict = {"id": job.id, "time": job.next_run_time.isoformat()} job_dict.update(job.kwargs) return job_dict
def get(self): """ GET to receive list of players in current game * `players` array includes ALL players (including gamemaster) * `gamemaster` field is useful for highlighting the gamemaster in the UI """ player_name = self.get_current_user() player = Player(self.db_conn, "name", player_name) game_id = player["current_game_id"] api_assert(game_id, 400, log_message="You are not currently in" " a game.") game = Game(self.db_conn, "game_id", game_id) return { "players": game["players"], "gamemaster": game["gamemaster"] }
def get(self): """ GET to receive state of current_game `winner`: Potential winner of the game, or empty string `balls_on_table`: Array of balls still on the table """ player_name = self.get_current_user() player = Player(self.db_conn, "name", player_name) game_id = player["current_game_id"] api_assert(game_id, 400, log_message="You are not currently in" " a game.") res = {} res["balls_on_table"] = get_balls_on_table(self.db_conn, game_id) res["winner"] = get_game_winner(self.db_conn, game_id) return res
def get(self, game_id): """GET game with game_id""" with db_session: game = GameEntity.get(id=game_id) api_assert(game is not None, 400, log_message="No such game {} exists!".format(game_id)) game_dict = game.to_dict(with_collections=True) if not game_dict["location"]: game_dict["location"] = "" if not game_dict["date"]: game_dict["date"] = "" if not game_dict["cancelled"]: game_dict["cancelled"] = False if not game_dict["final_score"]: game_dict["final_score"] = "" return game_dict
def put(self): """ PUT to create a team * `name` * `usernames`: list of players in team (INCLUDING YOURSELF!!) * `sport`: One of "Basketball" or "Soccer" """ attrs = dict(self.body) with db_session: if TeamEntity.get(name=attrs['name']): raise APIError( 409, log_message="Team with name {} already exists!" .format(attrs['name']) ) # Add team mates players = [] for pname in attrs["usernames"]: player = PlayerEntity.get(username=pname) api_assert( player is not None, 400, log_message="No player exists with name {}!".format(pname) ) players.append(player) # Get sport sport = SportEntity[attrs['sport']] # Create team team = TeamEntity( name=attrs['name'], users=players, sport=sport, wins=0, losses=0, ties=0, points_ratio=0.0 ) return {'name': team.name}
def assert_non_tenant(db, player_name): """Assert that player ``player_name`` is not already in a room :type player_name: str :raises APIError: If ``player_name`` is already in a room. """ player = get_player(db, player_name) player_room = player["current_room"] api_assert( not player_room, 409, log_message=( "{} is already in a room: `{}`. Leave current room" " to join a new one.".format( player_name, player_room ) ) )
def create_room(db, room_name, password, owner): """Create a new room ``room_name`` Adds entry for room ``room_name`` to the database. :raises APIError: If a room with `room_name` has already been created. """ api_assert(not db["rooms"].find_one(name=room_name), 409, log_message="Room with name `{}` already exists.".format( room_name)) db["rooms"].insert( { "name": room_name, "password": password, "owner": owner, "current_players": "" } )
def post(self): """(Game host only) Update date and location of game * `date`: Must be in YYYY-MM-DD format * `location` * `id`: ID of game to change """ attrs = dict(self.body) with db_session: game = GameEntity.get(id=attrs['id']) api_assert( game is not None, 400, log_message="No such game {} exists!".format(attrs['id']) ) api_assert( game.host.username == self.get_current_user(), 403, log_message="Only the host of this game may edit it!" ) try: validate_date_text(attrs['date']) except ValueError as err: raise APIError( 400, log_message=str(err) ) game.location = attrs['location'] game.date = attrs['date'] commit() game_dict = {k: v for k, v in game.to_dict().items() if k in [ "id", "date", "location" ]} game_dict["date"] = str(game_dict["date"]) return game_dict
def get(self, game_id): """GET game with game_id""" with db_session: game = GameEntity.get(id=game_id) api_assert( game is not None, 400, log_message="No such game {} exists!".format(game_id) ) game_dict = game.to_dict(with_collections=True) if not game_dict["location"]: game_dict["location"] = "" if not game_dict["date"]: game_dict["date"] = "" if not game_dict["cancelled"]: game_dict["cancelled"] = False if not game_dict["final_score"]: game_dict["final_score"] = "" return game_dict
def post(self): """ POST the required credentials to get back a cookie * `username`: Username * `password`: Password """ api_assert( self.body['username'], 400, log_message="Username field is empty!" ) with db_session: player = PlayerEntity.get(username=self.body['username']) api_assert( player is not None, 400, log_message="No such player {}".format(self.body['username']) ) password = self.body['password'] # Check if the given password hashed with the player's known # salt matches the stored password password_match = bcrypt.hashpw( password.encode(), player.salt.encode() ) == player.password.encode() if password_match: self.set_secure_cookie( "user", self.body['username'], self.settings['app_config'].session_timeout_days ) return {"username": player.username} else: raise APIError( 400, log_message="Bad username/password combo" )
def put(self): """ PUT to create a team * `name` * `usernames`: list of players in team (INCLUDING YOURSELF!!) * `sport`: One of "Basketball" or "Soccer" """ attrs = dict(self.body) with db_session: if TeamEntity.get(name=attrs['name']): raise APIError( 409, log_message="Team with name {} already exists!".format( attrs['name'])) # Add team mates players = [] for pname in attrs["usernames"]: player = PlayerEntity.get(username=pname) api_assert( player is not None, 400, log_message="No player exists with name {}!".format(pname)) players.append(player) # Get sport sport = SportEntity[attrs['sport']] # Create team team = TeamEntity(name=attrs['name'], users=players, sport=sport, wins=0, losses=0, ties=0, points_ratio=0.0) return {'name': team.name}
def put(self, id): """ modify alarm schedule. values that do not change are not necessary. ### input * `time`: ISO8601 formatted string. this value means when alarm go off. * `sound_id`: random 32byte hexadecimal string. * `level`: type of alarm. (i.e. "alarm","notify","warning") * `repeat`: number of repeat sound. 0 means "repeats indefinitely". """ job = waker().scheduler.get_job(id) if (not job): api_assert(False, log_message="no such alarm.") return mod_args = {} for key in ["sound_id", "level", "repeat"]: if (key in self.body): mod_args[key] = self.body[key] else: mod_args[key] = job.kwargs[key] mc = music_controller() if not mc.exist(mod_args["sound_id"]): api_assert(False, log_message="such sound_id is not exists.") return mod_time = job.next_run_time if ("time" in self.body): mod_time = iso8601.parse_date(self.body["time"]) if mod_time < datetime.datetime.now(tz=TIMEZONE): api_assert(False, log_message="time must be later than present.") return job.modify(next_run_time=mod_time, kwargs=mod_args)
def post(self): """(Game host only) Update date and location of game * `date`: Must be in YYYY-MM-DD format * `location` * `id`: ID of game to change """ attrs = dict(self.body) with db_session: game = GameEntity.get(id=attrs['id']) api_assert(game is not None, 400, log_message="No such game {} exists!".format( attrs['id'])) api_assert(game.host.username == self.get_current_user(), 403, log_message="Only the host of this game may edit it!") try: validate_date_text(attrs['date']) except ValueError as err: raise APIError(400, log_message=str(err)) game.location = attrs['location'] game.date = attrs['date'] commit() game_dict = { k: v for k, v in game.to_dict().items() if k in ["id", "date", "location"] } game_dict["date"] = str(game_dict["date"]) return game_dict
def post(self): """ POST the required parameters to permanently register a new player * `username`: Username of the player * `password`: Password for future logins """ player_name = self.body["username"] password = self.body["password"] with DBLock(): # Check if a player with the given name already exists player_exists = self.db_conn['players'].find_one(name=player_name) api_assert(not player_exists, 409, log_message="{} is already registered.".format(player_name)) # Create a new user/write to DB salt = bcrypt.gensalt(rounds=12) self.db_conn['players'].insert( { "name": player_name, "current_game_id": "", "current_room": "", "balls": "", "salt": salt, "password": bcrypt.hashpw(str(password), salt), "games_won": "" } ) # We also do the step of logging the player in after registration self.set_secure_cookie( "user", player_name, options.session_timeout_days ) return {"username": player_name}
def post(self): """ (Game host only) POST to finalize game """ attrs = dict(self.body) with db_session: game = GameEntity.get(id=attrs['id']) api_assert( game is not None, 400, log_message="No such game {} exists!".format(attrs['id']) ) api_assert( game.host.username == self.get_current_user(), 403, log_message="Only the host of this game may edit it!" ) api_assert( game.cancelled is not True, 400, "This game has been cancelled. :(" ) api_assert( len(game.accepted_players) == len([p for t in game.teams for p in t.users]), 400, "Not everyone has accepted invites for this game yet!" ) team_names = [team.name for team in game.teams] team_a = TeamEntity[team_names[0]] team_b = TeamEntity[team_names[1]] # Make sure final_score has legit team names final_score = attrs['final_score'] for team_name, score in final_score.iteritems(): api_assert( team_name in team_names, 400, "{} is not part of this game!".format(team_name) ) # Set wins, losses and ties if final_score[team_a.name] > final_score[team_b.name]: team_a.wins += 1 team_b.losses += 1 elif final_score[team_a.name] < final_score[team_b.name]: team_b.wins += 1 team_a.losses += 1 else: team_a.ties += 1 team_b.ties += 1 # Calculate new points_ratio for team1, team2 in [(team_a, team_b), (team_b, team_a)]: total_games = team1.wins + team1.losses + team1.ties team1.points_ratio = ( ( team1.points_ratio * max(total_games, 0) ) + ( final_score[team1.name] / final_score[team2.name] ) ) / total_games # Set final score game.final_score = json.dumps(final_score) return "Game results recorded with final score: {}".format( json.dumps(final_score) )
def test_api_assert(self): """Test exceptions.api_assert""" with pytest.raises(exceptions.APIError): exceptions.api_assert(False, 400) exceptions.api_assert(True, 400)
def post(self): """POST the required parameter to create a new game; only the owner of a room can make this request * `nbpp`: Number of balls per player """ game_id = uuid.uuid4().hex gamemaster = self.get_current_user() player = get_player(self.db_conn, gamemaster) room_name = player["current_room"] room = get_room(self.db_conn, room_name) api_assert(room["owner"] == gamemaster, 403, log_message="You must own a room to create a game.") player_names = room["current_players"] nplayers = len(player_names) nbpp = self.body["nbpp"] # Make sure values make sense api_assert(nplayers <= nbpp * nplayers <= TOTAL_NUM_BALLS, 400, log_message=("Your math seems to be a little off; " "please pick a `number of balls per player` " "such that each player has at least one ball " "and there are enough to go around for " "everyone.") ) # Create set of 15 balls, shuffle, and then match to players balls = generate_balls(TOTAL_NUM_BALLS) shuffle(balls) players = {} for i in xrange(nplayers): _balls = [] for i in xrange(nbpp): _balls.append(balls.pop()) pname = player_names.pop() players[pname] = _balls unclaimed_balls = balls[:] # Create game self.db_conn["games"].insert( { "game_id": game_id, "players": stringify_list(players.keys()), "orig_players": stringify_list(players.keys()), "unclaimed_balls": stringify_list(unclaimed_balls), "orig_unclaimed_balls": stringify_list(unclaimed_balls), "gamemaster": gamemaster, "winner": None } ) # Assign each player their set of balls, set game, leave room for name, balls in players.iteritems(): p = get_player(self.db_conn, name) p["current_game_id"] = game_id p["balls"] = balls p["orig_balls"] = balls p["current_room"] = None # The room can be deleted self.db_conn["rooms"].delete(name=room_name) return {"game_id": game_id}