コード例 #1
0
ファイル: graphs.py プロジェクト: e6f4e37l/TypeRacerStats
    async def boxplot(self, ctx, *args):
        user_id = ctx.message.author.id

        if len(args) == 0: args = check_account(user_id)(args)

        if len(args) < 1 or len(args) > 4:
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).parameters(
                    f"{ctx.invoked_with} [user] <user_2>...<user_4>"))
            return

        args = list(args)
        for i, player in enumerate(args):
            args[i] = get_player(user_id, args[i])
            if escape_sequence(player):
                await ctx.send(
                    content=f"<@{user_id}>",
                    embed=Error(ctx, ctx.message).missing_information(
                        (f"[**{player}**]({Urls().user(player, 'play')}) "
                         "doesn't exist")))
                return

        conn = sqlite3.connect(DATABASE_PATH)
        c = conn.cursor()
        data = []
        try:
            title_text = ''
            for user in args:
                title_text += f"{user} vs. "
                user_data = c.execute(f"SELECT wpm FROM t_{user}")
                temp = [i[0] for i in user_data]
                data.append(temp)
        except sqlite3.OperationalError:
            conn.close()
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).not_downloaded())
            return
        conn.close()
        title_text = title_text[:-4]
        title_text += 'WPM'

        ax = plt.subplots()[1]
        ax.boxplot(data, showfliers=False)
        ax.set_xticklabels(list(args))
        ax.set_ylabel('WPM')
        ax.set_title(title_text)
        plt.grid(True)
        file_name = f"{title_text}.png"

        graph_colors = get_graph_colors(user_id)
        graph_color(ax, graph_colors, True)
        plt.savefig(file_name, facecolor=ax.figure.get_facecolor())
        wpm_picture = discord.File(file_name, filename=file_name)

        await ctx.send(file=wpm_picture)
        os.remove(file_name)
        plt.close()
        return
