Exemple #1
0
        def soup_parse(soup):
            submission_id = soup["id"]
            result = soup.find(class_="sub-result")["class"][-1]
            try:
                score = soup.find(class_="sub-result").find(
                    class_="score").text.split("/")
                score_num, score_denom = map(int, score)
                points = score_num / score_denom
            except ValueError:
                score_num, score_denom = 0, 0
                points = 0
            lang = soup.find(class_="language").text

            q = session.query(Language_DB).filter(
                Language_DB.short_name == lang)
            if q.count():
                lang = q.first().key
            # if not as short_name it'll be key, why? idk

            problem = soup.find(class_="name").find("a")["href"].split("/")[-1]
            name = soup.find(class_="name").find("a").text
            date = soup.find(class_="time-with-rel")["data-iso"]
            try:
                time = float(soup.find("div", class_="time")["title"][:-1])
            except ValueError:
                time = None
            except KeyError:
                time = None
            size = ""
            memory = soup.find("div", class_="memory").text.split(" ")
            if len(memory) == 2:
                memory, size = memory
                memory = float(memory)
                if size == "MB":
                    memory *= 1024
                if size == "GB":
                    memory *= 1024 * 1024
            else:
                # --- case
                memory = 0

            res = {
                "id": submission_id,
                "problem": problem,
                "user": username,
                "date": date,
                "language": lang,
                "time": time,
                "memory": memory,
                "points": points,
                "result": result,
                "score_num": score_num,
                "score_denom": score_denom,
                "problem_name": html.unescape(name),
            }
            ret = Submission(res)
            return ret
Exemple #2
0
 def get_unsolved_problems(self, username: str, types: List[str], low: int = 1, high: int = 50) -> Problem_DB:
     # Does not find problems if you first
     # +update_problems
     # +gimme
     # This is cause calling the /problems api does not return is_organization_private
     # The original goal of is_organization_private filter is to prevent leaking problems
     conds = [Problem_DB.types.contains(_type) for _type in types]
     sub_q = session.query(Submission_DB, func.max(Submission_DB.points))\
         .filter(Submission_DB._user == username)\
         .group_by(Submission_DB._code).subquery()
     q = session.query(Problem_DB)\
         .join(sub_q, Problem_DB.code == sub_q.c._code, isouter=True)\
         .filter(func.ifnull(sub_q.c.points, 0) < Problem_DB.points)\
         .filter(or_(*conds))\
         .filter(Problem_DB.points.between(low, high))\
         .filter(Problem_DB.is_organization_private == 0)\
         .filter(Problem_DB.is_public == 1)
     return q.all()
Exemple #3
0
    async def async_init(self):
        problem_qq = session.query(Problem_DB).filter(
            Problem_DB.code.in_(self._solved_problems))
        problem_q = session.query(Problem_DB.code).filter(
            Problem_DB.code.in_(self._solved_problems)).all()
        problem_q = list(map(itemgetter(0), problem_q))
        for problem_code in self._solved_problems:
            try:
                if problem_code not in problem_q:
                    api = API()
                    await api.get_problem(problem_code)
                    session.add(Problem_DB(api.data.object))
                    session.commit()
            except ObjectNotFound:
                pass
        self.solved_problems = problem_qq.all()

        organization_qq = session.query(Organization_DB).filter(
            Organization_DB.id.in_(self._organizations))
        organization_q = session.query(Organization_DB.id).filter(
            Organization_DB.id.in_(self._organizations)).all()
        organization_q = list(map(itemgetter(0), organization_q))
        for organization_id in self._organizations:
            if organization_id not in organization_q:
                api = API()
                await api.get_organizations()
                for organization in api.data.objects:
                    if organization.id not in organization_q and organization.id in self._organizations:
                        session.add(Organization_DB(organization))
                        session.commit()
                break
        self.organizations = organization_qq.all()

        for contest in self._contests:
            if contest["rating"]:
                self.max_rating = max(self.max_rating or 0, contest["rating"])

        self._contest_keys = list(map(itemgetter("key"), self._contests))

        contest_qq = session.query(Contest_DB).filter(
            Contest_DB.key.in_(self._contest_keys))
        contest_q = session.query(Contest_DB.key).filter(
            Contest_DB.key.in_(self._contest_keys)).all()
        contest_q = list(map(itemgetter(0), contest_q))
        for contest_key in self._contest_keys:
            try:
                if contest_key not in contest_q:
                    api = API()
                    await api.get_contest(contest_key)
                    # This causes db errors, and in the case the above doesn't catch it.
                    # This will be a last ditch effort
                    if session.query(Contest_DB).filter(
                            Contest_DB.key == contest_key).count():
                        continue
                    session.add(Contest_DB(api.data.object))
                    session.commit()
            except ObjectNotFound:
                pass
        self.contests = contest_qq.all()
