Commit 5f62f1dd authored by Jeremy Pallats's avatar Jeremy Pallats 💬
Browse files

Change to querying exact lists over resorting.

parent 0e341d70
Loading
Loading
Loading
Loading
Loading
+25 −17
Original line number Diff line number Diff line
@@ -300,27 +300,26 @@ class Admin(Action):
        """ Schedule all sheets for update. """
        cycle = cog.util.get_config("scanners", "hudson_cattle", "page", default="Cycle Unknown")
        prefix = "__Top Merits for {}__\n\n".format(cycle)
        parts = []

        all_dusers = cogdb.query.all_discord_with_merits(self.session)
        exclude_roles = ["FRC Leadership", "Special Agent"]
        parts = []

        dusers = reversed(sorted(all_dusers, key=lambda x: x.total_merits))
        top_recruits, top_members = dusers_topn(self.msg.guild, dusers, exclude_roles, limit=self.args.limit)
        top_all = cogdb.query.users_with_all_merits(self.session)
        top_recruits, top_members = filter_top_dusers(self.msg.guild, top_all, exclude_roles, limit=self.args.limit)
        lines = [["Top {} Recruits".format(limit), "Merits", "Top {} Members".format(limit), "Merits"]]
        lines += [[rec.display_name, rec.total_merits, mem.display_name, mem.total_merits] for rec, mem in zip(top_recruits, top_members)]
        lines += [[rec[0], rec[1], mem[0], mem[1]] for rec, mem in zip(top_recruits, top_members)]
        lines += zip(top_recruits, top_members)
        parts += cog.tbl.format_table(lines, header=True, prefix=prefix, suffix="\n\n")

        dusers = reversed(sorted(all_dusers, key=lambda x: x.total_fort_merits))
        top_recruits, top_members = dusers_topn(self.msg.guild, dusers, exclude_roles, limit=self.args.limit)
        top_fort = cogdb.query.users_with_fort_merits(self.session)
        top_recruits, top_members = filter_top_dusers(self.msg.guild, top_fort, exclude_roles, limit=self.args.limit)
        lines = [["Top {} Fort Recruits".format(limit), "Merits", "Top Fort {} Members".format(limit), "Merits"]]
        lines += [[rec.display_name, rec.total_fort_merits, mem.display_name, mem.total_fort_merits] for rec, mem in zip(top_recruits, top_members)]
        lines += [[rec[0], rec[1], mem[0], mem[1]] for rec, mem in zip(top_recruits, top_members)]
        parts += cog.tbl.format_table(lines, header=True, suffix="\n\n")

        dusers = reversed(sorted(all_dusers, key=lambda x: x.total_um_merits))
        top_recruits, top_members = dusers_topn(self.msg.guild, dusers, exclude_roles, limit=self.args.limit)
        top_um = cogdb.query.users_with_um_merits(self.session)
        top_recruits, top_members = filter_top_dusers(self.msg.guild, top_um, exclude_roles, limit=self.args.limit)
        lines = [["Top {} UM Recruits".format(limit), "Merits", "Top UM {} Members".format(limit), "Merits"]]
        lines += [[rec.display_name, rec.total_um_merits, mem.display_name, mem.total_um_merits] for rec, mem in zip(top_recruits, top_members)]
        lines += [[rec[0], rec[1], mem[0], mem[1]] for rec, mem in zip(top_recruits, top_members)]
        parts += cog.tbl.format_table(lines, header=True, suffix="\n\n")

        for part in cog.util.merge_msgs_to_least(parts):
@@ -1724,21 +1723,23 @@ def process_system_args(args):
    return system_names.split(',')


def dusers_topn(guild, dusers, exclude_roles, limit=5):
def filter_top_dusers(guild, dusers, exclude_roles, limit=5):
    """
    Generate a top N list from existing DiscordUser list.

    Args:
        guild: The guild to query and get member roles from.
        dusers: The DiscordUser list.
        dusers: The DiscordUser list, of form [[DiscordUser, merits]]
        exclude_roles: List of roles to exclude.
        limit: The top limit will be generated. Default 5.

    Returns: [top_recruits, top_members]
        top_recruits: A list of form [[name, merits], [name, merits]].
        top_members: A list of form [[name, merits], [name, merits]].
    """
    top_recruits, top_members = [], []

    for duser in dusers:
    for duser, merits in dusers:
        member = guild.get_member(duser.id)
        if not member:
            continue  # User left or wrong discord id
@@ -1748,13 +1749,20 @@ def dusers_topn(guild, dusers, exclude_roles, limit=5):
        if len(list(set(role_names) - set(exclude_roles))) != len(role_names):
            continue
        if "FRC Member" in role_names and len(top_members) != limit:
            top_members += [duser]
            top_members += [(duser.pref_name, merits)]
        elif "FRC Recruit" in role_names and len(top_recruits) != limit:
            top_recruits += [duser]
            top_recruits += [(duser.pref_name, merits)]

        if len(top_recruits) == limit and len(top_members) == limit:
            break

    while len(top_recruits) != limit:
        print(top_recruits)
        top_recruits += [('', '')]
    while len(top_members) != limit:
        print(top_members)
        top_members += [('', '')]

    return top_recruits, top_members


+50 −3
Original line number Diff line number Diff line
@@ -6,8 +6,10 @@ import logging
import os
import tempfile

import sqlalchemy as sqla
import sqlalchemy.exc as sqla_exc
import sqlalchemy.orm.exc as sqla_oexc
from sqlalchemy.sql.expression import or_

import cog.exc
import cog.sheets
@@ -99,11 +101,56 @@ def add_duser(session, member):
    return new_duser