コード例 #2
0
ファイル: graphs.py プロジェクト: e6f4e37l/TypeRacerStats
    async def histogram(self, ctx, *args):
        user_id = ctx.message.author.id

        if len(args) == 0: args = check_account(user_id)(args)

        if len(args) != 1:
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(
                    ctx, ctx.message).parameters(f"{ctx.invoked_with} [user]"))
            return

        player = get_player(user_id, args[0])
        if escape_sequence(player):
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    (f"[**{player}**]({Urls().user(player, 'play')}) "
                     "doesn't exist")))
            return
        conn = sqlite3.connect(DATABASE_PATH)
        c = conn.cursor()
        try:
            player_data = c.execute(f"SELECT wpm FROM t_{player}")
            data = [i[0] for i in player_data.fetchall()]
        except sqlite3.OperationalError:
            conn.close()
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).not_downloaded())
            return
        conn.close()

        ax = plt.subplots()[1]
        max_, min_ = max(data), min(data)

        if int(max_ - min_) // 10 == 0:
            patches = ax.hist(data, bins=1)[2]
        else:
            patches = ax.hist(data, bins=int(max_ - min_) // 10)[2]

        ax.set_xlabel('WPM')
        ax.set_ylabel('Frequency')
        plt.grid(True)
        ax.set_title(f"{player}'s WPM Histogram")
        file_name = f"{player} WPM.png"

        graph_colors = get_graph_colors(user_id)
        graph_color(ax, graph_colors, False, patches)
        plt.savefig(file_name, facecolor=ax.figure.get_facecolor())
        wpm_picture = discord.File(file_name, filename=file_name)
        await ctx.send(file=wpm_picture)
        os.remove(file_name)
        plt.close()
        return
コード例 #3
0
ファイル: graphs.py プロジェクト: e6f4e37l/TypeRacerStats
    async def textbestsgraph(self, ctx, *args):
        user_id = ctx.message.author.id

        if len(args) <= 1: args = check_account(user_id)(args)

        if len(args) == 1: args += ('races', )

        if len(args) != 2:
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).parameters(
                    f"{ctx.invoked_with} [user] [time/races/texts]"))
            return

        player = get_player(user_id, args[0])
        if args[1].lower() not in {'time', 'races', 'texts'}:
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).incorrect_format(
                    'Must provide a valid category: `time/races/texts`'))
            return

        if args[1].lower() == 'time':
            q_category = 't'
            category = 'Time'
        elif args[1].lower() == 'races':
            q_category = 'gn'
            category = 'Races'
        elif args[1].lower() == 'texts':
            q_category = None
            category = 'Texts'

        if escape_sequence(player):
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    (f"[**{player}**]({Urls().user(player, 'play')}) "
                     "doesn't exist")))
            return

        data_x, data_y = [], []
        text_ids = {}
        wpm_sum, wpm_count = 0, 0
        conn = sqlite3.connect(DATABASE_PATH)
        c = conn.cursor()
        try:
            user_data = c.execute(f"SELECT gn, t, tid, wpm FROM t_{player}")
            for race_data in user_data:
                if text_ids.get(race_data[2], False):
                    if race_data[3] > text_ids[race_data[2]]:
                        if not q_category:
                            data_x.append(len(data_x))
                        elif q_category == 'gn':
                            data_x.append(race_data[0])
                        else:
                            data_x.append(
                                datetime.datetime.fromtimestamp(race_data[1]))
                        wpm_sum += race_data[3] - text_ids[race_data[2]]
                        text_ids.update({race_data[2]: race_data[3]})
                        data_y.append(wpm_sum / wpm_count)
                else:
                    if not q_category:
                        data_x.append(len(data_x))
                    elif q_category == 'gn':
                        data_x.append(race_data[0])
                    else:
                        data_x.append(
                            datetime.datetime.fromtimestamp(race_data[1]))
                    text_ids.update({race_data[2]: race_data[3]})
                    wpm_sum += race_data[3]
                    wpm_count += 1
                    data_y.append(wpm_sum / wpm_count)
        except sqlite3.OperationalError:
            conn.close()
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).not_downloaded())
            return
        conn.close()

        data_x = reduce_list(data_x)
        data_y = reduce_list(data_y)

        ax = plt.subplots()[1]
        ax.plot(data_x, data_y)
        ax.set_title(f"{player}'s Text Bests Average Over {category}")

        if not q_category:
            ax.set_xlabel('Text Changes (Additions/Improvements)')
        elif q_category == 't':
            ax.set_xlabel('Date')
            ax.set_xticks(ax.get_xticks()[::2])
            formatter = mdates.DateFormatter("%b. %-d '%y")
            ax.xaxis.set_major_formatter(formatter)
        else:
            ax.set_xlabel('Race #')

        ax.set_ylabel('WPM')
        plt.grid(True)
        file_name = f"Text Bests Average Over {category}.png"

        graph_colors = get_graph_colors(user_id)
        graph_color(ax, graph_colors, False)
        plt.savefig(file_name, facecolor=ax.figure.get_facecolor())
        races_over_time_picture = discord.File(file_name, filename=file_name)

        await ctx.send(file=races_over_time_picture)
        os.remove(file_name)
        plt.close()
        return