Exemple #4
0
    async def async_init(self):
        user = session.query(User_DB).filter(User_DB.username == self._user)

        if user.count() == 0:
            api = API()
            await api.get_user(self._user)
            session.add(api.data.object)
            session.commit()
        self.user = user.first()

        contest = session.query(Contest_DB).filter(
            Contest_DB.key == self._contest)

        if contest.count() == 0:
            api = API()
            await api.get_contest(self._contest)
            session.add(api.data.object)
            session.commit()
        self.contest = contest.first()
Exemple #5
0
    async def get_user(self, username: str) -> User_DB:
        q = session.query(User_DB).\
            filter(func.lower(User_DB.username) == func.lower(username))
        # if q.count():
        #     # solved_problems checks if it has detailed rows
        #     if len(q.first().solved_problems) != 0:
        #         return q.first()

        a = API()
        await a.get_user(username)
        q = session.query(User_DB).\
            filter(func.lower(User_DB.username) == func.lower(a.data.object.username))
        if q.count():
            # Needs to be fetch, the default (evaluate) is not able to eval
            # the query
            q.delete(synchronize_session='fetch')
        session.add(User_DB(a.data.object))
        session.commit()
        return q.first()
Exemple #6
0
    async def update_submissions(self, ctx):
        """Updates the submissions of every user in db (Warning! Slow!)"""
        q = session.query(Submission_DB._user).distinct(Submission_DB._user)
        usernames = list(map(itemgetter(0), q.all()))
        await ctx.send(f"Recaching submissions for {len(usernames)}"
                       f" users. This will take a long time (perhaps hours).")
        session.query(Submission_DB).delete()
        session.commit()

        query = Query()
        count = 0
        msg = await ctx.send(f"{count}/{len(usernames)} users cached...")
        for username in usernames:
            await msg.edit(content=f"{count}/{len(usernames)} users cached..."
                           f" ({username})")
            await query.get_submissions(username)
            time.sleep(30)  # PLS DON'T GET CLOUDFLARED
            count += 1
        await msg.edit(content=f"{len(usernames)} users cache. Done!")
Exemple #7
0
 async def async_map(_type, objects):
     problems = session.query(Problem_DB.code, Problem_DB).all()
     problems = {k: v for k, v in problems}
     users = session.query(User_DB.username, User_DB).all()
     users = {k: v for k, v in users}
     languages = session.query(Language_DB.key, Language_DB).all()
     languages = {k: v for k, v in languages}
     to_gather = []
     lock_table = {}
     for obj in objects:
         # If a user attempts to cache submissions after the latest release of a contest, there is a chance it will cause a db error
         # this is because any submissions which are not in the db will be called by the api and stored into the db
         # but in between that moment of calling the api and storing into the db, another process will call the api for the same problem
         # because it is technically not in memory yet
         # This fix to this is two global tables and a lock
         to_gather.append(
             obj.async_init(problems, users, languages, lock_table))
     await asyncio.gather(*to_gather)
     session.commit()
Exemple #8
0
    async def _set(self, ctx, member, username: str):
        """Manually link two accounts together"""
        query = Query()
        member = await query.parseUser(ctx, member)

        if username != "+remove":
            user = await query.get_user(username)

            if user is None:
                await ctx.send(f'{username} does not exist on dmoj')
                return

            username = user.username

        handle = query.get_handle(member.id, ctx.guild.id)
        if handle == username:
            return await ctx.send(
                f'{member.display_name} is already linked with {handle}')

        if handle:
            handle = session.query(Handle_DB)\
                .filter(Handle_DB.id == member.id)\
                .filter(Handle_DB.guild_id == ctx.guild.id).first()
            session.delete(handle)
            session.commit()
            await ctx.send(
                f'Unlinked {member.display_name} with handle {handle.handle}')

        if username == "+remove":
            return

        if query.get_handle_user(username, ctx.guild.id):
            await ctx.send('This handle is already linked with another user')
            return

        handle = Handle_DB()
        handle.id = member.id
        handle.handle = username
        handle.user_id = user.id
        handle.guild_id = ctx.guild.id
        session.add(handle)
        session.commit()
        await ctx.send(f"Linked {member.name} with {username}.")

        rank_to_role = {
            role.name: role
            for role in ctx.guild.roles if role.name in RANKS
        }
        rank = self.rating_to_rank(user.rating)
        if rank in rank_to_role:
            await self._update_rank(ctx.author, rank_to_role[rank],
                                    'Dmoj account linked')
        else:
            await ctx.send("You are missing the " + rank.name + " role")
