async def update_usernames(self):
        async with (await self.bot.get_db_conn()).acquire() as connection:
            users_service = user_data_service.UserDataService(connection)
            users = await users_service.get_all_users()

            try:
                guild = await self.bot.fetch_guild(os.getenv('SERVER_ID'))
                guild_members = dict(
                    map(lambda x: (str(x.id), x.name), await
                        guild.fetch_members(limit=10000).flatten()))
            except:
                print(
                    'Warning: Could not update usernames - unable to fetch guild members.'
                )
                return

            for user in users:
                user_id = user[0]
                stored_username = user[1]

                if user_id not in guild_members.keys():
                    # print(f'user {stored_username} ({user_id}) not in guild')
                    continue

                actual_username = guild_members[user_id]

                if stored_username == actual_username:
                    continue

                await users_service.set_username(user_id, actual_username)

                print(
                    f'Updated username for user {user_id}: {stored_username} -> {actual_username}'
                )
Exemple #2
0
    async def get_rank(self,
                       ctx: SlashContext,
                       user: str,
                       programme: str = None,
                       year: int = constants.current_year):
        async with (await self.bot.get_db_conn()).acquire() as connection:
            user_id = re.sub(r"[<@!>]", "", user)

            if not user_id.isdigit():
                await ctx.send(ctx.author.mention + "Invalid user")
                return

            users = user_data_service.UserDataService(connection)
            res = await users.get_user_rank(user_id)

            if res:
                # Filter by programme
                if programme:
                    res = filter(
                        lambda x: True if x[3] == programme else False, res)

                # Filter by year
                res = filter(lambda x: True if x[2] == year else False, res)
                # Filter by is_private
                res = list(filter(lambda x: True if not x[0] else False, res))

                res.sort(key=lambda x: x[1])

                res = "\n".join(
                    map(lambda x: f"Rank: {x[1]} in {x[3]} {x[2]}", res))

                if len(res) == 0:
                    await ctx.send(
                        ctx.author.mention +
                        f" User {user} does not have recorded data that matches your filters"
                    )
                else:
                    await ctx.send(ctx.author.mention + f"User {user}: \n" +
                                   res)

            else:
                await ctx.send(
                    ctx.author.mention +
                    f"User {user} is either invalid or has not posted his ranking number."
                )

        return None
Exemple #3
0
    async def setrank(self,
                      ctx: SlashContext,
                      rank: int,
                      programme: str,
                      year: int = None):
        user = ctx.author
        user_id = str(user.id)

        if year is None:
            year = constants.current_year

        async with (await self.bot.get_db_conn()).acquire() as connection:
            ranks = ranks_service.RanksService(connection)
            users = user_data_service.UserDataService(connection)

            tr = connection.transaction()
            await tr.start()

            try:
                try:
                    await ranks.add_rank(rank,
                                         programme,
                                         year,
                                         user_id,
                                         source='command')
                except ValueError:
                    await ctx.send(user.mention +
                                   ' Invalid command arguments.')
                    await tr.rollback()
                    return
                except EntryAlreadyExistsError:
                    await ctx.send(
                        user.mention +
                        ' You have already set your ranking number. To set a different one, '
                        'clear it using `/clearrank` and try setting it again.'
                    )
                    await tr.rollback()
                    return

                await users.add_user(user_id, user.name)

            except:
                await tr.rollback()
                raise

            await tr.commit()
        await ctx.send(user.mention + ' Rank set.')