コード例 #4
0
ファイル: graphs.py プロジェクト: e6f4e37l/TypeRacerStats
    async def compare(self, ctx, *args):
        user_id = ctx.message.author.id
        MAIN_COLOR = get_supporter(user_id)

        if len(args) == 1: args = check_account(user_id)(args)

        if len(args) != 2:
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).parameters(
                               f"{ctx.invoked_with} [user_1] [user_2]"))
            return

        player = get_player(user_id, args[0])
        player_ = get_player(user_id, args[1])
        if escape_sequence(player):
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    (f"[**{player}**]({Urls().user(player, 'play')}) "
                     "doesn't exist")))
            return

        if escape_sequence(player_):
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    (f"[**{player_}**]({Urls().user(player_, 'play')}) "
                     "doesn't exist")))
            return

        conn = sqlite3.connect(DATABASE_PATH)
        c = conn.cursor()
        try:
            player_data = c.execute(
                f"SELECT tid, MAX(wpm) FROM t_{player} GROUP BY tid ORDER BY wpm"
            ).fetchall()
            player_data_ = c.execute(
                f"SELECT tid, MAX(wpm) FROM t_{player_} GROUP BY tid ORDER BY wpm"
            ).fetchall()
        except sqlite3.OperationalError:
            conn.close()
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).not_downloaded())
            return
        conn.close()

        player_dict = dict()
        for text in player_data:
            player_dict.update({text[0]: text[1]})

        first_player, second_player = [], []
        for text in player_data_:
            if player_dict.get(text[0], -1) > 0:
                difference = text[1] - player_dict[text[0]]
                if difference == 0: pass
                elif difference < 0:
                    first_player.append(-1 * difference)
                else:
                    second_player.append(difference)

        if len(first_player) + len(second_player) == 0:
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    f"**{player}** and **{player_}** have no texts in common"))
            return

        fig, (ax, ax_) = plt.subplots(1, 2, sharey=True)
        if first_player:
            first_max = max(first_player)
        else:
            first_max = 0
        if second_player:
            second_max = max(second_player)
        else:
            second_max = 0

        if int(first_max) // 10 == 0:
            patches = ax.hist(first_player, bins=1,
                              orientation='horizontal')[2]
        else:
            patches = ax.hist(first_player,
                              bins=int(first_max) // 10,
                              orientation='horizontal')[2]
        if int(second_max) // 10 == 0:
            patches_ = ax_.hist(second_player,
                                bins=1,
                                orientation='horizontal')[2]
        else:
            patches_ = ax_.hist(second_player,
                                bins=int(second_max) // 10,
                                orientation='horizontal')[2]

        ax.yaxis.tick_left()
        ax.set_ylim(ax.get_ylim()[::-1])
        ax.set_ylabel('Difference (WPM)')
        ax.grid()
        ax.set_title(player)

        ax_.grid()
        ax_.set_title(player_)

        max_xlim = max(ax.get_xlim()[1], ax_.get_xlim()[1])
        ax.set_xlim(0, max_xlim)
        ax.set_xlim(ax.get_xlim()[::-1])
        ax_.set_xlim(0, max_xlim)

        title = f"{player} vs. {player_} Text Bests Comparison"
        plt.subplots_adjust(wspace=0, hspace=0)
        file_name = f"{player}_{player_}_text_bests_comparison.png"

        graph_colors = get_graph_colors(user_id)
        graph_color(ax, graph_colors, False, patches)
        graph_color(ax_, graph_colors, False, patches)

        to_rgba = lambda x: (x // 65536 / 255,
                             ((x % 65536) // 256) / 255, x % 256 / 255)

        if graph_colors['text']:
            fig.suptitle(title, color=to_rgba(graph_colors['text']))
            fig.text(0.5,
                     0.025,
                     'Frequency (Texts)',
                     ha='center',
                     color=to_rgba(graph_colors['text']))
        else:
            fig.suptitle(title)
            fig.text(0.5, 0.025, 'Frequency (Texts)', ha='center')

        plt.savefig(file_name, facecolor=ax.figure.get_facecolor())
        plt.close()

        embed = discord.Embed(title=title, color=MAIN_COLOR)
        file_ = discord.File(file_name, filename=file_name)
        embed.set_image(url=f"attachment://{file_name}")
        embed.add_field(
            name=player,
            value=
            f"**{f'{len(first_player):,}'}** texts (+**{f'{round(sum(first_player), 2):,}'}** WPM)"
        )
        embed.add_field(
            name=player_,
            value=
            f"**{f'{len(second_player):,}'}** texts (+**{f'{round(sum(second_player), 2):,}'}** WPM)"
        )
        await ctx.send(file=file_, embed=embed)
        os.remove(file_name)
        return
コード例 #5
0
ファイル: graphs.py プロジェクト: e6f4e37l/TypeRacerStats
    async def pbgraph(self, ctx, *args):
        user_id = ctx.message.author.id

        if len(args) <= 1: args = check_account(user_id)(args)

        if len(args) == 1: args += ('races', )

        if len(args) != 2:
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).parameters(
                               f"{ctx.invoked_with} [user] [time/races]"))
            return

        player = get_player(user_id, args[0])
        if args[1].lower() not in {'time', 'races'}:
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).incorrect_format(
                               'Must provide a valid category: `time/races`'))
            return

        if args[1].lower() == 'time':
            q_category = 't'
            category = 'Time'
        else:
            q_category = 'gn'
            category = 'Races'

        if escape_sequence(player):
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    (f"[**{player}**]({Urls().user(player, 'play')}) "
                     "doesn't exist")))
            return

        data_x, data_y = [], []
        conn = sqlite3.connect(DATABASE_PATH)
        c = conn.cursor()
        try:
            player_data = c.execute(
                f"SELECT {q_category}, wpm FROM t_{player}")
            temp_x, temp_y = player_data.fetchone()
            if q_category == 't':
                data_x.append(datetime.datetime.fromtimestamp(temp_x))
            else:
                data_x.append(temp_x)
            data_y.append(temp_y)
            for row in player_data:
                if data_y[-1] > row[1]: continue
                if q_category == 't':
                    data_x.append(datetime.datetime.fromtimestamp(row[0]))
                else:
                    data_x.append(row[0])
                data_y.append(row[1])
        except sqlite3.OperationalError:
            conn.close()
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).not_downloaded())
            return
        conn.close()

        data_x = reduce_list(data_x)
        data_y = reduce_list(data_y)

        ax = plt.subplots()[1]
        ax.plot(data_x, data_y)
        ax.set_title(f"{player}'s PBs Over {category}")

        if q_category == 't':
            ax.set_xlabel('Date')
            ax.set_xticks(ax.get_xticks()[::2])
            formatter = mdates.DateFormatter("%b. %-d '%y")
            ax.xaxis.set_major_formatter(formatter)
        else:
            ax.set_xlabel('Race #')

        ax.set_ylabel('WPM')
        plt.grid(True)
        file_name = f"PBs Over {category}.png"

        graph_colors = get_graph_colors(user_id)
        graph_color(ax, graph_colors, False)
        plt.savefig(file_name, facecolor=ax.figure.get_facecolor())
        races_over_time_picture = discord.File(file_name, filename=file_name)

        await ctx.send(file=races_over_time_picture)
        os.remove(file_name)
        plt.close()
        return