Exemple #9
0
 async def cache_contests(self, ctx):
     '''Individually caches every contest'''
     msg = await ctx.send('Caching...')
     query = Query()
     contests = await query.get_contests()
     for contest in contests:
         q = session.query(Contest_DB).filter(Contest_DB.key == contest.key)
         if q.count() > 0:
             q.delete()
             session.commit()
         await query.get_contest(contest.key)
     return await msg.edit(content=f'Cached {len(contests)} contests')
Exemple #10
0
 async def unlink(self, ctx):
     """Unlink your discord account with your dmoj account"""
     query = Query()
     if not query.get_handle(ctx.author.id, ctx.guild.id):
         await ctx.send('You are not linked with any user')
         return
     handle = session.query(Handle_DB)\
         .filter(Handle_DB.id == ctx.author.id)\
         .filter(Handle_DB.guild_id == ctx.guild.id).first()
     session.delete(handle)
     session.commit()
     await ctx.send(f'Unlinked you with handle {handle.handle}')
Exemple #11
0
async def solved(ctx):
    """Shows a user's last solved problems"""
    minP = 0
    maxP = 100
    query = Query()
    username = None
    for arg in ctx.options.args:
        if arg.startswith("p>="):
            minP = max(minP, int(arg[3:]))
        elif arg.startswith("p<="):
            maxP = min(maxP, int(arg[3:]))
        else:
            username = arg
    username = (await query.get_user(username)).username
    if username is None:
        username = query.get_handle(ctx.author.id, ctx.get_guild().id)
    await query.get_submissions(username, result="AC")

    submissions = (session.query(Submission_DB).filter(
        Submission_DB._user == username).filter(
            Submission_DB.result == "AC").options(
                orm.joinedload(Submission_DB.problem, innerjoin=True)).join(
                    Submission_DB.problem).filter(
                        Problem_DB.is_organization_private == 0).filter(
                            Problem_DB.is_public == 1).order_by(
                                Submission_DB.date).all())
    uniqueSubmissions = []
    solved = set()
    for sub in submissions:
        if sub._code not in solved:
            solved.add(sub._code)
            if minP <= sub.points and sub.points <= maxP:
                uniqueSubmissions.append(sub)
    uniqueSubmissions.reverse()
    pag = lightbulb.utils.EmbedPaginator(max_chars=1024)

    for sub in uniqueSubmissions:
        age = (datetime.now() - sub.date).days
        pag.add_line(
            f"[{sub.problem[0].name}]({SITE_URL}/problem/{sub._code}) [{sub.points}] ({age} days ago)"
        )

    if len(uniqueSubmissions) == 0:
        pag.add_line("No submission")

    @pag.embed_factory()
    def build_embed(page_index, content):
        return hikari.Embed().add_field(name="Recently solved problems by " +
                                        username,
                                        value=content)

    navigator = nav.ButtonNavigator(pag.build_pages())
    await navigator.run(ctx)
Exemple #12
0
    async def rating(self, ctx, *usernames):
        """Plot rating progression"""
        usernames = list(usernames)

        query = Query()
        if usernames == []:
            usernames = [query.get_handle(ctx.author.id, ctx.guild.id)]

        users = await asyncio.gather(
            *[query.get_user(username) for username in usernames])
        usernames = [user.username for user in users]
        for i in range(len(users)):
            if users[i] is None:
                return await ctx.send(f'{usernames[i]} does not exist on DMOJ')
        if len(users) > 10:
            return await ctx.send('Too many users given, max 10')

        cond = [Contest_DB.rankings.contains(user.username) for user in users]
        q = session.query(Contest_DB).filter(or_(*cond))\
            .filter(Contest_DB.is_rated == 1)
        contests = q.all()

        def get_rating_change(rankings, users):
            ret = {}
            for ranking in rankings:
                for user in users:
                    if (user.username == ranking['user']
                            and ranking['new_rating']):
                        ret[user.username] = ranking['new_rating']
            return ret

        data = {}
        data['users'] = [user.username for user in users]
        for contest in contests:
            changes = get_rating_change(contest.rankings, users)
            data[contest.end_time] = []
            for user in users:
                if user.username in changes:
                    change = changes[user.username]
                    data[contest.end_time].append(change)
                else:
                    data[contest.end_time].append(None)
        plot_rating(data)
        with open('./graphs/plot.png', 'rb') as file:
            file = discord.File(io.BytesIO(file.read()), filename='plot.png')
        embed = discord.Embed(
            title='Contest Rating',
            color=0xfcdb05,
        )
        embed.set_image(url=f'attachment://plot.png', )

        return await ctx.send(embed=embed, file=file)
