async def _update_task(self, _): contest_cache = cf_common.cache2.contest_cache self.future_contests = contest_cache.get_contests_in_phase('BEFORE') self.active_contests = ( contest_cache.get_contests_in_phase('CODING') + contest_cache.get_contests_in_phase('PENDING_SYSTEM_TEST') + contest_cache.get_contests_in_phase('SYSTEM_TEST')) self.finished_contests = contest_cache.get_contests_in_phase( 'FINISHED') # Future contests already sorted by start time. self.active_contests.sort(key=lambda contest: contest.startTimeSeconds) self.finished_contests.sort(key=lambda contest: contest.end_time, reverse=True) # Keep most recent _FINISHED_LIMIT self.finished_contests = self.finished_contests[: _FINISHED_CONTESTS_LIMIT] self.logger.info(f'Refreshed cache') self.start_time_map.clear() for contest in self.future_contests: if not cf_common.is_nonstandard_contest(contest): # Exclude non-standard contests from reminders. self.start_time_map[contest.startTimeSeconds].append(contest) self._reschedule_all_tasks()
async def vc(self, ctx, *args: str): """Recommends a contest based on Codeforces rating of the handle provided. e.g ;vc mblazev c1729 +global +hello +goodbye +avito""" markers = [x for x in args if x[0] == '+'] handles = [x for x in args if x[0] != '+'] or ('!' + str(ctx.author), ) handles = await cf_common.resolve_handles(ctx, self.converter, handles, maxcnt=25) info = await cf.user.info(handles=handles) contests = cf_common.cache2.contest_cache.get_contests_in_phase( 'FINISHED') if not markers: divr = sum(user.effective_rating for user in info) / len(handles) div1_indicators = ['div1', 'global', 'avito', 'goodbye', 'hello'] markers = [ 'div3' ] if divr < 1600 else ['div2'] if divr < 2100 else div1_indicators recommendations = { contest.id for contest in contests if contest.matches(markers) and not cf_common.is_nonstandard_contest(contest) and not any( cf_common.is_contest_writer(contest.id, handle) for handle in handles) } # Discard contests in which user has non-CE submissions. visited_contests = await cf_common.get_visited_contests(handles) recommendations -= visited_contests if not recommendations: raise CodeforcesCogError('Unable to recommend a contest') recommendations = list(recommendations) random.shuffle(recommendations) contests = [ cf_common.cache2.contest_cache.get_contest(contest_id) for contest_id in recommendations[:25] ] def make_line(c): return f'[{c.name}]({c.url}) {cf_common.pretty_time_format(c.durationSeconds)}' def make_page(chunk): str_handles = '`, `'.join(handles) message = f'Recommended contest(s) for `{str_handles}`' vc_str = '\n'.join(make_line(contest) for contest in chunk) embed = discord_common.cf_color_embed(description=vc_str) return message, embed pages = [make_page(chunk) for chunk in paginator.chunkify(contests, 5)] paginator.paginate(self.bot, ctx.channel, pages, wait_time=5 * 60, set_pagenum_footers=True)
async def fullsolve(self, ctx, *args: str): """Displays a list of contests, sorted by number of unsolved problems. Contest names matching any of the provided tags will be considered. e.g ;fullsolve +edu""" handle, = await cf_common.resolve_handles(ctx, self.converter, ('!' + str(ctx.author),)) tags = [x for x in args if x[0] == '+'] problem_to_contests = cf_common.cache2.problemset_cache.problem_to_contests contests = [contest for contest in cf_common.cache2.contest_cache.get_contests_in_phase('FINISHED') if (not tags or contest.matches(tags)) and not cf_common.is_nonstandard_contest(contest)] # subs_by_contest_id contains contest_id mapped to [list of problem.name] subs_by_contest_id = defaultdict(set) for sub in await cf.user.status(handle=handle): if sub.verdict == 'OK': try: contest = cf_common.cache2.contest_cache.get_contest(sub.problem.contestId) problem_id = (sub.problem.name, contest.startTimeSeconds) for contestId in problem_to_contests[problem_id]: subs_by_contest_id[contestId].add(sub.problem.name) except cache_system2.ContestNotFound: pass contest_unsolved_pairs = [] for contest in contests: num_solved = len(subs_by_contest_id[contest.id]) try: num_problems = len(cf_common.cache2.problemset_cache.get_problemset(contest.id)) if 0 < num_solved < num_problems: contest_unsolved_pairs.append((contest, num_solved, num_problems)) except cache_system2.ProblemsetNotCached: # In case of recent contents or cetain bugged contests pass contest_unsolved_pairs.sort(key=lambda p: (p[2] - p[1], -p[0].startTimeSeconds)) if not contest_unsolved_pairs: await ctx.send(f'`{handle}` has no contests to fullsolve :confetti_ball:') return def make_line(entry): contest, solved, total = entry return f'[{contest.name}]({contest.url})\N{EN SPACE}[{solved}/{total}]' def make_page(chunk): message = f'Fullsolve list for `{handle}`' full_solve_list = '\n'.join(make_line(entry) for entry in chunk) embed = discord_common.cf_color_embed(description=full_solve_list) return message, embed pages = [make_page(chunk) for chunk in paginator.chunkify(contest_unsolved_pairs, 10)] paginator.paginate(self.bot, ctx.channel, pages, wait_time=5 * 60, set_pagenum_footers=True)
async def vc(self, ctx, *args: str): """Recommends a contest based on Codeforces rating of the handle provided. e.g ;vc mblazev c1729 +global +hello +goodbye +avito""" def strfilt(s): return ''.join(x for x in s.lower() if x.isalnum()) markers = [x for x in args if x[0] == '+'] handles = [x for x in args if x[0] != '+'] or ('!' + str(ctx.author), ) handles = await cf_common.resolve_handles(ctx, self.converter, handles) user_submissions = [ await cf.user.status(handle=handle) for handle in handles ] info = await cf.user.info(handles=handles) contests = await cf.contest.list() if not markers: divr = sum(user.rating or 1500 for user in info) / len(handles) div1_indicators = ['div1', 'global', 'avito', 'goodbye', 'hello'] divs = [ 'div3' ] if divr < 1600 else ['div2'] if divr < 2100 else div1_indicators else: divs = [strfilt(x) for x in markers] recommendations = { contest.id for contest in contests if contest.phase == 'FINISHED' and any(tag in strfilt(contest.name) for tag in divs) and not cf_common.is_nonstandard_contest(contest) } for subs in user_submissions: for sub in subs: recommendations.discard(sub.problem.contestId) if not recommendations: await ctx.send('Unable to recommend a contest') else: num_contests = len(recommendations) choice = max(random.randrange(num_contests), random.randrange(num_contests)) contest_id = sorted(list(recommendations))[choice] # from and count are for ranklist, set to minimum (1) because we only need name str_handles = '`, `'.join(handles) contest, _, _ = await cf.contest.standings(contest_id=contest_id, from_=1, count=1) embed = discord.Embed(title=contest.name, url=contest.url) await ctx.send(f'Recommended contest for `{str_handles}`', embed=embed)
async def mashup(self, ctx, *args): """Create a mashup contest using problems within +-100 of average rating of handles provided. Add tags with "+" before them. """ handles = [arg for arg in args if arg[0] != '+'] tags = [arg[1:] for arg in args if arg[0] == '+' and len(arg) > 1] handles = handles or ('!' + str(ctx.author), ) handles = await cf_common.resolve_handles(ctx, self.converter, handles) resp = [await cf.user.status(handle=handle) for handle in handles] submissions = [sub for user in resp for sub in user] solved = {sub.problem.name for sub in submissions} info = await cf.user.info(handles=handles) rating = int( round( sum(user.rating or 1500 for user in info) / len(handles), -2)) problems = [ prob for prob in cf_common.cache2.problem_cache.problems if abs(prob.rating - rating) <= 100 and prob.name not in solved and not any( cf_common.is_contest_writer(prob.contestId, handle) for handle in handles) and not cf_common.is_nonstandard_contest( cf_common.cache2.contest_cache.get_contest(prob.contestId)) ] if tags: problems = [prob for prob in problems if prob.tag_matches(tags)] if len(problems) < 4: await ctx.send('Problems not found within the search parameters') return problems.sort(key=lambda problem: cf_common.cache2.contest_cache. get_contest(problem.contestId).startTimeSeconds) choices = [] for i in range(4): k = max(random.randrange(len(problems) - i) for _ in range(2)) for c in choices: if k >= c: k += 1 choices.append(k) choices.sort() problems = reversed([problems[k] for k in choices]) msg = '\n'.join(f'{"ABCD"[i]}: [{p.name}]({p.url}) [{p.rating}]' for i, p in enumerate(problems)) str_handles = '`, `'.join(handles) embed = discord_common.cf_color_embed(description=msg) await ctx.send(f'Mashup contest for `{str_handles}`', embed=embed)
async def vc(self, ctx, *args: str): """Recommends a contest based on Codeforces rating of the handle provided. e.g ;vc mblazev c1729 +global +hello +goodbye +avito""" markers = [x for x in args if x[0] == '+'] handles = [x for x in args if x[0] != '+'] or ('!' + str(ctx.author), ) handles = await cf_common.resolve_handles(ctx, self.converter, handles, maxcnt=25) info = await cf.user.info(handles=handles) contests = cf_common.cache2.contest_cache.get_contests_in_phase( 'FINISHED') if not markers: divr = sum(user.effective_rating for user in info) / len(handles) div1_indicators = ['div1', 'global', 'avito', 'goodbye', 'hello'] markers = [ 'div3' ] if divr < 1600 else ['div2'] if divr < 2100 else div1_indicators recommendations = { contest.id for contest in contests if contest.matches(markers) and not cf_common.is_nonstandard_contest(contest) and not any( cf_common.is_contest_writer(contest.id, handle) for handle in handles) } # Discard contests in which user has non-CE submissions. visited_contests = await cf_common.get_visited_contests(handles) recommendations -= visited_contests if not recommendations: raise CodeforcesCogError('Unable to recommend a contest') recommendations = list(recommendations) random.shuffle(recommendations) contests = [ cf_common.cache2.contest_cache.get_contest(contest_id) for contest_id in recommendations[:5] ] msg = '\n'.join(f'{i+1}. [{c.name}]({c.url})' for i, c in enumerate(contests)) embed = discord_common.cf_color_embed(description=msg) str_handles = '`, `'.join(handles) await ctx.send(f'Recommended contest(s) for `{str_handles}`', embed=embed)
async def generate_ranklist(self, contest_id, *, fetch_changes=False, predict_changes=False): assert fetch_changes ^ predict_changes contest, problems, standings = await cf.contest.standings(contest_id=contest_id, show_unofficial=True) now = time.time() # Exclude PRACTICE and MANAGER standings = [row for row in standings if row.party.participantType in ('CONTESTANT', 'OUT_OF_COMPETITION', 'VIRTUAL')] if fetch_changes: # Fetch final rating changes from CF. # For older contests. is_rated = False try: changes = await cf.contest.ratingChanges(contest_id=contest_id) # For contests intended to be rated but declared unrated, an empty list is returned. is_rated = len(changes) > 0 except cf.RatingChangesUnavailableError: pass ranklist = Ranklist(contest, problems, standings, now, is_rated=is_rated) if is_rated: delta_by_handle = {change.handle: change.newRating - change.oldRating for change in changes} ranklist.set_deltas(delta_by_handle) elif predict_changes: # Rating changes have not been applied yet, predict rating changes. # For running/recent contests. _, _, standings_official = await cf.contest.standings(contest_id=contest_id) has_teams = any(row.party.teamId is not None for row in standings_official) if cf_common.is_nonstandard_contest(contest) or has_teams: # The contest is not rated ranklist = Ranklist(contest, problems, standings, now, is_rated=False) else: current_rating = await CacheSystem.getUsersEffectiveRating(activeOnly=False) current_rating = {row.party.members[0].handle: current_rating.get(row.party.members[0].handle, 1500) for row in standings_official} if 'Educational' in contest.name: # For some reason educational contests return all contestants in ranklist even # when unofficial contestants are not requested. current_rating = {handle: rating for handle, rating in current_rating.items() if rating < 2100} ranklist = Ranklist(contest, problems, standings, now, is_rated=True) ranklist.predict(current_rating) return ranklist
async def vc(self, ctx, *args: str): """Recommends a contest based on Codeforces rating of the handle provided. e.g ;vc mblazev c1729 +global +hello +goodbye +avito""" markers = [x for x in args if x[0] == '+'] handles = [x for x in args if x[0] != '+'] or ('!' + str(ctx.author),) handles = await cf_common.resolve_handles(ctx, self.converter, handles, maxcnt=10) user_submissions = [await cf.user.status(handle=handle) for handle in handles] info = await cf.user.info(handles=handles) contests = cf_common.cache2.contest_cache.get_contests_in_phase('FINISHED') problem_to_contests = cf_common.cache2.problemset_cache.problem_to_contests if not markers: divr = sum(user.effective_rating for user in info) / len(handles) div1_indicators = ['div1', 'global', 'avito', 'goodbye', 'hello'] markers = ['div3'] if divr < 1600 else ['div2'] if divr < 2100 else div1_indicators recommendations = {contest.id for contest in contests if contest.matches(markers) and not cf_common.is_nonstandard_contest(contest) and not any(cf_common.is_contest_writer(contest.id, handle) for handle in handles)} # discard contests in which user has non-CE submissions for subs in user_submissions: for sub in subs: if sub.verdict == 'COMPILATION_ERROR': continue try: contest = cf_common.cache2.contest_cache.get_contest(sub.problem.contestId) problem_id = (sub.problem.name, contest.startTimeSeconds) for cid in problem_to_contests[problem_id]: recommendations.discard(cid) except cache_system2.ContestNotFound: pass if not recommendations: raise CodeforcesCogError('Unable to recommend a contest') recommendations = list(recommendations) random.shuffle(recommendations) contests = [cf_common.cache2.contest_cache.get_contest(contest_id) for contest_id in recommendations[:5]] msg = '\n'.join(f'{i+1}. [{c.name}]({c.url})' for i, c in enumerate(contests)) embed = discord_common.cf_color_embed(description=msg) str_handles = '`, `'.join(handles) await ctx.send(f'Recommended contest(s) for `{str_handles}`', embed=embed)
def check(problem): contest = cf_common.cache2.contest_cache.get_contest( problem.contestId) return (not cf_common.is_nonstandard_contest(contest) and not cf_common.is_contest_writer(problem.contestId, handle))
async def vc(self, ctx, *args: str): """Recommends a contest based on Codeforces rating of the handle provided. e.g ;vc mblazev c1729 +global +hello +goodbye +avito""" def strfilt(s): return ''.join(x for x in s.lower() if x.isalnum()) markers = [x for x in args if x[0] == '+'] handles = [x for x in args if x[0] != '+'] or ('!' + str(ctx.author), ) handles = await cf_common.resolve_handles(ctx, self.converter, handles) user_submissions = [ await cf.user.status(handle=handle) for handle in handles ] info = await cf.user.info(handles=handles) contests = cf_common.cache2.contest_cache.get_contests_in_phase( 'FINISHED') problem_to_contests = cf_common.cache2.problemset_cache.problem_to_contests if not markers: divr = sum(user.effective_rating for user in info) / len(handles) div1_indicators = ['div1', 'global', 'avito', 'goodbye', 'hello'] divs = [ 'div3' ] if divr < 1600 else ['div2'] if divr < 2100 else div1_indicators else: divs = [strfilt(x) for x in markers] recommendations = { contest.id for contest in contests if any(tag in strfilt(contest.name) for tag in divs) and not cf_common.is_nonstandard_contest(contest) } # discard contests in which user has non-CE submissions for subs in user_submissions: for sub in subs: if sub.verdict == 'COMPILATION_ERROR': continue try: contest = cf_common.cache2.contest_cache.get_contest( sub.problem.contestId) problem_id = (sub.problem.name, contest.startTimeSeconds) for cid in problem_to_contests[problem_id]: recommendations.discard(cid) except cache_system2.ContestNotFound: pass if not recommendations: await ctx.send('Unable to recommend a contest') else: num_contests = len(recommendations) choice = max(random.randrange(num_contests), random.randrange(num_contests)) contest_id = sorted(list(recommendations))[choice] # from and count are for ranklist, set to minimum (1) because we only need name str_handles = '`, `'.join(handles) contest, _, _ = await cf.contest.standings(contest_id=contest_id, from_=1, count=1) embed = discord.Embed(title=contest.name, url=contest.url) await ctx.send(f'Recommended contest for `{str_handles}`', embed=embed)