Exemple #4
0
    async def get_rank(self,
                       ctx: SlashContext,
                       user: discord.User,
                       programme: str = None,
                       year: int = None):
        user_id = str(user.id)

        async with (await self.bot.get_db_conn()).acquire() as connection:
            users = user_data_service.UserDataService(connection)
            res = await users.get_user_ranks(user_id)

        if res:
            # Filter by is_private
            res = filter(lambda x: not x[0], res)

            # Filter by programme
            if programme:
                res = filter(lambda x: x[3] == programme, res)

            # Filter by year
            if year:
                res = filter(lambda x: x[2] == year, res)

            res = list(res)

            res.sort(key=lambda x: x[1])

            res = "\n".join(
                map(
                    lambda x: f"Rank {x[1]} in "
                    f"{programmes_helper.programmes[x[3]].uni_name} "
                    f"{programmes_helper.programmes[x[3]].display_name} "
                    f"{x[2]}", res))

        if res:
            await ctx.send(
                f"User {user} has shared the following public ranking numbers:\n\n"
                + res)
        else:
            await ctx.send(
                f"User {user} does not have public data that matches your filters"
            )
Exemple #5
0
    async def setrank(self,
                      ctx: SlashContext,
                      rank: int,
                      programme: str,
                      year: int = None):
        user = ctx.author
        user_id = str(user.id)

        if year is None:
            year = constants.current_year

        async with (await self.bot.get_db_conn()).acquire() as connection:
            ranks = ranks_service.RanksService(connection)
            users = user_data_service.UserDataService(connection)

            curr_rank_details = await ranks.get_rank_details_for_programme_and_user(
                programme, year, user_id)

            if curr_rank_details:
                curr_rank, curr_is_private = curr_rank_details
                if rank == curr_rank:
                    if not curr_is_private:
                        await ctx.send(
                            user.mention +
                            ' You have already set your ranking number. It can be seen via `/ranks`. '
                            'If you\'re trying to set an offer date, use `/setofferdate`.'
                        )
                    else:
                        await ranks.set_is_private_programme(
                            user_id, False, programme, year)
                        await ctx.send(
                            user.mention +
                            ' You have already set your ranking number, but it was private (you\'ve likely '
                            'set it by replying to a direct message by the bot).\nIt has now been made '
                            'visible and you can see it via `/ranks`. '
                            'If you want to make it private again, you can use `/toggleprivaterank`.\n'
                            'If you\'re trying to set an offer date, you can use `/setofferdate`.'
                        )
                else:
                    if not curr_is_private:
                        await ctx.send(
                            user.mention +
                            f' You have already set a different ranking number (**{curr_rank}**). '
                            'To change it, first clear the old one using `/clearrank` and then try setting '
                            'the new one again.')
                    else:
                        await ctx.send(
                            user.mention +
                            ' You have already set a different ranking number, but it is private (you\'ve '
                            'likely set it by replying to a direct message by the bot). '
                            'To change it, first clear the old one using `/clearrank` and then try setting '
                            'the new one again.')

                return

            tr = connection.transaction()
            await tr.start()

            try:
                try:
                    await ranks.add_rank(rank,
                                         programme,
                                         year,
                                         user_id,
                                         source='command')
                except ValueError:
                    await ctx.send(user.mention +
                                   ' Invalid command arguments.')
                    await tr.rollback()
                    return
                except EntryAlreadyExistsError:
                    await ctx.send(
                        user.mention +
                        ' You have already set your ranking number. To set a different one, '
                        'clear it using `/clearrank` and try setting it again.'
                    )
                    await tr.rollback()
                    return

                await users.add_user(user_id, user.name)

            except:
                await tr.rollback()
                raise

            await tr.commit()
        await ctx.send(
            user.mention +
            ' Your ranking number was successfully added. It is now set as public and can be '
            'seen via `/ranks`. If you want to make it private, you can use '
            '`/toggleprivaterank.`\n'
            'If you have received an offer, please use `/setofferdate` to set it.'
        )