Exemple #13
0
    async def get_languages(self, common_name=None) -> [Language_DB]:

        q = session.query(Language_DB).\
            filter(self.parse(Language_DB.common_name, common_name))
        if q.count():
            return q.all()
        a = API()
        await a.get_languages(common_name=common_name)
        languages = list(map(Language_DB, a.data.objects))
        for language in languages:
            session.add(language)
        session.commit()
        return languages
Exemple #14
0
    async def async_init(self):
        organization_qq = session.query(Organization_DB).\
            filter(Organization_DB.id.in_(self._organizations))
        organization_q = session.query(Organization_DB.id).\
            filter(Organization_DB.id.in_(self._organizations)).all()
        organization_q = list(map(itemgetter(0), organization_q))
        for organization_id in self._organizations:
            if organization_id not in organization_q:
                api = API()
                await api.get_organizations()
                for organization in api.data.objects:
                    if (organization.id not in organization_q
                            and organization.id in self._organizations):
                        session.add(Organization_DB(organization))
                        session.commit()
                break
        self.organizations = organization_qq.all()

        # perhaps I should check if it's the general or detailed version
        def get_code(problem):
            return problem["code"]

        self._problem_codes = list(map(get_code, self._problems))
        problem_qq = session.query(Problem_DB).\
            filter(Problem_DB.code.in_(self._problem_codes))
        problem_q = session.query(Problem_DB.code).\
            filter(Problem_DB.code.in_(self._problem_codes)).all()
        problem_q = list(map(itemgetter(0), problem_q))
        for problem_dict in self._problems:
            problem_code = problem_dict["code"]
            try:
                if problem_code not in problem_q:
                    api = API()
                    await api.get_problem(problem_code)
                    session.add(Problem_DB(api.data.object))
                    session.commit()
            except ObjectNotFound:
                pass
        self.problems = problem_qq.all()
Exemple #15
0
    async def solved(self, ctx, *args):
        """Shows a user's last solved problems"""
        minP = 0
        maxP = 100
        query = Query()
        username = None
        for arg in args:
            if arg.startswith("p>="):
                minP = max(minP, int(arg[3:]))
            elif arg.startswith("p<="):
                maxP = min(maxP, int(arg[3:]))
            else:
                username = (await query.get_user(arg)).username
        if username is None:
            username = query.get_handle(ctx.author.id, ctx.guild.id)
        await query.get_submissions(username, result='AC')

        submissions = session.query(Submission_DB)\
            .filter(Submission_DB._user == username)\
            .filter(Submission_DB.result == 'AC')\
            .options(orm.joinedload(Submission_DB.problem, innerjoin=True))\
            .join(Submission_DB.problem)\
            .filter(Problem_DB.is_organization_private == 0)\
            .filter(Problem_DB.is_public == 1)\
            .order_by(Submission_DB.date).all()
        uniqueSubmissions = []
        solved = set()
        for sub in submissions:
            if sub._code not in solved:
                solved.add(sub._code)
                if minP <= sub.points and sub.points <= maxP:
                    uniqueSubmissions.append(sub)
        uniqueSubmissions.reverse()
        page = ""
        content = []
        cnt = 0
        for sub in uniqueSubmissions:
            age = (datetime.now() - sub.date).days
            # sub.problem[0].name is rly slow
            page += f"[{sub.problem[0].name}]({SITE_URL}/problem/{sub._code}) [{sub.points}] ({age} days ago)\n"
            cnt += 1
            if cnt % 10 == 0:
                content.append(page)
                page = ""
        if page != "":
            content.append(page)
        if len(content) == 0:
            content.append("No submission")
        title = "Recently solved problems by " + username
        message = await ctx.send(embed=discord.Embed().add_field(name=title, value=content[0]))
        await scroll_embed(ctx, self.bot, message, title, content)
Exemple #16
0
 async def get_contest(self, key) -> Contest_DB:
     q = session.query(Contest_DB).\
         filter(Contest_DB.key == key)
     if q.count():
         # is_rated checks if it has detailed rows
         if q.first().is_rated is not None:
             return q.first()
     a = API()
     await a.get_contest(key)
     if q.count():
         q.delete()
     session.add(Contest_DB(a.data.object))
     session.commit()
     return q.first()
Exemple #17
0
    async def stats(self, ctx):
        '''Display cool dmoj stats that no one asked for'''
        problems = session.query(Problem_DB.points)\
            .order_by(Problem_DB.points.desc()).all()

        def tuple_first(data):
            return data[0]

        problems = list(map(tuple_first, problems))
        total_problems = len(problems)
        total_points = calculate_points(problems, total_problems)
        await ctx.send(
            'The theoretical maximum number of points you can achieve is %.2f\n'
            'There are %d public problems on DMOJ' %
            (total_points, total_problems))
