def get_user_games(user_id): """ Returns all the user's active games """ if user_id is 0: raise RemoteException('User ID cannot be blank') try: User.objects.get(obfuscated_id=user_id) except User.DoesNotExist: raise RemoteException('User does not exist') games1 = Game.objects.filter(user_id1=user_id, active=True) games2 = Game.objects.filter(user_id2=user_id, active=True) result = [] for g in games1: game_friend_id = g.user_id2 result.append( _get_remote_game(user_id=user_id, friend_id=game_friend_id, game_model=g)) for g in games2: game_friend_id = g.user_id1 result.append( _get_remote_game(user_id=user_id, friend_id=game_friend_id, game_model=g)) return GamePacket(result)
def _set_friendship(user_id, friend_id, relation): try: User.objects.get(obfuscated_id=user_id) User.objects.get(obfuscated_id=friend_id) except User.DoesNotExist: raise RemoteException('Invalid user id.') if user_id == friend_id: raise RemoteException( 'Cannot set friendship between two identical users') # If friend_id has blocked user_id, return invalid user id friendship, _ = Friend.objects.get_or_create(user_id1=friend_id, user_id2=user_id) if friendship.relation == config.FRIEND_STATUS_BLOCKED: raise RemoteException('Invalid user id.') # If too many friends deny request if relation == config.FRIEND_STATUS_FRIEND: if _get_num_friends( user_id=user_id) >= config.FRIEND_LIMIT or _get_num_friends( user_id=friend_id) >= config.FRIEND_LIMIT: raise RemoteException('Too many friends') # Otherwise, do as normal for the reverse relation if relation == config.FRIEND_STATUS_BLOCKED: friendship.relation = config.FRIEND_STATUS_REMOVED else: friendship.relation = relation friendship.save() # If removing or blocking friend and there is an active game, end the game if relation == config.FRIEND_STATUS_BLOCKED or relation == config.FRIEND_STATUS_REMOVED: existing_game = None try: existing_game = game.get_game_status(user_id=user_id, friend_id=friend_id) except RemoteException: pass if existing_game is not None and existing_game.active: game.end_game(user_id=user_id, game_id=existing_game.game_id, award_stars=False) # This relation is a straightforward assignment friendship, _ = Friend.objects.get_or_create(user_id1=user_id, user_id2=friend_id) friendship.relation = relation friendship.save() return SuccessPacket()
def _get_friend_id(user_model, game_model): user_id = user_model.obfuscated_id if (user_id == game_model.user_id1): return game_model.user_id2 elif (user_id == game_model.user_id2): return game_model.user_id1 else: raise RemoteException('Not a player in this game.')
def send_picture(user_id, game_id, photo): """ Marks a picture as sent """ if user_id is 0 or game_id is 0: raise RemoteException('User ID and game ID cannot be blank.') if photo is None: raise RemoteException('You have to send a photo') try: user = User.objects.get(obfuscated_id=user_id) except User.DoesNotExist: raise RemoteException('User does not exist') game = None try: game = Game.objects.get(id=game_id) except Game.DoesNotExist: raise RemoteException('Game does not exist') if game is None or game.active is False: raise RemoteException('Game is inactive') if _get_curr_photographer(game) != int(user_id): raise RemoteException('Not your turn to send a picture') friend_id = _get_friend_id(user_model=user, game_model=game) if (friend_id is None): raise RemoteException('User ID and game ID combination not valid') round_num = game.curr_round try: turn = Turn.objects.get(turn_num=round_num, game_id=game_id) turn.picture_added = True turn.save() file_photo = _decode_64_to_file(photo) # save photo with open( config.FILE_PATH + ('%s_%s.jpg' % (str(game_id), str(round_num))), 'wb+') as dest: for chunk in file_photo.chunks(): dest.write(chunk) return _get_remote_game(user_id=user_id, friend_id=friend_id, game_model=game) except Turn.DoesNotExist: raise RemoteException('Invalid turn')
def decrement_bank(user_id): """ Deducts a single star from the bank account of the user with the given user_id """ try: bank = Bank.objects.get(user_id=user_id) if bank.stars - 1 < 0: raise RemoteException( "Unable to deduct star from user: not enough balance") bank.stars -= 1 bank.save() return RemoteBank(stars=bank.stars) except Bank.DoesNotExist: raise RemoteException('Unable to find user bank account')
def update(user_id): """ Returns all the user's friends and games associated with friends """ if user_id is None: raise RemoteException('User ID cannot be blank.') try: user = User.objects.get(obfuscated_id=user_id) except User.DoesNotExist: raise RemoteException("User doesn't exist") friends = friend.get_user_friends(user_id=user_id).friends bank_account = bank.get_user_bank(user_id=user_id) score_account = score.get_user_score(user_id=user_id) return Poll(friends=friends, bank_account=bank_account, score=score_account)
def find_user(user_id, username): if username is None or username == '': raise RemoteException('Username cannot be blank.') user = None try: user = User.objects.get(obfuscated_id=user_id) except User.DoesNotExist: raise RemoteException('Username password combination not valid.') found_user = None try: found_user = User.objects.get(name=username) except User.DoesNotExist: raise RemoteException('User does not exist.') return friend.get_friend_details(user_id=user_id, friend_id=found_user.obfuscated_id)
def give_up_turn(user_id, game_id): """ Give up on the current turn """ game = None curr_time = timezone.now() if user_id is 0 or game_id is 0: raise RemoteException('User ID and game ID cannot be blank.') try: user = User.objects.get(obfuscated_id=user_id) except User.DoesNotExist: raise RemoteException('User does not exist') try: game = Game.objects.get(id=game_id) except Game.DoesNotExist: raise RemoteException('Game does not exist') try: current_turn = Turn.objects.get(turn_num=game.curr_round, game_id=game_id) except Turn.DoesNotExist: raise RemoteException('Turn does not exist') if not current_turn.picture_seen: raise RemoteException('Have not seen picture yet.') friend_id = _get_friend_id(user_model=user, game_model=game) if (friend_id is None): raise RemoteException('User ID and game ID combination not valid') is_photographer = int(user_id) == _get_curr_photographer(game) if is_photographer: raise RemoteException('Not your turn to guess') current_turn.guessed = True current_turn.guessed_correctly = False current_turn.save() # Don't add points since users don't get points round_num = game.curr_round filename = config.FILE_PATH + ('%s_%s.jpg' % (str(game_id), str(round_num))) if os.path.isfile(filename): os.remove(filename) if round_num == game.max_rounds: return end_game(user_id, game_id) else: return _start_new_round(user_id=user_id, game_id=game_id)
def request_hint(user_id, word): if word is None: raise RemoteException("Word cannot be blank") try: word_prompt = WordPrompt.objects.get(word=word) except WordPrompt.DoesNotExist: raise RemoteException("Word not in database") try: user = User.objects.get(obfuscated_id=user_id) except User.DoesNotExist: raise RemoteException("User does not exist") word_class = word_prompt.word_class word_category = word_prompt.category bank_account = bank.decrement_bank(user_id=user_id) return RemoteWord(word=word, word_class=word_class, word_category=word_category, bank_account=bank_account)
def add_to_score(user_id, points): """ Adds the specified number of points to the score of the user with the given user_id THIS FUNCTION SHOULD NEVER BE EXPOSED IN URLS AND VIEWS. WE DO NOT WANT USERS TO BE ABLE TO ADD IN GAME CURRENCY VIA AN API CALL """ if points < 0: raise RemoteException('Unable to add negative score') try: score = Score.objects.get(user_id=user_id) score.points += points score.save() return RemoteScore(points=score.points) except Score.DoesNotExist: raise RemoteException('Unable to find user score')
def _get_words_played(game_id): turns = Turn.objects.filter(game_id=game_id) words_played = [] for t in turns: word_id = t.word_prompt_id try: word = WordPrompt.objects.get(id=word_id).word except WordPrompt.DoesNotExist: raise RemoteException('Error: unable to find words') words_played.append(word) return words_played
def get_game_status(user_id, friend_id): """ Returns the most recent game """ if user_id is 0 or friend_id is 0: raise RemoteException('User ID and Friend ID cannot be blank') try: User.objects.get(obfuscated_id=user_id) User.objects.get(obfuscated_id=friend_id) except User.DoesNotExist: raise RemoteException('User does not exist') games1 = Game.objects.filter(user_id1=user_id, user_id2=friend_id).order_by('-id') games2 = Game.objects.filter(user_id2=user_id, user_id1=friend_id).order_by('-id') most_recent_game = None if len(games1) > 0: game_1 = games1[0] if len(games2) > 0: game_2 = games2[0] # Both are not empty, choose bigger id if game_1.id > game_2.id: most_recent_game = game_1 else: most_recent_game = game_2 else: most_recent_game = game_1 elif len(games2) > 0: most_recent_game = games2[0] if most_recent_game is None: raise RemoteException('No game between the users') return _get_remote_game(user_id=user_id, friend_id=friend_id, game_model=most_recent_game)
def add_to_bank(user_id, stars): """ Adds the specified number of stars to the bank account of the user with the given user_id THIS FUNCTION SHOULD NEVER BE EXPOSED IN URLS AND VIEWS. WE DO NOT WANT USERS TO BE ABLE TO ADD IN GAME CURRENCY VIA AN API CALL """ try: bank = Bank.objects.get(user_id=user_id) if bank.stars + stars < 0: raise RemoteException( 'Unable to subtract %d stars: not enough balance.' % (-1 * stars)) bank.stars += stars bank.save() return RemoteBank(stars=bank.stars) except Bank.DoesNotExist: raise RemoteException('Unable to find user bank account')
def get_friend_details(user_id, friend_id): friend_user = User.objects.get(obfuscated_id=friend_id) game_obj = None try: game_obj = game.get_game_status(user_id=user_id, friend_id=friend_id) except RemoteException: pass friend_status = get_friend_status(user_id1=user_id, user_id2=friend_id) reverse_status = get_friend_status(user_id1=friend_id, user_id2=user_id) if reverse_status == config.FRIEND_STATUS_BLOCKED and friend_status == config.FRIEND_STATUS_REMOVED: raise RemoteException('Cannot find user.') if game_obj is None: return FriendUser(user_id=friend_id, username=friend_user.name, relation=friend_status, has_active_game=False) elif game_obj.active: return FriendUser(user_id=friend_id, username=friend_user.name, relation=friend_status, has_active_game=True, is_turn=game_obj.is_turn, is_photographer=game_obj.is_photographer, user_score=game_obj.user_score, friend_score=game_obj.friend_score) else: return FriendUser(user_id=friend_id, username=friend_user.name, relation=friend_status, has_active_game=False, user_score=game_obj.user_score, friend_score=game_obj.friend_score)
def start_new_game(user_id, friend_id): """ Create a new Game """ if user_id is 0 or friend_id is 0: raise RemoteException('User ID and friend ID cannot be blank.') if user_id == friend_id: raise RemoteException('Cannot start a game with yourself.') game = None try: user = User.objects.get(obfuscated_id=user_id) except User.DoesNotExist: raise RemoteException('User 1 does not exist') try: User.objects.get(obfuscated_id=friend_id) except User.DoesNotExist: raise RemoteException('User 2 does not exist') if (friend.get_friend_status(user_id1=user_id, user_id2=friend_id) != config.FRIEND_STATUS_FRIEND or friend.get_friend_status(user_id1=friend_id, user_id2=user_id) != config.FRIEND_STATUS_FRIEND): raise RemoteException( 'Unable to start a game with someone who is not a friend') if (_is_active_game(user_id1=user_id, user_id2=friend_id)): raise RemoteException('Game already exists') game = Game.objects.create(user_id1=user_id, user_id2=friend_id, active=True, curr_round=0, max_rounds=config.MAX_ROUNDS) game_id = game.id return _start_new_round(user_id=user_id, game_id=game_id)
def _get_remote_game(user_id, friend_id, game_model): curr_round = game_model.curr_round game_id = game_model.id active = game_model.active words_seen = [] curr_word = None words_played = _get_words_played(game_id=game_model.id) if (len(words_played) > 0): words_seen = words_played if active: curr_word = words_played[-1] is_photographer = None is_turn = None elapsed_time = None current_score = None friend_score = 0 user_score = 0 if game_model.user_id1 == user_id: user_score = game_model.user1_score friend_score = game_model.user2_score else: user_score = game_model.user2_score friend_score = game_model.user1_score if active: try: current_turn = Turn.objects.get(turn_num=game_model.curr_round, game_id=game_model.id) is_photographer = ( _get_curr_photographer(game_model) == int(user_id)) is_turn = (is_photographer != current_turn.picture_added) if is_turn and not is_photographer and current_turn.picture_seen: # Guessing elapsed_time = timezone.now() - current_turn.picture_seen_date current_score = _calculate_score(elapsed_time) elapsed_time = elapsed_time.seconds except Turn.DoesNotExist: raise RemoteException('Turn does not exist') return RemoteGame(game_id=game_id, user_id=user_id, friend_id=friend_id, active=active, curr_round=curr_round, user_score=user_score, friend_score=friend_score, words_seen=words_seen, curr_word=curr_word, is_photographer=is_photographer, is_turn=is_turn, current_score=current_score, elapsed_time=elapsed_time)
def _start_new_round(user_id, game_id): """ Starts a new round by giving the user a new word prompt Only the photographer can start a new round """ try: user = User.objects.get(obfuscated_id=user_id) except User.DoesNotExist: raise RemoteException('User does not exist') try: game = Game.objects.get(id=game_id) except Game.DoesNotExist: raise RemoteException('Game does not exist') friend_id = _get_friend_id(user_model=user, game_model=game) if friend_id is None: raise RemoteException('User ID and game ID combination not valid') if not game.active: raise RemoteException('Game is inactive') if game.curr_round >= game.max_rounds: raise RemoteException('Max number of rounds reached') # Check if user is photographer of PREVIOUS round if int(user_id) == _get_curr_photographer(game_model=game): raise RemoteException('This user cannot start a new round') words_seen = _get_words_played(game_id=game_id) new_word = _get_random_word() while (new_word.word in words_seen): new_word = _get_random_word() # Add the new word into the list words_seen.append(new_word.word) round_num = game.curr_round + 1 Turn.objects.create(turn_num=round_num, game_id=game_id, word_prompt_id=new_word.id) game.curr_round = round_num game.save() friend_score = 0 user_score = 0 if game.user_id1 == user_id: user_score = game.user1_score friend_score = game.user2_score else: user_score = game.user2_score friend_score = game.user1_score return RemoteGame(game_id=game_id, user_id=user_id, friend_id=friend_id, active=True, curr_round=round_num, user_score=user_score, friend_score=friend_score, words_seen=words_seen, curr_word=new_word.word, is_photographer=True, is_turn=True)
def get_new_word(user_id, game_id): """ Get new word prompt """ try: user = User.objects.get(obfuscated_id=user_id) except User.DoesNotExist: raise RemoteException('User does not exist') try: game = Game.objects.get(id=game_id) except Game.DoesNotExist: raise RemoteException('Game does not exist') friend_id = _get_friend_id(user_model=user, game_model=game) if friend_id is None: raise RemoteException('User ID and game ID combination not valid') if not game.active: raise RemoteException('Game is inactive') # Remove a star bank.decrement_bank(user_id=user_id) if int(user_id) != _get_curr_photographer(game_model=game): raise RemoteException('This user cannot get a new word prompt') words_seen = _get_words_played(game_id=game_id) new_word = _get_random_word() while (new_word.word in words_seen): new_word = _get_random_word() round_num = game.curr_round turn = Turn.objects.get(turn_num=round_num, game_id=game_id) old_word = WordPrompt.objects.get(id=turn.word_prompt_id).word words_seen.remove(old_word) turn.word_prompt_id = new_word.id turn.save() words_seen.append(new_word.word) friend_score = 0 user_score = 0 if game.user_id1 == user_id: user_score = game.user1_score friend_score = game.user2_score else: user_score = game.user2_score friend_score = game.user1_score return RemoteGame(game_id=game_id, user_id=user_id, friend_id=friend_id, active=True, curr_round=round_num, user_score=user_score, friend_score=friend_score, words_seen=words_seen, curr_word=new_word.word, is_photographer=True, is_turn=True, bank_account=bank.get_user_bank(user_id=user_id))
def validate_guess(user_id, game_id, guess, score): """ Checks if guess is correct. """ game = None curr_time = timezone.now() if user_id is 0 or game_id is 0: raise RemoteException('User ID and game ID cannot be blank.') try: user = User.objects.get(obfuscated_id=user_id) except User.DoesNotExist: raise RemoteException('User does not exist') try: game = Game.objects.get(id=game_id) except Game.DoesNotExist: raise RemoteException('Game does not exist') try: current_turn = Turn.objects.get(turn_num=game.curr_round, game_id=game_id) except Turn.DoesNotExist: raise RemoteException('Turn does not exist') if not current_turn.picture_seen: raise RemoteException('Have not seen picture yet.') friend_id = _get_friend_id(user_model=user, game_model=game) if (friend_id is None): raise RemoteException('User ID and game ID combination not valid') is_photographer = int(user_id) == _get_curr_photographer(game) if is_photographer: raise RemoteException('Not your turn to guess') current_word_id = current_turn.word_prompt_id try: current_word = WordPrompt.objects.get(id=current_word_id).word except WordPrompt.DoesNotExist: raise RemoteException('Word does not exist.') if (guess.strip().lower() == current_word.lower()): current_turn.guessed = True current_turn.guessed_correctly = True current_turn.save() # Add points to users elapsed_time = curr_time - current_turn.picture_seen_date guesser_score = _calculate_score(elapsed_time) score = int(score) if score - guesser_score < config.SCORE_DELTA: guesser_score = score ## OTHERWISE, we need to somehow warn user? sender_score = config.SCORE_SENDING if user_id == game.user_id1: game.user1_score += guesser_score game.user2_score += sender_score else: game.user2_score += guesser_score game.user1_score += sender_score game.save() round_num = game.curr_round filename = config.FILE_PATH + ('%s_%s.jpg' % (str(game_id), str(round_num))) if os.path.isfile(filename): os.remove(filename) if round_num == game.max_rounds: return end_game(user_id, game_id) else: return _start_new_round(user_id=user_id, game_id=game_id) else: raise RemoteException('Guess is incorrect')
def end_game(user_id, game_id, award_stars=True): """ Ends a pre-existing game by setting it to inactive """ if user_id is 0 or game_id is 0: raise RemoteException('User ID and game ID cannot be blank.') try: user = User.objects.get(obfuscated_id=user_id) except User.DoesNotExist: raise RemoteException('User does not exist') try: game = Game.objects.get(id=game_id) except Game.DoesNotExist: raise RemoteException('Game does not exist') if (game.active == False): raise RemoteException('Game is already inactive') friend_id = _get_friend_id(user_model=user, game_model=game) if (friend_id is None): raise RemoteException('User ID and game ID combination not valid') words_seen = _get_words_played(game_id=game_id) game.active = False game.save() # Remove picture if exists filename = config.FILE_PATH + ('%s_%s.jpg' % (str(game_id), str(game.curr_round))) if os.path.isfile(filename): os.remove(filename) if award_stars: # Award stars to users user1_stars = int(game.user1_score / config.SCORE_PER_STAR) user2_stars = int(game.user2_score / config.SCORE_PER_STAR) if game.user1_score > game.user2_score: user1_stars += config.WINNER_BONUS_STAR elif game.user1_score < game.user2_score: user2_stars += config.WINNER_BONUS_STAR else: user1_stars += config.TIE_BONUS_STAR user2_stars += config.TIE_BONUS_STAR bank.add_to_bank(user_id=game.user_id1, stars=user1_stars) bank.add_to_bank(user_id=game.user_id2, stars=user2_stars) # Award points to users also score.add_to_score(user_id=game.user_id1, points=game.user1_score) score.add_to_score(user_id=game.user_id2, points=game.user2_score) friend_score = 0 user_score = 0 if game.user_id1 == user_id: user_score = game.user1_score friend_score = game.user2_score else: user_score = game.user2_score friend_score = game.user1_score return RemoteGame(game_id=game_id, user_id=user_id, friend_id=friend_id, active=False, user_score=user_score, friend_score=friend_score, curr_round=game.curr_round, words_seen=words_seen, bank_account=bank.get_user_bank(user_id=user_id))
def get_picture(user_id, game_id): """ Gets a picture for the specified user_id and game_id """ if user_id is 0 or game_id is 0: raise RemoteException('User ID and game ID cannot be blank.') try: user = User.objects.get(obfuscated_id=user_id) except User.DoesNotExist: raise RemoteException('User does not exist') game = None try: game = Game.objects.get(id=game_id) except Game.DoesNotExist: raise RemoteException('Game does not exist') if game is None or game.active is False: raise RemoteException('Game is inactive') friend_id = _get_friend_id(user_model=user, game_model=game) if (friend_id is None): raise RemoteException('User ID and game ID combination not valid') is_photographer = int(user_id) == _get_curr_photographer(game) if is_photographer: raise RemoteException('Not your turn to get the picture') round_num = game.curr_round try: turn = Turn.objects.get(turn_num=round_num, game_id=game_id) if not turn.picture_added: raise RemoteException('Picture not sent yet') curr_time = timezone.now() if not turn.picture_seen: turn.picture_seen = True turn.picture_seen_date = curr_time turn.save() current_score = _calculate_score(curr_time - turn.picture_seen_date) filename = config.FILE_PATH + ('%s_%s.jpg' % (str(game_id), str(round_num))) if os.path.isfile(filename): with open(filename, 'rb') as f: return RemoteImage(dataURL=_encode_file_to_64(f), current_score=current_score) else: raise RemoteException('Cannot find image') except Turn.DoesNotExist: raise RemoteException('Invalid turn')