コード例 #6
0
ファイル: graphs.py プロジェクト: e6f4e37l/TypeRacerStats
    async def adjustedgraph(self, ctx, *args):
        user_id = ctx.message.author.id
        MAIN_COLOR = get_supporter(user_id)
        account = account_information(user_id)
        universe = account['universe']

        ag = ctx.invoked_with.lower() in ['adjustedgraph'
                                          ] + get_aliases('adjustedgraph')
        mg = ctx.invoked_with.lower() in ['matchgraph'
                                          ] + get_aliases('matchgraph')

        if len(args) == 0 or (len(args) == 1 and args[0][0] == '-'):
            args = check_account(user_id)(args)

        if len(args) > 2 or len(args) == 0:
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).parameters(
                    f"{ctx.invoked_with} [user] [race_num]` or `{ctx.invoked_with} [url]"
                ))
            return

        race_num = 0
        if len(args) == 2 and args[1][0] == '-':
            try:
                race_num = int(args[1])
                args = (args[0], )
            except ValueError:
                pass

        if len(args) == 1:
            try:
                args[0].index('result?')
                replay_url = args[0]
                urls = [replay_url]
            except ValueError:
                try:
                    player = get_player(user_id, args[0])
                    urls = [Urls().get_races(player, universe, 1)]
                    race_api_response = await fetch(urls, 'json')
                    last_race = race_api_response[0][0]['gn']
                    if race_num < 0: last_race += race_num
                    race_api_response = race_api_response[0][0]
                    replay_url = Urls().result(player, last_race, universe)
                    urls = [replay_url]
                except:
                    await ctx.send(
                        content=f"<@{user_id}>",
                        embed=Error(ctx, ctx.message).missing_information((
                            f"[**{player}**](https://data.typeracer.com/pit/race_history?user={player}&universe={universe}) "
                            "doesn't exist or has no races in the "
                            f"{href_universe(universe)} universe")))
                    return

        elif len(args) == 2:
            try:
                player = get_player(user_id, args[0])
                replay_url = Urls().result(player, int(args[1]), universe)
                urls = [replay_url]
            except ValueError:
                await ctx.send(content=f"<@{user_id}>",
                               embed=Error(ctx, ctx.message).incorrect_format(
                                   '`race_num` must be a positive integer'))
                return

        def helper_scraper(soup):
            escapes = ''.join([chr(char) for char in range(1, 32)])
            try:
                typinglog = re.sub(
                    '\\t\d', 'a',
                    re.search(
                        r'typingLog\s=\s"(.*?)";',
                        response).group(1).encode().decode(
                            'unicode-escape').translate(escapes)).split('|')
                return [int(c) for c in re.findall(r"\d+", typinglog[0])][2:]
            except:
                return None

        try:
            response = (await fetch(urls, 'text'))[0]
            if not response:
                raise KeyError
            soup = BeautifulSoup(response, 'html.parser')
            times = helper_scraper(soup)

            race_text = soup.select("div[class='fullTextStr']")[0].text.strip()
            player = soup.select(
                "a[class='userProfileTextLink']")[0]["href"][13:]
            race_details = soup.select("table[class='raceDetails']")[0].select(
                'tr')
            universe = 'play'
            opponents = []
            for detail in race_details:
                cells = detail.select('td')
                category = cells[0].text.strip()
                if category == 'Race Number':
                    race_number = int(cells[1].text.strip())
                elif category == 'Universe':
                    universe = cells[1].text.strip()
                elif category == 'Opponents':
                    opponents = [i['href'] for i in cells[1].select('a')]
        except:
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information((
                    '`var typingLog` was not found in the requested URL;\n'
                    f"Currently linked to the {href_universe(universe)} universe\n\n"
                )))
            return

        if universe == 'lang_ko':
            mult = 24000
        elif universe == 'lang_zh' or universe == 'new_lang_zh-tw' or universe == 'lang_zh-tw' or universe == 'lang_ja':
            mult = 60000
        else:
            mult = 12000

        def wpm_helper(times):
            temp, total_time = [], 0
            for i, time_ in enumerate(times):
                total_time += time_
                try:
                    temp.append((i + 1) * mult / total_time)
                except ZeroDivisionError:
                    pass
            return temp

        if ag:
            times.pop(0)
            data_y = wpm_helper(times)
        else:
            unl = wpm_helper(times)
            data = {
                player: [
                    unl, unl[-1], times[0],
                    replay_url.split('https://data.typeracer.com/pit/')[1]
                ]
            }
            for opponent in opponents:
                try:
                    urls = ["https://data.typeracer.com/pit/" + opponent]
                    response = (await fetch(urls, 'text'))[0]
                    if not response:
                        raise KeyError
                    soup = BeautifulSoup(response, 'html.parser')
                    times = helper_scraper(soup)
                    unl = wpm_helper(times)
                    data.update({
                        opponent.split('|')[1][3:]:
                        [unl, unl[-1], times[0], opponent]
                    })
                except:
                    pass
            data = {
                k: v
                for k, v in sorted(
                    data.items(), key=lambda x: x[1][1], reverse=True)
            }

        if ag:
            title_1 = f"Adjusted WPM Over {player}'s {num_to_text(race_number)} Race"
            title = f"{title_1}\nUniverse: {universe}"
        else:
            title_1 = f"Unlagged WPM Over {player}'s {num_to_text(race_number)} Race"
            title = f"{title_1}\nUniverse: {universe}"

        description = f"**Quote**\n\"{race_text[0:1008]}\""

        text_length = len(race_text) > 9

        ax = plt.subplots()[1]
        if ag:
            if text_length:
                starts = data_y[0:9]
                remaining = data_y[9:]
            ax.plot([i for i in range(1, len(data_y) + 1)], data_y)
        else:
            value, i, starts, remaining = '', 0, [], []
            for name, data_y in data.items():
                wpm_ = data_y[0]
                if text_length:
                    starts += wpm_[0:9]
                    remaining += wpm_[9:]
                ax.plot([i for i in range(1, len(wpm_) + 1)], wpm_, label=name)
                segment = (
                    f"{NUMBERS[i]} [{name}]({f'https://data.typeracer.com/pit/{data_y[3]}'})"
                    f" - {round(data_y[1], 2)} WPM ({f'{data_y[2]:,}'}ms start)\n"
                )
                if len(value + segment) <= 1024:
                    value += segment
                i += 1
            if len(data) > 1:
                plt.tight_layout(rect=[0.02, 0.02, 0.75, 0.92])
                ax.legend(loc='upper left',
                          bbox_to_anchor=(1.03, 1),
                          shadow=True,
                          ncol=1)

        ax.set_title(title)
        ax.set_xlabel('Keystrokes')
        ax.set_ylabel('WPM')
        plt.grid(True)
        file_name = 'WPM Over Race.png'

        embed = discord.Embed(title=title_1,
                              color=discord.Color(MAIN_COLOR),
                              description=description,
                              url=replay_url)
        if text_length:
            max_starts, max_remaining = max(starts), max(remaining)
            messed_up_scaled = max_starts > max_remaining
            if messed_up_scaled:
                if ctx.invoked_with[-1] != '*':
                    ax.set_ylim(0, 1.05 * max_remaining)
                    embed.set_footer(
                        text=
                        f"The `y`-axis has been scaled; run `{ctx.invoked_with}*` to see the entire graph"
                    )

        graph_colors = get_graph_colors(user_id)
        graph_color(ax, graph_colors, False)
        plt.savefig(file_name, facecolor=ax.figure.get_facecolor())
        plt.close()

        file_ = discord.File(file_name, filename='image.png')
        embed.set_image(url='attachment://image.png')
        if mg:
            embed.add_field(name='Ranks (ranked by unlagged WPM)',
                            value=value[:-1])
        os.remove(file_name)

        await ctx.send(file=file_, embed=embed)
        return