Exemple #18
0
 async def force(self, ctx, _type, key):
     """Force a recache of a problem, or contest"""
     if _type.lower() == "contest":
         q = session.query(Contest_DB).filter(Contest_DB.key == key)
         if q.count() == 0:
             await ctx.send(f"There is no contests with the key {key} "
                            f"cached. Will try fetching contest")
         else:
             q.delete()
             session.commit()
         query = Query()
         await query.get_contest(key)
         await ctx.send(f"Recached contest {key}")
     if _type.lower() == "problem":
         q = session.query(Problem_DB).filter(Problem_DB.code == key)
         if q.count() == 0:
             await ctx.send(f"There is no contests with the key {key} "
                            f"cached. Will try fetching contest")
         else:
             q.delete()
             session.commit()
         query = Query()
         await query.get_problem(key)
         await ctx.send(f"Recached contest {key}")
Exemple #19
0
    async def get_problem(self, code) -> Problem_DB:
        q = session.query(Problem_DB).\
            filter(Problem_DB.code == code)
        if q.count():
            # has_rating check if it has a detailed row
            if q.first().short_circuit is not None:
                return q.first()

        a = API()
        await a.get_problem(code)
        if q.count():
            q.delete()
        session.add(Problem_DB(a.data.object))
        session.commit()
        return q.first()
Exemple #20
0
    async def async_init(self):
        problem_qq = session.query(Problem_DB).\
            filter(Problem_DB.code.in_(self._solved_problems))
        problem_q = session.query(Problem_DB.code).\
            filter(Problem_DB.code.in_(self._solved_problems)).all()
        problem_q = list(map(itemgetter(0), problem_q))
        for problem_code in self._solved_problems:
            try:
                if problem_code not in problem_q:
                    api = API()
                    await api.get_problem(problem_code)
                    session.add(Problem_DB(api.data.object))
                    session.commit()
            except ObjectNotFound:
                pass
        self.solved_problems = problem_qq.all()

        organization_qq = session.query(Organization_DB).\
            filter(Organization_DB.id.in_(self._organizations))
        organization_q = session.query(Organization_DB.id).\
            filter(Organization_DB.id.in_(self._organizations)).all()
        organization_q = list(map(itemgetter(0), organization_q))
        for organization_id in self._organizations:
            if organization_id not in organization_q:
                api = API()
                await api.get_organizations()
                for organization in api.data.objects:
                    if (organization.id not in organization_q
                            and organization.id in self._organizations):
                        session.add(Organization_DB(organization))
                        session.commit()
                break
        self.organizations = organization_qq.all()

        def get_key(contest):
            return contest["key"]

        self._contest_keys = list(map(get_key, self._contests))

        contest_qq = session.query(Contest_DB).\
            filter(Contest_DB.key.in_(self._contest_keys))
        contest_q = session.query(Contest_DB.key).\
            filter(Contest_DB.key.in_(self._contest_keys)).all()
        contest_q = list(map(itemgetter(0), contest_q))
        for contest_key in self._contest_keys:
            try:
                if contest_key not in contest_q:
                    api = API()
                    await api.get_contest(contest_key)
                    session.add(Contest_DB(api.data.object))
                    session.commit()
            except ObjectNotFound:
                pass
        self.contests = contest_qq.all()
Exemple #21
0
    async def postcontest(self, ctx, key):
        """Updates post-contest role"""

        await ctx.message.delete()
        query = Query()

        username = query.get_handle(ctx.author.id, ctx.guild.id)

        if username is None:
            return await ctx.send("Your account is not linked!")

        q = session.query(Contest_DB).filter(Contest_DB.key == key)
        # Clear cache
        if q.count():
            q.delete()
            session.commit()
        try:
            contest = await query.get_contest(key)
        except ObjectNotFound:
            await ctx.send("Contest not found")
            return

        if contest.is_organization_private:
            return await ctx.send("Contest not found")

        role = get(ctx.guild.roles, name="postcontest " + key)
        if not role:
            return await ctx.send(f"No `postcontest {key}` role found.")

        for ranking in contest.rankings:
            if ranking['user'].lower() != username.lower():
                continue

            endTime = datetime.strptime(ranking['end_time'],
                                        '%Y-%m-%dT%H:%M:%S%z')
            if endTime > datetime.now(timezone.utc).astimezone():
                return await ctx.send("Your window is not done.")
            else:
                try:
                    await ctx.author.add_roles(role)
                except discord.Forbidden:
                    return await ctx.send("No permission to assign the role.")
                return await ctx.send("You've been added to post contest.")
        return await ctx.send("You haven't joined the contest yet.")
