async def teamrate(self, ctx, *handles: str): """Provides the combined rating of the entire team. If +server is provided as the only handle, will display the rating of the entire server. Supports multipliers. e.g: ;teamrate gamegame*1000""" handles = handles or ('!' + str(ctx.author),) is_entire_server = (handles == ('+server',)) if is_entire_server: res = cf_common.user_db.get_cf_users_for_guild(ctx.guild.id) ratings = [cf_user.rating for user_id, cf_user in res if cf_user.rating is not None] user_str = '+server' else: def normalize(x): return [i.lower() for i in x] handle_counts = {} parsed_handles = [] for i in handles: parse_str = normalize(i.split('*')) if len(parse_str) > 1: try: handle_counts[parse_str[0]] = int(parse_str[1]) except ValueError: raise CodeforcesCogError("Can't multiply by non-integer") else: handle_counts[parse_str[0]] = 1 parsed_handles.append(parse_str[0]) if sum(handle_counts.values()) > 100000: raise CodeforcesCogError('Too large of a team!') cf_handles = await cf_common.resolve_handles(ctx, self.converter, parsed_handles, mincnt=1, maxcnt=1000) cf_handles = normalize(cf_handles) cf_to_original = {a: b for a, b in zip(cf_handles, parsed_handles)} original_to_cf = {a: b for a, b in zip(parsed_handles, cf_handles)} users = await cf.user.info(handles=cf_handles) user_strs = [] for a, b in handle_counts.items(): if b > 1: user_strs.append(f'{original_to_cf[a]}*{b}') elif b == 1: user_strs.append(original_to_cf[a]) elif b < 0: raise CodeforcesCogError('How can you have negative members in team?') user_str = ', '.join(user_strs) ratings = [user.rating for user in users if user.rating for _ in range(handle_counts[cf_to_original[user.handle.lower()]])] if len(ratings) == 0: raise CodeforcesCogError("No CF usernames with ratings passed in.") left = -100.0 right = 10000.0 teamRating = Codeforces.composeRatings(left, right, ratings) embed = discord.Embed(title=user_str, description=teamRating, color=cf.rating2rank(teamRating).color_embed) await ctx.send(embed = embed)
def _make_pages(users): chunks = paginator.chunkify(users, _HANDLES_PER_PAGE) pages = [] done = 0 style = table.Style('{:>} {:<} {:<} {:<}') for chunk in chunks: t = table.Table(style) t += table.Header('#', 'Name', 'Handle', 'Rating') t += table.Line() for i, (member, handle, rating) in enumerate(chunk): rank = cf.rating2rank(rating) rating_str = 'N/A' if rating is None else str(rating) t += table.Data(i + done, member.display_name, handle, f'{rating_str} ({rank.title_abbr})') table_str = '```\n' + str(t) + '\n```' embed = discord_common.cf_color_embed(description=table_str) pages.append(('Handles of server members', embed)) done += len(chunk) return pages
def _make_pages(users, title): chunks = paginator.chunkify(users, _HANDLES_PER_PAGE) pages = [] done = 0 style = table.Style("{:>} {:<} {:<} {:<}") for chunk in chunks: t = table.Table(style) t += table.Header("#", "Name", "Handle", "Rating") t += table.Line() for i, (member, handle, rating) in enumerate(chunk): name = member.display_name if len(name) > _NAME_MAX_LEN: name = name[: _NAME_MAX_LEN - 1] + "…" rank = cf.rating2rank(rating) rating_str = "N/A" if rating is None else str(rating) t += table.Data(i + done, name, handle, f"{rating_str} ({rank.title_abbr})") table_str = "```\n" + str(t) + "\n```" embed = discord_common.cf_color_embed(description=table_str) pages.append((title, embed)) done += len(chunk) return pages
def _make_pages_gudgitters(users, title): chunks = paginator.chunkify(users, _HANDLES_PER_PAGE) pages = [] done = 0 style = table.Style('{:>} {:<} {:<} {:<}') for chunk in chunks: t = table.Table(style) t += table.Header('#', 'Name', 'Handle', 'Rating') t += table.Line() for i, (member, handle, rating) in enumerate(chunk): name = member if len(name) > _NAME_MAX_LEN: name = name[:_NAME_MAX_LEN - 1] + '…' rank = cf.rating2rank(rating) rating_str = 'N/A' if rating is None else str(rating) t += table.Data(i + done, name, handle, f'{rating_str}') table_str = '```\n' + str(t) + '\n```' embed = discord_common.cf_color_embed(description=table_str) pages.append((title, embed)) done += len(chunk) return pages
async def visualrank(self, ctx, contest_id: int, *args: str): """Plot rating changes by rank. Add handles to specify a handle in the plot. if arguments contains `+server`, it will include just server members and not all codeforces users. Specify `+zoom` to zoom to the neighborhood of handles.""" args = set(args) (in_server, zoom), handles = cf_common.filter_flags(args, ['+server', '+zoom']) handles = await cf_common.resolve_handles(ctx, self.converter, handles, mincnt=0, maxcnt=20) rating_changes = await cf.contest.ratingChanges(contest_id=contest_id) if in_server: guild_handles = set(handle for discord_id, handle in cf_common.user_db.get_handles_for_guild(ctx.guild.id)) rating_changes = [rating_change for rating_change in rating_changes if rating_change.handle in guild_handles or rating_change.handle in handles] if not rating_changes: raise GraphCogError(f'No rating changes for contest `{contest_id}`') users_to_mark = {} for rating_change in rating_changes: user_delta = rating_change.newRating - rating_change.oldRating if rating_change.handle in handles: users_to_mark[rating_change.handle] = (rating_change.rank, user_delta) ymargin = 50 xmargin = 50 if users_to_mark and zoom: xmin = min(point[0] for point in users_to_mark.values()) xmax = max(point[0] for point in users_to_mark.values()) ymin = min(point[1] for point in users_to_mark.values()) ymax = max(point[1] for point in users_to_mark.values()) else: ylim = 0 if users_to_mark: ylim = max(abs(point[1]) for point in users_to_mark.values()) ylim = max(ylim, 200) xmin = 0 xmax = max(rating_change.rank for rating_change in rating_changes) ymin = -ylim ymax = ylim ranks = [] delta = [] color = [] for rating_change in rating_changes: user_delta = rating_change.newRating - rating_change.oldRating if (xmin - xmargin <= rating_change.rank <= xmax + xmargin and ymin - ymargin <= user_delta <= ymax + ymargin): ranks.append(rating_change.rank) delta.append(user_delta) color.append(cf.rating2rank(rating_change.oldRating).color_graph) title = rating_changes[0].contestName plt.clf() fig = plt.figure(figsize=(12, 8)) plt.title(title) plt.xlabel('Rank') plt.ylabel('Rating Changes') mark_size = 2e4 / len(ranks) plt.xlim(xmin - xmargin, xmax + xmargin) plt.ylim(ymin - ymargin, ymax + ymargin) plt.scatter(ranks, delta, s=mark_size, c=color) for handle, point in users_to_mark.items(): plt.annotate(handle, xy=point, xytext=(0, 0), textcoords='offset points', ha='left', va='bottom', fontsize='large') plt.plot(*point, marker='o', markersize=5, color='black') discord_file = gc.get_current_figure_as_file() plt.close(fig) embed = discord_common.cf_color_embed(title=title) discord_common.attach_image(embed, discord_file) discord_common.set_author_footer(embed, ctx.author) await ctx.send(embed=embed, file=discord_file)
def rating_to_displayable_rank(rating): rank = cf.rating2rank(rating).title role = rank_to_role.get(rank) return role.mention if role else rank
async def visualrank(self, ctx, contest_id: int, *args: str): """Plot rating changes by rank. Add handles to specify a handle in the plot. if arguments contains `+server`, it will include just server members and not all codeforces users. Specify `+zoom` to zoom to the neighborhood of handles.""" args = set(args) (in_server, zoom), handles = cf_common.filter_flags(args, ["+server", "+zoom"]) handles = await cf_common.resolve_handles(ctx, self.converter, handles, mincnt=0, maxcnt=20) users = cf_common.cache2.rating_changes_cache.get_rating_changes_for_contest( contest_id) if not users: raise GraphCogError( f"No rating change cache for contest `{contest_id}`") if in_server: guild_handles = [ handle for discord_id, handle in cf_common.user_db.get_handles_for_guild(ctx.guild.id) ] users = [user for user in users if user.handle in guild_handles] ranks = [] delta = [] color = [] users_to_mark = dict() for user in users: user_delta = user.newRating - user.oldRating ranks.append(user.rank) delta.append(user_delta) color.append(cf.rating2rank(user.oldRating).color_graph) if user.handle in handles: users_to_mark[user.handle] = (user.rank, user_delta) title = users[0].contestName plt.clf() fig = plt.figure(figsize=(12, 8)) plt.title(title) plt.xlabel("Rank") plt.ylabel("Rating Changes") ymargin = 50 xmargin = 50 if users_to_mark and zoom: xmin = min(point[0] for point in users_to_mark.values()) xmax = max(point[0] for point in users_to_mark.values()) ymin = min(point[1] for point in users_to_mark.values()) ymax = max(point[1] for point in users_to_mark.values()) mark_size = 2e4 / (xmax - xmin + 2 * xmargin) plt.xlim(xmin - xmargin, xmax + xmargin) plt.ylim(ymin - ymargin, ymax + ymargin) else: ylim = 0 if users_to_mark: ylim = max(abs(point[1]) for point in users_to_mark.values()) ylim = max(ylim, 200) xmax = max(user.rank for user in users) mark_size = 2e4 / (xmax + 2 * xmargin) plt.xlim(-xmargin, xmax + xmargin) plt.ylim(-ylim - ymargin, ylim + ymargin) plt.scatter(ranks, delta, s=mark_size, c=color) for handle, point in users_to_mark.items(): plt.annotate( handle, xy=point, xytext=(0, 0), textcoords="offset points", ha="left", va="bottom", fontsize="large", ) plt.plot(*point, marker="o", markersize=5, color="black") discord_file = gc.get_current_figure_as_file() plt.close(fig) embed = discord_common.cf_color_embed(title=title) discord_common.attach_image(embed, discord_file) discord_common.set_author_footer(embed, ctx.author) await ctx.send(embed=embed, file=discord_file)