コード例 #7
0
ファイル: graphs.py プロジェクト: e6f4e37l/TypeRacerStats
    async def improvement(self, ctx, *args):
        user_id = ctx.message.author.id

        if len(args) == 0: args = check_account(user_id)(args)

        if len(args) == 1: args += ('races', )

        if len(args) != 2:
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).parameters(
                               f"{ctx.invoked_with} [user] <time/races>"))
            return

        player = get_player(user_id, args[0])
        if args[1].lower() not in ['time', 'races']:
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).incorrect_format(
                               'Must provide a valid category: `time/races`'))
            return

        if args[1].lower() == 'time':
            q_category = 't'
            category = 'Time'
        else:
            q_category = 'gn'
            category = 'Races'

        if escape_sequence(player):
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    (f"[**{player}**]({Urls().user(player, 'play')}) "
                     "doesn't exist")))
            return

        data_x, data_y, max_point = [], [], 0
        conn = sqlite3.connect(DATABASE_PATH)
        c = conn.cursor()
        try:
            player_data = c.execute(
                f"SELECT wpm, {q_category} FROM t_{player} ORDER by {q_category}"
            )
            for row in player_data:
                if q_category == 't':
                    data_x.append(datetime.datetime.fromtimestamp(row[1]))
                else:
                    data_x.append(row[1])
                row_wpm = row[0]
                max_point = max(max_point, row_wpm)
                data_y.append(row_wpm)
        except sqlite3.OperationalError:
            conn.close()
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).not_downloaded())
            return
        conn.close()

        length = len(data_x)
        if length < 15:
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    f"`{ctx.invoked_with}` requires at least 15 races to generate a graph"
                ))
            return

        elif length < 7500:
            sma = length // 15
        else:
            sma = 500
        moving_y = [sum(data_y[0:sma]) / sma]
        moving_y += [sum(data_y[i - sma:i]) / sma for i in range(sma, length)]
        moving_x = [data_x[0]] + data_x[sma:]
        moving_y = reduce_list(moving_y)
        moving_x = reduce_list(moving_x)

        max_line = max(moving_y)

        ax = plt.subplots()[1]
        ax.scatter(data_x, data_y, marker='.', alpha=0.1, color='#000000')
        ax.plot(moving_x, moving_y, color='#FF0000')
        ax.set_title(
            f"{player}'s WPM Over {category}\n(Moving Average of {sma} Races)")

        if q_category == 't':
            ax.set_xlabel('Date')
            ax.set_xticks(ax.get_xticks()[::2])
            formatter = mdates.DateFormatter("%b. %-d '%y")
            ax.xaxis.set_major_formatter(formatter)
        else:
            ax.xaxis.set_major_formatter(
                ticker.FuncFormatter(self.large_num_formatter))
            ax.set_xlabel('Race #')

        if max_point > 2 * max_line: ax.set_ylim(0, 2 * max_line)
        ax.set_ylabel('WPM')
        plt.grid(True)
        file_name = f"WPM Over {category}.png"

        graph_colors = get_graph_colors(user_id)
        graph_color(ax, graph_colors, False)
        plt.savefig(file_name, facecolor=ax.figure.get_facecolor())
        races_over_time_picture = discord.File(file_name, filename=file_name)

        await ctx.send(file=races_over_time_picture)
        os.remove(file_name)
        plt.close()
        return