def all_discord_with_merits(session):
def users_with_all_merits(session):
    """
    Return the list of all current discord users that have this cycle merits.
    Query and return the list of DiscordUsers by their total merit contributions.

    Args:
        session: A session onto the database.

    Returns:
        A list of objects of form: [[DiscordUser, total_merits], ... ]
    """
    return session.query(DiscordUser, (FortUser.dropped + UMUser.combo).label("total_merits")).\
        join(FortUser, FortUser.name == DiscordUser.pref_name).\
        join(UMUser, UMUser.name == DiscordUser.pref_name).\
        filter(or_(FortUser.dropped > 0, UMUser.combo > 0)).\
        order_by(sqla.desc("total_merits")).\
        all()


def users_with_fort_merits(session):
    """
    Query and return the list of DiscordUsers by their total fort contributions.

    Args:
        session: A session onto the database.

    Returns:
        A list of objects of form: [[DiscordUser, fort_merits], ... ]
    """
    return session.query(DiscordUser, FortUser.dropped).\
        join(FortUser, FortUser.name == DiscordUser.pref_name).\
        filter(FortUser.dropped > 0).\
        order_by(FortUser.dropped.desc()).\
        all()


def users_with_um_merits(session):
    """
    return [x for x in session.query(DiscordUser).all() if x.total_merits]
    Query and return the list of DiscordUsers by their total undermining contributions.

    Args:
        session: A session onto the database.

    Returns:
        A list of objects of form: [[DiscordUser, um_merits], ... ]
    """
    return session.query(DiscordUser, UMUser.combo).\
        join(UMUser, UMUser.name == DiscordUser.pref_name).\
        filter(UMUser.combo > 0).\
        order_by(UMUser.combo.desc()).\
        all()


def check_pref_name(session, new_name):
+16 −22
Original line number Diff line number Diff line
@@ -86,30 +86,10 @@ class DiscordUser(Base):
        """ Mention this user in a response. """
        return "<@{}>".format(self.id)

    @property
    @hybrid_property
    def total_merits(self):
        """ The total merits a user has done this cycle. """
        return self.total_fort_merits + self.total_um_merits

    @property
    def total_fort_merits(self):
        """ The total merits a user has done forting this cycle. """
        try:
            total = self.fort_user.dropped
        except AttributeError:
            total = 0

        return total

    @property
    def total_um_merits(self):
        """ The total merits a user has done undermining cycle. """
        try:
            total = self.um_user.held + self.um_user.redeemed
        except AttributeError:
            total = 0

        return total
        return self.fort_user.dropped + self.um_user.combo


class FortUser(Base):
@@ -522,6 +502,20 @@ class UMUser(Base):
            where(UMHold.user_id == self.id).\
            label('redeemed')

    @hybrid_property
    def combo(self):
        """ Total merits undermined by this cmdr. """
        return self.held + self.redeemed

    @combo.expression
    def combo_exp(self):
        """ Total merits undermined by this cmdr. """
        return sqla.func.sum(
            sqla.select([sqla.func.sum(UMHold.redeemed, UMHold.held)]).
            where(UMHold.user_id == self.id).
            label('combo')
        )

    def __repr__(self):
        keys = ['id', 'name', 'row', 'cry']
        kwargs = ['{}={!r}'.format(key, getattr(self, key)) for key in keys]
+0 −22
Original line number Diff line number Diff line
@@ -1362,25 +1362,3 @@ async def test_cmd_near_if(f_bot):

    actual = str(f_bot.send_message.call_args).replace("\\n", "\n")
    assert "LHS 397        | 19.32    | [L] Zillig Depot" in actual


@pytest.mark.asyncio
async def test_dusers_topn(f_dusers, f_um_testbed, f_fort_testbed):
    recruit_role = tc.Role("FRC Recruit")
    member_role = tc.Role("FRC Member")
    ignore_role = tc.Role("Nothing")
    role_map = {
        1: tc.Member("Test1", [recruit_role, ignore_role]),
        2: tc.Member("Test2", [member_role, ignore_role]),
        3: tc.Member("Test3", [recruit_role]),
    }

    def get_member_(id):
        return role_map[id]

    fguild = aiomock.Mock()
    fguild.get_member = get_member_

    rec, mem = cog.actions.dusers_topn(fguild, f_dusers, [], limit=5)
    assert rec[1].id == 3
    assert mem[0].id == 2
+18 −0
Original line number Diff line number Diff line
@@ -626,3 +626,21 @@ def test_track_ids_newer_than(session, f_track_testbed):
    date = datetime.datetime(year=2000, month=1, day=10, hour=0, minute=0, second=0, microsecond=0)
    objs = cogdb.query.track_ids_newer_than(session, date)
    assert sorted([x.id for x in objs]) == ["J3N-53B", "OVE-111"]


def test_users_with_all_merits(session, f_dusers, f_fort_testbed, f_um_testbed):
    cap = cogdb.query.users_with_all_merits(session)
    assert [x[0].display_name for x in cap] == ["User1", "User2"]
    assert [x[1] for x in cap] == [15050, 8050]


def test_users_with_fort_merits(session, f_dusers, f_fort_testbed, f_um_testbed):
    cap = cogdb.query.users_with_fort_merits(session)
    assert [x[0].display_name for x in cap] == ["User2", "User3", "User1"]
    assert [x[1] for x in cap] == [2000, 1800, 1100]


def test_users_with_um_merits(session, f_dusers, f_fort_testbed, f_um_testbed):
    cap = cogdb.query.users_with_um_merits(session)
    assert [x[0].display_name for x in cap] == ["User1", "User2"]
    assert [x[1] for x in cap] == [13950, 6050]
Loading