Exemple #6
0
    async def updateusernames(self, ctx, dry_run: str = None):

        if ctx.message.author.id != constants.administrator_user_id or not ctx.guild:
            await ctx.send(
                ctx.message.author.mention +
                ' You don\'t have permission to execute this command')
            return

        save_changes = True

        if dry_run is not None:
            if dry_run == 'dry-run':
                save_changes = False
                await ctx.send(ctx.message.author.mention +
                               f' dry run: not saving changes to database')
            else:
                raise commands.UserInputError

        async with (await self.bot.get_db_conn()).acquire() as connection:
            users_service = user_data_service.UserDataService(connection)
            users = await users_service.get_all_users()

            guild_members = dict(
                map(lambda x: (str(x.id), x.name), ctx.guild.members))

            await ctx.send(ctx.message.author.mention +
                           f' Checking {len(users)} users...')

            results = {'success': [], 'user-not-found': []}

            for user in users:
                user_id = user[0]
                stored_username = user[1]

                if user_id not in guild_members.keys():
                    results['user-not-found'].append(
                        f'{stored_username} ({user_id})')
                    continue

                actual_username = guild_members[user_id]

                if stored_username == actual_username:
                    continue

                if save_changes:
                    await users_service.set_username(user_id, actual_username)

                results['success'].append(
                    f'{stored_username} → {actual_username}')

        await ctx.send(ctx.message.author.mention + ' Done checking users, '
                       f'{len(results["success"])} usernames updated')

        results_embed = discord.Embed(title=f".updateusernames results",
                                      color=0x36bee6)

        for result in results.keys():
            user_string = '\n'.join(
                f'`{u}`'
                for u in results[result]) if results[result] else '_None_'
            results_embed.add_field(name=result,
                                    value=user_string,
                                    inline=True)

        if dry_run:
            results_embed.set_footer(
                text='dry run: not saving changes to database')

        dm_channel = await ctx.message.author.create_dm()
        await dm_channel.send(embed=results_embed)
Exemple #7
0
    async def import_ranks_from_csv(self, csv_data: str, source: str,
                                    all_members):
        """
        This method imports data into "ranks" in the following format:
        programme_id(str),ranking_number(int),offer_date(dd/mm),year(int),is_private(bool),discord_tag(Optional;User#1234)
        :param csv_data: The rows of the CSV, separated by '\n'
        :param all_members: bot.get_all_members()
        :param source: data source
        :return: None
        """

        ranks = ranks_service.RanksService(self.db_conn)
        users = user_data_service.UserDataService(self.db_conn)

        rows = csv_data.split('\n')

        i = 0
        skipped = 0
        unknown_user = 0
        inserted = 0
        for row_str in rows:
            if len(row_str) == 0:
                continue

            try:
                i += 1
                row = row_str.split(',')

                programme = programmes_helper.programmes[row[0]]
                ranking_number = int(row[1])
                offer_date_arr = row[2].split('/')
                offer_date = offer_date_util.parse_offer_date(
                    offer_date_arr[0], offer_date_arr[1])
                year = int(row[3])
                is_private = row[4].lower() == 'true'
                discord_tag = row[5].split('#') if len(row[4]) > 0 else None

                # If the ranking number is below the programme limit and discord_tag is null, do not import it
                if ranking_number <= programme.places[
                        year] and discord_tag is None:
                    skipped += 1
                    continue

                discord_id = None

                # If discord_tag is not null, try to find the user id using the Discord API
                if discord_tag is not None:
                    user = get(all_members,
                               name=discord_tag[0],
                               discriminator=discord_tag[1])

                    if user:
                        discord_id = str(user.id)
                        await users.add_user(discord_id, user.name)
                    else:
                        print("Can't find user " + row[4])
                        unknown_user += 1
                        if ranking_number <= programme.places[year]:
                            skipped += 1
                            continue

                try:
                    await ranks.add_rank(
                        ranking_number, programme.id, year, discord_id,
                        offer_date if ranking_number > programme.places[year]
                        else None, source, is_private)
                except EntryAlreadyExistsError:
                    skipped += 1
                    continue

                inserted += 1

            except Exception as inner:
                raise RuntimeError(
                    f'An exception occurred while processing row {i}'
                ) from inner

        return inserted, skipped, unknown_user