Example #1
0
    def test_initialized_other_thread(self):
        self.fixtures = None
        pugsql.get_modules().clear()

        def init():
            self.fixtures = pugsql.module('tests/sql/fixtures')
            self.fixtures.connect('sqlite:///./tests/data/fixtures.sqlite3')

        t = threading.Thread(target=init)
        t.start()
        t.join()

        self.assertEqual({
            'username': '******',
            'user_id': 1
        }, self.fixtures.user_for_id(user_id=1))
Example #2
0
 def setUp(self):
     pugsql.get_modules().clear()
     self.fixtures = pugsql.module('tests/sql/fixtures')
     self.fixtures.connect('sqlite:///./tests/data/fixtures.sqlite3')
Example #3
0
    def game_info(self, game_id: int):
        """
        Main function for transforming the game summary and logs. Also outputs to Database.
        :param game_id: Integer for the game ID, used for identification and for urls.
        """

        # Sends slack notification that job has begun; creates url Strings
        slack_message(f'Loading game ID {game_id}', 'scheduled-jobs')
        results_url = f'https://en.boardgamearena.com/#!table?table={game_id}'
        replay_url = f'https://en.boardgamearena.com/#!gamereview?table={game_id}'

        # Return and write locally the results data.
        summary_results = self.game_results(results_url)
        pickle.dump(summary_results, open('data/results.pkl', 'wb'))

        # Return and write locally the log data.
        logs = self.game_logs(replay_url)
        pickle.dump(logs, open('data/logs.pkl', 'wb'))

        # Assign player order by finding logs where the first player of each round switches
        player_order = list(
            set([
                x[0:x.find(' is')] for x in logs if 'is now first player' in x
            ][0:4]))

        # For each log item, assign the player ID.
        player_nums = []
        for x in logs:
            # Abandoned games are discarded.
            if 'chose to abandon' in x.lower():
                self.game_ids.remove(game_id)
                slack_message('game abandoned', 'scheduled-jobs')
                return

            # These log items do not have any game impact and/or are not specific to a player
            elif 'end of the game' in x.lower():
                player_nums.append(-1)

            elif 'end of game' in x.lower():
                player_nums.append(-1)

            elif 'rematch' in x.lower():
                player_nums.append(-1)

            elif 'colors of' in x.lower():
                player_nums.append(-1)

            else:
                if 'out of time' in x.lower():
                    x = x[0:x.find('out of time') + 11]

                for i in range(len(player_order)):
                    if x.find(player_order[i]) >= 0:
                        player_nums.append(i)

        # Assigns player index/number in place of name in the logs.
        for player in player_order:
            logs = [x.replace(player, 'player') for x in logs]

        # For each log, replaces numbers with the character i,
        # and creates a separate field in the table structure to capture the value;
        # This normalizes player actions in the log table.
        values = [int((re.findall('\d+', x) or [-1])[0]) for x in logs]
        logs = [re.sub(r'\d', 'i', x) for x in logs]

        log_df = pd.DataFrame({
            'player_number': player_nums,
            'value': values,
            'action_name': logs,
        })

        # Sets flag for a new turn/round; and assigns a turn/round number using that flag.
        log_df.loc[log_df['action_name'] == 'player is now first player',
                   'new_turn'] = 1
        log_df['turn_number'] = log_df['new_turn'].fillna(0).cumsum()

        # Sets move number as an index starting at 1.
        log_df['move_number'] = log_df.index + 1

        log_df['game_id'] = game_id
        log_df = log_df.drop('new_turn', axis=1)

        # Creates empty lists of the appropriate length for missing data.
        for key in summary_results.keys():
            if len(summary_results[key]) == 0:
                summary_results[key] = [None] * len(
                    summary_results['Player Names'])
            if ((len(summary_results[key])
                 == 2 * len(summary_results['Player Names'])
                 and key == 'winpoints')):
                summary_results[key] = [
                    x.strip() for x in summary_results[key] if len(x) > 0
                ]

        # Converts summary results into a DataFrame and does some string cleanup.
        summary_df = pd.DataFrame(summary_results)
        summary_df.columns = summary_df.columns.str.replace(
            "'", "").str.lower().str.replace(' ', '_')
        summary_df['game_id'] = game_id
        summary_df['player_idx'] = [
            player_order.index(x) for x in summary_results['Player Names']
        ]

        if (log_df.isnull().values.any()) or (
                summary_df.isnull().values.any()):
            slack_message(f'Missing data found in tables:\n{summary_df}',
                          'job-errors')
            return

        # Writes tables to the database.
        pugsql.get_modules().clear()
        queries = pugsql.module('sql/')
        queries.connect(engine_builder(engine=False))

        log_row_ct = queries.insert_logs(log_df.to_dict(orient='records'))
        summary_row_ct = queries.insert_summary(
            summary_df.to_dict(orient='records'))

        # Removes completed game ID from the list and writes local list of recent game IDs.
        self.game_ids.remove(game_id)
        self.write_new_game_ids()

        slack_message(
            f'Loaded game ID {game_id}'
            f'\n{summary_row_ct} rows added to summary'
            f'\n{log_row_ct} rows added to logs', 'scheduled-jobs')