コード例 #8
0
ファイル: graphs.py プロジェクト: e6f4e37l/TypeRacerStats
        ax.yaxis.set_major_formatter(
            ticker.FuncFormatter(self.large_num_formatter))
        ax.set_ylabel(units)
        plt.grid(True)

        if len(data_y) > 1:
            plt.tight_layout(rect=[0, 0, 0.75, 1])
            ax.legend(loc='upper left',
                      bbox_to_anchor=(1.03, 1),
                      shadow=True,
                      ncol=1)
        file_name = f"{units} Over Time.png"

        graph_colors = get_graph_colors(user_id)
        graph_color(ax, graph_colors, False)
        plt.savefig(file_name, facecolor=ax.figure.get_facecolor())
        over_time_picture = discord.File(file_name, filename=file_name)

        await ctx.send(file=over_time_picture)
        os.remove(file_name)
        plt.close()
        return

    @commands.check(
        lambda ctx: check_dm_perms(ctx, 4) and check_banned_status(ctx))
    @commands.command(aliases=get_aliases('improvement'))
    async def improvement(self, ctx, *args):
        user_id = ctx.message.author.id

        if len(args) == 0: args = check_account(user_id)(args)
コード例 #9
0
ファイル: text_stats.py プロジェクト: e6f4e37l/TypeRacerStats
    async def personalbest(self, ctx, *args):
        user_id = ctx.message.author.id
        MAIN_COLOR = get_supporter(user_id)

        if len(args) == 0:
            args = check_account(user_id)(args)

        if len(args) == 0 or len(args) > 2:
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).parameters(
                               f"{ctx.invoked_with} [user] [text_id]"))
            return

        player = get_player(user_id, args[0])
        if escape_sequence(player):
            await ctx.send(
                content=f"<@{user_id}>",
                embed=Error(ctx, ctx.message).missing_information(
                    (f"[**{player}**]({Urls().user(player, 'play')}) "
                     "doesn't exist")))
            return

        cur_wpm = 0
        if len(args) == 2:
            try:
                if args[1] == '*':
                    text_id = get_cached_id(ctx.message.channel.id)
                    if not text_id:
                        text_id = int(args[1])
                else:
                    text_id = int(args[1])
            except ValueError:
                await ctx.send(content=f"<@{user_id}>",
                               embed=Error(ctx, ctx.message).incorrect_format(
                                   f"**{args[1]}** is not a valid text ID"))
                return
        else:
            try:
                urls = [Urls().get_races(player, 'play', 1)]
                response = (await fetch(urls, 'json'))[0][0]
                text_id = int(response['tid'])
                cur_wpm = float(response['wpm'])
            except:
                await ctx.send(content=f"<@{user_id}>",
                               embed=Error(ctx,
                                           ctx.message).missing_information())
                return

        with open(TEXTS_FILE_PATH_CSV, 'r') as csvfile:
            reader = csv.reader(csvfile)
            next(reader)
            for row in reader:
                if int(row[0]) == text_id:
                    text = row[1]
                    break
                else:
                    continue

        try:
            if len(text) > 1024:
                text = f"\"{text[0:1020]}…\""
            else:
                text = f"\"{text}\""
        except NameError:
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).incorrect_format(
                               f"{text_id} is not a valid text ID"))
            return

        count, sum_, best, best_gn, worst_gn = (0, ) * 5
        worst = 100000
        conn = sqlite3.connect(DATABASE_PATH)
        c = conn.cursor()
        try:
            user_data = c.execute(
                f"SELECT gn, wpm FROM t_{player} WHERE tid = ?",
                (text_id, )).fetchall()
            for row in user_data:
                count += 1
                sum_ += row[1]
                if row[1] > best:
                    best_gn, best = row
                if row[1] < worst:
                    worst_gn, worst = row
        except sqlite3.OperationalError:
            conn.close()
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).not_downloaded())
            return
        conn.close()

        if cur_wpm > best:
            color, description = 754944, f"**Improved by {round(cur_wpm - best, 2)} WPM (to {round(cur_wpm, 2)} WPM)**"
        else:
            color, description = MAIN_COLOR, ''

        if not count:
            await ctx.send(content=f"<@{user_id}>",
                           embed=Error(ctx, ctx.message).missing_information(
                               f"**{player}** has not completed the text yet"))
            return

        value = f"**Times:** {f'{count:,}'}\n"
        if cur_wpm:
            value += f"**Last Race:** {f'{cur_wpm:,}'} WPM\n"
        value += (f"**Average:** {f'{round(sum_ / count, 2):,}'} WPM\n"
                  f"**Fastest:** {f'{best:,}'} WPM "
                  f"(Race #{f'{best_gn:,}'}) [:cinema:]"
                  f"({Urls().result(player, best_gn, 'play')})\n"
                  f"**Slowest:** {f'{worst:,}'} WPM "
                  f"(Race #{f'{worst_gn:,}'}) [:cinema:]"
                  f"({Urls().result(player, worst_gn, 'play')})\n")

        title = f"Quote #{text_id} Statistics for {player}"
        cache_id(ctx.message.channel.id, text_id)
        if description:
            embed = discord.Embed(title=title,
                                  color=discord.Color(color),
                                  url=Urls().text(text_id),
                                  description=description)
        else:
            embed = discord.Embed(title=title,
                                  color=discord.Color(color),
                                  url=Urls().text(text_id))

        embed.set_thumbnail(url=Urls().thumbnail(player))
        embed.add_field(name='Quote', value=text, inline=False)
        embed.add_field(name='Speeds', value=value)

        if ctx.invoked_with[-1] == '*' and len(user_data) > 1:
            ax = plt.subplots()[1]

            data_y = [i[1] for i in user_data]
            if cur_wpm: data_y += [cur_wpm]
            length = len(data_y)
            data_x = [i + 1 for i in range(length)]

            if length < 30:
                ax.plot(data_x, data_y)
            else:
                ax.scatter(data_x,
                           data_y,
                           marker='.',
                           alpha=0.1,
                           color='#000000')
                if length < 500:
                    sma = length // 10
                else:
                    sma = 50

                moving_y = [sum(data_y[0:sma]) / sma]
                moving_y += [
                    sum(data_y[i - sma:i]) / sma for i in range(sma, length)
                ]
                moving_x = [data_x[0]] + data_x[sma:]
                moving_y = reduce_list(moving_y)
                moving_x = reduce_list(moving_x)
                ax.plot(moving_x, moving_y, color='#FF0000')
                title += f"\n(Moving Average of {sma} Races)"

            ax.set_title(title)
            ax.set_xlabel('Attempt #')
            ax.set_ylabel('WPM')
            plt.grid(True)
            file_name = f"{title}.png"

            graph_colors = get_graph_colors(user_id)
            graph_color(ax, graph_colors, False)
            plt.savefig(file_name, facecolor=ax.figure.get_facecolor())
            plt.close()

            file_ = discord.File(file_name, filename='image.png')
            embed.set_image(url='attachment://image.png')
            os.remove(file_name)
            await ctx.send(file=file_, embed=embed)
            return

        await ctx.send(embed=embed)
        return