Exemple #22
0
 async def top(self, ctx, arg='rating'):
     '''Shows registered server members in ranked order'''
     arg = arg.lower()
     if arg not in [
             'rating', 'maxrating', 'points', 'solved', 'raw', 'maxraw'
     ]:
         return await ctx.send_help('top')
     users = session.query(User_DB).join(Handle_DB, Handle_DB.handle == User_DB.username)\
         .filter(Handle_DB.guild_id == ctx.guild.id)
     leaderboard = []
     for user in users:
         if arg == 'rating':
             leaderboard.append([-(user.rating or -9999), user.username])
         elif arg == 'maxrating':
             leaderboard.append(
                 [-(user.max_rating or -9999), user.username])
         elif arg == 'points':
             leaderboard.append([-user.performance_points, user.username])
         elif arg == 'solved':
             leaderboard.append([-(user.problem_count or 0), user.username])
         elif arg == 'raw':
             leaderboard.append(
                 [-(user.raw_rating or -9999), user.username])
         elif arg == 'maxraw':
             leaderboard.append(
                 [-(user.max_raw_rating or -9999), user.username])
     leaderboard.sort()
     content = []
     page = ''
     for i, user in enumerate(leaderboard):
         if user[0] == 9999:
             page += f'{i+1} {user[1]} unrated\n'
         else:
             page += f'{i+1} {user[1]} {-round(user[0],3)}\n'
         if i % 10 == 9:
             content.append(page)
             page = ''
     if page != '':
         content.append(page)
     if len(content) == 0:
         content.append('No users')
     message = await ctx.send(embed=discord.Embed().add_field(
         name='Top DMOJ ' + arg, value=content[0]))
     await scroll_embed(ctx, self.bot, message, 'Top DMOJ ' + arg, content)
Exemple #23
0
async def rate_contest(contest):
    users = []
    for ranking in contest.rankings:
        if all(not solution for solution in ranking['solutions']) and not contest.rate_all or \
            (contest.rating_floor or -1e9) > (ranking['old_rating'] or RATING_INIT) or\
                (contest.rating_ceiling or 1e9) < (ranking['old_rating'] or RATING_INIT):
            continue
        users.append(ranking)
    usernames = [user['user'] for user in users]
    ranks = list(
        tie_ranker(users,
                   key=itemgetter('score', 'cumulative_time', 'tiebreaker')))
    old_ratings = []
    raw_ratings = []
    counts = []
    perfs = []
    query = Query()
    for username in usernames:
        user = session.query(User_DB).filter(User_DB.username == username)
        user = user[0] if user.count() else await query.get_user(username)
        raw_ratings.append(user.raw_rating or MEAN_INIT)
        old_ratings.append(user.rating or RATING_INIT)
        perf = []
        if user._contests:
            for contest in user._contests:
                if contest['performance']:
                    perf.append(contest['performance'])
        counts.append(len(perf))
        perfs.append(perf)

    rating, new_raw, performance = recalculate_ratings(ranks, raw_ratings,
                                                       counts, perfs)
    predictions = {}
    for name, a, b, c, d in zip(usernames, ranks, old_ratings, rating,
                                performance):
        predictions[name] = {
            'rank': a,
            'old_rating': b,
            'new_rating': c,
            'rating_change': c - (b or RATING_INIT),
            'performance': d,
        }
    return predictions
Exemple #24
0
    async def stats(self, ctx):
        """Display cool dmoj stats that no one asked for"""
        problems = session.query(Problem_DB.points)\
            .order_by(Problem_DB.points.desc()).all()

        def tuple_first(data):
            return data[0]

        def calculate_points(points, fully_solved):
            b = 150 * (1 - 0.997**fully_solved)
            p = 0
            for i in range(min(100, len(points))):
                p += (0.95**i) * points[i]
            return b + p

        problems = list(map(tuple_first, problems))
        total_problems = len(problems)
        total_points = calculate_points(problems, total_problems)
        await ctx.send("The theoretical maximum number of points you can achieve is %.2f\n"
                       "There are %d public problems on DMOJ" % (total_points, total_problems))
