async def post_game(self, args: typing.Dict): logger.debug(f'post_game {args}') request = valid_request_from_dict(AddGameRequest, args) players = [] for nick in request.nicks_won + request.nicks_lost: player = await self._search.load_player_by_nick(nick) if player is None: raise RuntimeError(f'Player not found: {nick}') if player.get_rating() is None: raise RuntimeError(f'Player rating is not set') players.append(player) game = Game(nicks_won=request.nicks_won, nicks_lost=request.nicks_lost, score_won=request.score_won, score_lost=request.score_lost, date=datetime.now()) for player in players: game.set_rating_before(player.nick, player.get_rating()) self.ranking.calculate(game) await self._manage.save_game(game) for player in players: player.set_rating(game.rating_after(player.nick)) await self._manage.save_player(player) return game.as_dict()
async def load_game_by_id(game_id): g = Game(game_id=game_id) res = await db.execute(Search.sql_load_game_by_id(game_id)) if len(res) == 0: raise RuntimeError(f'Could not find game game_id({g.id})') res = res[0] g.date = datetime.datetime.strptime(res[0], '%Y-%m-%d %H:%M:%S') g.score_won = res[1] g.score_lost = res[2] res = await db.execute(Search.sql_load_game_players_by_id(game_id)) for record in res: won = record[1] p = await Search.load_player_by_id(player_id=record[0]) if won: g.nicks_won.append(p.nick) else: g.nicks_lost.append(p.nick) res = await db.execute( Search.sql_rating_change(g.id, p.id, ratingSystem)) if len(res) == 0: raise RuntimeError( f'Could not find rating change for game_id({g.id}), player_id({p.id})' ) g.set_rating_before(p.nick, Rating(value=res[0][0], accuracy=res[0][2])) g.set_rating_after(p.nick, Rating(value=res[0][1], accuracy=res[0][3])) logger.debug(f'load_game_by_id {g}') return g
async def test_all(): g = None team_won = [] team_lost = [] try: # create players for i in range(0, 4): p = Player(nick=f'NewPlayer{i}') p.set_rating(Rating(value=1200, accuracy=0)) await Manage.delete_player(p) await Manage.save_player(p) if i < 2: team_won.append(p) else: team_lost.append(p) g = Game(date=datetime.now(), nicks_won=[p.nick for p in team_won], nicks_lost=[p.nick for p in team_lost], score_won=23, score_lost=21) for p in team_won: g.set_rating_before(p.nick, p.get_rating()) p.set_rating(Rating(value=1300, accuracy=0)) g.set_rating_after(p.nick, p.get_rating()) for p in team_lost: g.set_rating_before(p.nick, p.get_rating()) p.set_rating(Rating(value=1100, accuracy=0)) g.set_rating_after(p.nick, p.get_rating()) await Manage.save_game(g) test_g = await Search.load_game_by_id(g.id) assert test_g.nicks_won == ['NewPlayer0', 'NewPlayer1'] assert test_g.nicks_lost == ['NewPlayer2', 'NewPlayer3'] for nick in test_g.nicks_won: assert test_g.rating_before(nick) == Rating(value=1200, accuracy=0) assert test_g.rating_after(nick) == Rating(value=1300, accuracy=0) for nick in test_g.nicks_lost: assert test_g.rating_before(nick) == Rating(value=1200, accuracy=0) assert test_g.rating_after(nick) == Rating(value=1100, accuracy=0) assert test_g.score_won == 23 assert test_g.score_lost == 21 finally: # clear await Manage.delete_game(g) for p in team_won + team_lost: await Manage.delete_player(p)
async def test_all(): # create players team_won = [] team_lost = [] g = None try: for i in range(0, 4): p = Player(nick=f'NewPlayer{i}', phone=f'7916123456{i}') p.set_rating(Rating(value=1200 + i, accuracy=1)) if i < 2: team_won.append(p) else: team_lost.append(p) await Manage.delete_player(p) await Manage.save_player(p) # find players LIKE players = await Search.load_players_nick_like('New') assert len(players) == 4 players = await Search.load_players_nick_like('NewNotExists') assert len(players) == 0 # game g = Game(date=datetime.now(), nicks_won=[p.nick for p in team_won], nicks_lost=[p.nick for p in team_lost], score_won=15, score_lost=10) for p in team_won: g.set_rating_before(p.nick, p.get_rating()) p.set_rating(Rating(value=p.get_rating().value + 100, accuracy=p.get_rating().accuracy)) g.set_rating_after(p.nick, p.get_rating()) await Manage.save_player(p) for p in team_lost: g.set_rating_before(p.nick, p.get_rating()) p.set_rating(Rating(value=p.get_rating().value - 100, accuracy=p.get_rating().accuracy)) g.set_rating_after(p.nick, p.get_rating()) await Manage.save_player(p) await Manage.save_game(g) # find game res = await Search.games(player=team_lost[0]) assert res[0].id == g.id # find games vs res = await Search.games(player=team_won[0], vs_players=[team_lost[0]]) assert len(res) == 1 assert res[0].id == g.id # not find games vs res = await Search.games(player=team_lost[0], vs_players=[team_lost[1]]) assert len(res) == 0 # find games with res = await Search.games(player=team_lost[1], with_players=[team_lost[0]]) assert len(res) == 1 assert res[0].id == g.id # not find games with res = await Search.games(player=team_lost[0], with_players=[team_won[1]]) assert len(res) == 0 # find games vs and with res = await Search.games(player=team_lost[0], vs_players=[team_won[0]], with_players=[team_lost[1]]) assert len(res) == 1 assert res[0].id == g.id # not find games vs res = await Search.games(player=team_lost[1], vs_players=[team_won[1]], with_players=[team_won[0]]) assert len(res) == 0 res = await Search.games(player=team_lost[0], vs_players=[team_lost[1]], with_players=[team_lost[1]]) assert len(res) == 0 finally: # clear await Manage.delete_game(g) for player in team_lost + team_won: await Manage.delete_player(player)
class Session(AbstractSession, xworkflows.WorkflowEnabled): state = SessionWorkflow() def __init__(self, backend, text): AbstractSession.__init__(self) logger.debug('start') self.backend = backend self.text = text self._game = None self._player = None # A list of transitions and checks performed on an user_input without command # check returns tuple (index of transition, parsed args) # (source state, check, transitions) raw_input_transitions = (('s_game_adding_player', 'check_adding_player', ('game_player_confirm', 'game_add_new_player')), ('s_game_new_player_phone', 'check_adding_player_phone', ('game_new_player_phone')), ('s_game_set_won_score', 'check_setting_score', ('game_set_score_won')), ('s_game_set_lost_score', 'check_setting_score', ('game_set_score_lost')), ('s_nick_adding_player', 'check_adding_player', ('nick_already_exists', 'nick_new_player')), ('s_nick_new_player_phone', 'check_adding_player_phone', ('nick_new_player_phone')), ('s_players', 'check_player_found', ('players_found', 'players_not_found')), ('s_games', 'check_game_found', ('games_found', 'games_not_found'))) def _check_error_is_fatal(self, err, processing_message): if err is not None and err['error_type'] != 'negative response': raise RuntimeError(json.dumps(err)) ''' Checks ''' # return tuple (index of transition, parsed arguments) def check_adding_player(self, user_input, processing_message=None): logger.debug('check_adding_player') if type(user_input) == Player: return (0, user_input) err, player = self.backend.get_player(nick=user_input) self._check_error_is_fatal(err, processing_message) if player is not None: return (0, player) else: return (1, Player(nick=user_input, phone=None)) def check_player_found(self, user_input, processing_message=None): logger.debug('check_player_found') if type(user_input) == Player: user_input = user_input.nick err, player = self.backend.get_player(nick=user_input) self._check_error_is_fatal(err, processing_message) if player is not None: return (0, player) else: return (1, None) def check_game_found(self, user_input, processing_message=None): logger.debug('check_game_found') if type(user_input) == Player: user_input = user_input.nick err, games = self.backend.get_games(nick=user_input) self._check_error_is_fatal(err, processing_message) if games is not None and len(games) > 0: return (0, games) else: return (1, None) def check_adding_player_phone(self, user_input, processing_message=None): logger.debug('check_adding_player_phone') try: phone = int(self._normalize_phone(user_input)) return (0, f'{phone}') except ValueError: self.show_message(message=self.text.check_phone_incorrect(), processing_message=processing_message, reply=True) return (-1, None) def check_setting_score(self, user_input, processing_message=None): logger.debug('check_setting_score') try: score = int(user_input) if score >= 0: if self.state.is_s_game_set_lost_score or score > 14: if self.state.is_s_game_set_lost_score and score + 1 >= self._game.score_won: self.show_message( message=self.text. check_score_looser_less_than_winner(), processing_message=processing_message, reply=True) return (-1, None) return (0, score) else: self.show_message( message=self.text.check_score_winner_min_value(), processing_message=processing_message, reply=True) return (-1, None) else: self.show_message(message=self.text.check_score_min_value(), processing_message=processing_message, reply=True) return (-1, None) except ValueError: self.show_message(message=self.text.check_score_is_not_a_number(), processing_message=processing_message, reply=True) return (-1, None) ''' Transitions ''' @xworkflows.transition() def goto_init(self, user_input=None, processing_message=None, message=None): logger.debug('goto_init') self.show_message(message=message, buttons=['/game', '/players', '/games', '/cancel'], keyboard=True, processing_message=processing_message) @xworkflows.transition() def game_add(self, user_input=None, processing_message=None): logger.debug('game_add') self._game = Game() self.show_message(message=self.text.game(), processing_message=processing_message) @xworkflows.transition() def game_player_confirm(self, player, processing_message=None): logger.debug('game_player_confirm') self._player = player @xworkflows.transition() def game_add_new_player(self, player, processing_message=None): logger.debug('game_add_new_player') self._player = player self.show_message(message=self.text.game_add_new_player(), processing_message=processing_message) @xworkflows.transition() def game_new_player_phone(self, phone, processing_message=None): logger.debug('game_new_player_phone') self._player.phone = self._normalize_phone(phone) err, self._player = self.backend.add_player( player=self._player, who=processing_message.ids.user_id) self._check_error_is_fatal(err, processing_message) @xworkflows.transition() def game_set_score_won(self, score, processing_message=None): logger.debug('game_set_score_won') self._game.score_won = score self.show_message( message=self.text.game_score_winner_set_next_looser(), processing_message=processing_message) @xworkflows.transition() def game_set_score_lost(self, score, processing_message=None): logger.debug('game_set_score_lost') self._game.score_lost = score '''self.show_message( message=self.text.score_set_done(), processing_message=processing_message )''' @xworkflows.transition() def game_set_scores(self, arg=None, processing_message=None): logger.debug('game_set_scores') self.show_message(message=self.text.game_score_set_winner(), processing_message=processing_message) @xworkflows.transition() def game_next_player(self, arg=None, processing_message=None): logger.debug('game_next_player') pass @xworkflows.transition() def game_save(self, processing_message=None): logger.debug('game_save') err, g = self.backend.add_game(game=self._game, who=processing_message.ids.user_id) self._check_error_is_fatal(err, processing_message) self._game = g self.show_message(message=self.text.game_saved(), processing_message=processing_message) @xworkflows.transition() def nick(self, user_input=None, processing_message=None): logger.debug('nick') self.show_message(message=self.text.nick(), processing_message=processing_message) @xworkflows.transition() def nick_already_exists(self, arg=None, processing_message=None): logger.debug('nick_already_exists') self.show_message(message=self.text.nick_already_exists(), processing_message=processing_message) @xworkflows.transition() def nick_new_player(self, player, processing_message=None): logger.debug('nick_new_player') self._player = player self.show_message(message=self.text.nick_adding_enter_phone(), processing_message=processing_message) @xworkflows.transition() def nick_new_player_phone(self, phone, processing_message=None): logger.debug('nick_new_player_phone') self._player.phone = self._normalize_phone(phone) err, self._player = self.backend.add_player( player=self._player, who=processing_message.ids.user_id) self._check_error_is_fatal(err, processing_message) self.show_message(message=self.text.nick_added(self._player), processing_message=processing_message) @xworkflows.transition() def players_wait(self, processing_message=None): logger.debug('players_wait') self.show_message(message=self.text.players(), buttons=[ Button(text='search', switch_inline='/players ', callback=None) ], processing_message=processing_message) @xworkflows.transition() def players_found(self, player, processing_message=None): logger.debug('players_found') self.show_message(message=self.text.players_found(player), processing_message=processing_message) @xworkflows.transition() def players_not_found(self, player, processing_message=None): logger.debug('players_not_found') self.show_message(message=self.text.players_not_found(), processing_message=processing_message) @xworkflows.transition() def games_wait(self, processing_message=None): logger.debug('games_wait') @xworkflows.transition() def games_found(self, games, processing_message=None): logger.debug('games_found') self.show_message(message=self.text.games_found(games, len(games)), processing_message=processing_message) @xworkflows.transition() def games_not_found(self, game, processing_message=None): logger.debug('games_not_found') self.show_message(message=self.text.games_not_found(), processing_message=processing_message) @xworkflows.on_enter_state('s_game_player_confirmed') def _on_s_game_player_confirmed(self, transition_res=None, transition_arg=None, processing_message=None): logger.debug('_on_s_game_player_confirmed') self._add_player_to_game() self.show_message(message=self.text.game_player_added(), processing_message=processing_message) if len(self._game.nicks_won) < 2 or len(self._game.nicks_lost) < 2: self.game_next_player(processing_message=processing_message) else: self.game_set_scores(processing_message=processing_message) @xworkflows.on_enter_state('s_game_adding_player') def _on_s_game_adding_player(self, transition_res=None, transition_arg=None, processing_message=None): logger.debug('_on_s_game_adding_player') self.show_message(message=self.text.game_add_next_player(self._game), buttons=[ Button(text='search', switch_inline='/players ', callback=None) ], processing_message=processing_message) @xworkflows.on_enter_state('s_game_created') def _on_s_game_created(self, transition_res=None, transition_arg=None, processing_message=None): logger.debug('_on_s_game_created') self.game_save(processing_message) @xworkflows.on_enter_state('s_players') def _on_s_players(self, transition_res=None, transition_arg=None, processing_message=None): logger.debug('_on_s_players') @xworkflows.on_enter_state('s_games') def _on_s_players(self, transition_res=None, transition_arg=None, processing_message=None): logger.debug('_on_s_games') self.show_message(message=self.text.games(), buttons=[ Button(text='search', switch_inline='/players ', callback=None) ], processing_message=processing_message) def game(self, user_input=None, processing_message=None): logger.debug('players') if user_input is None or len(user_input) == 0 and self.state.is_init: return self.game_add(processing_message=processing_message) if user_input is not None: params = user_input.split(';') nicks_won = params[0:2] nicks_lost = params[2:4] score_won = params[4] score_lost = params[5] self._game = Game(nicks_won=nicks_won, nicks_lost=nicks_lost, score_won=score_won, score_lost=score_lost) for nick in params[0:3]: err, p = self.backend.get_player(nick=nick) self._check_error_is_fatal(err, processing_message) if err is not None: return self.goto_init( message=self.text.nick_not_exists() + '\n' + self.text.cancel(), processing_message=processing_message) self._game.set_rating_before(nick=nick, rating=p.get_rating()) err, g = self.backend.add_game(game=self._game, who=processing_message.ids.user_id) self._check_error_is_fatal(err, processing_message) if err is not None: return self.goto_init(message=self.text.game_save_fail(err), processing_message=processing_message) self._game = g self.show_message(message=self.text.game_saved(), processing_message=processing_message) def players(self, user_input=None, processing_message=None): logger.debug('players') if user_input is None or len(user_input) == 0 and self.state.is_init: return self.players_wait(processing_message=processing_message) if user_input is not None: user_input = user_input.strip() if user_input is not None and len(user_input) > 2: err, players = self.backend.get_players(nick_like=user_input) self._check_error_is_fatal(err, processing_message) self.show_contacts(contacts=players, processing_message=processing_message) def games(self, user_input=None, processing_message=None): logger.debug('games') if user_input is None or len(user_input) == 0 and self.state.is_init: return self.games_wait(processing_message=processing_message) if user_input is not None: user_input = user_input.strip() if user_input is not None and len(user_input) > 2: err, games = self.backend.get_games(nick=user_input) self._check_error_is_fatal(err, processing_message) self.show_message(message=json.dumps(games), processing_message=processing_message) def start(self, user_input=None, processing_message=None): logger.debug('start') self.goto_init(message=self.text.start(), processing_message=processing_message) def cancel(self, user_input=None, processing_message=None): logger.debug('cancel') self.goto_init(message=self.text.cancel(), processing_message=processing_message) def help(self, user_input=None, processing_message=None): logger.debug('help') self.show_message(message=self.text.help(), processing_message=processing_message) def _normalize_phone(self, args=None): p = '' for i in args: p = p + i return p.replace(' ', '') def _add_player_to_game(self): g = self._game if len(g.nicks_won) < 2: g.nicks_won.append(self._player.nick) else: if len(g.nicks_lost) < 2: g.nicks_lost.append(self._player.nick)