コード例 #10
0
ファイル: supporter.py プロジェクト: e6f4e37l/TypeRacerStats
    async def setgraphcolor(self, ctx, *args):
        supporters = load_supporters()
        user_id = str(ctx.message.author.id)
        color = 0

        if len(args) == 0:
            supporters[user_id]['graph_color'] = {
                'bg': None,
                'graph_bg': None,
                'axis': None,
                'line': None,
                'text': None,
                'grid': None,
                'cmap': None
            }

        else:
            category = args[0].lower()
            if not category in [
                    'bg', 'graph_bg', 'axis', 'line', 'text', 'grid', 'cmap'
            ]:
                await ctx.send(content=f"<@{user_id}>",
                               embed=Error(ctx, ctx.message).incorrect_format(
                                   ('Must provide a valid category: '
                                    '`[bg/graph_bg/axis/line/text/grid]`')))
                return

            if len(args) == 1:
                supporters[user_id]['graph_color'][category] = None
            else:
                try:
                    if category == 'cmap':
                        raise ValueError

                    color = int(f"0x{args[1]}", 16)
                    if color < 0 or color > 16777216:
                        raise ValueError
                except ValueError:
                    try:
                        if category == 'cmap':
                            cmaps = get_cmaps()
                            color = cmaps[args[1].lower()]
                        else:
                            colors = get_colors()
                            color = colors[args[1].lower()]
                    except KeyError:
                        await ctx.send(
                            content=f"<@{ctx.message.author.id}>",
                            embed=Error(ctx, ctx.message).incorrect_format((
                                f"[**{args[1]}** is not a valid hex_value or cmap]"
                                '(https://www.w3schools.com/colors/colors_picker.asp)'
                            )))
                        return
                supporters[user_id]['graph_color'][category] = color
                if category == 'cmap' and not supporters[user_id][
                        'graph_color']['line']:
                    supporters[user_id]['graph_color']['line'] = 0x447BAF

                if isinstance(color, str):
                    color = 0

        update_supporters(supporters)

        ax = plt.subplots()[1]
        ax.plot([x for x in range(0, 50)], [y**0.5 for y in range(0, 50)])

        ax.set_title('Sample Graph')
        ax.set_xlabel('x-axis')
        ax.set_ylabel('y-axis')

        plt.grid(True)
        file_name = 'Sample Graph.png'
        graph_colors = supporters[user_id]['graph_color']
        graph_colors.update({'name': '!'})
        graph_color(ax, graph_colors, False)
        plt.savefig(file_name, facecolor=ax.figure.get_facecolor())
        plt.close()

        file_ = discord.File(file_name, filename='image.png')
        embed = discord.Embed(title='Color Updated',
                              color=discord.Color(color))
        embed.set_image(url='attachment://image.png')
        os.remove(file_name)

        await ctx.send(file=file_, embed=embed)
        return