Beispiel #1
0
    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()
Beispiel #2
0
    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)
Beispiel #3
0
    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)
Beispiel #4
0
    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)
Beispiel #5
0
    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)
Beispiel #6
0
    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)
Beispiel #7
0
    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
Beispiel #8
0
    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)
Beispiel #9
0
 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))
Beispiel #10
0
    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)