Exemple #25
0
async def top(ctx):
    """Shows registered server members in ranked order"""
    arg = ctx.options.arg.lower()
    if arg != "rating" and arg != "maxrating" and arg != "points" and arg != "solved":
        return await ctx.respond_help("top")
    users = (
        session.query(User_DB)
        .join(Handle_DB, Handle_DB.handle == User_DB.username)
        .filter(Handle_DB.guild_id == ctx.get_guild().id)
    )
    leaderboard = []
    for user in users:
        if arg == "rating":
            leaderboard.append([-(user.rating or -9999), user.username])
        elif arg == "maxrating":
            leaderboard.append([-(user.max_rating or -9999), user.username])
        elif arg == "points":
            leaderboard.append([-user.performance_points, user.username])
        elif arg == "solved":
            leaderboard.append([-(user.problem_count or 0), user.username])
    leaderboard.sort()
    pag = lightbulb.utils.EmbedPaginator()
    for i, user in enumerate(leaderboard):
        if (arg == "rating" or arg == "maxrating") and user[0] == 9999:
            pag.add_line(f"{i+1} {user[1]} unrated")
        else:
            pag.add_line(f"{i+1} {user[1]} {-round(user[0],3)}")

    if len(leaderboard) == 0:
        pag.add_line("No users")

    @pag.embed_factory()
    def build_embed(page_index, content):
        return hikari.Embed().add_field(name="Top DMOJ " + arg, value=content)

    navigator = nav.ButtonNavigator(pag.build_pages())
    await navigator.run(ctx)
Exemple #26
0
async def vc(ctx):
    usernames = ctx.options.usernames
    print(usernames)
    query = Query()
    if usernames == []:
        username = query.get_handle(ctx.author.id, ctx.get_guild().id)
        if username:
            usernames = [username]

    users = await asyncio.gather(
        *[query.get_user(username) for username in usernames])
    usernames = [user.username for user in users]
    for i in range(len(users)):
        if users[i] is None:
            return await ctx.respond(f"{usernames[i]} does not exist on DMOJ")

    q = session.query(Contest_DB)
    for user in users:
        # if the user has attempted any problems from the problem set
        sub_q = (session.query(Submission_DB, func.max(
            Submission_DB.points)).filter(
                Submission_DB._user == user.username).group_by(
                    Submission_DB._code).subquery())
        sub_q = (session.query(Problem_DB.code).join(
            sub_q, Problem_DB.code == sub_q.c._code,
            isouter=True).filter(func.ifnull(sub_q.c.points, 0) != 0))
        sub_q = list(map(itemgetter(0), sub_q.all()))
        q = (q.filter(not_(Contest_DB.rankings.contains(
            user.username))).filter(
                ~Contest_DB.problems.any(Problem_DB.code.in_(sub_q))).filter(
                    Contest_DB.is_private == 0).filter(
                        Contest_DB.is_organization_private == 0))

    if q.count() == 0:
        await ctx.respond("Cannot find any contests which "
                          "all users have not done")
        return

    contests = q.all()

    while True:
        contest = random.choice(contests)
        try:
            contest = await query.get_contest(contest.key, cached=False)
            break
        except ObjectNotFound:
            pass

    # When problems are private, it says there are no problems
    window = "No"
    is_rated = "Not Rated"
    if contest.time_limit:
        window = f"{round(contest.time_limit/60/60, 2)} Hr"
    if contest.is_rated:
        is_rated = "Rated"
    embed = hikari.Embed(
        title=contest.name,
        url=f"https://dmoj.ca/contest/{contest.key}",
        description=
        f"{window} window | {len(contest.problems)} Problems | {is_rated}",
        color=0xFCDB05,
    )
    await ctx.respond(embed=embed)
Exemple #27
0
async def predict(ctx):
    username = ctx.options.username
    amounts = ctx.options.point_vals

    query = Query()
    username = username or query.get_handle(ctx.author.id, ctx.get_guild().id)
    if username is None and len(amounts) > 0:
        username = str([0])
        amounts.pop(0)

    if amounts == []:
        return await ctx.respond("No points given!")

    if username is None:
        return await ctx.respond("No username given!")

    username = username.replace("'", "")

    amounts = amounts[:10]

    user = await query.get_user(username)
    if user is None:
        return await ctx.respond(f"{username} does not exist on DMOJ")

    username = user.username
    q = (session.query(Submission_DB).options(orm.joinedload("problem")).join(
        User_DB, User_DB.username == Submission_DB._user,
        aliased=True).filter(User_DB.username == user.username))

    if q.count():
        submissions = q.all()
        msg = None
    else:
        await ctx.respond("No submissions cached, "
                          "Please use +cache or /cache to get new submissions")
        return

    problems_ACed = dict()
    code_to_points = dict()
    for submission in submissions:
        code = submission.problem[0].code
        points = submission.points
        result = submission.result

        if points is not None:
            if result == "AC":
                problems_ACed[code] = 1
            if code not in code_to_points:
                code_to_points[code] = points
            elif points > code_to_points[code]:
                code_to_points[code] = points

    fully_solved = len(problems_ACed)
    points = list(code_to_points.values())
    points.sort(reverse=True)

    embed = hikari.Embed(
        title=f"Point prediction for {username}",
        description="Current points: %.2fp" %
        calculate_points(points, fully_solved),
        color=0xFCDB05,
    )

    embed.set_thumbnail(await query.get_pfp(username))

    for predict_val in amounts:
        points.append(int(predict_val))
        fully_solved += 1
        points.sort(reverse=True)
        updated_points = calculate_points(points, fully_solved)
        embed.add_field(
            name="Solve another %sp" % predict_val,
            value="Total points: %.2fp" % updated_points,
            inline=False,
        )

    if msg:
        await msg.delete()
    await ctx.respond(embed=embed)
    return
Exemple #28
0
    async def ranklist(self, ctx, key):
        """List rating predictions of a contest"""
        q = session.query(Contest_DB).filter(Contest_DB.key == key)
        # Clear cache
        if q.count():
            q.delete()
            session.commit()
        query = Query()
        try:
            contest = await query.get_contest(key)
        except ObjectNotFound:
            await ctx.send("Contest not found")
            return

        q = session.query(Handle_DB).filter(Handle_DB.guild_id == ctx.guild.id)
        handles = q.all()

        def to_handle(handle):
            return handle.handle

        usernames = list(map(to_handle, handles))

        # The only way to calculate rating changes is by getting the volitility of all the users
        # that means 100+ seperate api calls
        # How does evan do it?
        import requests
        r = requests.get(f"https://evanzhang.ca/rating/contest/{key}/api")
        rankings = r.json()["users"]

        # Don't really need this, just sanity check
        # users = await asyncio.gather(*[query.get_user(username)
        #                              for username in users])
        # usernames = [user.username for user in users]
        # Filter for those who participated in contest
        usernames = list(set(usernames) & set(rankings.keys()))

        # The length is 0 is contest is still ongoing
        problems = len(contest.problems)
        print(usernames)
        data = []
        for ranking in contest.rankings:
            for username in usernames:
                if ranking["user"] == username:
                    evan_ranking = rankings[username]
                    rank = {
                        "rank": int(evan_ranking["rank"]),
                        "username": username + ":",
                        "old_rating": evan_ranking["old_rating"],
                        "new_rating": evan_ranking["new_rating"],
                    }
                    if evan_ranking["rating_change"] > 0:
                        rank["rating_change"] = "+" + str(
                            evan_ranking["rating_change"])
                    else:
                        rank["rating_change"] = evan_ranking["rating_change"]
                    # This is a quick fix :>
                    problems = len(ranking["solutions"])
                    for i in range(1, problems + 1):
                        solution = ranking["solutions"][i - 1]
                        if solution:
                            rank[str(i)] = int(solution["points"])
                        else:
                            rank[str(i)] = 0
                    data.append(rank)
        max_len = {}
        max_len["rank"] = len("#")
        max_len["username"] = len("Handle")

        for rank in data:
            for k, v in rank.items():
                max_len[k] = max(len(str(v)), max_len.get(k, 0))

        format_output = "{:>" + str(max_len["rank"]) + "} "
        format_output += "{:" + str(max_len["username"] + 1) + "}  "
        for i in range(1, problems + 1):
            format_output += "{:" + str(max_len[str(i)]) + "} "

        format_output += " "
        format_output += "{:>" + str(max_len["rating_change"]) + "}  "
        format_output += "{:" + str(max_len["old_rating"]) + "} "
        format_output += "{:" + str(max_len["new_rating"]) + "} "
        output = format_output.format(
            "#", "Handle", *[str(i) for i in range(1, problems + 1)], "∆",
            "Old", "New")
        output += "\n"
        hyphens = format_output.format(
            "—" * max_len["rank"],
            "—" * max_len["username"],
            *["—" * max_len[str(i)] for i in range(1, problems + 1)],
            "—" * max_len["rating_change"],
            "—" * max_len["old_rating"],
            "—" * max_len["new_rating"],
        )
        output += hyphens
        output += "\n"
        for rank in data:
            output += format_output.format(
                rank["rank"],
                rank["username"],
                *[rank[str(i)] for i in range(1, problems + 1)],
                rank["rating_change"],
                rank["old_rating"],
                rank["new_rating"],
            )
            output += "\n"
        output += hyphens
        output += "\n"
        await ctx.send("```yaml\n" + output + "```")
Exemple #29
0
 def get_random_problem(self, low=1, high=10):
     q = session.query(Problem_DB)\
         .filter(Problem_DB.points.between(low, high))\
         .order_by(func.random()).limit(1)
     if q.count():
         return q.first()
Exemple #30
0
 def get_handle_user(self, handle, guild_id):
     q = session.query(Handle_DB).\
         filter(Handle_DB.handle == handle).\
         filter(Handle_DB.guild_id == guild_id)
     if q.count():
         return q.first().id